Weedu.psm1

$script:hostUrl= $null;
$script:sessionVar = $null;
$script:logonUser = $null;

function GetUID($data) {
    return [string]::Join(",", [array]($data | ForEach-Object {
        $member = $_ | Get-Member | Where-Object { $_.Name -in ("idb_uid", "UID", "aktualisierteEntitaeten") } | Select-Object -First 1;
        if ($member) {
            return GetUID($_ | Select-Object -ExpandProperty $member.Name);
        }
        return $_;
    }));
}

function AsString($data) {
    if ($data -is [byte[]]) {
        return [System.Text.Encoding]::UTF8.GetString($data);
    }
    return [string]$data;
}

function New-Sicherheitseinstellung {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, Position = 0)]
        [string]$Dateneigentuemer
    )
    process {
        return [PSCustomObject]@{
            Dateneigentuemer = $Dateneigentuemer;
            Gruppen = @();
        }
    }
}

function Add-SicherheitseinstellungGruppe {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [PSCustomObject]$Sicherheitseinstellung,
        [Parameter(Mandatory, Position = 0)]
        [string]$Gruppe,
        [Parameter(Mandatory, Position = 1)]
        [string]$Stufe
    )
    process {
        $Sicherheitseinstellung.Gruppen += [PSCustomObject]@{
            Gruppe = $Gruppe;
            Stufe = $Stufe;
        };
        return $Sicherheitseinstellung;
    }
}

function Connect-Weedu {
    [CmdletBinding(DefaultParameterSetName = "UsernamePassword")]
    param(
        [Parameter(Mandatory, Position = 0, ParameterSetName = "UsernamePassword")]
        [string]$Benutzername,
        [Parameter(Mandatory, ParameterSetName = "UsernamePassword")]
        [securestring]$Kennwort,
        [Parameter(Mandatory, Position = 0, ParameterSetName = "Credential")]
        [pscredential]$Credential,
        [Parameter(Mandatory, Position = 1, HelpMessage = "Url zu welcher die Verbindung hergestellt werden soll. Bsp.: https://akad-de.test-weedu.ch")]
        [string]$HostUrl
    )
    process{
        if (-not $PSBoundParameters.ContainsKey("Credentials")) {
            $Credential = [System.Management.Automation.PSCredential]::new($Benutzername, $Kennwort);
        }
        $networkCreds = $Credential.GetNetworkCredential();
        $encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$($networkCreds.UserName):$($networkCreds.Password)"));
        $basicAuthValue = "Basic $encodedCreds";

        $Headers = @{
            Accept = "application/json, text/javascript, */*"
            Authorization = $basicAuthValue
        };

        if (-not [regex]::IsMatch($HostUrl, "^[^./\\:]+:")) {
            $HostUrl = "https://"+($HostUrl.TrimStart('/'));
            Write-Verbose "URL mit Protokoll erweitert: $HostUrl";
        }

        try {
            $response = Invoke-WebRequest -Uri "$($HostUrl)/sts/authentication" -Method POST -Headers $Headers -SessionVariable session;
        } catch [System.Net.WebException] {
            if ($_.Exception.Response) {
                $responseStream = $_.Exception.Response.GetResponseStream();
                if ($responseStream.CanSeek) {
                    $responseStream.Seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null;
                }
                $response = [Microsoft.PowerShell.Commands.WebResponseObject]::new($_.Exception.Response, $responseStream);
            } else {
                $response = [PSCustomObject]@{
                    StatusDescription = $_.Exception.Message;
                    Headers = @{};
                };
            }
        }

        $script:logonUser = $response.Headers["X-Weedu-Portal-UserId"];

        if($response.StatusCode -eq 200 -and $script:logonUser) {
            $script:sessionVar = $session;
            $script:hostUrl = $HostUrl.TrimEnd("/");
            Write-Information "Login als $script:logonUser an $script:hostUrl erfolgreich" -InformationAction Continue;
        } else {
            $script:sessionVar = $null;
            $script:hostUrl = $null;
            Write-Error "Login Fehlgeschlagen ($($response.StatusDescription)), bitte erneut versuchen" -TargetObject ($response.RawContent) -ErrorAction Stop;
        }
    }
}

function Disconnect-Weedu {
    [CmdletBinding()]
    param()
    process {
        $script:sessionVar = $null;
        $script:hostUrl = $null;
    }
}

