feat: Implement mfr truncation fix and enhance invoice handling
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:
Stefan
2026-09-30 16:56:08 +02:00
parent 7357f60120
commit c741535aec
79 changed files with 2739 additions and 143 deletions
+31 -1
View File
@@ -181,6 +181,36 @@ Confirming the preview modal posts to **`req/sconf`**
`[InvoiceId] = fds__newInvoiceId(YEAR())` → format `R<year>-<0000>`
(the draft only ever had the internal numeric `Id`). `Version` is bumped
and any linked "replaces" bookkeeping (storno/credit chains) is updated.
- **Invoice date** = `DateFinalized` — printed on the PDF and used as the
e-invoice issue date (`InvoiceDates.IssueDate`, ADR 0032).
- **Backdating (ADR 0032):** users with `fds_inv > 2` see a checkbox in the
preview dialog (`inv/dpreview` returns `backdate`) to date the invoice to
the last working day (Mon–Fri) of the previous month, as long as no
invoice has been finalised after that day. `req/sconf` then passes
`@backdate = 1`; `fds__fn_invoiceBackdate` is re-checked and the proc
throws 60001 (nothing finalised) if it is no longer allowed. Number year and
`DueDate` follow the backdated day. The proc serializes all finalisations
(transaction + `sp_getapplock`) so the check and the numbering cannot
interleave with a concurrent finalisation. The controller maps 60001 to
the "create again without backdating" message; a 0-row result (already
final/sent, e.g. double submit) is reported as "already finalised".
- **Same date everywhere (ADR 0035):** invoice lists, revenue reports,
the DATEV export (period + `Belegdatum`), request details and reminders
all use `[dbo].[fds__fn_invoiceDate]` — `DateFinalized` as German
calendar day, drafts fall back to `DateCreated` — so a backdated invoice
appears in the previous month everywhere, including DATEV. The reminder
PDF's own date follows the same rule (reminder `DateFinalized`).
- **Abschlagsrechnungen (ADR 0034):** `IntInvoiceAllocation` lines carry the
credited intermediate invoice in `reference_InvId` (seeded by
`fds__prepInvoice` and `fds__prepStorno_recreate`, round-tripped by the
editor as item `ref`); finalisation records it in
`fds__invoice_details.AllocatedTo_InvId` so the same Abschlagsrechnung is
not offered again. The claim only counts while the crediting invoice is
alive: once it is cancelled (storno, or replaced via storno + recreate)
`fds__fn_IntermediateIsAllocatedToOther` treats the Abschlagsrechnung as
free again and the next finalisation takes the allocation over. A
Stornorechnung (`'c'`) never claims an allocation, even though a storno
copy mirrors the credit lines.
2. On success (`IsFinal == true` returned), the controller:
- Reloads the invoice via `LoadInvoiceAsync`.
- Calls `StoreInvoiceDocumentFileAsync` → renders the **final** PDF
@@ -283,7 +313,7 @@ Once an invoice is `IsFinal`, the request/invoice list menus
| **View / display PDF** | `$inv.disp(id, 'inv')` | `inv/rdoc` | Renders page images for on-screen display only. If a finalised, non-external invoice has no stored PDF, it is regenerated and persisted first (ADR 0025) |
| **Download / open PDF** | `req/idoc` or `inv/pget`-derived | `req/idoc` | Serves the stored (or freshly rendered) PDF inline |
| **Mark paid / unpaid** | `$inv.setPyd`/`setUpd` | `inv/setpyd` / `inv/setupd` | `fds__setInvoicePayed` / `fds__setInvoiceUNPayed` |
| **Storno (cancel)** | `$inv.storno` → `$inv.cSt` | `inv/storno` (mode `simple`/`copy`) | `fds__createStorno_simple` / `fds__createStorno_copy`; the replaced invoice is cross-linked (`Replaces_InvId`) and cancelled once the storno itself is finalised |
| **Storno (cancel)** | `$inv.storno` → `$inv.cSt` (first `inv/pget`: refreshes mfr entities only for numeric mfr ids, no-op for Intranet ids) | `inv/storno` (mode `simple`/`copy`) | `fds__createStorno_simple` / `fds__createStorno_copy`; the replaced invoice is cross-linked (`Replaces_InvId`) and cancelled once the storno itself is finalised |
| **Credit note** | `$inv.credit` → `$inv.cSt` | `inv/credit` (mode `credit`) | `fds__createCredit_simple` — same preview/finalise/email loop as a normal invoice |
| **Continue editing a draft** | `$inv.cntInv` / `clCntInv` | `inv/get` | Only available while `isFinal == false`; re-opens the same stateless editor |