Public/Invoke-LedgerSupplierInvoicePosting.ps1

<#
.SYNOPSIS
Posts a supplier invoice to the ledger, creating a bookkeeping verification.

.DESCRIPTION
Creates a verification for a Draft supplier invoice: each cost account is debited
with its net amount, each input-VAT account is debited with its VAT, and the
payable account (2440 Leverantörsskulder) is credited with the gross total. The
fiscal year is resolved from the invoice date unless one is supplied explicitly.

After posting, the invoice status becomes 'Booked' and the verification number
and fiscal year are recorded on the invoice so the payable can always be
reconciled against the ledger.

The operation refuses to run twice for the same invoice (an already-posted
invoice throws).

.PARAMETER JournalPath
The path to an existing journal directory. If omitted, uses the current journal
set via Set-LedgerCurrentJournal.

.PARAMETER InvoiceNumber
The number of the supplier invoice to post.

.PARAMETER FiscalYear
Optional. The fiscal year to post the verification into. If omitted, the fiscal
year containing the invoice date is used.

.PARAMETER PassThru
If specified, returns the updated supplier invoice object. By default the command
produces no output.

.EXAMPLE
Invoke-LedgerSupplierInvoicePosting -JournalPath .\MinFirma.ledger -InvoiceNumber 1

Posts supplier invoice 1, creating a verification such as:
  5010 Lokalhyra +8000
  2640 Ingående moms +2000
  2440 Leverantörsskulder -10000

.EXAMPLE
Get-LedgerSupplierInvoice -Status Draft | ForEach-Object { Invoke-LedgerSupplierInvoicePosting -InvoiceNumber $_.InvoiceNumber }

Posts every draft supplier invoice in the current journal.
#>

function Invoke-LedgerSupplierInvoicePosting {
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Parameter()]
        [string]$JournalPath,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [int]$InvoiceNumber,

        [Parameter()]
        [string]$FiscalYear,

        [Parameter()]
        [switch]$PassThru
    )
    process {
        $JournalPath = Resolve-LedgerJournalPath -JournalPath $JournalPath -SchemaCheck Write

        $invoiceDir = Get-LedgerSupplierInvoiceDirectory -JournalPath $JournalPath
        $filePath = Join-Path $invoiceDir (Get-LedgerSupplierInvoiceFileName -InvoiceNumber $InvoiceNumber)
        if (-not (Test-Path $filePath)) {
            throw "Supplier invoice $InvoiceNumber does not exist."
        }

        $invoice = Read-LedgerSupplierInvoiceFile -Path $filePath

        if ($invoice.Status -ne 'Draft') {
            throw "Supplier invoice $InvoiceNumber is already posted (status '$($invoice.Status)', verification $($invoice.BookedVerification))."
        }

        # Resolve the fiscal year from the invoice date unless one was supplied.
        if (-not $FiscalYear) {
            $FiscalYear = Find-FiscalYearForDate -JournalPath $JournalPath -Date $invoice.InvoiceDate
            if (-not $FiscalYear) {
                throw "No fiscal year covers the invoice date $($invoice.InvoiceDate.ToString('yyyy-MM-dd')). Create one with New-LedgerFiscalYear or specify -FiscalYear."
            }
        }

        # Aggregate amounts per account so the verification is compact.
        $accountSums = [ordered]@{}
        $addAmount = {
            param($account, $amount)
            if ($accountSums.Contains($account)) {
                $accountSums[$account] += $amount
            }
            else {
                $accountSums[$account] = $amount
            }
        }

        # Debit cost accounts (net) and input VAT.
        foreach ($row in $invoice.Rows) {
            & $addAmount $row.Account $row.Amount
            if ($row.VatAmount -ne 0) {
                & $addAmount $row.VatAccount $row.VatAmount
            }
        }

        # Credit the payable with the gross total.
        & $addAmount $invoice.PayableAccount (-$invoice.Total)

        $entryRows = foreach ($account in $accountSums.Keys) {
            @{ Account = $account; Amount = [Math]::Round([decimal]$accountSums[$account], 2) }
        }

        $description = "Leverantörsfaktura $InvoiceNumber - $($invoice.Description)"

        if (-not $PSCmdlet.ShouldProcess("Supplier invoice $InvoiceNumber", "Post to fiscal year $FiscalYear")) {
            return
        }

        $verification = Add-LedgerEntry -JournalPath $JournalPath -FiscalYear $FiscalYear `
            -Date $invoice.InvoiceDate -Description $description -Rows @($entryRows) -PassThru

        $invoice.Status = 'Booked'
        $invoice.BookedVerification = $verification.VerificationNumber
        $invoice.BookedFiscalYear = $FiscalYear
        Save-LedgerSupplierInvoiceFile -Invoice $invoice

        if ($PassThru) {
            Get-LedgerSupplierInvoice -JournalPath $JournalPath -InvoiceNumber $InvoiceNumber
        }
    }
}