Weedu.psm1
$script:hostUrl= $null; $script:sessionVar = $null; $script:logonUser = $null; function ConvertTo-PlainText([securestring]$secure) { return [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure)); } function GetUID($data) { return [string]::Join(",", [array]($data | ForEach-Object { $member = $_ | Get-Member | Where-Object { $_.Name -in ("idb_uid", "UID") } | Select-Object -First 1; if ($member) { return ($_ | Select-Object -ExpandProperty $member.Name); } return $_; })); } function Connect-Weedu { [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [string]$Benutzername, [Parameter(Mandatory)] [securestring]$Kennwort, [Parameter(Mandatory, Position = 1, HelpMessage = "Url zu welcher die Verbindung hergestellt werden soll. Bsp.: https://akad-de.test-weedu.ch")] [string]$HostUrl ) process{ $encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$($Benutzername):$(ConvertTo-PlainText $Kennwort)")); $basicAuthValue = "Basic $encodedCreds"; $Headers = @{ Accept = "application/json, text/javascript, */*" Authorization = $basicAuthValue }; try { $response = Invoke-WebRequest -Uri "$($HostUrl)/sts/authentication" -Method POST -Headers $Headers -SessionVariable session; } catch [System.Net.WebException] { $response = [Microsoft.PowerShell.Commands.WebResponseObject]::new([System.Net.WebResponse]($_.Exception.Response)); } $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, bitte erneut versuchen"; } } } 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"; 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] { $response = [Microsoft.PowerShell.Commands.WebResponseObject]::new([System.Net.WebResponse]($_.Exception.Response)); } 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 = $response.Content | ConvertFrom-Json; } "text/xml" { $result = [xml]$response.Content; } default { $result = $response.Content; } } if ($response.StatusCode -ge 300) { Write-Error -Message "Weedu-Request $Method $Uri nicht erfolgreich: $($response.StatusCode) $($response.StatusDescription)" -TargetObject $result; 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"; break; } if ($PageSize -le 0) { Write-Error "$($PageSize) ist kein gueltiger Wert fuer PageSize! Bitte ein Wert groesser 0 eingeben"; break; } if ($PageCount -le 0) { Write-Error "$($PageCount) ist kein gueltiger Wert fuer PageCount! Bitte ein Wert groesser 0 eingeben"; 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()] param( [Parameter(Mandatory, HelpMessage = "WeeduUid der Person", ValueFromPipeline)] [Guid]$PersonUid, [Parameter(Mandatory, HelpMessage = "Nummer des AbacusMandanten")] [int]$MandantenNummer, [Parameter()] $KundenNummer, [Parameter()] $AdressNummer, [Parameter()] $ADLoginName ) process{ if (-not ($PSBoundParameters.ContainsKey("KundenNummer") -or $PSBoundParameters.ContainsKey("AdressNummer") -or $PSBoundParameters.ContainsKey("ADLoginName"))) { Write-Error "KundenNummer/AdressNummer/AdLoginName, mindestens ein Parameter ist erforderlich."; 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; } return Invoke-Weedu -Uri "/rest/Personen/{0}/AbacusMandanten/{1}" -UriParams @($PersonUid, $MandantenNummer) -Method PUT -Body $Body; } } 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"; 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"; 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"; 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"; 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)); } } } 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 ($PositionstextListe | ForEach-Object { $i = 1 } { [PSCustomObject]@{ "Element" = $_; "Prioritaet" = $i++; } } ); } if ($PSBoundParameters.ContainsKey("RechnungstextHeaderListe")) { $Body | Add-Member -MemberType NoteProperty -Name "RechnungstextHeaderListe" -Value ($RechnungstextHeaderListe | ForEach-Object { $i = 1 } { [PSCustomObject]@{ "Element" = $_; "Prioritaet" = $i++; } } ); } if ($PSBoundParameters.ContainsKey("RechnungstextFooterListe")) { $Body | Add-Member -MemberType NoteProperty -Name "RechnungstextFooterListe" -Value ($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 |