ADR-015: TaxLedgerEntry 1:N Cardinality per FinancialLedger
Status: ✅ Approved — 2026-04-12 Origin: Invoice Service ProtocolImpacts:
schema-commerce.md§tax_ledger_entries,PRODUCT_domain-model.md§TaxLedgerEntry,tax-engine.md,datev-integration.md
Context
The original schema defined a 1:1 relationship between FinancialLedger and TaxLedgerEntry. However, a single departure can produce revenue from two distinct tax regimes simultaneously:
- Tour margin →
MARGIN_SCHEME_25(§ 25 UStG), when any Fremdleistung is present - Onboard sales (beverages, snacks) →
STANDARD_VAT(19%), always Eigenleistung
tax-engine.md §Edge Cases explicitly states: "OnboardSales (beverages, snacks) → STANDARD_VAT — always Eigenleistung, tracked separately." This means a departure with both hotel bookings and onboard sales requires two separate tax computations with different strategies — impossible under a 1:1 constraint.
Decision
Change FINANCIAL_LEDGER ||--|| TAX_LEDGER_ENTRY to FINANCIAL_LEDGER ||--o{ TAX_LEDGER_ENTRY.
The tax_strategy column (STANDARD_VAT or MARGIN_SCHEME_25) discriminates each TaxLedgerEntry. A FinancialLedger can have 0..N entries, typically:
| Scenario | TaxLedgerEntries |
|---|---|
| Pure charter (bus only, no hotel) | 1 × STANDARD_VAT |
| Tour with hotel (no onboard sales) | 1 × MARGIN_SCHEME_25 |
| Tour with hotel + onboard sales | 1 × MARGIN_SCHEME_25 + 1 × STANDARD_VAT |
Ledger still OPEN (trip not yet completed) | 0 (entries created at finalization) |
Consequences
Positive:
- Correctly models mixed tax strategies on a single departure
- The system can report OnboardSale VAT independently from tour margin tax
- DATEV export can group entries by
tax_strategyfor correct BU-Schlüssel mapping
Negative:
tax-calculator.service.tsmust produce one TLE per tax strategy per departure (increased complexity)- Invoice rendering must handle multiple tax lines per ledger
- DATEV CSV export must aggregate entries per strategy (separate BU-Schlüssel rows)
Neutral:
- Departures with only one tax strategy continue to produce exactly one entry — backwards compatible