feat: Implement mfr truncation fix and enhance invoice handling
Playwright Tests / test (push) Canceled after 0s
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.
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
---
|
||||
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.
|
||||
Reference in New Issue
Block a user