Public/Copy-LedgerHolding.ps1
|
<# .SYNOPSIS Copies the securities holdings of one fiscal year into another. .DESCRIPTION Rolls the holdings recorded with Set-LedgerHolding (holdings.txt) forward into a new fiscal year, typically together with Copy-LedgerOpeningBalance at year end. All fields are copied as-is, including Price, PriceDate and Source, so the new year starts with last year's closing prices; update them with Set-LedgerHolding when the new balance date is reached (Test-LedgerFiscalYear flags price dates that do not match the fiscal year's end date). The target fiscal year must be open. If it already has holdings, the command throws unless -Force is given, in which case the target's holdings are replaced. A warning is emitted for each holding whose account has no opening balance in the target year. .PARAMETER JournalPath The path to an existing journal directory. If omitted, uses the current journal set via Set-LedgerCurrentJournal. .PARAMETER FromFiscalYear The source fiscal year to copy holdings from. If omitted, uses the current fiscal year set via Set-LedgerCurrentFiscalYear. .PARAMETER ToFiscalYear The target fiscal year to copy holdings into. .PARAMETER Force Replace holdings already recorded in the target fiscal year. .EXAMPLE Copy-LedgerHolding -JournalPath .\HEG.ledger -FromFiscalYear '2025-09_2026-08' -ToFiscalYear '2026-09_2027-08' Copies the holdings to the new fiscal year. .EXAMPLE Copy-LedgerOpeningBalance -JournalPath .\HEG.ledger -FromFiscalYear '2025-09_2026-08' -ToFiscalYear '2026-09_2027-08' Copy-LedgerHolding -JournalPath .\HEG.ledger -FromFiscalYear '2025-09_2026-08' -ToFiscalYear '2026-09_2027-08' Set-LedgerHolding -JournalPath .\HEG.ledger -FiscalYear '2026-09_2027-08' -Account 1350 -Name 'Swedbank A' ` -Price 301.50 -PriceDate '2027-08-31' -Source 'Nasdaq Stockholm, stängningskurs' Year-end roll-forward: copy balances and holdings, then update the price at the new balance date. #> function Copy-LedgerHolding { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter()] [string]$JournalPath, [Parameter(ValueFromPipelineByPropertyName)] [Alias('Name')] [string]$FromFiscalYear, [Parameter(Mandatory)] [string]$ToFiscalYear, [Parameter()] [switch]$Force ) process { $JournalPath = Resolve-LedgerJournalPath -JournalPath $JournalPath -SchemaCheck Write $FromFiscalYear = Resolve-LedgerFiscalYear -FiscalYear $FromFiscalYear -JournalPath $JournalPath $FromDir = Join-Path $JournalPath $FromFiscalYear if (-not (Test-Path $FromDir -PathType Container)) { throw "Source fiscal year not found: $FromFiscalYear" } $ToDir = Join-Path $JournalPath $ToFiscalYear if (-not (Test-Path $ToDir -PathType Container)) { throw "Target fiscal year not found: $ToFiscalYear" } if ($FromFiscalYear -eq $ToFiscalYear) { throw 'Source and target fiscal year must differ.' } Assert-LedgerFiscalYearOpen -YearDir $ToDir -FiscalYear $ToFiscalYear -Action 'copy holdings into it' $Holdings = Read-LedgerHoldings -YearDir $FromDir if ($Holdings.Count -eq 0) { throw "No holdings recorded in $FromFiscalYear." } $Existing = Read-LedgerHoldings -YearDir $ToDir if ($Existing.Count -gt 0 -and -not $Force) { throw "Target fiscal year $ToFiscalYear already has holdings. Use -Force to replace them." } $Opening = @{} foreach ($Row in (Read-LedgerOpeningBalance -YearDir $ToDir)) { $Opening[[string]$Row.Account] = [decimal]$Row.Amount } foreach ($Account in ($Holdings | ForEach-Object { $_.Account } | Select-Object -Unique)) { if (-not $Opening.ContainsKey($Account) -or $Opening[$Account] -eq 0) { Write-Warning "Account $Account has no opening balance in $ToFiscalYear. Copy the opening balance first (Copy-LedgerOpeningBalance) or check the holding." } } if (-not $PSCmdlet.ShouldProcess($ToFiscalYear, "Copy $($Holdings.Count) holding(s) from $FromFiscalYear")) { return } Write-LedgerHoldings -YearDir $ToDir -Rows $Holdings foreach ($h in $Holdings) { $Out = $h.PSObject.Copy() $Out | Add-Member -NotePropertyName FiscalYear -NotePropertyValue $ToFiscalYear -Force $Out } } } |