presseportale/docs/user-admin/Billing-und-Rechnungskreise.md
Kevin Adametz 62e6b7e70f Doku: zentrale Billing-Referenz und Status-Sync Phase 9D
- Neues Dokument docs/user-admin/Billing-und-Rechnungskreise.md:
  hybride Rechnungskreise (STR-/MAN-/Archiv), Tarif-Datenmodell,
  MAN-Faelligkeitslauf, USt-Regeln, Befehle/Scheduler, Konfiguration
  (billing.php + Cashier-ENV) und offene Punkte
- README-Index + STATUS-ABGLEICH (Finanzen-Sektion) aktualisiert
- PROGRESS-Eintrag Phase 9D (Datenmodell, Rechnungskreise,
  Grandfather-Migration, USt)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 11:02:45 +00:00

144 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Billing & Rechnungskreise (hybrides Modell)
Stand: 12.06.2026 — Datenmodell, MAN-Kreis und USt-Behandlung umgesetzt
(Phase 9D); Stripe-Checkout/Webhooks in Arbeit (Phase 9E).
Dieses Dokument ist die zentrale Referenz für das Abrechnungssystem:
Rechnungskreise, Tarif-Datenmodell, Steuerlogik, Befehle und Konfiguration.
Verwandte Dokumente:
- [`docs/Decision-Update Preisstruktur & Veröffentlichungs-Flow.md`](../Decision-Update%20Preisstruktur%20&%20Ver%C3%B6ffentlichungs-Flow.md) — verbindliche Launch-Entscheidungen (Tarife, Kontingente, Flow, Netto-Preise).
- [`docs/PHASE-9-FLOW-UND-TARIFE-PLAN.md`](../PHASE-9-FLOW-UND-TARIFE-PLAN.md) — Umsetzungsplan mit Päckchen-Status.
- `dev/migration 2026/05-DATABASE-MERGE.md` §5.5/§5.6 — Rechnungsarchiv (D-12) und Grandfathering (D-13).
---
## 1. Die drei Rechnungswelten
| Welt | Präfix | Tabelle | Inhalt |
|---|---|---|---|
| **Stripe-Shop** | `STR-` | `invoices` | Alle **neuen** Abschlüsse (Abos, Einzel-PM, Credits). Abwicklung komplett über Stripe; Rechnungen werden per Webhook in `invoices` gespiegelt und erhalten eine fortlaufende STR-Nummer. *(Spiegelung: Phase 9E)* |
| **Manuell/Legacy** | `MAN-` | `invoices` | Laufende, noch aktive Alt-Zahlungsvereinbarungen ab Relaunch. Fälligkeit wird täglich geprüft, Rechnung wie im Altsystem ausgestellt. |
| **Alt-Archiv** | — | `legacy_invoices` | Read-only Archiv aller importierten Legacy-Rechnungen (D-12). Wird nie verändert; PDFs werden on-demand aus den Archivdaten erzeugt. |
**Rechnungsnummern**: `InvoiceNumberGenerator` vergibt atomar (Row-Lock auf
`invoice_number_sequences`) fortlaufende, lückenlose Nummern pro Kreis:
`STR-00001`, `MAN-00001`, … (Padding: `billing.invoice_number_padding`).
---
## 2. Tarif-Datenmodell
| Tabelle | Zweck |
|---|---|
| `plans` | Tarif-Katalog (Starter/Business/Pro/Agency): Monats-/Jahrespreis **netto**, PM-Kontingent/Monat, Tageslimit, Stripe-Produkt-/Preis-IDs. Seeder: `PlanSeeder` (idempotent). |
| `subscriptions`, `subscription_items` | Laravel-Cashier-Tabellen — Zustand der Stripe-Abos. `User` ist `Billable`. |
| `single_purchases` | Einmalkäufe: Einzel-PM (19 €), Extra-PM, Boost, Veröffentlichungsnachweis-PDF. Status: pending → paid → consumed (oder refunded). |
| `payment_options` / `user_payment_options` | Legacy-Zahlungsvereinbarungen. Grandfathered-Einträge tragen die Netto-Vertragsbasis in `legacy_conditions`; versteckte Katalog-Platzhalter `LEGACY-{PE\|BP}-{Artikel}`. |
| `invoice_number_sequences` | Fortlaufende Nummern pro Rechnungskreis. |
**Submit-Gate** (`User::hasActiveBooking()`, hinter `billing.enforce_booking`):
Eine aktive Buchung ist ein Cashier-Abo **oder** ein bezahlter, noch nicht
eingelöster Einzel-/Extra-PM-Kauf **oder** eine aktive/grandfathered
Legacy-Vereinbarung. Bestandskunden behalten damit nach Gate-Aktivierung
volle Einreichungsrechte.
---
## 3. MAN-Kreis: Fälligkeitslauf für Legacy-Zahlungen
Täglicher Scheduler-Lauf (04:30): `billing:generate-manual-invoices`
1. Findet `user_payment_options` mit Status `active`/`grandfathered`,
**ohne** `stripe_subscription_id`, deren `current_period_end` erreicht ist.
2. Friert die Rechnungsadresse als Snapshot ein (`invoice_billing_addresses`,
inkl. `vat_id`).
3. Stellt die Rechnung aus: Netto-Basis × USt-Regel (Abschnitt 4),
MAN-Nummer, Zahlungsziel `billing.manual_due_days` (Default 14 Tage).
4. Schaltet die Periode weiter (`monthly`/`yearly` aus `legacy_conditions`
bzw. `payment_options.interval`).
Nicht abrechenbare Fälle (fehlende Rechnungsadresse, kein Intervall) werden
geloggt, **die Periode bleibt stehen** — der nächste Lauf versucht es erneut.
Optionen: `--dry-run`, `--limit=50`.
**Befüllung**: `legacy:grandfather-subscriptions` (Migrations-Runbook, nach
`legacy:archive-invoices`) leitet die aktiven jährlichen Vereinbarungen aus
dem Rechnungsarchiv ab — Replay-fähig für den Lauf kurz vor Relaunch.
Details: `dev/migration 2026/05-DATABASE-MERGE.md` §5.6.
---
## 4. USt-Behandlung (Entscheidung 12.06.2026)
**Alle neuen Preise sind Netto-Preise.** Die Steuer wird zur
Rechnungsstellung über `App\Services\Billing\VatResolver` aus der
Rechnungsadresse bestimmt:
| Fall | Behandlung | Rechnung |
|---|---|---|
| Deutschland | immer mit Steuer (`billing.vat_rate`, Default 19 %) | Netto + USt ausgewiesen |
| EU mit gültiger USt-ID | befreit (Reverse Charge) | `is_netto`, Pflichthinweis in `tax_note` |
| EU ohne USt-ID | mit Steuer | Netto + USt ausgewiesen |
| Drittland | grundsätzlich befreit | `is_netto`, Hinweis „nicht im Inland steuerbar" |
- Die USt-ID wird im Profil gepflegt (bestehendes Feld) und zusätzlich an
der Rechnungsadresse (`billing_addresses.vat_id`) gespeichert; jede
Rechnung friert sie im Adress-Snapshot ein.
- „Gültig" = vorhanden + formal plausibel (Länder-Präfix, EL für
Griechenland). **Offen: echte VIES-Validierung** — vor Aktivierung von
Gate/Checkout umsetzen.
- **Legacy-Umrechnung**: Das Altsystem fakturierte brutto (199 € inkl.
Steuer; Befreite mit Netto-Ausweis 167,23 €). Die Grandfather-Migration
leitet daraus die Netto-Basis ab (`legacy_conditions.net_cents`) — für
deutsche Bestandskunden bleibt der Bruttobetrag unverändert, die Steuer
wird künftig nur sauber ausgewiesen.
---
## 5. Befehle & Scheduler
| Befehl | Zweck | Scheduler |
|---|---|---|
| `billing:generate-manual-invoices` | MAN-Fälligkeitslauf (Abschnitt 3) | täglich 04:30 |
| `legacy:grandfather-subscriptions` | Aktive Legacy-Abos aus dem Archiv migrieren | manuell (Migrations-Runbook) |
| `press-releases:reset-monthly-quota` | Quota-Stub-Reset (entfällt mit Plan-Kontingent, 9E) | monatlich, 1. um 00:05 |
---
## 6. Konfiguration
`config/billing.php`:
| Schlüssel | ENV | Default | Bedeutung |
|---|---|---|---|
| `enforce_booking` | `BILLING_ENFORCE_BOOKING` | `false` | Submit-Gate scharf schalten (Launch-Schalter) |
| `invoice_number_padding` | — | `5` | Stellen der laufenden Nummer |
| `manual_due_days` | `BILLING_MANUAL_DUE_DAYS` | `14` | Zahlungsziel MAN-Rechnungen |
| `vat_rate` | `BILLING_VAT_RATE` | `0.19` | USt-Satz für steuerpflichtige Fälle |
| `eu_country_codes` | — | EU-27 ohne DE | Basis der Drittland-/EU-Unterscheidung |
Stripe/Cashier (`config/cashier.php`):
| ENV | Bedeutung |
|---|---|
| `STRIPE_KEY` / `STRIPE_SECRET` | Publishable/Secret Key (Test-Keys gesetzt) |
| `STRIPE_WEBHOOK_SECRET` | Signatur-Prüfung des Webhook-Endpoints — wird beim Einrichten des Endpoints gesetzt (9E) |
| `CASHIER_CURRENCY` | Default `usd` → für uns `eur` setzen (9E) |
---
## 7. Offene Punkte (Stand 12.06.2026)
1. **Phase 9E**: Stripe-Produkte/Preise anlegen (netto) + IDs in `plans`
pflegen, Checkout (Abo + Einmalkauf), Webhooks inkl. Spiegelung der
Stripe-Rechnungen nach `invoices` mit STR-Nummer, Slot-Logik von
`users.press_release_quota`-Stub auf Plan-Kontingent umstellen.
2. **VIES-Validierung** der USt-ID (aktuell Formatprüfung).
3. **PDF-Erzeugung** für MAN-/STR-Rechnungen (Layout inkl. `tax_note`);
Archiv-PDFs existieren bereits on-demand.
4. **`ExpireGrandfatheredSubscriptions`**: Benachrichtigung zur Umstellung
auf neue Tarife am `grandfathered_until` (D-13-Rest).
5. **Steuerberater-Abnahme** der USt-Regeln und Rechnungstexte vor dem
ersten produktiven MAN-/STR-Lauf.