From 554166803b9f1bc635c33f4079753ef518e4eff9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thomas=20G=C3=BCnther?=
Date: Tue, 11 Aug 2026 16:01:00 +0200
Subject: [PATCH] Tax excpetion texts
---
CLAUDE.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
diff --git a/CLAUDE.md b/CLAUDE.md
index ed8db94..4ef37b8 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -15,3 +15,60 @@ Routing, Mails) stehen in `.ai/conventions.md` und werden hier eingebunden — *
- [Zahlungsmodule](app/EventPaymentModules/CLAUDE.md) — interface-gesteuerte Zahlungsarten (Optionen, Config,
Anmelde-Zusammenfassung, GiroCode/Fähigkeits-Interfaces, neues Modul hinzufügen).
+
+## Umsatzsteuer / Steuerinformationen
+
+Rechtlich korrekter Begriff ist **Umsatzsteuer (USt)**, nicht MwSt. Ziel: tagesbasierte Anmelde-Rechnungen („Teilnahme
+Veranstaltung X in Gruppe Y" × Anwesenheitstage × Beitrag/Tag). Grundlage ist gebaut, die Rechnungs-PDF/-berechnung
+folgt noch.
+
+### Tenant-Einstellungen (Tab „Steuerinformationen")
+
+Felder auf `tenants` (Self-Service `TenantData.vue` + verwaltet `TenantEdit.vue`, Partial `TenantTaxInfo.vue`,
+Controller `TenantTax*` / `ManagedTenantTax*`, Action `UpdateTenantTax`):
+
+- `tax_liable` (bool) — ob USt-Pflicht besteht.
+- `vat_rate` (int, %) — USt-Satz, nur relevant wenn `tax_liable` (0–100, geclamped).
+- `vat_pricing_mode` (Enum `App\Enumerations\VatPricingMode`, natives PHP-Enum: `inclusive` | `add_on`).
+- `tax_exemption_reason` (FK → `tax_exemption_reasons.slug`, nullable) — Grund wenn **nicht** steuerpflichtig.
+ DB-Default neuer Tenants: `ustg_4_25a` (Jugendhilfe).
+- `tax_exemption_note` (text, nullable) — Freitext, nur bei Grund `custom` (`requires_note`).
+
+### Preismodus — **das Kernverhalten**
+
+Es wird **immer der finale Brutto-Beitrag** pro Anmeldung gespeichert (`EventParticipant.amount`, keine USt-Spalten am
+Teilnehmer). Der Modus wirkt **nur bei der Preisermittlung im Anmeldeprozess**:
+
+- **`inclusive`** (Brutto / „zusammenfassen", Supermarkt-Prinzip, Default): der konfigurierte Beitrag **ist** der
+ Endpreis, USt ist bereits enthalten → Preis bleibt gleich; die USt wird erst **auf der Rechnung herausgerechnet**:
+ `USt = brutto × Satz / (100 + Satz)`, `netto = brutto − USt`.
+- **`add_on`** (Netto / „aufaddieren"): der konfigurierte Beitrag ist netto → Endpreis = `Beitrag × (1 + Satz/100)`. Die
+ USt wird bei der Anmeldung **aufgeschlagen** → Teilnehmer zahlt mehr.
+
+Umsetzung in `App\Resources\EventResource::calculateAmount()`: nach allen Multiplikatoren (Early-Bird, `pay_per_day`,
+Geschwister) gilt
+`if ($event->tax_liable && $event->vat_pricing_mode === VatPricingMode::AddOn->value) $fee->multiply(1 + $event->vat_rate/100);`
+danach `round(…, 2)`. Bei `inclusive`/nicht steuerpflichtig bleibt der Beitrag unverändert.
+
+### Event-Snapshot (unveränderlich)
+
+Bei Event-Anlage (`CreateEventCommand::execute()`) werden `tax_liable`, `vat_rate`, `vat_pricing_mode`,
+`tax_exemption_reason`, `tax_exemption_note` aus dem Tenant **auf das Event kopiert und dort eingefroren** (Spalten auf
+`events`). Preisermittlung und spätere Rechnungen lesen vom Event, nicht vom Tenant → spätere Tenant-Änderungen berühren
+bestehende Events/Rechnungen nicht.
+
+### Befreiungsgründe (DB-gestützt)
+
+Tabelle `tax_exemption_reasons`, Model `App\Enumerations\TaxExemptionReason` (Eloquent, `slug` PK; `name`,
+`invoice_text`, `requires_note`, `sort_order`). `options()` liefert `{value,label,text}` sortiert nach `sort_order`. FK
+von `tenants` **und** `events`. Seed in Migration `170010`, verfeinert in `170020`. Gründe (in Reihenfolge):
+`ustg_19` (§ 19 Kleinunternehmer), `ustg_4_24` (§ 4 Nr. 24 Jugendherbergen),
+`ustg_4_25a` (§ 4 Nr. 25 Buchst. a — Jugendhilfe-Veranstaltungen, Default), `ustg_4_22a` (§ 4 Nr. 22 Buchst. a —
+Vorträge/Kurse), `custom` (Freitext). Jeder Grund kapselt den Rechnungs-Pflichthinweis nach **§ 14 Abs. 4 Nr. 8 UStG**
+(`invoice_text`; bei `custom` der Tenant-Freitext via `invoiceText(?note)`). Gemeinnützigkeit allein befreit **nicht** —
+es braucht immer einen konkreten Rechtsgrund.
+
+### Noch offen (nächster Schritt)
+
+Die eigentliche **Rechnung**: tagesbasierte Positionen, USt aus dem gespeicherten Brutto herausrechnen (Satz vom
+Event-Snapshot); bei Befreiung kein USt-Ausweis, stattdessen der Pflichthinweis des Grundes.