Public/Export-LedgerSie.ps1
|
<# .SYNOPSIS Exports a fiscal year to a SIE 4E file. .DESCRIPTION Writes a SIE 4 transaction file (typ 4E) containing the chart of accounts, the opening (#IB), closing (#UB) and result (#RES) balances, and all verifications for a fiscal year. The file uses CP437 (PC-8) encoding as required by the SIE standard, decimal amounts use a period as decimal separator, and dates are written as yyyymmdd. All verifications are placed in series 'A' with their existing numbers. .PARAMETER JournalPath The path to an existing journal directory. .PARAMETER FiscalYear The fiscal year identifier (e.g. '2024-01_2024-12'). .PARAMETER Path Destination path for the SIE file. Conventionally uses the .se or .sie extension. .PARAMETER Force Overwrite the destination file if it already exists. .EXAMPLE Export-LedgerSie -JournalPath .\MinFirma.ledger -FiscalYear '2024-01_2024-12' -Path .\minfirma-2024.se Exports a fiscal year to a SIE 4E file in the current directory. .EXAMPLE Export-LedgerSie -JournalPath .\Konsult.ledger -FiscalYear '2024-07_2025-06' -Path C:\Bokslut\konsult-rar.se -Force Exports a broken fiscal year, overwriting any existing destination file. #> function Export-LedgerSie { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter()] [string]$JournalPath, [Parameter(ValueFromPipelineByPropertyName)] [Alias('Name')] [string]$FiscalYear, [Parameter(Mandatory)] [string]$Path, [switch]$Force ) process { $JournalPath = Resolve-LedgerJournalPath -JournalPath $JournalPath $FiscalYear = Resolve-LedgerFiscalYear -FiscalYear $FiscalYear -JournalPath $JournalPath if (-not (Test-Path $JournalPath -PathType Container)) { throw "Journal not found: $JournalPath" } $YearDir = Join-Path $JournalPath $FiscalYear if (-not (Test-Path $YearDir -PathType Container)) { throw "Fiscal year not found: $FiscalYear" } if ((Test-Path $Path) -and -not $Force) { throw "Destination file already exists: $Path. Use -Force to overwrite." } $journal = Get-LedgerJournal -Path $JournalPath $year = Get-LedgerFiscalYear -JournalPath $JournalPath | Where-Object { $_.Name -eq $FiscalYear } if (-not $year) { throw "Fiscal year year.txt missing for $FiscalYear" } $startDate = ([datetime]$year.StartDate).ToString('yyyyMMdd') $endDate = ([datetime]$year.EndDate).ToString('yyyyMMdd') $accounts = @(Get-LedgerAccount -JournalPath $JournalPath) $entries = @(Get-LedgerEntry -JournalPath $JournalPath -FiscalYear $FiscalYear | Sort-Object VerificationNumber) $today = (Get-Date).ToString('yyyyMMdd') $moduleVersion = (Get-Module PSLedger).Version.ToString() $sb = New-Object System.Text.StringBuilder $nl = "`r`n" $append = { param($line) [void]$sb.Append($line) [void]$sb.Append($nl) } & $append (Format-SieRecord -Tag 'FLAGGA' -Fields @('0')) & $append (Format-SieRecord -Tag 'PROGRAM' -Fields @('PSLedger', $moduleVersion)) & $append (Format-SieRecord -Tag 'FORMAT' -Fields @('PC8')) & $append (Format-SieRecord -Tag 'GEN' -Fields @($today)) & $append (Format-SieRecord -Tag 'SIETYP' -Fields @('4')) & $append (Format-SieRecord -Tag 'FNAMN' -Fields @($journal.Name)) if ($journal.OrgNumber) { & $append (Format-SieRecord -Tag 'ORGNR' -Fields @($journal.OrgNumber)) } & $append (Format-SieRecord -Tag 'RAR' -Fields @('0', $startDate, $endDate)) # Dimensions and objects $dimensions = @(Get-LedgerDimension -JournalPath $JournalPath) foreach ($dim in $dimensions) { & $append (Format-SieRecord -Tag 'DIM' -Fields @([string]$dim.DimensionNumber, $dim.Name)) } $objects = @(Get-LedgerObject -JournalPath $JournalPath) foreach ($obj in $objects) { & $append (Format-SieRecord -Tag 'OBJEKT' -Fields @([string]$obj.DimensionNumber, $obj.ObjectNumber, $obj.Name)) } foreach ($a in $accounts) { & $append (Format-SieRecord -Tag 'KONTO' -Fields @($a.AccountNumber, $a.AccountName)) } # Balance metadata (year index 0): # #IB opening balances (ingående balans), from ib.txt # #UB closing balances for balance-sheet accounts (1xxx-2xxx) # #RES period result for result accounts (3xxx-8xxx) # Read-LedgerOpeningBalance returns its rows as one array object, so # assign it before piping or Sort-Object receives a single item. $openingRows = Read-LedgerOpeningBalance -YearDir $YearDir $openingRows = @($openingRows | Sort-Object Account) foreach ($ib in $openingRows) { & $append (Format-SieRecord -Tag 'IB' -Fields @('0', $ib.Account, [decimal]$ib.Amount)) } $balances = @(Get-LedgerBalance -JournalPath $JournalPath -FiscalYear $FiscalYear) foreach ($b in ($balances | Where-Object { $_.AccountNumber -match '^[12]' -and [decimal]$_.Balance -ne 0 })) { & $append (Format-SieRecord -Tag 'UB' -Fields @('0', $b.AccountNumber, [decimal]$b.Balance)) } foreach ($b in ($balances | Where-Object { $_.AccountNumber -match '^[3-8]' -and [decimal]$_.Balance -ne 0 })) { & $append (Format-SieRecord -Tag 'RES' -Fields @('0', $b.AccountNumber, [decimal]$b.Balance)) } foreach ($e in $entries) { $entryDate = ([datetime]$e.Date).ToString('yyyyMMdd') & $append (Format-SieRecord -Tag 'VER' -Fields @('A', [string]$e.VerificationNumber, $entryDate, $e.Description)) & $append '{' foreach ($row in $e.Rows) { $objList = '' if ($row.Objects -and $row.Objects.Count -gt 0) { $objParts = foreach ($k in ($row.Objects.Keys | Sort-Object)) { "$k `"$($row.Objects[$k])`"" } $objList = $objParts -join ' ' } & $append (Format-SieTransRecord -Account $row.Account -Amount ([decimal]$row.Amount) -ObjectList $objList) } & $append '}' } if ($PSCmdlet.ShouldProcess($Path, "Export SIE file for $FiscalYear")) { Write-SieText -Path $Path -Text $sb.ToString() } } } |