Add user-defined invoice set structure dialog (ADR 0016)
Playwright Tests / test (push) Canceled after 0s
Playwright Tests / test (push) Canceled after 0s
Lets a back-office user correct/define which invoice line belongs to which "set" and how deep it is nested, instead of relying only on the implicit "everything after a Set header until the next Set" grouping fds__prepInvoice derives. Sets may contain sets; every change applies immediately to the cached draft session and is reflected in the editor and PDF (ADR 0006/0008). Backend: - InvoiceSetStructure: pure parent-link forest (assign, change level, create, place/move-sibling, depth-first linearise + level recompute). - InvoiceDraftSetStructureDeltas: new inv/dpatch targets set.assign, set.level, set.create, set.move. - InvoiceSetPricing.Build is now order-preserving and nesting-aware (governed by the nearest converted ancestor); the set-item switch sums a header's transitive descendants so nested conversions never double-count. - FuchsPdf indents nested set members per level, mirroring the editor. Frontend: - New fis.inv_sets.js: "Set-Struktur bearbeiten" dialog — per-block table of all lines with drag & drop reorder (before/after/into), ▲▼ order and ◀▶ level buttons, fold/unfold per set (view-only), and a create bar for new Set/Text lines. Posts one delta per action; the dialog is a pure view of the server state (ADR 0006). - _Layout.cshtml / fis_main.js: cache-bust the dynamically imported per-module bundles (web/fis.<mdl>.<locale>.js/.css) with an asset version derived from the newest bundle write time, so a gulp rebuild is picked up without a stale cached module. Docs: ADR 0016, live-draft-editing concept, INVOICE_SET_PRICING.md, USER_GUIDE.md, ARCHITECTURE.md updated. Tests: InvoiceSetStructureTests (new) + additions to InvoiceDraftServiceTests/InvoiceSetPricingTests. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
---
|
||||
status: Active
|
||||
lastUpdated: 2026-07-10
|
||||
lastUpdated: 2026-09-09
|
||||
applyTo:
|
||||
- "Fuchs/Services/InvoiceDraft*"
|
||||
- "Fuchs/Services/IInvoiceDraft*"
|
||||
- "Fuchs/code/InvoiceSetStructure.cs"
|
||||
- "Fuchs/Services/ReminderDraft*"
|
||||
- "Fuchs/Services/IReminderDraft*"
|
||||
- "Fuchs/code/InvoiceDraftSession.cs"
|
||||
@@ -18,6 +19,8 @@ applyTo:
|
||||
relatedDecisions:
|
||||
- "0006-backend-authoritative-draft-editing.md"
|
||||
- "0007-targeted-draft-signalr-groups.md"
|
||||
- "0009-block-setprice-modes-are-irreversible-mutations.md"
|
||||
- "0016-user-defined-set-structure.md"
|
||||
---
|
||||
|
||||
# Live draft editing (backend-authoritative invoice previews)
|
||||
@@ -83,6 +86,19 @@ Expiry: Server (timer) --SignalR draftExpiring{token,secondsLeft}--> warn "bit
|
||||
`BuildInvoiceRequestList`), applies deltas (`ApplyDelta`), builds the view-state DTO,
|
||||
flushes to the DB by reusing `IInvoiceService.RegisterInvoiceAsync` (no new persistence
|
||||
path), renders previews from a synthesised registration, and discards by reloading.
|
||||
- **Set structure** (ADR 0016). Every cached line carries a parent link (`SetItmId` → the
|
||||
owning `typ == "set"` line, null/self = top level); sets may nest. `fds__prepInvoice`'s
|
||||
implicit tagging is the initial structure; the user edits it in the "Set-Struktur
|
||||
bearbeiten" dialog (`$inv.sets`, `fis.inv_sets.js`) which posts one delta per action —
|
||||
`set.assign` { parent } (drop onto a set / pull out), `set.level` ±1 (indent under the
|
||||
preceding sibling set / outdent to the grandparent), `set.create` { typ: set|text, title,
|
||||
parent }, `set.move` ±1 | { anchor, where } (reorder among siblings / drag before or after
|
||||
a row; the subtree travels along). `InvoiceDraftSetStructureDeltas` delegates to the pure
|
||||
`InvoiceSetStructure` (assign / change level / create / place, then **linearise** the block depth-first and mirror
|
||||
`setId`/`level` onto the `items` contract). `Refresh` publishes `lvl` on every line
|
||||
(`RecomputeLevels`), rendered as an indent class in the editor and a per-level indent in the
|
||||
PDF. `item.setprice` sums a header's **transitive** descendants. Structure deltas never
|
||||
change values — only order, membership, levels and therefore position numbers.
|
||||
- **Cache** (`InvoiceDraftCache`, singleton) stores sessions by token with an idle sliding
|
||||
TTL; `InvoiceDraftExpiryService` (a `BackgroundService`) warns before, and evicts after,
|
||||
the TTL. TTL/warn-lead are configurable under `Fuchs:DraftEditing`.
|
||||
@@ -97,12 +113,15 @@ Expiry: Server (timer) --SignalR draftExpiring{token,secondsLeft}--> warn "bit
|
||||
## Key files
|
||||
- `Fuchs/code/InvoiceDraftSession.cs` — session + `ChangeHistoryEntry` + `InvoiceDraftSums`.
|
||||
- `Fuchs/code/InvoiceDraftCalculator.cs` — pure recompute + validate.
|
||||
- `Fuchs/code/InvoiceSetStructure.cs` — pure set forest: parent links, levels, linearise, assign/level/create (ADR 0016).
|
||||
- `Fuchs/Services/InvoiceDraftSetStructureDeltas.cs` — the `set.*` patch targets.
|
||||
- `Fuchs/Services/InvoiceDraftCache.cs` / `IInvoiceDraftCache.cs` — in-memory store + TTL.
|
||||
- `Fuchs/Services/InvoiceDraftEditService.cs` / `IInvoiceDraftService.cs` — orchestration + delta contract.
|
||||
- `Fuchs/Services/InvoiceDraftExpiryService.cs` — idle warn/evict monitor.
|
||||
- `Fuchs/Notifications/DraftPreviewHub.cs`, `DraftNotifier.cs`, `IDraftNotifier.cs` — targeted signals.
|
||||
- `Fuchs/Controllers/IntranetController.InvoiceDraft.cs` — `inv/d*` endpoints.
|
||||
- `Fuchs/js/intranet/fis_main.js`, `Fuchs/js/intranet/modules/fis.inv_shared.js` — client.
|
||||
- `Fuchs/js/intranet/modules/fis.inv_sets.js` — the set-structure dialog (`$inv.sets`), a view of `dstate.req`.
|
||||
|
||||
## Reminders (Zahlungserinnerung)
|
||||
|
||||
@@ -151,3 +170,5 @@ amount pair.
|
||||
## 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)
|
||||
- [0009 — Block set-price modes are irreversible mutations](../Decisions/0009-block-setprice-modes-are-irreversible-mutations.md)
|
||||
- [0016 — User-defined set structure](../Decisions/0016-user-defined-set-structure.md)
|
||||
|
||||
Reference in New Issue
Block a user