function Invoke-Weedu {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, HelpMessage = "URI ohne Host f�r Weedu-Request, kann Platzhalter enthalten: {0}")]
        [string]$Uri,
        [Parameter(HelpMessage = "Ersetzungen f�r Platzhalter der URI")]
        [object[]]$UriParams,
        [string]$Method = "GET",
        [object]$Body,
        [switch]$StatusOnly
    )
    process{
        if (!$script:sessionVar) {
            Write-Error "Weedu Anmeldung ist erforderlich: Connect-Weedu" -ErrorAction Stop;
            break;
        }
        if ($UriParams) {
            $Uri = [string]::Format($Uri, [array]($UriParams | ForEach-Object { if ([string]::IsNullOrEmpty($_)) { "" } else { [uri]::EscapeDataString([System.Convert]::ToString($_, [cultureinfo]::InvariantCulture)) }}));
        }
        $Uri = "$($script:hostUrl)/$($Uri.TrimStart("/"))";

        $Headers = @{
            Accept = "application/json, text/javascript, */*"
        }

        try {
            if ($Body) {
                $Body = ($Body | ConvertTo-JSON -Compress -Depth 100);
                Write-Verbose "Weedu-Request Body: $Body";
                $response = Invoke-WebRequest -Uri $Uri -Method $Method -Headers $Headers -WebSession $script:sessionVar -Body ([System.Text.Encoding]::UTF8.GetBytes($Body)) -ContentType "application/json; charset=utf-8";
            } else {
                $response = Invoke-WebRequest -Uri $Uri -Method $Method -Headers $Headers -WebSession $script:sessionVar;
            }
        } catch [System.Net.WebException] {
            $responseStream = $_.Exception.Response.GetResponseStream();
            if ($responseStream.CanSeek) {
                $responseStream.Seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null;
            }
            $response = [Microsoft.PowerShell.Commands.WebResponseObject]::new([System.Net.WebResponse]($_.Exception.Response), $responseStream);
        }
        Write-Verbose "Weedu-Request: $Method $Uri => $($response.StatusCode) $($response.StatusDescription)";

        if ($StatusOnly) {
            $result = $response.StatusCode;
        } else {
            $responseContentType = $response.Headers.'Content-Type'.Split(";")[0].Trim();
            
            switch ($responseContentType) {
                "application/json" {
                    $result = (AsString($response.Content)) | ConvertFrom-Json;
                }
                "text/xml" {
                    $result = [xml](AsString($response.Content));
                }
                default {
                    $result = $response.Content;
                }
            }

            if ($response.StatusCode -ge 300) {
                Write-Warning -Message $result;
                Write-Error -Message "Weedu-Request $Method $Uri nicht erfolgreich: $($response.StatusCode) $($response.StatusDescription)" -TargetObject $result -ErrorAction Stop;
                break;
            }
        }
        return $result;
    }
}

