Files
Fuchs_Intranet/Fuchs/Docs/Decisions/0034-abschlagsrechnung-credit-follows-the-live-invoice.md
Stefan c741535aec
Playwright Tests / test (push) Canceled after 0s
feat: Implement mfr truncation fix and enhance invoice handling
- Added a new SQL script to address the "String or binary data would be truncated" error in mfr sync by widening the Note fields in service objects and companies to NVARCHAR(MAX).
- Updated the table types and associated stored procedures to accommodate the changes, ensuring compatibility with existing data.
- Queued a re-sync for previously rejected rows due to truncation issues.
- Created application logins for fuchs_fds and fuchs_web with placeholder passwords for initial setup.
- Introduced functions for invoice backdating and invoice date retrieval to streamline invoice processing.
- Developed a password generation function with customizable parameters for length and character types.
- Added a random number generation function to support password creation.
2026-09-30 16:56:08 +02:00

65 lines
3.9 KiB
Markdown

---
status: Accepted
date: 2026-09-30
applyTo:
- "Fuchs_Database/dbo/Functions/fds__fn_IntermediateIsAllocatedToOther.sql"
- "Fuchs_Database/dbo/Stored Procedures/fds__setInvoiceFinal.sql"
- "Fuchs_Database/dbo/Stored Procedures/fds__prepInvoice.sql"
- "Fuchs_Database/dbo/Stored Procedures/fds__prepStorno_recreate.sql"
- "Fuchs_Database/dbo/Stored Procedures/fds__createStorno_*.sql"
- "Fuchs_Database/Scripts/2026-09-30_backfill_intermediate_invoice_allocations.sql"
supersededBy: ""
---
# 0034 — An Abschlagsrechnung credit follows the live invoice
## Context
A Schlussrechnung credits the Abschlagsrechnungen (intermediate invoices, `InvoiceType = 'i'`)
already paid for the same service requests via `IntInvoiceAllocation` lines. Finalising it records
the claim in `fds__invoice_details.AllocatedTo_InvId` of each credited intermediate so that
`fds__prepInvoice` does not offer the same Abschlagsrechnung again.
Until 2026-09-30 that claim was never actually written (the editor dropped `reference_InvId`, the
MERGE aborted on the NULL). The moment it is written, the question "what happens to the claim when
the crediting invoice is cancelled?" becomes real: a Storno (simple/copy) or a Storno + recreate
leaves `AllocatedTo_InvId` pointing at the cancelled invoice, and the next invoice for the same
service requests would silently bill the customer again for money already paid.
## Decision
- **A claim counts only while the crediting invoice is alive.** An `AllocatedTo_InvId` that points
at a cancelled invoice (`IsCanceled = 1`), at a non-existent invoice, or at the invoice that the
one being finalised replaces (`CanceledInvoice` line → `@Replaces`) is void: the Abschlagsrechnung
is offered again by `fds__prepInvoice` / `fds__prepStorno_recreate` and may be taken over by the
next finalisation. The single source of truth for "already credited elsewhere" is
`[dbo].[fds__fn_IntermediateIsAllocatedToOther]`; `fds__setInvoiceFinal`'s `existing` CTE is its
set-based twin and must apply the same rule.
- **The link is not cleared on cancellation.** Cancellation has several paths (storno simple/copy,
storno + recreate, the retired `fds__setInvoiceCancelled`); rather than releasing the link in each,
the readers ignore dead links. The last claimant therefore stays visible for auditing.
- **A Stornorechnung (`InvoiceType = 'c'`) never claims.** `fds__createStorno_copy` mirrors the
cancelled invoice's `IntInvoiceAllocation` lines (with their references) so the storno document
shows them, but finalising the storno must not move the allocation onto it.
- **Storno + recreate carries the references.** `fds__prepStorno_recreate` emits its credit lines
with the same type as `fds__prepInvoice` (`IntInvoiceAllocation`, not `PartialInvoice`) and the
credited invoice in `reference_InvId`, and its `CanceledInvoice` line references the replaced
invoice — otherwise the replacement neither takes over the credit nor records what it replaces.
## Consequences
- The recreate flow now produces `Replaces_InvId` / `DateCancelled` on the replaced invoice via the
regular `fds__setInvoiceFinal` bookkeeping (it did not before: no reference reached the proc).
- A second recreate (cancel the replacement, recreate again) keeps the credit because the claim on
the replaced invoice is treated as void during the replacement's finalisation.
- The one-off backfill script records the *earliest* live claimant; it is consistent with this rule.
- An intermediate that is itself cancelled is not offered (unchanged).
## Alternatives considered
- **Release `AllocatedTo_InvId` when the crediting invoice is cancelled** — rejected: four write
paths to keep in sync, and the audit trail of who last credited the Abschlagsrechnung is lost.
- **Keep `PartialInvoice` for recreated credit lines and teach `fds__setInvoiceFinal` both types** —
rejected: one line type per meaning; `PartialInvoice` remains the Abschlagsbetrag charge line of an
intermediate invoice.