feat(invoices): close Phase-2 gaps — finance cross-link + structured addresses

Three items from docs/plans/invoices-module.md §"Offene Punkte" that
actually block real-world dogfooding:

1. Bezahlte Rechnung → Finance-Einnahme

  - financeStore.upsertTransactionFromInvoice(): deterministic id
    (invoice-tx-{invoiceId}) so marking the same invoice paid twice
    updates instead of duplicating. Uses table.put for the upsert.
  - invoicesStore.markPaid() calls it after the status transition,
    decrypts to get the gross + snapshot, converts minor→major for
    the finance row, formats description as "Rechnung {number} — {client}".
  - Best-effort: the call is try/catched so the invoice write (the
    thing the user initiated) never fails because of a finance bridge
    hiccup. Logs a warning instead.
  - Multi-currency caveat: finance's bare-number model loses the
    currency — documented in the upsert helper's comment. Works for
    single-currency freelancers (the 95% case).

2. Strukturierte Adressen für QR-Bill

  - LocalInvoiceSettings gains senderStreet/Zip/City/Country (nullable,
    so existing rows don't need a migration). Encryption registry
    updated to cover the new fields — same sensitivity tier as the
    legacy senderAddress blob.
  - InvoiceClientSnapshot gains street/zip/city/country, same shape
    as Debtor.
  - qr-bill.buildQRBillData prefers structured fields; falls back to
    parseAddress(senderAddress) for users who haven't touched the new
    settings form. Same preference chain on the client/debtor side.
  - PDF header + DetailView recipient block prefer structured too —
    stays in lockstep with what the QR-Bill reads.
  - SenderProfileForm replaces the single textarea with four labeled
    inputs. Legacy free-text address moves behind a <details> as a
    "weird edge case" escape hatch (Postfach, c/o etc.).
  - ClientPicker: same split, with contacts-source mapping using
    structured fields directly (contacts already have street/postalCode/
    city so no info loss).
  - Three new qr-bill tests cover the preference order: structured
    wins, legacy falls back, malformed snapshot omits debtor.

3. MODULE_REGISTRY.md

  - Added `invoices` under "Finanzen" with the cross-link note.

Tests: 48/48 green (up from 45), 0 type errors. Open Phase-2/3 items
still parked: camt.053 bank reconciliation, number-sequence multi-
device collision, unfreezing the paid→void edge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-20 18:58:18 +02:00
parent 3194180efb
commit 394aa79328
14 changed files with 413 additions and 35 deletions

View file

@ -88,11 +88,12 @@ Alle 76 Module der Mana-App (`apps/mana/apps/web/src/lib/modules/`).
| `citycorners` | CityCorners | Stadtführer für Konstanz |
| `wetter` | Wetter | Open-Meteo Wetter, DWD-Warnungen, Regen-Nowcast, Multi-Model-Vergleich |
## Finanzen (2)
## Finanzen (3)
| Modul | Name | Beschreibung |
|---|---|---|
| `finance` | Finance | Einnahmen/Ausgaben-Tracking mit Kategorien und Budgets |
| `invoices` | Rechnungen | Rechnungen stellen (PDF + Schweizer QR-Bill), Mail-Versand, Zahlungs-Tracking. Bezahlte Rechnungen erscheinen automatisch als Einnahme im Finance-Modul. |
| `credits` | Credits & Abo | Credit-Verwaltung und Subscription-Management |
## Tagesübersicht (2)