function Invoke-WqlQuery{
    [CmdletBinding(SupportsPaging, DefaultParameterSetName = "Paged")]
    param(
        [Parameter(Mandatory, HelpMessage = "WQL Abfrage. Bsp: Personen [ohne Aktionen]", ValueFromPipeline, Position = 0)]
        [string]$WQLAbfrage,
        [Parameter(HelpMessage = "Default 1", ParameterSetName = "Paged")]
        [int]$Page = 1,
        [Parameter(HelpMessage = "Default 100", ParameterSetName = "Paged")]
        [int]$PageSize = 100,
        [Parameter(HelpMessage = "Default 1", ParameterSetName = "Paged")]
        [int]$PageCount = 1,
        [Parameter(HelpMessage = "Automatisches Paging f�r alle Eintr�ge", ParameterSetName = "Auto")]
        [switch]$Auto
    )
    process{
        if ($Page -le 0) {
            Write-Error "$($Page) ist kein gueltiger Wert fuer Page! Bitte ein Wert groesser 0 eingeben" -ErrorAction Stop;
            break;
        }
        if ($PageSize -le 0) {
            Write-Error "$($PageSize) ist kein gueltiger Wert fuer PageSize! Bitte ein Wert groesser 0 eingeben" -ErrorAction Stop;
            break;
        }
        if ($PageCount -le 0) {
            Write-Error "$($PageCount) ist kein gueltiger Wert fuer PageCount! Bitte ein Wert groesser 0 eingeben" -ErrorAction Stop;
            break;
        }
        $skip = $PSCmdlet.PagingParameters.Skip;
        $first = [math]::Min($PSCmdlet.PagingParameters.First, [int]::MaxValue);
        if ($Auto) {
            $PageSize = [math]::Min(1000, $skip + $first);
            $PageCount = [int]::MaxValue;
        }
        $Page = $Page + [int]([math]::Floor($PSCmdlet.PagingParameters.Skip / $PageSize));
        $PageCount = $PageCount - [int]($PSCmdlet.PagingParameters.Skip / $PageSize);
        if ($PageCount -le 0) {
            Write-Error "Die angegebenen Parameter f�hren immer zu 0 Resultaten";
            break;
        }
        $skip = $skip % $PageSize;
        $result = Invoke-Weedu -Uri "/index/Abfragen/IndexData?auswahlSpalte=false&wql={0}" -UriParams @($WQLAbfrage) -Method POST -Body ([PSCustomObject]@{ page=$Page; pageSize=$PageSize; });
        $count = $result.result.Total;
        if ($count -eq 0 -or $result.result.data.Count -eq 0) {
            Write-Warning "Die Abfrage lieferte keine Ergebnisse"
        } else {
            $elementVon = ($Page - 1) * $PageSize + 1 + $skip;
            $elementBis = [math]::Min($Page * $PageSize * $PageCount, [math]::Min($PSCmdlet.PagingParameters.Skip + $first, $count));
            Write-Information "$elementVon - $elementBis von $($count) Elementen" -InformationAction Continue;
        }
        if ($PSCmdlet.PagingParameters.IncludeTotalCount) {
            Write-Output $PSCmdlet.PagingParameters.NewTotalCount($count, 1.0);
        }
        if ($result.result.data.Count -gt 0) {
            $result.result.data | Select-Object -Skip $skip -First ([Math]::Min($first, [int]::MaxValue));
            $first = $first - ($PageSize - $skip);
            $Page++;
            $PageCount--;
            while (($first -gt 0) -and ($PageCount -gt 0)) {
                $result = Invoke-Weedu -Uri "/index/Abfragen/IndexData?auswahlSpalte=false&wql={0}" -UriParams @($WQLAbfrage) -Method POST -Body ([PSCustomObject]@{ page=$Page; pageSize=$PageSize; });
                $result.result.data | Select-Object -First ([Math]::Min($first, [int]::MaxValue));
                $first = $first - $PageSize;
                $Page++;
                $PageCount--;
            }
        }
    }
}

function Set-PersonenMapping {
    [CmdletBinding(DefaultParameterSetName = "KundennummerExplizit")]
    param(
        [Parameter(Mandatory, HelpMessage = "WeeduUid der Person", ValueFromPipeline)]
        [Guid]$PersonUid,
        [Parameter(Mandatory, HelpMessage = "Nummer des AbacusMandanten")]
        [int]$MandantenNummer,
        [Parameter(ParameterSetName = "KundennummerExplizit")]
        $KundenNummer,
        [Parameter(ParameterSetName = "KundennummerGeneriert")]
        [switch]$GeneriereKundenNummer,
        [Parameter()]
        $AdressNummer,
        [Parameter()]
        $ADLoginName
    )
    process{
        if (-not ($GeneriereKundenNummer -or $PSBoundParameters.ContainsKey("KundenNummer") -or $PSBoundParameters.ContainsKey("AdressNummer") -or $PSBoundParameters.ContainsKey("ADLoginName"))) {
            Write-Error "KundenNummer/AdressNummer/AdLoginName, mindestens ein Parameter ist erforderlich." -ErrorAction Stop;
            return;
        }
        $Body = New-Object PSCustomObject
        if ($PSBoundParameters.ContainsKey("ADLoginName")) {
            $Body | Add-Member NoteProperty ADUserLogonName $ADLoginName;
        }
        if ($PSBoundParameters.ContainsKey("AdressNummer")) {
            $Body | Add-Member NoteProperty AbacusAdressnummer $AdressNummer;
        }
        if ($PSBoundParameters.ContainsKey("KundenNummer")) {
            $Body | Add-Member NoteProperty AbacusKundennummer $KundenNummer;
        }
        if ($GeneriereKundenNummer) {
            $Body | Add-Member NoteProperty GeneriereKundennummer $True;
        }
        return Invoke-Weedu -Uri "/rest/Personen/{0}/AbacusMandanten/{1}" -UriParams @($PersonUid, $MandantenNummer) -Method PUT -Body $Body;
    }
}

