feat(backend+frontend): invoice module with ZUGFeRD XML, itemised positions, and contact decryption #286

Merged
niboer merged 3 commits from feat/issue-16-invoice into main 2026-07-10 00:37:22 +02:00
Owner

Allgemein

  • closes #16
  • refs #283
  • REBASE-MERGE

Altes Verhalten

  • Rechnung hatte nur einen Posten "Teilnahme an {event.title}"
  • PDF-Tabelle nur 2 Spalten (Position | Betrag)
  • Adressdaten (Absender/Empfaenger) wurden unverschluesselt aus der DB gelesen (Verschluesselter Kryptotext im PDF)
  • decrypt(invoice.xmlPayload) wurde im Download aufgerufen aber das Ergebnis nie genutzt
  • Massenlauf erstellte doppelte Rechnungen bei erneutem Ausloesen
  • 17 inkrementelle Commits ohne thematische Trennung

Neues Verhalten

  • JSONB-Spalte items auf Invoice: speichert Einzelpositionen [{ label, grossCents, quantity }]
  • deriveItems() leitet Positionen aus formDefinition + formFinal ab, aggregiert identische
  • PDF mit 4-Spalten-Tabelle (Artikel | Anzahl | Preis | Summe), Linien via Font-Metriken berechnet
  • ZUGFeRD-XML mit korrekter BilledQuantity > 1
  • Download nutzt decrypt(invoice.xmlPayload) fuer konsistentes XML im PDF-Anhang
  • Neuer shared Helper decryptContactData() / decryptAddressOnly() fuer Adress-Entschluesselung
  • Alle 9 Adressstellen in invoices.ts entschluesseln jetzt korrekt (Bugfix)
  • Massenlauf ueberspringt bereits abgerechnete Gaeste/Gruppen
  • postinstall-Kaskade: Root -> Backend (prisma generate) -> Frontend (orval)
  • Branch in 3 thematische Commits aufgeteilt,

Begruendung

Das ZUGFeRD-Rechnungsmodul war bisher ohne Einzelpositionen und mit unverschluesselten Adressen nicht produktiv nutzbar. Die Aufteilung in Backend/Frontend/Chore-Commits ermoeglicht fokussierte Reviews und saubere Merge-Historie.

Test-Hinweise

  • cd backend && npm run gen:test-pdf erzeugt Test-PDF nach output/test-rechnung.pdf
  • cd backend && npm run test — alle 622 Tests gruen
  • npm run test:build — Lint + Typecheck + Tests
  • E2E: npm run test:integration:fast (benoetigt laufende Docker-Container)

Checkliste

  • use:testid an neuen <button>, <a>, <form>-Elementen gesetzt?
  • Korrespondierender E2E-Test in e2e/specs/ angelegt?
