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.