# Enterprise Subscription ## Beschreibung und Kontext Enterprise-Kunden wollen mit Pauschalen arbeiten statt mit nutzungsbasierter Stripe-Abrechnung. Der Sysadmin (Platform Admin) erstellt ein individuelles Abo mit fixen Limiten (Users, Features, Storage, AI-Budget) und einem Pauschalpreis. Die Abrechnung erfolgt per E-Mail-Rechnung, nicht über Stripe. Alle Limiten sind hart — Überschreitung wird blockiert, nicht nachverrechnet. Business-Treiber: Grosse Kunden verlangen Planbarkeit und interne Verrechnung statt Kreditkarten-Abrechnung. ## Fokus und kritische Details - `assertCapacity` muss bei Enterprise die Custom-Felder statt den Plan-Katalog lesen - `creditSubscriptionBudget`, `adjustAiBudgetForUserChange`, `reconcileMandateStorageBilling` brauchen Enterprise-Guards - AI-Budget Top-Up via Stripe bleibt für Enterprise-Mandanten weiterhin möglich (separater Flow) - `syncQuantityToStripe` ist bereits sicher (Guard auf `stripeSubscriptionId=None`) - `SubscriptionCapacityException.userAction` darf bei Enterprise nicht `UPGRADE_SUBSCRIPTION` sein ## Ziel und Nicht-Ziele - Ziel: Sysadmin kann Enterprise-Abo erstellen, erneuern, anpassen; fixe Limiten mit Hard-Block; Rechnung per E-Mail an Mandate Admins - Ziel: Auto-Renewal via Cron-Job (eventManager) für Enterprise-Abos mit autoRenew=True - Ziel: Mandate Admin sieht Enterprise-Abo readonly im Dashboard - Explizit NICHT: PDF-Invoice-System, Frontend Enterprise-Formular (Phase 2) - Explizit NICHT: Enterprise als Self-Service für Mandate Admin ## Betroffene Module - Gateway: datamodelSubscription, interfaceDbSubscription, interfaceDbBilling, interfaceDbApp, mainServiceSubscription, routeSubscription, routeBilling, enterpriseRenewalScheduler (neu), app.py - Frontend: Subscription-Status-Anzeige (readonly für Enterprise), Sysadmin-Panel Enterprise-Badge - DB-Migration: nein (neue Felder auf MandateSubscription, auto-schema) - Andere Komponenten: E-Mail-Versand (notifyMandateAdmins, bestehende Logik) ## Entscheidungen | Datum | Entscheidung | Begründung | |-------|-------------|------------| | 2026-05-09 | Custom-Werte auf MandateSubscription statt eigene Plan-Tabelle | Einfacher, kein neues DB-Schema nötig, Plan-Katalog bleibt statisch | | 2026-05-09 | Rechnung als strukturierte E-Mail, kein PDF | Vertrag wird extern geregelt, E-Mail reicht als Bestätigung | | 2026-05-09 | Renew = altes Abo EXPIRED + neues ACTIVE (Duplikat) | Saubere Audit-History, Budget-Gutschrift wie bei Erstaktivierung | | 2026-05-09 | Auto-Renewal via eventManager.registerCron (täglich 06:00 UTC) | Bestehende APScheduler-Infrastruktur nutzen, analog Audit-Log-Cleanup | | 2026-05-09 | Rechnungs-E-Mail nur an Mandate Admins | Gleiche Logik wie bestehende Pläne, kein Sysadmin als Empfänger | | 2026-05-09 | Storage-Overage bei Enterprise: Hard-Block statt Pool-Abzug | Konsistent mit dem Pauschalen-Konzept | ## Umsetzungs-Checkliste - [x] Datenmodell: `ENTERPRISE` in `BUILTIN_PLANS` + `enterprise*`-Felder auf `MandateSubscription` (`isEnterprise`, `enterpriseFlatPriceCHF`, `enterpriseMaxUsers`, `enterpriseMaxFeatureInstances`, `enterpriseMaxDataVolumeMB`, `enterpriseBudgetAiCHF`, `enterpriseNote`) - [x] Helper: `getEffectiveLimits(sub, plan)` in `datamodelSubscription.py` — liest bei `isEnterprise` die Custom-Felder - [x] Guard: `assertCapacity` → über `getEffectiveLimits` abgedeckt (Enterprise-Felder automatisch) - [x] Guard: `creditSubscriptionBudget` → Enterprise-Budget-Gutschrift - [x] Guard: `adjustAiBudgetForUserChange` → bei Enterprise übersprungen - [x] Guard: `reconcileMandateStorageBilling` → bei Enterprise übersprungen - [x] Guard: `getDataVolumeWarning` → Enterprise-Felder - [x] Guard: `SubscriptionCapacityException` → `userAction=CONTACT_ADMIN` bei Enterprise - [x] Service: `createEnterprise`, `renewEnterprise`, `updateEnterprise` in `mainServiceSubscription.py` - [x] E-Mail: `_buildEnterpriseInvoiceHtml` + `_sendEnterpriseInvoiceEmail` via `notifyMandateAdmins` - [x] Auto-Renewal: `enterpriseRenewalScheduler.py` via `eventManager.registerCron` (täglich 06:00 UTC) - [x] Routes: `POST enterprise/create`, `POST enterprise/renew`, `PUT enterprise/update` in `routeSubscription.py` - [x] Admin-View: `_buildEnrichedSubscriptions` + `_computeUsage` für Enterprise - [x] RBAC / Permissions: alle 3 Endpoints `isPlatformAdmin`-only - [x] Neutralisierung betroffen? Nein - [x] Billing-Impact? Ja — neue Abrechnungsform ## Akzeptanzkriterien | # | Kriterium (Given-When-Then) | Prio | |---|---------------------------|------| | 1 | Given Sysadmin, When POST enterprise/create mit validen Parametern, Then Enterprise-Abo ACTIVE mit Custom-Limiten + AI-Budget gutgeschrieben + Rechnung per E-Mail an Mandate Admins + Sysadmin | must | | 2 | Given Enterprise-Abo aktiv, When Mandate Admin User 21 hinzufügen will (Limit=20), Then Hard-Block mit Meldung "Enterprise-Limit erreicht, kontaktieren Sie den Administrator" | must | | 3 | Given Enterprise-Abo aktiv, When User hinzugefügt wird (unter Limit), Then KEIN pro-rata AI-Budget-Adjustment | must | | 4 | Given Enterprise-Abo aktiv, When Storage-Nutzung steigt über Limit, Then Hard-Block, KEIN Pool-Abzug | must | | 5 | Given Enterprise-Abo aktiv, When Mandate Admin AI-Budget Top-Up via Stripe macht, Then funktioniert wie bei normalem Abo | must | | 6 | Given Enterprise-Abo aktiv, When Sysadmin PUT enterprise/update mit neuen Limiten, Then Limiten sofort wirksam | must | | 7 | Given Enterprise-Abo aktiv, When Sysadmin POST enterprise/renew, Then altes Abo EXPIRED + neues ACTIVE + Budget + Rechnung | must | | 8 | Given Enterprise-Abo aktiv, When Mandate Admin GET /status, Then sieht Enterprise-Abo mit Limiten und Nutzung (readonly) | must | | 9 | Given Enterprise-Abo aktiv, When Mandate Admin versucht Plan zu wechseln, Then nicht möglich (kein Self-Service) | must | ## Testplan | ID | AC | Art | Automatisiert | Repo-Pfad | Status | |----|----|-----|--------------|-----------|--------| | T1 | 1 | api | manuell | — | done | | T2 | 2 | api | manuell | — | done | | T3 | 3 | api | manuell | — | done | | T4 | 4 | api | manuell | — | done | | T5 | 5 | api | manuell | — | done | | T6 | 6 | api | manuell | — | done | | T7 | 7 | api | manuell | — | done | | T8 | 8,9 | api | manuell | — | done | ## Links - Cursor Plan: enterprise_subscription_aca22e32.plan.md ## Abschluss - [x] Dieses Dokument Status → `done` (2026-05-15) - [x] Eintrag in `c-work/_CHANGELOG.md`