function New-IndexRegel {
    [CmdletBinding(DefaultParameterSetName = "File")]
    param(
        [Parameter(Mandatory, HelpMessage = "Uid der Regel, empfohlen ist eine Well-Known-UID: !RegelName", Position = 0)]
        [string]$RegelUid,
        [Parameter(Mandatory, Position = 1)]
        [string]$RegelTyp,
        [Parameter(Mandatory, Position = 2)]
        [string]$Bezeichnung,
        [Parameter(Mandatory, Position = 3)]
        [string]$Beschreibung,
        [Parameter(Mandatory, HelpMessage = "Logik der Regel (Razor), z.B. als Here-String", ParameterSetName = "Content")]
        [string]$Logik,
        [Parameter(Mandatory, HelpMessage = "Logik der Regel (Razor) aus einer Datei", ValueFromPipeline, ParameterSetName = "File")]
        $LogikFile,
        [Parameter(Mandatory, HelpMessage = "Sicherheitseinstellung (erstellt z.B. mit New-Sicherheitseinstellung)", Position = 4)]
        [PSCustomObject]$SicherheitsEinstellung
    )
    process {
        if ($PSBoundParameters.ContainsKey("LogikFile")) {
            $Logik = [string](Get-Content $LogikFile -Encoding UTF8 -Raw);
        }
        $Body = [PSCustomObject]@{
                Uid = GetUID($RegelUid);
                RegelTyp = $RegelTyp;
                Bezeichnung = $Bezeichnung;
                Beschreibung = $Beschreibung;
                Logik = $Logik;
                Sicherheitseinstellung = $SicherheitsEinstellung;
            };
        return Invoke-Weedu -Uri "/rest/Regeln" -UriParams @() -Method POST -Body $Body;
    }
}

function Set-IndexRegel {
    [CmdletBinding(DefaultParameterSetName = "File")]
    param(
        [Parameter(Mandatory, HelpMessage = "Uid der Regel, z.B. eine Well-Known-UID: !RegelName", Position = 0)]
        [string]$RegelUid,
        [Parameter(Position = 1)]
        [string]$Bezeichnung,
        [Parameter(Position = 2)]
        [string]$Beschreibung,
        [Parameter(HelpMessage = "Logik der Regel (Razor), z.B. als Here-String", ParameterSetName = "Content")]
        [string]$Logik,
        [Parameter(HelpMessage = "Logik der Regel (Razor) aus einer Datei", ValueFromPipeline, ParameterSetName = "File")]
        $LogikFile
    )
    process {
        $Body = [PSCustomObject]@{};
        if ($PSBoundParameters.ContainsKey("Bezeichnung")) {
            $Body | Add-Member NoteProperty Bezeichnung $Bezeichnung;
        }
        if ($PSBoundParameters.ContainsKey("Beschreibung")) {
            $Body | Add-Member NoteProperty Beschreibung $Beschreibung;
        }
        if ($PSBoundParameters.ContainsKey("LogikFile")) {
            $Body | Add-Member NoteProperty Logik ([string](Get-Content $LogikFile -Encoding UTF8 -Raw));
        }
        if ($PSBoundParameters.ContainsKey("Logik")) {
            $Body | Add-Member NoteProperty Logik $Logik;
        }
        return Invoke-Weedu -Uri "/rest/Regeln/{0}" -UriParams @(GetUID($RegelUid)) -Method PUT -Body $Body;
    }
}

function Get-IndexRegel {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline, HelpMessage = "Uid der Regel, z.B. eine Well-Known-UID: !RegelName", Position = 0)]
        [string]$RegelUid
    )
    process {
        return Invoke-Weedu -Uri "/rest/Regeln/{0}" -UriParams @(GetUID($RegelUid)) -Method GET;
    }
}

