User Stories — Epic 6: Finanzen
Methodik: Connextra (Als/möchte/damit) + Gherkin-Akzeptanzkriterien (Gegeben/Wenn/Dann) + INVEST-Qualitätsgate. Sprache der Stories DE; Fachbegriffe/Status/Codes wie im Schema (stabile
movement_type-/payment_method-Codes, nie übersetzt). Bezug: Modul 6 (15-finanzen.md), Schema Dok 30 §2.7invoices, §4 (movements/payment_links/monthly_closings), §11 (Auto-Posting AP-1..AP-9), §13 (RLS-Matrix); Compliance Dok 45 (OR 957–963b / GeBüV / MWSTG). Rollen-Codes kanonisch (UPPERCASE):ADMIN,BUCHHALTUNG,OPERATIONS,FAHRER,AFFILIATE,READONLY. IDs:US-FIN-NN(stabil). Status: Entwurf (zur Freigabe).
Personas in diesem Epic
| Rolle | Person | Bezug zu den Finanzen |
|---|---|---|
BUCHHALTUNG | Mariela | Kernnutzerin: manuelle Erfassung, Zahlungen, Debitoren/Kreditoren, Monatsabschluss (open↔review), Export — das Tagesgeschäft im Ledger |
ADMIN | Marcel | alles + Storno (void), Periode close/reopen, Lookup-/MWST-Pflege, Anfangssalden |
OPERATIONS | Markus | nur lesend im Ledger (operative Übersicht Kasse/Debitoren); löst über Logistik/Depot Auto-Postings aus |
READONLY | Treuhänder/Gast | nur lesen; offizielle Schnittstelle ist der Treuhänder-Export (CSV/Excel/PDF + MWST-Kennzahlen) |
System | Auto-Posting | Edge-/Server-Function post_movement() via service_role — bucht Events idempotent ins Ledger, nie über Client-Keys |
INVEST-Gate (für alle Stories dieses Epics geprüft): jede Story ist unabhängig schneidbar, verhandelbar, liefert Geschäftswert, schätzbar, klein genug für einen Schritt und über die Gherkin-Szenarien testbar (inkl. Negativfälle, RLS, Revisionssicherheit).
US-FIN-01 — Manuelle Bewegung erfassen (Erfassung_Digitar-Ersatz)
Als BUCHHALTUNG möchte ich einen Geldfluss als einzelne movements-Zeile erfassen (Vorgang, Zahlweise, Datum, Partei, Gesamtbetrag, Bezahlt, Notiz), damit jeder Cash-/Bank-Vorgang genau einmal und revisionssicher im Ledger landet.
Akzeptanzkriterien
- Gegeben ich wähle
movement_type_code+payment_method_codeaus den Bottom-Sheet-Selects und gebe Datum/Betrag, wenn ich speichere, dann entsteht einemovements-Zeile mitsource_type='manual', undperiod_key,cash_in/out,bank,open_debt,statusergeben sich abgeleitet ausv_movements(nicht gespeichert). - Gegeben eine
cash-Zahlweise (CASH), wenn die Zeile gebucht ist, dann erzeugt sie entwedercash_inodercash_out, nie beides (disjunkte Typ-Mengen). - Gegeben eine
bank-Zahlweise (BANK/TWINT/CARD), dann fliesst der Betrag inbank_chf, nicht in die Kasse. - Gegeben
total_chf < paid_chfbeiINVOICE, wenn ich speichere, dann warnt das System „Überzahlung" und speichert nur mitallow_overpayment.
Traceability: movements (§3.2/Dok 30 §4.1), v_movements (§3.5), Workflow A (§4). (Deckt data-01 ab: alle Beträge in einer Geld-Domäne numeric(12,2).)
US-FIN-02 — Auto-Posting: Event erzeugt Bewegung (idempotent)
Als System (Auto-Posting via service_role) möchte ich aus jedem operativen Event (Rechnung, Zahlung, Depot, Provision) genau eine movements-Zeile per Upsert erzeugen, damit das Ledger die Single Source of Truth bleibt und Re-Delivery/Races kein Duplikat erzeugen.
Akzeptanzkriterien
- Gegeben ein operatives Event mit
source_type+source_id, wennpost_movement()läuft, dann macht es ein Upsert aufmovementsmitonConflict (source_type, source_id)→ ein Event = max. eine Buchung. - Gegeben dasselbe Event wird erneut zugestellt (Retry/Race), wenn
post_movement()erneut läuft, dann wird dieselbe Zeile überschrieben — kein Duplikat (movements_source_uniq). - Gegeben der Ursprung wird korrigiert (z. B. Betrag geändert), wenn das Folge-Event eintrifft, dann überschreibt der Upsert die bestehende Zeile statt eine neue anzulegen.
- Gegeben ein Auto-Posting-Versuch durch einen Client-Key, dann wird er abgelehnt — Auto-Posting läuft ausschliesslich über
service_role(kein Client kann Quellbuchungen fälschen). - Gegeben eine manuelle Zeile (
source_id=NULL), dann greift das Unique bewusst nicht (mehrere manuelle Buchungen erlaubt).
Traceability: Auto-Posting-Engine §4-B, Dok 30 §11.1–11.3 (AP-1..AP-9, Pseudocode post_movement), movements_source_uniq (§11.2). (Deckt Idempotenz-Härtung 8.9 ab.)
US-FIN-03 — Rechnung im Ledger buchen (Forderung, genau einmal)
Als System (ausgelöst durch Rechnungsstellung in Modul 4/5) möchte ich bei invoices-Anlage eine INVOICE-Forderungsbuchung erzeugen, damit die Kundenforderung genau einmal im Ledger erscheint — ohne Doppelzählung.
Akzeptanzkriterien
- Gegeben eine neu angelegte
invoices-Zeile, wenn AP-1 läuft, dann entstehtmovements(INVOICE,source_type='invoice_issued',source_id=invoices.id,total_chf=invoices.total_chf,paid_chf=0, KanalOPEN,period_keyausissue_date) undinvoices.movement_idwird gesetzt (Buchungsbrücke). - Gegeben eine angenommene Offerte, wenn sie konvertiert wird, dann wird keine
RECEIVABLEgebucht — die Forderung entsteht nur bei Rechnungsstellung (F-06/AP-10 entfällt; keineRECEIVABLE+INVOICE-Doppelzählung inv_debitoren/v_dashboard). - Gegeben die Rechnungssumme wird im Ursprung korrigiert, wenn AP-1 erneut feuert, dann überschreibt der Upsert dieselbe
INVOICE-Zeile (Betrag aktualisiert, kein Duplikat).
Traceability: Dok 30 §2.7 (invoices, movement_id), §11.1 (AP-1), F-06 (§2 Finanzen).
US-FIN-04 — Kundenzahlung erfassen mit expliziter Verknüpfung
Als BUCHHALTUNG möchte ich zu einer offenen INVOICE eine Zahlung erfassen und sie explizit an die Rechnung hängen, damit die Restschuld prüfbar (statt per String-Match) berechnet wird.
Akzeptanzkriterien
- Gegeben eine offene
INVOICE, wenn ich „Zahlung erfassen" auslöse, dann entstehtmovements(INVOICE_PAYMENT,source_type='debtor_payment') und einepayment_links-Zeile (payment_id,target_id=INVOICE-movement,amount_chf > 0). - Gegeben eine Splitzahlung auf mehrere Rechnungen, wenn ich erfasse, dann entstehen mehrere
payment_links-Zeilen, deren Summe≤ paid_chfist. - Gegeben dieselbe
(payment_id, target_id)-Kombination, wenn sie doppelt angelegt würde, dann wird sie durchpayment_links_uniqabgelehnt. - Gegeben Altdaten ohne Link, wenn die Restschuld berechnet wird, dann greift der Migrations-Fallback
reference + contact_id(View deckt beide Pfade ab).
Traceability: Dok 30 §4.2 (payment_links), §11.1 (AP-2), Workflow C (§4).
US-FIN-05 — Rechnungsstatus OPEN/PARTIAL/PAID (Trigger)
Als BUCHHALTUNG möchte ich, dass sich invoices.status automatisch aus den zugeordneten Zahlungen ergibt, damit Mahnwesen und Debitoren auf einem verlässlichen Status fussen.
Akzeptanzkriterien
- Gegeben Zahlungen sind via
payment_linksderINVOICE-Bewegung zugeordnet, wenn der Trigger aufpayment_links/movementsfeuert, dann schreibt erinvoices.status:Σ ≥ total_chf→PAID,0 < Σ < total_chf→PARTIAL,Σ = 0→ bleibtOPEN(F-04, bewusste Ausnahme von „Status nur aus Views"; der Restschuld-Betrag bleibt View-abgeleitet). - Gegeben der Rechnungsstatus, dann dient er Mahnwesen/Debitoren — er ist kein Auslöser der Affiliate-Provision (Geschäftsentscheid 2026-06-28 #19): die Provision entsteht bei Offerte-Konversion (
pending) und wird fällig/freigegeben beim Tracking-DELIVEREDder Sendung (US-AFF-05/US-AFF-06). Die früherePAID-Bindung ist aufgehoben. - Gegeben eine Teil- oder Vollzahlung, dann wird kein Provisions-Trigger ausgelöst (Provisions-Lifecycle ist von
invoices.statusentkoppelt).
Traceability: Dok 30 §2.7 (F-04), Workflow C Schritt 4 (§4). (Entscheid 2026-06-28 #19: Provisions-Auslöser = DELIVERED, nicht PAID/Zahlung — vormals 🔲 E14, jetzt entschieden.)
US-FIN-06 — Debitoren & Restschuld (abgeleitet)
Als BUCHHALTUNG möchte ich offene Kundenforderungen mit Restschuld und Ampel sehen, damit ich weiss, wer noch zahlen muss — ohne dass ein Saldo doppelt gespeichert wird.
Akzeptanzkriterien
- Gegeben Forderungen vom Typ
INVOICE/RECEIVABLE/DEPOSIT_CASH, wenn ichv_debitorenöffne, dann sehe ich nur Zeilen mitopen_debt_chf > 0.05, jeweils mitstatus_code(🟢PAID/ 🟠PARTIAL/ 🔴OPEN). - Gegeben eine Zahlung wird erfasst, wenn die View neu rechnet, dann sinkt
open_debt_chf = max(0, total − Σ Zahlungen)(primär überpayment_links, Fallbackreference + contact_id) — Restschuld nie negativ. - Gegeben
Σ Zahlungen > total(Überzahlung), dann istopen_debt_chf = 0und ein Warn-Badge „Überzahlung" erscheint.
Traceability: v_debitoren (§3.6), v_movements.open_debt_chf (§3.5), Validierung Restschuld-Match (§7).
US-FIN-07 — Interner Transfer Kasse ↔ Bank (Balance-Invariante)
Als BUCHHALTUNG möchte ich Geld zwischen Kasse, Bank und Twint umbuchen (z. B. CASH_TO_BANK), damit Kassen- und Bankbestand stimmen — ohne dass der Transfer als Ertrag/Aufwand oder als offene Schuld erscheint.
Akzeptanzkriterien
- Gegeben ich wähle
CASH_TO_BANKmit Betrag/Datum, wenn ich speichere, dann entsteht der Transfer mit gemeinsamemtransfer_group_id, StatusINTERNAL_TRANSFER,open_debt_chf = 0, und er ist aus Debitoren/Kreditoren/Erfolgsrechnung ausgeschlossen. - Gegeben ein Transfer mit zwei Beinen (
transfer_group_id), wenn die Beträge der beiden Beine nicht übereinstimmen, dann wird die Buchung abgelehnt — die Balance-Invariante erzwingt gleiche Beträge je Gruppe (Negativszenario: unbalancierter Transfer). - Gegeben ein Transfer würde den Kassenbestand negativ machen, wenn ich speichere, dann erscheint eine Soft-Warnung (kein harter Block; realer Bargeldbestand kann abweichen).
Traceability: movement_types.is_internal_transfer (§3.1), Workflow D (§4), Validierung interner Transfer (§7). (Deckt data-05 ab: transfer_group_id-Balance-Invariante per Trigger erzwungen — sonst unbalancierte interne Transfers.)
US-FIN-08 — Laufender Kassensaldo & Dashboard-KPIs
Als OPERATIONS/BUCHHALTUNG möchte ich den chronologischen Kassenverlauf mit laufendem Saldo sowie die Finanz-KPIs sehen, damit ich Liquidität und offene Posten auf einen Blick erfasse.
Akzeptanzkriterien
- Gegeben zahlungswirksame Bewegungen, wenn ich die Kasse (
v_kasse) öffne, dann sehe ich je Zeilecash_in/cash_outund einen berechneten laufenden Saldo (running_cash_chf, Window-Funktion) — der Saldo ist nicht gespeichert. - Gegeben das Finanz-Dashboard, wenn ich es öffne, dann zeigt
v_dashboardKasse-netto, Bank-Eingang/-Ausgang, Debitoren offen, Kreditoren offen, Anzahlungen offen. - Gegeben
OPERATIONS, wenn er Kasse/Dashboard öffnet, dann kann er lesen, aber im Ledger nichts schreiben (Fussnote 9, §13).
Traceability: v_kasse/v_dashboard (§3.6), RLS-Matrix movements OPS = R (Dok 30 §13).
US-FIN-09 — Kreditoren manuell erfassen (Lieferanten)
Als BUCHHALTUNG möchte ich Lieferantenrechnungen und -zahlungen als manuelle Bewegungen erfassen, damit offene Kreditoren mit Priorität/Fälligkeit sichtbar sind — auch ohne eigene Kreditoren-Tabelle.
Akzeptanzkriterien
- Gegeben eine Lieferantenrechnung, wenn ich sie erfasse, dann entsteht eine manuelle
movements-Zeile (source_type='manual',SUPPLIER_DEBT/EXPENSE) — kein Auto-Posting (AP-7/AP-8 entfallen, F-02). - Gegeben eine Lieferantenzahlung, wenn ich sie erfasse, dann entsteht
SUPPLIER_PAYMENTund optionalpayment_linksauf dieSUPPLIER_DEBT. - Gegeben offene Kreditoren, wenn ich
v_kreditorenöffne, dann sind sie nach Priorität sortiert (Überfällig/Bald fällig/Offen/Bezahlt; „Dringend" via Flag 🔲).
Traceability: F-02 (§2 Finanzen), v_kreditoren (§3.6), Dok 30 §11.1 (AP-7/AP-8 entfallen).
US-FIN-10 — Anzahlung/Depot buchen & verknüpfen
Als System (ausgelöst durch Depot-Order/-Zahlung in Modul 5/6) möchte ich Depot-Soll und Depot-Abono idempotent buchen und explizit verknüpfen, damit Anzahlungsstand und Restbetrag sicher zusammenfinden.
Akzeptanzkriterien
- Gegeben eine Depot-Order, wenn AP-3 läuft, dann entsteht die
DEPOSIT_CASH-Soll-Bewegung (source_type='deposit_order',total=deposit_total_chf,paid=0) unddeposit_orders.movement_idwird gesetzt. - Gegeben eine Depot-Zahlung, wenn AP-4 läuft, dann entsteht der Depot-Abono (
source_type='deposit_payment', Kanal =payment_methods.channel) undpayment_links(payment_id=Abono, target_id=Depot-Soll)— die Soll-Bewegung wird überdeposit_payments.deposit_order_id → deposit_orders.movement_idgefunden, nicht perreference-Match (F-14). - Gegeben eine Depot-Order ohne Zahlung, dann zeigt
v_anzahlungenStatusDEPOSIT_NO_PAYMENT(🟡). - Gegeben ein verfallenes Depot (
FORFEITED), wenn AP-6 bucht, dann alsINCOME(Ertrag, 0 Cash) — 🔲 ob Ertrag oder neutral ist Geschäftsentscheid.
Traceability: Dok 30 §11.1 (AP-3/AP-4/AP-6), F-14, v_anzahlungen (§3.6), Workflow B (§4). (Entscheid-abhängig: verfallenes Depot als INCOME vs. neutral — 🔲 AP-6.)
US-FIN-11 — Monatsabschluss & Periodensperre (close)
Als ADMIN/BUCHHALTUNG möchte ich einen Monat prüfen und abschliessen, damit die Periode revisionssicher gesperrt ist (OR 957 / GeBüV).
Akzeptanzkriterien
- Gegeben ein Monat in
v_monatskontrolle, wenn offene Debitoren/Kreditoren/Anzahlungen > 0 sind, dann stehtmonthly_closings.status='review'und der Button „Periode abschliessen" weist auf die offenen Posten hin. - Gegeben ich schliesse ab, wenn
status='closed'gesetzt wird, dann werdenopening/closing cash+bankals Snapshot,closed_at/closed_bygeschrieben und die Periode ist gesperrt. - Gegeben eine
closed-Periode, wenn jemand eine Bewegung einfügen/ändern oder eine Zeile per Datumswechsel in diese Periode verschieben will, dann blockiertfn_guard_closed_period()die Mutation (auch für Auto-Posting → fällt in die offene Folgeperiode). - Gegeben
statusnuropen/review/closedzulässig, dann wird jeder andere Wert vom Check-Constraint abgelehnt.
Traceability: monthly_closings (Dok 30 §4.3), fn_guard_closed_period() (§11.2 / §13 Querschnitts-Invarianten), Workflow E (§4), Compliance §2.5.
US-FIN-12 — Periode wiedereröffnen (audit-pflichtig)
Als ADMIN möchte ich eine abgeschlossene Periode nur auditiert wiedereröffnen können, damit Nachbuchungen nachvollziehbar bleiben und keine stille Rückdatierung möglich ist.
Akzeptanzkriterien
- Gegeben eine
closed-Periode, wenn ich „Reopen" auslöse, dann dürfen das nurADMIN, es werdenreopened_at/reopened_bygesetzt undaudit_logerhältaction='reopen_period'→ Korrekturspur bleibt sichtbar. - Gegeben
BUCHHALTUNG, wenn sie eineclosed-Periode wiedereröffnen will, dann wird es abgelehnt (Reopen nurADMIN;BUCHHALTUNGbewegt nuropen↔review, Dok 30 §13). - Gegeben eine Nachbuchung ohne Reopen, wenn sie nötig ist, dann ist die Alternative eine Buchung in der offenen Folgeperiode mit Korrektur-Referenz.
Traceability: monthly_closings.reopened_* (Dok 30 §4.3), Compliance §2.5 (Art. 9), RLS-Matrix (§13).
US-FIN-13 — Beleg verknüpfen & Storno statt Löschen
Als BUCHHALTUNG/ADMIN möchte ich Belege an Bewegungen hängen und Fehlbuchungen ausschliesslich stornieren (nie hart löschen), damit das Ledger ein unveränderbares Journal bleibt (OR 957 / GeBüV).
Akzeptanzkriterien
- Gegeben eine Bewegung, wenn ich einen Beleg ablege, dann verknüpft
storage_objects(owner_type='movement', owner_id)ihn eindeutig (10-Jahres-Aufbewahrungretain_until). - Gegeben eine Fehlbuchung, wenn ich sie korrigiere, dann geht das nur via
void-RPC (voided_at/voided_by/void_reason) durchADMIN/BUCHHALTUNG+ Neubuchung — die Original-Zeile bleibt im Audit, die View filtert sie aus. - Gegeben ein hartes
DELETEaufmovements, dann wird es für jede Rolle (inkl.ADMIN) blockiert (for delete using (false)). - Gegeben eine quellen-erzeugte Zeile (
source_type ≠ 'manual'), wenn jemand sie direkt im Ledger ändern will, dann ist sie read-only — nur der operative Ursprung wird editiert (Upsert). - Gegeben eine stornierte Zahlung, wenn das
voidgreift, dann kaskadiert es überpayment_links(payment-void) — und weilpayment_links.payment_idaufrestrictstattcascade deletesteht, kann kein hartes Löschen das No-Delete-Ledger umgehen.
Traceability: movements.voided_at (Dok 30 §4.1), payment_links (§4.2), storage_objects (Compliance §1.4/§2.8), RLS mov_no_delete (§3.7), Querschnitts-Invarianten (§13). (Deckt data-04 ab: payment_links.payment_id on delete restrict — kein Cascade-Delete am No-Delete-Ledger vorbei. Deckt append-only/Revisionssicherheit GeBüV ab.)
US-FIN-14 — Audit-Spur jeder Mutation (append-only)
Als READONLY (Treuhänder) möchte ich, dass jede Mutation an movements und monthly_closings unveränderbar protokolliert wird, damit die Revision wer/wann/alt→neu lückenlos nachvollziehen kann.
Akzeptanzkriterien
- Gegeben eine schreibende Operation auf
movements/monthly_closings, wenn sie ausgeführt wird, dann schreibtfn_audit()(SECURITY DEFINER) genau eineaudit_log-Zeile mitold_data/new_data(JSONB),diff,actor_user_id, denormalisiertemactor_emailund DB-seitigem Zeitstempelat. - Gegeben das
audit_log, wenn jemand (auchADMINoderservice_role) eine Zeile ändern/löschen will, dann wird es durch Grant-Entzug + Rules (do instead nothing) verhindert — append-only. - Gegeben
READONLY/Treuhänder, wenn er das Journal liest, dann kann er lesen, aber nichts mutieren;AFFILIATEhat keinen Zugriff.
Traceability: audit_log (Dok 30 §6.1, K-15), Compliance §2.1–2.4, RLS-Matrix audit_log (§13). (Deckt append-only/Revisionssicherheit GeBüV ab.)
US-FIN-15 — Finanz-RLS: Rollen-Trennung (RLS-Negativfall)
Als ADMIN möchte ich, dass die Schreib-/Leserechte am Ledger strikt rollengebunden sind, damit keine Rolle fremde oder gesperrte Finanzdaten manipulieren kann (revDSG + Least Privilege).
Akzeptanzkriterien
- Gegeben
BUCHHALTUNG/ADMIN, wenn sie eine Bewegung ändern, dann nur fallssource_type='manual'und Periode nichtclosed(mov_update-Policy); sonst abgelehnt. - Gegeben
OPERATIONS, dann nur Lesezugriff im Ledger (kein Insert/Update/Delete). - Gegeben
FAHRER, dann sieht er nur eigene Logistik-Quellen (source_type IN ('deposit_order','deposit_payment')) und kann sonst nichts (Fussnote 10). - Gegeben
AFFILIATE, wenn er Finanzdaten abruft, dann kein Zugriff (movements,payment_links,audit_log— harte Mandantentrennung). - Gegeben
READONLY/Treuhänder, dann nur lesend über alle Finanz-Views.
Traceability: RLS-Matrix Dok 30 §13 (movements/payment_links/monthly_closings, Fussnoten 8–10), Beispiel-Policies §3.7, Compliance §4.5/§8.2–8.3. (Deckt revDSG-Mandantentrennung + Least Privilege ab.)
US-FIN-16 — MWST erfassen mit mitgespeichertem Satz (schema-ready)
Als BUCHHALTUNG möchte ich, dass bei aktiver MWST der angewandte Satz auf der Bewegung mitgespeichert wird, damit ein späterer Satzwechsel keine bereits gebuchten Beträge verfälscht (Snapshot-Stabilität).
Akzeptanzkriterien
- Gegeben
vat_enabled=trueundvat_rates.active=true, wenn ich eine Bewegung erfasse, dann verlangt das Formular einenvat_code, undvat_amount_chfwird im Schreibpfad (Server-Action bzw.post_movement()) ausvat_rates.rate_pctberechnet und auf der Zeile gespeichert — nicht in einer View, keine generated column (darate_pctin Fremdtabelle). - Gegeben eine gespeicherte MWST-Bewegung, wenn sich der Satz in
vat_ratesspäter ändert (neuevalid_from), dann bleibt der angewandte Satz auf der historischen Zeile erhalten (mitgespeichert) — kein Snapshot-Drift. - Gegeben ein Re-Upsert einer quellen-erzeugten MWST-Buchung, dann ist
vat_amount_chfidempotent (gleicher Wert). - Gegeben
vat_enabled=false, dann bleiben alle Bewegungenvat_code=NULL/vat_amount_chf=NULL(MWST-neutral); Aktivierung wirkt nur prospektiv.
Traceability: movements.vat_code/vat_amount_chf (Dok 30 §4.1, F-18), vat_rates (§3.1), Compliance §3.2/§3.9, Workflow F (§4). (Deckt data-02 ab: angewandten MWST-Satz auf movements/invoices mitspeichern — sonst Snapshot-Drift bei Satzwechsel.)
US-FIN-17 — MWST scharfschalten (Aktivierung)
Als ADMIN möchte ich MWST kontrolliert aktivieren können, damit vor Eintritt der Steuerpflicht keine falschen Steuerkennzahlen entstehen.
Akzeptanzkriterien
- Gegeben Eintritt der Steuerpflicht, wenn ich
vat_rates.active=true+ Feature-Flagvat_enabledsetze, dann ist MWST ab diesem Zeitpunkt aktiv — ohne Schema-Migration. - Gegeben aktive MWST, wenn Rechnungen/Exporte erzeugt werden, dann tragen sie Satz je Position, MWST-Betrag und die geforderten Pflichtangaben (Art. 26 MWSTG).
- Gegeben die Methodenwahl, dann ist sie als Settings-Flag
vat_method ∈ {effective, flat_rate}abgebildet (Saldosteuersatz vs. effektiv) — 🔲 Methode/Satz/brutto-netto Treuhänderentscheid.
Traceability: vat_rates.active/Settings vat_enabled/vat_method (Compliance §3.1/§3.3), Workflow F (§4). (Entscheid-abhängig: MWST-Scharfschaltung/Methode/brutto-netto — 🔲 E13.)
US-FIN-18 — Treuhänder-Export
Als READONLY (Treuhänder) / BUCHHALTUNG möchte ich eine Periode als CSV/Excel/PDF (inkl. MWST-Kennzahlen, wenn aktiv) exportieren, damit die formale Buchhaltung über die offizielle Schnittstelle gespeist wird.
Akzeptanzkriterien
- Gegeben ein Zeitraum + Format (CSV/Excel/PDF), wenn ich exportiere, dann erzeugt eine Edge-Function die Datei in Supabase Storage und liefert einen Download-Link.
- Gegeben die Option „nur abgeschlossene Perioden", wenn ich sie wähle, dann enthält der Export ausschliesslich
closed-Perioden. - Gegeben MWST aktiv, dann enthält der Export die MWST-Kennzahlen (Umsatz nach Satz, geschuldete Steuer, Vorsteuer bei effektiver Methode); ist MWST inaktiv, sind die MWST-Spalten ausgeblendet.
- Gegeben ein Export-Vorgang, dann wird
audit_logmitaction='export'(wer/wann) protokolliert; der Export enthält nur das fachlich Nötige (revDSG).
Traceability: UI Treuhänder-Export (§5.7), Compliance §2.6/§3.8/§7, Audit-Abdeckung §8.10. (Abgrenzung: Caja ist Vorsystem — der Export ist die einzige offizielle Ausgabe, nicht der rechtsgültige Abschluss. 🔲 Exportformat Bexio/Abacus/Banana.)
Schätzung & Priorität (Vorschlag — vom Team final zu bestätigen)
| Story | Größe | Priorität (MoSCoW) |
|---|---|---|
| US-FIN-01 Manuelle Bewegung | M | Must |
| US-FIN-02 Auto-Posting (idempotent) | L | Must |
| US-FIN-03 Rechnung buchen (INVOICE) | M | Must |
| US-FIN-04 Kundenzahlung + payment_links | M | Must |
| US-FIN-05 Rechnungsstatus OPEN/PARTIAL/PAID | M | Must |
| US-FIN-06 Debitoren/Restschuld | S | Must |
| US-FIN-07 Interner Transfer (Balance) | M | Must |
| US-FIN-08 Kassensaldo & Dashboard | M | Must |
| US-FIN-09 Kreditoren manuell | S | Should |
| US-FIN-10 Anzahlung/Depot | L | Must |
| US-FIN-11 Monatsabschluss/Sperre (close) | L | Must |
| US-FIN-12 Periode reopen (audit) | S | Should |
| US-FIN-13 Beleg & Storno (kein Delete) | M | Must |
| US-FIN-14 Audit-Spur (append-only) | M | Must |
| US-FIN-15 Finanz-RLS (Rollen-Trennung) | M | Must |
| US-FIN-16 MWST mit Satz-Snapshot | M | Should |
| US-FIN-17 MWST scharfschalten | S | Could |
| US-FIN-18 Treuhänder-Export | L | Must |
Größe = grobe Aufwands-Indikation (S/M/L), nicht Story Points. Priorität nach MoSCoW (Must/Should/Could). Beides ist ein Vorschlag zur Release-Planung — die verbindliche Schätzung/Priorisierung macht das Team im Backlog. MWST-Stories (16/17) sind
Should/Could, weil schema-ready, aber bis zur Steuerpflicht inaktiv.
Abdeckung & offene Punkte (dieses Epic)
- 18 Stories decken das Finanz-Modul end-to-end ab: Erfassung (manuell + Auto-Posting) → Rechnung/Forderung → Zahlung/Status → Debitoren/Kreditoren → Kasse/Bank/Transfer → Depot/Anzahlung → Monatsabschluss/Sperre/Reopen → Beleg/Storno/Audit/RLS → MWST (ready + Aktivierung) → Treuhänder-Export. Alle abgeleiteten Grössen bleiben View-berechnet (keine Doppelspeicherung).
- Audit-Verzahnung (als testbare Szenarien encodiert):
- data-01 (eine Geld-Domäne
numeric(12,2)) → US-FIN-01. - data-02 (angewandten MWST-Satz mitspeichern, Snapshot-Drift) → US-FIN-16.
- data-04 (
payment_links.payment_id on delete restrict, No-Delete-Ledger) → US-FIN-13. - data-05 (
transfer_group_id-Balance-Invariante, Negativfall unbalanciert) → US-FIN-07. - append-only / Revisionssicherheit (GeBüV) → US-FIN-11/12/13/14 (Periodensperre, auditiertes Reopen, Storno statt Delete, unveränderbares
audit_log). - Idempotenz (8.9) → US-FIN-02 (Upsert auf
(source_type, source_id)). - revDSG-Mandantentrennung / Least Privilege → US-FIN-15 (RLS-Negativfälle FAHRER/AFFILIATE).
- data-01 (eine Geld-Domäne
- Entscheid-abhängig (🔲 markiert):
- E13 — MWST: Scharfschaltung/Methode (Saldo vs. effektiv) und brutto/netto
total_chf→ US-FIN-16/17. - AP-6 — verfallenes Depot: als
INCOME-Ertrag vs. neutral → US-FIN-10. - E14 — Provisions-Auslöser: ✅ entschieden 2026-06-28 (#19) — Auslöser ist
DELIVERED(Freigabeapproved), Entstehung bei Offerte-Konversion; nichtPAID/Zahlung → US-FIN-05, US-AFF-05/06. - Mehrwährung: aktuell reines CHF; DOP/USD (
currency+ Kurs) out of scope — kein eigener Story-Schnitt, als Modul-offener-Punkt vermerkt (§9, Compliance §1.7). - Weitere offene Punkte (Anfangssalden, „Dringend"-Flag Kreditoren,
INCOMEim Dropdown, Exportformat) bleiben Modul-offene Punkte (§9) und sind in den betroffenen Stories als 🔲 referenziert.
- E13 — MWST: Scharfschaltung/Methode (Saldo vs. effektiv) und brutto/netto
- Nächster Schritt: Format-/Inhaltsfreigabe → Backlog-Übernahme mit verbindlicher Schätzung + INVEST-Red-Team-Pass; MWST-Stories erst bei Eintritt der Steuerpflicht aktivieren.