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.
69 lines
4.0 KiB
Markdown
69 lines
4.0 KiB
Markdown
---
|
||
status: Accepted
|
||
date: 2026-09-30
|
||
applyTo:
|
||
- "Fuchs_Database/dbo/Functions/fds__fn_invoiceBackdate.sql"
|
||
- "Fuchs_Database/dbo/Stored Procedures/fds__setInvoiceFinal.sql"
|
||
- "Fuchs/code/InvoiceDates.cs"
|
||
- "Fuchs/Services/ERechnungMapper.cs"
|
||
- "Fuchs/Services/InvoiceService.cs"
|
||
- "Fuchs/Controllers/IntranetController.InvoiceDraft.cs"
|
||
- "Fuchs/Controllers/IntranetController.Requests.cs"
|
||
- "Fuchs/js/intranet/modules/fis.inv_shared.js"
|
||
supersededBy: ""
|
||
---
|
||
|
||
# 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 to `DateCreated` for drafts.
|
||
- **Backdating option.** When finalising, a user with `fds_inv > 2` may 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/dpreview` returns it so the preview dialog shows the
|
||
per-invoice checkbox; `req/sconf` passes `@backdate = 1`, and `fds__setInvoiceFinal` re-evaluates
|
||
the function and `THROW`s (60001) when it is no longer allowed — nothing is finalised then.
|
||
- **Serialized finalisation.** `fds__setInvoiceFinal` runs check + numbering `UPDATE` in one
|
||
transaction under an exclusive, transaction-owned app lock (`sp_getapplock`, resource
|
||
`fds__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/sconf` maps 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, and `DueDate` is 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 EventId `InvoiceBackdated`.
|
||
|
||
## 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.
|