function Invoke-RechnungStorno {
    [CmdletBinding(DefaultParameterSetName = "UID")]
    param(
        [Parameter(Mandatory, HelpMessage = "Uid der Rechnung", ValueFromPipeline, ParameterSetName = "UID", Position = 0)]
        [Guid]$RechnungUid,
        [Parameter(Mandatory, HelpMessage = "Mandantennummer der Rechnung", ValueFromPipelineByPropertyName, ParameterSetName = "Nummer", Position = 0)]
        [int]$MandantenNummer,
        [Parameter(Mandatory, HelpMessage = "Rechnungsnummer", ValueFromPipelineByPropertyName, ParameterSetName = "Nummer", Position = 1)]
        $RechnungsNummer,
        [Parameter(HelpMessage = "Bemerkung zur Stornierung")]
        [string]$Bemerkung
    )
    process{
        $Body = [PSCustomObject]@{
                Bemerkung = $Bemerkung
            };

        if ($RechnungUid) {
            if ($MandantenNummer -or $RechnungsNummer) {
                Write-Error "Bei Angabe einer RechnungsUid kann keine MandantenNummer oder RechnungsNummer angegeben werden" -ErrorAction Stop;
                break;
            }
            return Invoke-Weedu -Uri "/rest/Rechnungen/{0}/Stornieren" -UriParams @($RechnungUid) -Method POST -Body $Body;
        } elseif ([bool]$MandantenNummer -and [bool]$RechnungsNummer) {
            return Invoke-Weedu -Uri "/rest/Rechnungen/{0}/{1}/Stornieren" -UriParams @($MandantenNummer, $RechnungsNummer) -Method POST -Body $Body;
        } else {
            Write-Error "Entweder nur RechnungsUid oder aber MandantenNummer und RechnungsNummer m�ssen angegeben werden" -ErrorAction Stop;
            break;
        }
    }
}

function Invoke-RechnungErneutVerrechnen {
    [CmdletBinding(DefaultParameterSetName = "UID")]
    param(
        [Parameter(Mandatory, HelpMessage = "Uid der Rechnung", ValueFromPipeline, ParameterSetName = "UID", Position = 0)]
        [Guid]$RechnungUid,
        [Parameter(Mandatory, HelpMessage = "Mandantennummer der Rechnung", ValueFromPipelineByPropertyName, ParameterSetName = "Nummer", Position = 0)]
        [int]$MandantenNummer,
        [Parameter(Mandatory, HelpMessage = "Rechnungsnummer", ValueFromPipelineByPropertyName, ParameterSetName = "Nummer", Position = 1)]
        $RechnungsNummer,
        [Parameter(HelpMessage = "Optionale explizite Angabe von Verrechnen Ab")]
        $VerrechnenAb,
        [Parameter(HelpMessage = "Optionale explizite Angabe von Ausloesen Bis")]
        $AusloesenBis
    )
    process{
        if ($RechnungUid) {
            if ($MandantenNummer -or $RechnungsNummer) {
                Write-Error "Bei Angabe einer RechnungsUid kann keine MandantenNummer oder RechnungsNummer angegeben werden" -ErrorAction Stop;
                break;
            }
            $rechnung = Invoke-Weedu -Uri "/rest/Rechnungen/{0}" -UriParams @($RechnungUid) -Method GET;
        } elseif ([bool]$MandantenNummer -and [bool]$RechnungsNummer) {
            $rechnung = Invoke-Weedu -Uri "/rest/Rechnungen/{0}/{1}" -UriParams @($MandantenNummer, $RechnungsNummer) -Method GET;
        } else {
            Write-Error "Entweder nur RechnungsUid oder aber MandantenNummer und RechnungsNummer m�ssen angegeben werden" -ErrorAction Stop;
            break;
        }

        $Body = New-Object PSCustomObject;
        if ($VerrechnenAb) {
            $Body | Add-Member -MemberType NoteProperty -Name "VerrechnenAb" -Value $VerrechnenAb;
        }
        if ($AusloesenBis) {
            $Body | Add-Member -MemberType NoteProperty -Name "AusloesenBis" -Value $AusloesenBis;
        }

        Write-Output "Rechnung $($rechnung.Rechnungsnummer) (Referenz $($rechnung.ReferencePurchaseOrder)) an $($rechnung.RechnungsempfaengerBezeichnung) wird erneut verrechnet"
        foreach ($quellSystem in $rechnung.Rechnungspositionen | ForEach-Object { $_.QuellSystem } | Sort-Object | Get-Unique) {
            $rechnungsPositionen = [string]::Join(",", ($rechnung.Rechnungspositionen | Where-Object { $_.QuellSystem -eq $quellSystem } | ForEach-Object { [guid]$_.ShipmentItemUid }));
            Write-Verbose "Quellsystem: $quellSystem, Positionen: $rechnungsPositionen";
            switch ($quellSystem) {
            "CommerceCH" {
                    Invoke-Weedu -Uri "/commerce-ch/rest/ShipmentItems/ErneutVerrechnen?uids={0}" -UriParams @($rechnungsPositionen) -Method POST -Body $Body;
                }
            "CommerceDE" {
                    Invoke-Weedu -Uri "/commerce-de/rest/ShipmentItems/ErneutVerrechnen?uids={0}" -UriParams @($rechnungsPositionen) -Method POST -Body $Body;
                }
            default {
                    Write-Warning "Reschnungspositionen des unbekannten Quellsystems $quellSystem werden nicht erneut verrechnet";
                }
            }
        }
    }
}

