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