Private/SieImport.ps1
|
# Recoverable SIE import. An immutable plan in <year>/import.txt binds the import # to its source file, reserves the verification numbers and records hashes of the # planned opening balance and verifications, so an interrupted Import-LedgerSie # can be completed by repeating it with the same file. function Get-LedgerSieImportPath { [CmdletBinding()] param ( [Parameter(Mandatory)] [string]$JournalPath, [Parameter(Mandatory)] [string]$FiscalYear ) Join-Path $JournalPath $FiscalYear 'import.txt' } function Read-LedgerSieImport { [CmdletBinding()] param ([Parameter(Mandatory)] [string]$Path) $fields = @{} foreach ($line in Get-Content -LiteralPath $Path -Encoding UTF8 -ErrorAction Stop) { if ([string]::IsNullOrWhiteSpace($line) -or $line.StartsWith(';')) { continue } if ($line -notmatch '^([A-Za-z]+):\t(.+)$' -or $fields.ContainsKey($Matches[1])) { throw "Invalid SIE import record '$Path': malformed or duplicate field." } $fields[$Matches[1]] = $Matches[2] } $invariant = [Globalization.CultureInfo]::InvariantCulture $id = [guid]::Empty $first = 0 $count = -1 $hash = '^[0-9a-f]{64}$' $valid = $fields.FormatVersion -eq '1' -and [guid]::TryParseExact([string]$fields.OperationId, 'D', [ref]$id) -and $id -ne [guid]::Empty -and $fields.FiscalYear -eq (Split-Path (Split-Path $Path -Parent) -Leaf) -and [string]$fields.SourceHash -cmatch $hash -and [string]$fields.VerificationsHash -cmatch $hash -and [int]::TryParse([string]$fields.FirstVerification, [Globalization.NumberStyles]::None, $invariant, [ref]$first) -and $first -gt 0 -and [int]::TryParse([string]$fields.VerificationCount, [Globalization.NumberStyles]::None, $invariant, [ref]$count) -and (-not $fields.ContainsKey('OpeningBalanceHash') -or $fields.OpeningBalanceHash -cmatch $hash) -and (-not $fields.ContainsKey('PriorOpeningBalanceHash') -or ($fields.ContainsKey('OpeningBalanceHash') -and $fields.PriorOpeningBalanceHash -cmatch $hash)) if (-not $valid) { throw "Invalid SIE import record '$Path': missing or invalid identity, numbers or hashes." } [pscustomobject]@{ OperationId = $id FiscalYear = $fields.FiscalYear SourceHash = $fields.SourceHash FirstVerification = $first VerificationCount = $count VerificationsHash = $fields.VerificationsHash OpeningBalanceHash = $fields.OpeningBalanceHash PriorOpeningBalanceHash = $fields.PriorOpeningBalanceHash } } function Save-LedgerSieImport { [CmdletBinding()] param ( [Parameter(Mandatory)] [string]$Path, [Parameter(Mandatory)] [psobject]$Operation ) $lines = @( '; PSLedger SIE import' "FormatVersion:`t1" "OperationId:`t$($Operation.OperationId)" "FiscalYear:`t$($Operation.FiscalYear)" "SourceHash:`t$($Operation.SourceHash)" "FirstVerification:`t$($Operation.FirstVerification)" "VerificationCount:`t$($Operation.VerificationCount)" "VerificationsHash:`t$($Operation.VerificationsHash)" ) if ($Operation.OpeningBalanceHash) { $lines += "OpeningBalanceHash:`t$($Operation.OpeningBalanceHash)" } if ($Operation.PriorOpeningBalanceHash) { $lines += "PriorOpeningBalanceHash:`t$($Operation.PriorOpeningBalanceHash)" } Set-LedgerFileContent -Path $Path -NoClobber -Value $lines } function Get-LedgerTextLinesHash { # SHA-256 of lines as written to a file (LF-normalised), matching Get-LedgerContentHash. [CmdletBinding()] [OutputType([string])] param ([Parameter(Mandatory)] [AllowEmptyCollection()] [AllowEmptyString()] [string[]]$Lines) Get-LedgerSha256Hex -Bytes ([Text.Encoding]::UTF8.GetBytes(($Lines -join "`n") + "`n")) } |