Changed Reminder Systematics analogue to invoices

This commit is contained in:
Stefan
2026-07-10 22:50:26 +02:00
parent 83d1c28b29
commit 5c0fdc6c1d
24 changed files with 1810 additions and 145 deletions
+51 -2
View File
@@ -4,11 +4,16 @@ lastUpdated: 2026-07-10
applyTo:
- "Fuchs/Services/InvoiceDraft*"
- "Fuchs/Services/IInvoiceDraft*"
- "Fuchs/Services/ReminderDraft*"
- "Fuchs/Services/IReminderDraft*"
- "Fuchs/code/InvoiceDraftSession.cs"
- "Fuchs/code/InvoiceDraftCalculator.cs"
- "Fuchs/code/ReminderDraftSession.cs"
- "Fuchs/code/ReminderDraftCalculator.cs"
- "Fuchs/Notifications/DraftPreviewHub.cs"
- "Fuchs/Notifications/*DraftNotifier*"
- "Fuchs/Controllers/IntranetController.InvoiceDraft.cs"
- "Fuchs/Controllers/IntranetController.ReminderDraft.cs"
- "Fuchs/js/intranet/**"
relatedDecisions:
- "0006-backend-authoritative-draft-editing.md"
@@ -23,8 +28,8 @@ server memory, not in the browser. The browser posts single edits, the server mu
the cached record, recomputes totals/VAT and re-validates, then pushes a "state changed"
signal so the browser re-fetches and re-renders. This makes the backend the single source
of truth (server-computed sums, consistency checks, in-place PDF preview, change history,
explicit discard), reversing the earlier stateless editor. Invoices are the pilot;
reminders are intended to mirror the same design.
explicit discard), reversing the earlier stateless editor. Invoices were the pilot;
reminders now mirror the same design (see "Reminders" below).
## How it works
@@ -92,6 +97,50 @@ Expiry: Server (timer) --SignalR draftExpiring{token,secondsLeft}--> warn "bit
- `Fuchs/Controllers/IntranetController.InvoiceDraft.cs``inv/d*` endpoints.
- `Fuchs/js/intranet/fis_main.js`, `Fuchs/js/intranet/modules/fis.inv_shared.js` — client.
## Reminders (Zahlungserinnerung)
Reminders mirror the same backend-authoritative model with a reminder-shaped session. A
reminder chases a single invoiced amount, so the machinery is simpler than an invoice's:
there are no line-item blocks, VAT grouping or reordering — just recipient fields and the
amount pair.
- **Endpoints** are `rem/d*` (`dopen`/`dstate`/`dpatch`/`dpreview`/`dsave`/`dhistory`/`dclose`),
dispatched from `Do_Process_Reminder`. Finalise + email still runs through the existing
`rem/conf` (`HandleReminderConf`), exactly as invoices finalise through `req/sconf`.
- **Session** (`ReminderDraftSession`) holds the editor's `new` (subject / invoiceaddress /
invoiceemail / text / amount / amount_payed / CustomValues) and `rem` (invid / type /
invoiceid / invoicedate) blocks, plus server-computed `Sums` (`AmountTotal`, `AmountPayed`,
`AmountOpen`). It reuses the shared `ChangeHistoryEntry`; validation uses
`ReminderDraftValidationMessage`.
- **Calculation** (`ReminderDraftCalculator`, static/pure): `AmountOpen = AmountTotal AmountPayed`,
plus email/address/subject/open-amount plausibility checks. Exhaustively unit-tested.
- **Deltas** (`ReminderDraftDelta`): scalar `email`/`address`/`subject`/`text` (HTML-sanitised via
the shared `InvoiceDraftEditService.HtmlToPlain`), the numeric `amount`/`amount_payed`
(normalised to an invariant decimal string), and `contact` (→ `CustomValues`).
- **Orchestration** (`ReminderDraftEditService`, scoped) flushes to the DB by reusing
`IReminderService.RegisterReminderAsync`, and renders previews from a synthesised
`ReminderRegistration` (including the single-invoice `invoices` row the reminder PDF table
renders) so a preview needs no DB round-trip. **Note:** `RegisterReminderAsync` is create-only
(there is no `fds__setReminder` update proc), so a re-saved reminder draft does not update the
prior DB row — the primary flow (preview → confirm) flushes once immediately before finalising.
- **Cache/expiry** (`ReminderDraftCache` singleton + `ReminderDraftExpiryService`) mirror the
invoice ones and share the same `Fuchs:DraftEditing` TTL config.
- **Signals** reuse the shared `DraftPreviewHub` + `IDraftNotifier` unchanged — the token-keyed
groups serve invoice and reminder drafts alike.
- **Frontend** (`$inv.rd` in `fis.inv_shared.js`) opens/joins on `rem/dopen`, posts one delta per
inline edit and per item-row amount change, renders the open-amount footer + validation from the
server state, and previews/finalises through `rem/dpreview``rem/dsave``rem/conf`. It shares
the invoice editor DOM; `$inv.d` and `$inv.rd` each key off their own token, so the shared inline
editor safely no-ops for whichever mode is inactive.
### Reminder key files
- `Fuchs/code/ReminderDraftSession.cs` — session + `ReminderDraftSums` + `ReminderDraftValidationMessage`.
- `Fuchs/code/ReminderDraftCalculator.cs` — pure open-amount recompute + validate.
- `Fuchs/Services/ReminderDraftCache.cs` / `IReminderDraftCache.cs` — in-memory store + TTL.
- `Fuchs/Services/ReminderDraftEditService.cs` / `IReminderDraftService.cs` — orchestration + delta contract.
- `Fuchs/Services/ReminderDraftExpiryService.cs` — idle warn/evict monitor.
- `Fuchs/Controllers/IntranetController.ReminderDraft.cs``rem/d*` endpoints.
## Related decisions
- [0006 — Backend-authoritative draft editing](../Decisions/0006-backend-authoritative-draft-editing.md)
- [0007 — Targeted draft SignalR groups](../Decisions/0007-targeted-draft-signalr-groups.md)