Public/Export-LedgerPayslip.ps1
|
<# .SYNOPSIS Exports a payslip to a PDF, Word, Markdown or plain-text document. .DESCRIPTION Renders a single payslip (lönebesked) as a printable document and writes it to a file. The document contains the employer (from the journal), the employee (from the employee register), the pay period and date, the gross salary, the preliminary tax withheld, the net pay to be disbursed and the employer's social security contribution. The PDF is produced by a dependency-free built-in writer (standard fonts, no external module). Amounts use Swedish formatting. .PARAMETER JournalPath The path to an existing journal directory. If omitted, uses the current journal set via Set-LedgerCurrentJournal. .PARAMETER PayslipNumber The number of the payslip to export. .PARAMETER Path Destination path for the document. .PARAMETER Format The output format: 'Pdf' (default), 'Word' (.docx), 'Markdown' or 'Text'. .PARAMETER Force Overwrite the destination file if it already exists. .EXAMPLE Export-LedgerPayslip -JournalPath .\MinFirma.ledger -PayslipNumber 1 -Path .\lonebesked-1.pdf Writes payslip 1 as a PDF document. .EXAMPLE Export-LedgerPayslip -PayslipNumber 1 -Path .\lonebesked-1.docx -Format Word -Force Writes payslip 1 as a Word document, overwriting any existing file. #> function Export-LedgerPayslip { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter()] [string]$JournalPath, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [int]$PayslipNumber, [Parameter(Mandatory)] [string]$Path, [Parameter()] [ValidateSet('Pdf', 'Word', 'Markdown', 'Text')] [string]$Format = 'Pdf', [Parameter()] [switch]$Force ) process { $JournalPath = Resolve-LedgerJournalPath -JournalPath $JournalPath if ((Test-Path $Path) -and -not $Force) { throw "Destination file already exists: $Path. Use -Force to overwrite." } $payslip = Get-LedgerPayslip -JournalPath $JournalPath -PayslipNumber $PayslipNumber if (-not $payslip) { throw "Payslip $PayslipNumber does not exist." } $journal = Get-LedgerJournal -Path $JournalPath $employee = Get-LedgerEmployee -JournalPath $JournalPath -EmployeeNumber $payslip.EmployeeNumber $blocks = @(Build-LedgerPayslipBlock -Payslip $payslip -Journal $journal -Employee $employee) if (-not $PSCmdlet.ShouldProcess($Path, "Export payslip $PayslipNumber")) { return } switch ($Format) { 'Word' { ConvertTo-LedgerReportDocx -Block $blocks -Path $Path } 'Pdf' { ConvertTo-LedgerReportPdf -Block $blocks -Path $Path } 'Markdown' { ConvertTo-LedgerReportMarkdown -Block $blocks | Set-Content -Path $Path -Encoding UTF8 } default { ConvertTo-LedgerReportText -Block $blocks | Set-Content -Path $Path -Encoding UTF8 } } } } function Build-LedgerPayslipBlock { <# .SYNOPSIS Builds the shared report block model for a single payslip document. #> [CmdletBinding()] param ( [Parameter(Mandatory)] [psobject]$Payslip, [Parameter(Mandatory)] [psobject]$Journal, [Parameter()] [psobject]$Employee ) $blocks = New-Object System.Collections.Generic.List[object] # --- Employer ------------------------------------------------------------- $blocks.Add(@{ Type = 'Title'; Text = $Journal.Name }) if ($Journal.OrgNumber) { $blocks.Add(@{ Type = 'Paragraph'; Text = "Org.nr $($Journal.OrgNumber)" }) } # --- Payslip heading + metadata ------------------------------------------ $blocks.Add(@{ Type = 'Heading'; Level = 1; Text = "Lönespecifikation $($Payslip.PayslipNumber)" }) $blocks.Add(@{ Type = 'Paragraph'; Text = "Utbetalningsdatum: $($Payslip.PayDate.ToString('yyyy-MM-dd'))" }) if ($Payslip.PeriodStart -and $Payslip.PeriodEnd) { $blocks.Add(@{ Type = 'Paragraph'; Text = "Löneperiod: $($Payslip.PeriodStart.ToString('yyyy-MM-dd')) – $($Payslip.PeriodEnd.ToString('yyyy-MM-dd'))" }) } if ($Payslip.Description) { $blocks.Add(@{ Type = 'Paragraph'; Text = "Avser: $($Payslip.Description)" }) } # --- Employee ------------------------------------------------------------- $blocks.Add(@{ Type = 'Spacer' }) $empName = if ($Payslip.PSObject.Properties['EmployeeName'] -and $Payslip.EmployeeName) { $Payslip.EmployeeName } elseif ($Employee) { $Employee.Name } else { '' } $blocks.Add(@{ Type = 'Heading'; Level = 2; Text = 'Anställd' }) $blocks.Add(@{ Type = 'Paragraph'; Text = "$($Payslip.EmployeeNumber) $empName" }) if ($Employee -and $Employee.PersonalNumber) { $blocks.Add(@{ Type = 'Paragraph'; Text = "Personnr $($Employee.PersonalNumber)" }) } # --- Salary breakdown table ---------------------------------------------- $blocks.Add(@{ Type = 'Spacer' }) $rows = @( , @('Bruttolön', (Format-LedgerAmount -Value $Payslip.GrossSalary)) , @('Preliminär skatt', ('-' + (Format-LedgerAmount -Value $Payslip.TaxAmount))) , @('Nettolön', (Format-LedgerAmount -Value $Payslip.NetPay)) ) $blocks.Add(@{ Type = 'Table' Header = @('Post', 'Belopp') Align = @('left', 'right') Rows = @($rows) }) $blocks.Add(@{ Type = 'Heading'; Level = 2; Text = "Att utbetala: $(Format-LedgerAmount -Value $Payslip.NetPay) kr" }) # --- Employer contribution (informational) ------------------------------- $blocks.Add(@{ Type = 'Spacer' }) $ratePct = (Format-LedgerAmount -Value ([decimal]$Payslip.EmployerContributionRate * 100)) + ' %' $blocks.Add(@{ Type = 'Paragraph'; Text = "Arbetsgivaravgift ($ratePct): $(Format-LedgerAmount -Value $Payslip.EmployerContribution) kr" }) $blocks.Add(@{ Type = 'Paragraph'; Text = "Total lönekostnad för arbetsgivaren: $(Format-LedgerAmount -Value $Payslip.TotalEmployerCost) kr" }) $blocks } |