### Allgemein - closes #16 - refs #283 - *REBASE-MERGE* ### Altes Verhalten - Rechnung hatte nur einen Posten "Teilnahme an {event.title}" - PDF-Tabelle nur 2 Spalten (Position | Betrag) - Adressdaten (Absender/Empfaenger) wurden unverschluesselt aus der DB gelesen (Verschluesselter Kryptotext im PDF) - `decrypt(invoice.xmlPayload)` wurde im Download aufgerufen aber das Ergebnis nie genutzt - Massenlauf erstellte doppelte Rechnungen bei erneutem Ausloesen - 17 inkrementelle Commits ohne thematische Trennung ### Neues Verhalten - JSONB-Spalte `items` auf Invoice: speichert Einzelpositionen `[{ label, grossCents, quantity }]` - `deriveItems()` leitet Positionen aus `formDefinition` + `formFinal` ab, aggregiert identische - PDF mit 4-Spalten-Tabelle (Artikel | Anzahl | Preis | Summe), Linien via Font-Metriken berechnet - ZUGFeRD-XML mit korrekter `BilledQuantity` > 1 - Download nutzt `decrypt(invoice.xmlPayload)` fuer konsistentes XML im PDF-Anhang - Neuer shared Helper `decryptContactData()` / `decryptAddressOnly()` fuer Adress-Entschluesselung - Alle 9 Adressstellen in `invoices.ts` entschluesseln jetzt korrekt (Bugfix) - Massenlauf ueberspringt bereits abgerechnete Gaeste/Gruppen - postinstall-Kaskade: Root -> Backend (prisma generate) -> Frontend (orval) - Branch in 3 thematische Commits aufgeteilt, ### Begruendung Das ZUGFeRD-Rechnungsmodul war bisher ohne Einzelpositionen und mit unverschluesselten Adressen nicht produktiv nutzbar. Die Aufteilung in Backend/Frontend/Chore-Commits ermoeglicht fokussierte Reviews und saubere Merge-Historie. ### Test-Hinweise - `cd backend && npm run gen:test-pdf` erzeugt Test-PDF nach `output/test-rechnung.pdf` - `cd backend && npm run test` — alle 622 Tests gruen - `npm run test:build` — Lint + Typecheck + Tests - E2E: `npm run test:integration:fast` (benoetigt laufende Docker-Container) ### Checkliste - [x] `use:testid` an neuen `<button>`, `<a>`, `<form>`-Elementen gesetzt? - [x] Korrespondierender E2E-Test in `e2e/specs/` angelegt?
- Invoice + InvoiceJob Prisma models with DB sequence
- invoice-generator: ZUGFeRD XML (EN 16931), pdf-lib PDF with 4-column table
- OpenAPI schema with 7 invoice endpoints
- items Json? column for itemised positions via deriveItems() from form data
- Identical positions aggregated by label + price across guests in groups
- Contact decryption helper (decryptContactData / decryptAddressOnly)
- All 9 address usages in invoices.ts now properly decrypt encrypted data
- Download route uses decrypt(invoice.xmlPayload) instead of regeneration
- Massenlauf skips already-billed personId/groupId combinations

refs #16
- Invoice tab in event detail page with download action menu
- Invoice run creation via ActionMenu with polling status
- Self-service invoice listing for current user + group members
- Person/group name instead of truncated UUID in Empfaenger column
- Orval API types regenerated from OpenAPI schema
- i18n keys (DE/EN) for invoice module
- E2E test: form fill -> invoice run -> PDF download

refs #16
chore: add postinstall cascade and restructure commit layout
Some checks failed
CI Backend / lint-backend (pull_request) Failing after 29s
CI Backend / test-backend (pull_request) Has been skipped
CI Frontend / lint-frontend (pull_request) Failing after 27s
CI Frontend / test-frontend (pull_request) Has been skipped
CI Meta / conventional-commit (pull_request) Successful in 1s
E2E / e2e (pull_request) Failing after 23s
ec82f2f359
- postinstall: Root -> Backend (prisma generate) -> Frontend (orval)
- scripts/install-all.mjs handles sub-package installs
- Branch restructured into backend/frontend/chore commits

