Playwright Tests / test (push) Canceled after 0s
- 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.
4.0 KiB
4.0 KiB
status, date, applyTo, supersededBy
| status | date | applyTo | supersededBy | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Accepted | 2026-09-30 |
|
0032 — Invoices may be backdated to the last working day of the previous month (admins only)
Context
Work done at the end of a month is often invoiced in the first days of the next month. The customer
wants those invoices dated to the previous month. The invoice date was always the moment of
finalisation (fds__setInvoiceFinal: DateFinalized = GETUTCDATE()), and the invoice number is
drawn from the year of that date (fds__newInvoiceId(Year(...))).
Two invoice dates also existed side by side: the PDF printed DateCreated (draft creation) as
"Rechnungsdatum", while the embedded ZUGFeRD/XRechnung issue date (BT-2) used DateFinalized — a
visible date and an e-invoice date that could disagree.
Decision
- One invoice date. PDF "Rechnungsdatum" and e-invoice issue date both come from
InvoiceDates.IssueDate:DateFinalized(converted from the stored UTC to the German calendar day), falling back toDateCreatedfor drafts. - Backdating option. When finalising, a user with
fds_inv > 2may date the invoice to the last working day (Mon–Fri) of the previous month — a fixed date, not a free choice. Public holidays are not excluded (possible later extension). - Only while no invoice exists after that date. Invoice numbers must ascend with invoice dates, so the option is offered and accepted only while no invoice has been finalized after the backdate day (a 2 h margin covers the UTC storage).
- Single source of truth:
[dbo].[fds__fn_invoiceBackdate](@authuser)returns the allowed date or NULL (authorisation + eligibility).inv/dpreviewreturns it so the preview dialog shows the per-invoice checkbox;req/sconfpasses@backdate = 1, andfds__setInvoiceFinalre-evaluates the function andTHROWs (60001) when it is no longer allowed — nothing is finalised then. - Serialized finalisation.
fds__setInvoiceFinalruns check + numberingUPDATEin one transaction under an exclusive, transaction-owned app lock (sp_getapplock, resourcefds__setInvoiceFinal) taken by every finalisation, so a concurrent normal finalisation cannot slip between the eligibility check and the backdated write and break the date/number order. req/sconfmaps only SQL error 60001 to the "no longer possible, create without backdating" message. A 0-row result (invoice already final/sent, e.g. a double submit) is reported as "already finalised" — never as a backdate rejection, which would invite a duplicate invoice.- A backdated invoice is stored with
DateFinalized = <day> 12:00(UTC, stays on the same German day), its number comes from that day's year, andDueDateis recomputed from the payment term anchored on that day.
Consequences
- The PDF of every newly finalised invoice now shows the finalisation date instead of the draft creation date (intended: it now matches the e-invoice and the number sequence).
- In January the option yields a December date and therefore a number from the previous year's range.
- Lists, reports, DATEV and reminders follow the same invoice date — see ADR 0035.
- Telemetry:
fuchs.invoices.backdated(outcome ok / rejected / already_final / error); successful backdating logs EventIdInvoiceBackdated.
Alternatives considered
- Free date picker for admins — rejected: breaks the date/number order too easily; the need is exactly "previous month's last working day".
- Global admin switch "backdate everything until the first invoice of the month" — rejected by the customer in favour of an explicit per-invoice checkbox.