Public/Remove-LedgerAttachment.ps1

<#
.SYNOPSIS
Removes an attachment from a verification.

.DESCRIPTION
Deletes the specified file from a verification's attachment directory.
If the directory becomes empty after removal, it is also deleted.

Attachments cannot be removed from a closed fiscal year.

.PARAMETER JournalPath
The path to an existing journal directory. If omitted, uses the current journal.

.PARAMETER FiscalYear
The fiscal year identifier. If omitted, uses the latest fiscal year.
Accepts pipeline input from fiscal year objects.

.PARAMETER VerificationNumber
The verification number to remove the attachment from.

.PARAMETER FileName
The name of the file to remove.

.EXAMPLE
Remove-LedgerAttachment -VerificationNumber 3 -FileName 'faktura-101.pdf'

Removes faktura-101.pdf from verification 3.

.EXAMPLE
Get-LedgerAttachment -VerificationNumber 1 | Where-Object FileName -like '*.tmp' |
    Remove-LedgerAttachment

Removes all .tmp attachments from verification 1.
#>

function Remove-LedgerAttachment {
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Parameter()]
        [string]$JournalPath,

        [Parameter(ValueFromPipelineByPropertyName)]
        [string]$FiscalYear,

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

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string]$FileName
    )

    process {
        $JournalPath = Resolve-LedgerJournalPath -JournalPath $JournalPath -SchemaCheck Write
        $journalLock = $null
        try {
            $journalLock = Enter-LedgerJournalLock -JournalPath $JournalPath
            $FiscalYear = Resolve-LedgerFiscalYear -FiscalYear $FiscalYear -JournalPath $JournalPath

            $YearDir = Join-Path $JournalPath $FiscalYear
            if (-not (Test-Path $YearDir -PathType Container)) {
                throw "Fiscal year not found: $FiscalYear"
            }
            Assert-LedgerFiscalYearOpen -YearDir $YearDir -FiscalYear $FiscalYear -Action 'remove attachments'

            $FileName = Assert-LedgerSafeLeafName -Name $FileName -Kind 'attachment'

            $attachDir = Join-Path $YearDir ('ver' + $VerificationNumber.ToString('0000'))
            if (-not (Test-Path $attachDir -PathType Container)) {
                throw "No attachments found for verification $VerificationNumber."
            }

            $filePath = Join-Path $attachDir $FileName
            if (-not (Test-Path $filePath -PathType Leaf)) {
                throw "Attachment not found: $FileName"
            }

            if ($PSCmdlet.ShouldProcess($filePath, 'Remove attachment')) {
                # A sealed attachment is not deleted silently: the removal is logged in
                # the integrity chain so Test-LedgerIntegrity does not report it missing.
                $integrityKey = "$VerificationNumber/$FileName"
                if (Test-LedgerIntegrityAttachmentSealed -YearDir $YearDir -Key $integrityKey) {
                    $removed = [pscustomobject]@{ Kind = 'AttachmentRemoved'; Key = $integrityKey; Hash = (Get-LedgerContentHash -Path $filePath) }
                    Write-LedgerIntegrityRecord -YearDir $YearDir -Record @($removed)
                }
                Remove-Item -Path $filePath -Force

                # Remove directory if empty
                $remaining = Get-ChildItem -Path $attachDir -File
                if (-not $remaining) {
                    Remove-Item -Path $attachDir -Force
                }
            }
        }
        finally { Exit-LedgerJournalLock -Lock $journalLock }
    }
}