From 4c7cc4069a979db0edae6ef8efc92e7cab6596e4 Mon Sep 17 00:00:00 2001 From: ValueOn AG Date: Sat, 21 Mar 2026 01:34:59 +0100 Subject: [PATCH] konzept billing ext --- concepts/Billing-Konzept.md | 84 +++++++++++-------------------------- 1 file changed, 24 insertions(+), 60 deletions(-) diff --git a/concepts/Billing-Konzept.md b/concepts/Billing-Konzept.md index 71ce24d..74e90c5 100644 --- a/concepts/Billing-Konzept.md +++ b/concepts/Billing-Konzept.md @@ -46,9 +46,10 @@ Mandant (Kostenstelle) |--------|--------------|--------------|---------| | `PREPAY_MANDATE` | Vorbezahltes Guthaben auf Mandantenebene | Mandant | Gemeinsames Budget für alle User | | `PREPAY_USER` | Vorbezahltes Guthaben pro User | User (im Mandanten-Kontext) | Individuelles Budget | -| `CREDIT_POSTPAY` | Nutzung auf Kredit mit Nachzahlung | Mandant (Rechnungsadresse erforderlich) | Monatsrechnung | | `UNLIMITED` | Keine Kostenlimitierung | - | Nur für interne Mandanten | +**Hinweis:** Das frühere Modell `CREDIT_POSTPAY` ist **nicht mehr** Teil des Produkts. Alte gespeicherte Werte werden beim Lesen auf `PREPAY_MANDATE` normalisiert (ohne DB-Migration). + ### Modell: PREPAY_MANDATE ``` @@ -74,7 +75,7 @@ Mandant "SOHA Treuhand AG" ``` Mandant "ROOT" (Bootstrap-Mandant) ├── Abrechnungsmodell: PREPAY_USER -├── Startguthaben für neue User: 10.00 CHF +├── Startguthaben bei neuem User nur hier (Root): z. B. 5.00 CHF (defaultUserCredit) └── Nutzer: ├── User A: Guthaben 10.00 CHF → verbraucht 3.50 → Rest 6.50 CHF └── User B: Guthaben 25.00 CHF → verbraucht 12.00 → Rest 13.00 CHF @@ -82,26 +83,13 @@ Mandant "ROOT" (Bootstrap-Mandant) **Eigenschaften:** - Jeder User hat eigenes Guthaben im Mandanten-Kontext -- Neuer User erhält Startguthaben (konfigurierbar) +- **Automatisches** Startguthaben nur, wenn ein User dem **Root-Mandanten** neu zugeordnet wird; bei allen anderen Mandanten wird das Nutzerkonto mit 0 CHF angelegt (Aufladung z. B. per Admin/Stripe) - User kann Guthaben aufladen - Bei Guthaben = 0 → AI-Features blockiert für diesen User -### Modell: CREDIT_POSTPAY +### Modell: CREDIT_POSTPAY (entfernt) -``` -Mandant "Enterprise AG" -├── Abrechnungsmodell: CREDIT_POSTPAY -├── Rechnungsadresse: Enterprise AG, Musterstrasse 1, 8000 Zürich -├── Kreditlimit: 2000.00 CHF (optional) -├── Aktueller Saldo: -450.00 CHF (Schulden) -└── Monatsende: Rechnung über 450.00 CHF -``` - -**Eigenschaften:** -- Nutzung ohne Vorauszahlung -- Monatliche Abrechnung -- Optionales Kreditlimit -- Für verifizierte Geschäftskunden +Nicht mehr unterstützt. Siehe Hinweis in der Modell-Übersicht. --- @@ -110,18 +98,7 @@ Mandant "Enterprise AG" ### Neue Tabellen ```python -# modules/datamodels/datamodelBilling.py - -class BillingAddress(BaseModel): - """Rechnungsadresse für CREDIT_POSTPAY Mandanten""" - - company: str # Firmenname - street: str # Strasse und Hausnummer - zip: str # PLZ - city: str # Ort - country: str # Land (Default: "CH") - vatNumber: Optional[str] # MwSt-Nummer (optional) - +# modules/datamodels/datamodelBilling.py (vereinfacht, Stand Implementierung) class BillingAccount(BaseModel): """Abrechnungskonto pro Mandant oder User-Mandant-Kombination""" @@ -130,8 +107,7 @@ class BillingAccount(BaseModel): mandateId: str # FK → Mandate userId: Optional[str] # FK → User (nur bei PREPAY_USER) accountType: str # MANDATE | USER - balance: float # Aktuelles Guthaben/Saldo in CHF - creditLimit: Optional[float] # Kreditlimit in CHF (nur CREDIT_POSTPAY) + balance: float # Aktuelles Guthaben in CHF (Prepaid) warningThreshold: float # Warnschwelle in CHF lastWarningAt: Optional[datetime] # Letzte Warnung gesendet enabled: bool # Account aktiv @@ -161,15 +137,11 @@ class BillingSettings(BaseModel): id: str # UUID mandateId: str # FK → Mandate (UNIQUE) - billingModel: str # PREPAY_MANDATE | PREPAY_USER | CREDIT_POSTPAY | UNLIMITED + billingModel: str # PREPAY_MANDATE | PREPAY_USER | UNLIMITED # Konfiguration - defaultUserCredit: float # Startguthaben in CHF für neue User (bei PREPAY_USER) + defaultUserCredit: float # Startguthaben nur sinnvoll Root + PREPAY_USER (Bootstrap setzt z. B. 5 CHF); sonst typ. 0 warningThresholdPercent: float # Warnschwelle in % (z.B. 10) - blockOnZeroBalance: bool # AI blockieren bei Guthaben = 0 - - # Rechnungsadresse (erforderlich für CREDIT_POSTPAY) - billingAddress: Optional[BillingAddress] # Benachrichtigungen notifyEmails: List[str] # Email-Adressen für Monatsberichte @@ -322,13 +294,12 @@ class BillingService: return BillingCheckResult(allowed=True) if account.balance < estimatedCost: - if self.settings.blockOnZeroBalance: - return BillingCheckResult( - allowed=False, - reason='INSUFFICIENT_BALANCE', - currentBalance=account.balance, - requiredAmount=estimatedCost - ) + return BillingCheckResult( + allowed=False, + reason='INSUFFICIENT_BALANCE', + currentBalance=account.balance, + requiredAmount=estimatedCost + ) return BillingCheckResult(allowed=True, currentBalance=account.balance) @@ -473,9 +444,8 @@ def _setupRootMandate(): id=generateUuid(), mandateId=rootMandate.id, billingModel='PREPAY_USER', - defaultUserCredit=10.0, # 10 CHF Startguthaben + defaultUserCredit=5.0, # 5 CHF Startguthaben (siehe DEFAULT_USER_CREDIT_CHF im Code) warningThresholdPercent=20, - blockOnZeroBalance=True, notifyOnWarning=True ) @@ -518,7 +488,7 @@ def _setupNewUserInRoot(user: User): mandateId=rootMandate.id, userId=user.id, accountType='USER', - balance=settings.defaultUserCredit, # 10 CHF + balance=settings.defaultUserCredit, # z. B. 5 CHF Default warningThreshold=settings.defaultUserCredit * 0.2, # 20% enabled=True ) @@ -788,7 +758,6 @@ Das Automation-Editor Modal wird in drei Tabs aufgeteilt: - Übersicht aller Mandanten mit Billing-Status - Abrechnungsmodell ändern - Guthaben manuell aufladen (Stripe-Vorbereitung für Zukunft) -- Rechnungsadresse verwalten (für CREDIT_POSTPAY) - Email-Empfänger verwalten ``` @@ -801,7 +770,7 @@ Das Automation-Editor Modal wird in drei Tabs aufgeteilt: │ │ ──────────────────────────────────────────────── │ │ │ │ ROOT PREPAY_USER n/a ✓ │ │ │ │ SOHA Treuhand PREPAY_MANDATE CHF 299 ✓ │ │ -│ │ Enterprise AG CREDIT_POSTPAY CHF -450 ✓ │ │ +│ │ Enterprise AG PREPAY_MANDATE CHF 1200 ✓ │ │ │ │ Test GmbH PREPAY_MANDATE CHF 5 ⚠ │ │ │ └──────────────────────────────────────────────────┘ │ │ │ @@ -812,11 +781,6 @@ Das Automation-Editor Modal wird in drei Tabs aufgeteilt: │ │ Abrechnungsmodell: [PREPAY_MANDATE ▼] │ │ │ │ │ │ │ │ Warnschwelle: [10] % │ │ -│ │ Blockieren bei 0: [✓] │ │ -│ │ │ │ -│ │ Rechnungsadresse (für CREDIT_POSTPAY): │ │ -│ │ [Firma: ________________] [Strasse: _________] │ │ -│ │ [PLZ: ____] [Ort: _______] [MwSt-Nr: ________] │ │ │ │ │ │ │ │ Benachrichtigungs-Emails: │ │ │ │ [admin@soha.ch, billing@soha.ch ] │ │ @@ -975,7 +939,7 @@ PowerOn Platform 1. **Root-Mandant Setup** - `PREPAY_USER` Modell - - 10 CHF Startguthaben für neue User + - 5 CHF automatisches Startguthaben nur für neu im Root-Mandanten angelegte User (`DEFAULT_USER_CREDIT_CHF`) - RBAC-Berechtigungen für alle LLM-Provider 2. **Bestehende User migrieren** @@ -1064,7 +1028,7 @@ Jedes AICore-Plugin definiert seine eigenen Preise via `calculatePriceCHF()`. Di |---------|--------------| | **AICore** | Plugin-System für LLM-Provider Integration | | **Billing Account** | Konto für Guthaben/Saldo (pro Mandant oder User) | -| **Billing Address** | Rechnungsadresse, erforderlich für CREDIT_POSTPAY | +| **parseBillingModelFromStoredValue** | Mappt unbekannte/legacy `billingModel`-Strings auf `PREPAY_MANDATE` | | **Connector** | AICore-Plugin für einen spezifischen Provider | | **Kostenstelle** | Mandant, dem Kosten zugeordnet werden | | **Mandate** | Mandant/Tenant in der Multi-Tenant-Architektur | @@ -1133,9 +1097,9 @@ frontend_nyla/src/ ### Phase 1: Backend Grundstruktur -- [ ] `datamodelBilling.py` erstellen (BillingAccount, BillingTransaction, BillingSettings, UsageStatistics, BillingAddress) -- [ ] `interfaceDbBilling.py` erstellen (CRUD-Operationen) -- [ ] Datenbank-Migrations-Script erstellen +- [x] `datamodelBilling.py` (BillingAccount, BillingTransaction, BillingSettings, UsageStatistics) +- [x] `interfaceDbBilling.py` (CRUD-Operationen) +- [x] Kein separates Migrations-Script nötig (Legacy-Werte werden beim Lesen normalisiert) - [ ] AICore-Plugins: Preise von USD auf CHF umstellen (`calculatepriceCHF` mit echten CHF-Werten) ### Phase 2: Service-Layer