refs #16
niboer force-pushed feat/issue-16-invoice from ec82f2f359
Some checks failed
CI Backend / lint-backend (pull_request) Failing after 29s
CI Backend / test-backend (pull_request) Has been skipped
CI Frontend / lint-frontend (pull_request) Failing after 27s
CI Frontend / test-frontend (pull_request) Has been skipped
CI Meta / conventional-commit (pull_request) Successful in 1s
E2E / e2e (pull_request) Failing after 23s
to 3d2c913b24
Some checks failed
CI Backend / lint-backend (pull_request) Failing after 30s
CI Backend / test-backend (pull_request) Has been skipped
CI Frontend / lint-frontend (pull_request) Failing after 27s
CI Frontend / test-frontend (pull_request) Has been skipped
CI Meta / conventional-commit (pull_request) Successful in 1s
E2E / e2e (pull_request) Failing after 22s
2026-07-09 23:53:05 +02:00
Compare
niboer force-pushed feat/issue-16-invoice from 3d2c913b24
Some checks failed
CI Backend / lint-backend (pull_request) Failing after 30s
CI Backend / test-backend (pull_request) Has been skipped
CI Frontend / lint-frontend (pull_request) Failing after 27s
CI Frontend / test-frontend (pull_request) Has been skipped
CI Meta / conventional-commit (pull_request) Successful in 1s
E2E / e2e (pull_request) Failing after 22s
to f77a883b1b
Some checks failed
CI Backend / lint-backend (pull_request) Successful in 55s
CI Frontend / lint-frontend (pull_request) Successful in 1m20s
CI Meta / conventional-commit (pull_request) Successful in 1s
E2E / e2e (pull_request) Failing after 1m11s
CI Backend / test-backend (pull_request) Successful in 3m34s
CI Frontend / test-frontend (pull_request) Successful in 2m3s
2026-07-10 00:02:22 +02:00
Compare
niboer changed title from feat(backend+frontend): invoice module with ZUGFeRD XML, itemised positions, and contact decryption to WIP: feat(backend+frontend): invoice module with ZUGFeRD XML, itemised positions, and contact decryption 2026-07-10 00:07:08 +02:00
niboer force-pushed feat/issue-16-invoice from f77a883b1b
Some checks failed
CI Backend / lint-backend (pull_request) Successful in 55s
CI Frontend / lint-frontend (pull_request) Successful in 1m20s
CI Meta / conventional-commit (pull_request) Successful in 1s
E2E / e2e (pull_request) Failing after 1m11s
CI Backend / test-backend (pull_request) Successful in 3m34s
CI Frontend / test-frontend (pull_request) Successful in 2m3s
to c613faa40e
Some checks failed
CI Backend / test-backend (pull_request) Has been cancelled
CI Frontend / lint-frontend (pull_request) Has been cancelled
CI Frontend / test-frontend (pull_request) Has been cancelled
CI Meta / conventional-commit (pull_request) Has been cancelled
E2E / e2e (pull_request) Has been cancelled
CI Backend / lint-backend (pull_request) Has been cancelled
2026-07-10 00:21:46 +02:00
Compare
niboer force-pushed feat/issue-16-invoice from c613faa40e
Some checks failed
CI Backend / test-backend (pull_request) Has been cancelled
CI Frontend / lint-frontend (pull_request) Has been cancelled
CI Frontend / test-frontend (pull_request) Has been cancelled
CI Meta / conventional-commit (pull_request) Has been cancelled
E2E / e2e (pull_request) Has been cancelled
CI Backend / lint-backend (pull_request) Has been cancelled
to ba7f9d0c7d
Some checks failed
CI Backend / lint-backend (pull_request) Successful in 55s
CI Frontend / lint-frontend (pull_request) Successful in 1m20s
E2E / e2e (pull_request) Successful in 4m48s
CI Backend / test-backend (pull_request) Successful in 3m33s
CI Frontend / test-frontend (pull_request) Successful in 1m59s
CI Meta / conventional-commit (pull_request) Failing after 1s
CI Backend / lint-backend (push) Successful in 57s
CI Frontend / lint-frontend (push) Successful in 1m19s
CI Meta / conventional-commit (push) Has been skipped
CI Backend / test-backend (push) Successful in 3m34s
CD / build-and-push (push) Successful in 1m15s
CI Frontend / test-frontend (push) Successful in 2m5s
2026-07-10 00:22:23 +02:00
Compare
niboer changed title from WIP: feat(backend+frontend): invoice module with ZUGFeRD XML, itemised positions, and contact decryption to feat(backend+frontend): invoice module with ZUGFeRD XML, itemised positions, and contact decryption 2026-07-10 00:26:08 +02:00
niboer scheduled this pull request to auto merge when all checks succeed 2026-07-10 00:31:55 +02:00
niboer merged commit ba7f9d0c7d into main 2026-07-10 00:37:22 +02:00
niboer deleted branch feat/issue-16-invoice 2026-07-10 00:37:22 +02:00
Sign in to join this conversation.
No description provided.