function Join-Batch {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline, HelpMessage = "Objekte oder IDs f�r die Pipeline. Wenn vorhanden, so wird das Property idb_uid oder UID extrahiert.")]
        $PipelineInput,
        [Parameter(HelpMessage = "Anzahl Items pro Batch")]
        [int]$BatchSize
    )
    begin{
        if (-not ($batchSize -ge 1)) {
            $batchSize = 100;
        }
        $batchCollect = [System.Collections.ArrayList]::new($batchSize);
    }
    process {
        foreach ($item in $pipelineInput) {
            $member = $item | Get-Member | Where-Object { $_.Name -in ("idb_uid", "UID") } | Select-Object -First 1;
            if ($member) {
                $batchCollect.Add(($item | Select-Object -ExpandProperty $member.Name)) | Out-Null;
            } else {
                $batchCollect.Add($item) | Out-Null;
            }
            if ($batchCollect.Count -ge $batchSize) {
                Write-Output ([string]::Join(",", $batchCollect.ToArray()));
                $batchCollect.Clear();
            }
        }
    }
    end {
        if ($batchCollect.Count) {
            Write-Output ([string]::Join(",", $batchCollect.ToArray()));
        }
    }
}


function Set-Rechnungsposition {
    [CmdletBinding(DefaultParameterSetName = "UID")]
    param(
        [Parameter(Mandatory, ValueFromPipeline=$true, Position = 0)]
        $RechnungspositionUid,
        [Parameter(Mandatory, ParameterSetName = "Nummer")]
        $Mandantennummer,
        [Parameter(ParameterSetName = "UID")]
        $PlatzhalterProdukt,
        [string]$AbacusAblauf,    
        [Parameter(Mandatory, ParameterSetName = "Nummer")]
        $PlatzhalterProduktNummer,
        [string]$WaehrungsCode,
        [guid]$RechnungsempfaengerUid,
        $AuftragserfasserUid,
        $LeistungsempfaengerUid,
        $RechnungsadresseUid,
        $VerrechnenAb,
        $AusloesenBis,
        [guid]$OrderItemUid,
        [int]$Menge,
        [double]$Stueckpreis,
        $Stueckrabatt,
        [string]$Positionstitel,
        $PositionstextListe,
        $RechnungstextHeaderListe,
        $RechnungstextFooterListe,
        $Quellprodukt_OrderItemUid,
        $Quellprodukt_Sku,
        $Quellprodukt_Ean,
        $Quellprodukt_Price,
        $MwStCode,
        $Lieferdatum,
        [guid]$Gruppierungsmerkmal,
        $DispoDatum,
        $KostentraegerText,
        $LeistungszeitraumVon,
        $LeistungszeitraumBis,
        $HabenKonto,
        $SollKonto,
        $HabenKostenstelle,
        $SollKostenstelle        
    )
    process{
        $RechnungspositionUid = GetUID($RechnungspositionUid);
         $Body = New-Object PSCustomObject;
        Write-Verbose "Rechnungspositionen: $RechnungspositionUid";
        
        if ($PSBoundParameters.ContainsKey("Mandantennummer")) {
            $Body | Add-Member -MemberType NoteProperty -Name "Mandantennummer" -Value $Mandantennummer;
        }
        if ($PSBoundParameters.ContainsKey("PlatzhalterProdukt")) {
            $Body | Add-Member -MemberType NoteProperty -Name "PlatzhalterProdukt" -Value $PlatzhalterProdukt;
        }
        if ($PSBoundParameters.ContainsKey("AbacusAblauf")) {
            $Body | Add-Member -MemberType NoteProperty -Name "AbacusAblauf" -Value $AbacusAblauf;
        }
        if ($PSBoundParameters.ContainsKey("PlatzhalterProduktNummer")) {
            $Body | Add-Member -MemberType NoteProperty -Name "PlatzhalterProduktNummer" -Value $PlatzhalterProduktNummer;
        }
        if ($PSBoundParameters.ContainsKey("WaehrungsCode")) {
            $Body | Add-Member -MemberType NoteProperty -Name "WaehrungsCode" -Value $WaehrungsCode;
        }
        if ($PSBoundParameters.ContainsKey("RechnungsempfaengerUid")) {
            $Body | Add-Member -MemberType NoteProperty -Name "RechnungsempfaengerUid" -Value $RechnungsempfaengerUid;
        }
        if ($PSBoundParameters.ContainsKey("AuftragserfasserUid")) {
            $Body | Add-Member -MemberType NoteProperty -Name "AuftragserfasserUid" -Value $AuftragserfasserUid;
        }
        if ($PSBoundParameters.ContainsKey("LeistungsempfaengerUid")) {
            $Body | Add-Member -MemberType NoteProperty -Name "LeistungsempfaengerUid" -Value $LeistungsempfaengerUid;
        }
        if ($PSBoundParameters.ContainsKey("RechnungsadresseUid")) {
            $Body | Add-Member -MemberType NoteProperty -Name "RechnungsadresseUid" -Value $RechnungsadresseUid;
        }
        if ($PSBoundParameters.ContainsKey("VerrechnenAb")) {
            $Body | Add-Member -MemberType NoteProperty -Name "VerrechnenAb" -Value $VerrechnenAb;
        }
        if ($PSBoundParameters.ContainsKey("AusloesenBis")) {
            $Body | Add-Member -MemberType NoteProperty -Name "AusloesenBis" -Value $AusloesenBis;
        }
        if ($PSBoundParameters.ContainsKey("OrderItemUid")) {
            $Body | Add-Member -MemberType NoteProperty -Name "OrderItemUid" -Value $OrderItemUid;
        }
        if ($PSBoundParameters.ContainsKey("Menge")) {
            $Body | Add-Member -MemberType NoteProperty -Name "Menge" -Value $Menge;
        }
        if ($PSBoundParameters.ContainsKey("Stueckpreis")) {
            $Body | Add-Member -MemberType NoteProperty -Name "Stueckpreis" -Value $Stueckpreis;
        }
        if ($PSBoundParameters.ContainsKey("Stueckrabatt")) {
            $Body | Add-Member -MemberType NoteProperty -Name "Stueckrabatt" -Value $Stueckrabatt;
        }
        if ($PSBoundParameters.ContainsKey("Positionstitel")) {
            $Body | Add-Member -MemberType NoteProperty -Name "Positionstitel" -Value $Positionstitel;
        }
        if ($PSBoundParameters.ContainsKey("MwStCode")) {
            $Body | Add-Member -MemberType NoteProperty -Name "MwStCode" -Value $MwStCode;
        }
        if ($PSBoundParameters.ContainsKey("Lieferdatum")) {
            $Body | Add-Member -MemberType NoteProperty -Name "Lieferdatum" -Value $Lieferdatum;
        }
        if ($PSBoundParameters.ContainsKey("Gruppierungsmerkmal")) {
            $Body | Add-Member -MemberType NoteProperty -Name "Gruppierungsmerkmal" -Value $Gruppierungsmerkmal;
        }
        if ($PSBoundParameters.ContainsKey("DispoDatum")) {
            $Body | Add-Member -MemberType NoteProperty -Name "DispoDatum" -Value $DispoDatum;
        }
        if ($PSBoundParameters.ContainsKey("KostentraegerText")) {
            $Body | Add-Member -MemberType NoteProperty -Name "KostentraegerText" -Value $KostentraegerText;
        }
        if ($PSBoundParameters.ContainsKey("LeistungszeitraumVon")) {
            $Body | Add-Member -MemberType NoteProperty -Name "LeistungszeitraumVon" -Value $LeistungszeitraumVon;
        }
        if ($PSBoundParameters.ContainsKey("LeistungszeitraumBis")) {
            $Body | Add-Member -MemberType NoteProperty -Name "LeistungszeitraumBis" -Value $LeistungszeitraumBis;
        }
        if ($PSBoundParameters.ContainsKey("HabenKonto")) {
            $Body | Add-Member -MemberType NoteProperty -Name "HabenKonto" -Value $HabenKonto;
        }
        if ($PSBoundParameters.ContainsKey("SollKonto")) {
            $Body | Add-Member -MemberType NoteProperty -Name "SollKonto" -Value $SollKonto;
        }
        if ($PSBoundParameters.ContainsKey("HabenKostenstelle")) {
            $Body | Add-Member -MemberType NoteProperty -Name "HabenKostenstelle" -Value $HabenKostenstelle;
        }
        if ($PSBoundParameters.ContainsKey("SollKostenstelle")) {
            $Body | Add-Member -MemberType NoteProperty -Name "SollKostenstelle" -Value $SollKostenstelle;
        }
        if ($PSBoundParameters.ContainsKey("PositionstextListe")) {
            $Body | Add-Member -MemberType NoteProperty -Name "PositionstextListe" -Value ([array]($PositionstextListe | ForEach-Object { $i = 1 } { [PSCustomObject]@{ "Element" = $_; "Prioritaet" = $i++; } } ));
        }
        if ($PSBoundParameters.ContainsKey("RechnungstextHeaderListe"))
        {
            $Body | Add-Member -MemberType NoteProperty -Name "RechnungstextHeaderListe" -Value ([array]($RechnungstextHeaderListe | ForEach-Object { $i = 1 } { [PSCustomObject]@{ "Element" = $_; "Prioritaet" = $i++; } } ));
        }
        if ($PSBoundParameters.ContainsKey("RechnungstextFooterListe"))
        {
            $Body | Add-Member -MemberType NoteProperty -Name "RechnungstextFooterListe" -Value ([array]($RechnungstextFooterListe | ForEach-Object { $i = 1 } { [PSCustomObject]@{ "Element" = $_; "Prioritaet" = $i++; } } ));
        }
        if ($PSBoundParameters.ContainsKey("Quellprodukt_OrderItemUid") -or $PSBoundParameters.ContainsKey("Quellprodukt_Sku") -or $PSBoundParameters.ContainsKey("Quellprodukt_Ean") -or $PSBoundParameters.ContainsKey("Quellprodukt_Price"))
        {
            $Quellprodukt = New-Object PSCustomObject;
            if ($PSBoundParameters.ContainsKey("Quellprodukt_OrderItemUid")) {
                $Quellprodukt | Add-Member -MemberType NoteProperty -Name "OrderItemUid" -Value $Quellprodukt_OrderItemUid;
            }
            if ($PSBoundParameters.ContainsKey("Quellprodukt_Price")) {
                $Quellprodukt | Add-Member -MemberType NoteProperty -Name "Price" -Value $Quellprodukt_Price;
            }
            if ($PSBoundParameters.ContainsKey("Quellprodukt_Sku")) {
                $Quellprodukt | Add-Member -MemberType NoteProperty -Name "Sku" -Value $Quellprodukt_Sku;
            }
            if ($PSBoundParameters.ContainsKey("Quellprodukt_Ean")) {
                $Quellprodukt | Add-Member -MemberType NoteProperty -Name "Ean" -Value $Quellprodukt_Ean;
            }
            $Body | Add-Member -MemberType NoteProperty -Name "Quellprodukt" -Value $Quellprodukt;
        }
        
        Invoke-Weedu -Uri "rest/Rechnungspositionen?uids={0}" -UriParams @($RechnungspositionUid) -Method PUT -Body $Body;
    }
}

Export-ModuleMember -function Connect-Weedu
Export-ModuleMember -function Disconnect-Weedu
Export-ModuleMember -function Join-Batch
Export-ModuleMember -function Invoke-Weedu
Export-ModuleMember -function Invoke-WqlQuery
Export-ModuleMember -function Set-PersonenMapping
Export-ModuleMember -function Invoke-RechnungStorno
Export-ModuleMember -function Invoke-RechnungErneutVerrechnen
Export-ModuleMember -function Set-Rechnungsposition
Export-ModuleMember -function New-Sicherheitseinstellung
Export-ModuleMember -function Add-SicherheitseinstellungGruppe
Export-ModuleMember -function New-IndexRegel
Export-ModuleMember -function Set-IndexRegel
Export-ModuleMember -function Get-IndexRegel