Changed Reminder Systematics analogue to invoices
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user