41 KiB
Billing & Verrechnungssystem Konzept
Übersicht
Dieses Dokument beschreibt das Konzept für ein Kosten- und Verrechnungssystem (Billing) für LLM-Nutzung in der PowerOn-Plattform. Das System ermöglicht die Abrechnung von AI-Services auf Mandanten- und Benutzerebene mit verschiedenen Abrechnungsmodellen.
Grundprinzipien
1. Mandantenzentrierte Verrechnung
- Alle Kosten werden einem Mandanten zugeordnet
- Der Mandant ist die zentrale Abrechnungseinheit
- Benutzer können mehreren Mandanten angehören → Kosten werden pro Mandant getrennt erfasst
2. Kostenstellen-Hierarchie
Mandant (Kostenstelle)
├── Abrechnungsmodell (definiert durch SysAdmin)
├── Budget/Guthaben
├── Benutzer mit Zugriff
│ ├── User A → individuelle Nutzung
│ └── User B → individuelle Nutzung
└── Features mit LLM-Nutzung
├── Chat Playground
├── Chatbot (Feature-Instanzen)
└── Automation
3. LLM-Provider-Steuerung
- Benutzer können pro Workflow definieren, welche AICore-Provider erlaubt sind
- Zuordnung auf Level AICore-Connector:
anthropic,openai,perplexity,tavily,internal - Ermöglicht Kostenkontrolle und Compliance-Anforderungen
4. Transparente Kostenerfassung
- Jede AI-Operation wird mit Kosten erfasst (
priceCHF) - Echtzeit-Sichtbarkeit für Benutzer
- Monatliche Abrechnungsberichte
Abrechnungsmodelle
Übersicht der Modelle
| Modell | Beschreibung | Kostenstelle | Nutzung |
|---|---|---|---|
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 |
Modell: PREPAY_MANDATE
Mandant "SOHA Treuhand AG"
├── Abrechnungsmodell: PREPAY_MANDATE
├── Guthaben: 500.00 CHF
├── Warnschwelle: 50.00 CHF (10%)
└── Nutzer:
├── User A: verbraucht 120.50 CHF → Mandanten-Konto -120.50
└── User B: verbraucht 80.25 CHF → Mandanten-Konto -80.25
Restguthaben: 299.25 CHF
Eigenschaften:
- Mandant lädt Guthaben auf (via Admin oder Payment)
- Alle User des Mandanten teilen das Guthaben
- Bei Guthaben = 0 → AI-Features blockiert (mit Warnung)
- Warnschwelle konfigurierbar (Email-Benachrichtigung)
Modell: PREPAY_USER
Mandant "ROOT" (Bootstrap-Mandant)
├── Abrechnungsmodell: PREPAY_USER
├── Startguthaben für neue User: 10.00 CHF
└── 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
Eigenschaften:
- Jeder User hat eigenes Guthaben im Mandanten-Kontext
- Neuer User erhält Startguthaben (konfigurierbar)
- User kann Guthaben aufladen
- Bei Guthaben = 0 → AI-Features blockiert für diesen User
Modell: CREDIT_POSTPAY
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
Datenmodell
Neue Tabellen
# 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)
class BillingAccount(BaseModel):
"""Abrechnungskonto pro Mandant oder User-Mandant-Kombination"""
id: str # UUID
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)
warningThreshold: float # Warnschwelle in CHF
lastWarningAt: Optional[datetime] # Letzte Warnung gesendet
enabled: bool # Account aktiv
class BillingTransaction(BaseModel):
"""Einzelne Transaktion (Aufladung, Verbrauch, Gutschrift)"""
id: str # UUID
accountId: str # FK → BillingAccount
transactionType: str # CREDIT | DEBIT | ADJUSTMENT
amount: float # Betrag in CHF (positiv)
description: str # Beschreibung
# Referenz zur Quelle
referenceType: Optional[str] # WORKFLOW | PAYMENT | ADMIN | SYSTEM
referenceId: Optional[str] # ID des referenzierten Objekts
# Kontext
workflowId: Optional[str] # Bei WORKFLOW-Verbrauch
featureInstanceId: Optional[str] # Bei Feature-Nutzung
aicoreProvider: Optional[str] # anthropic, openai, etc.
class BillingSettings(BaseModel):
"""Abrechnungseinstellungen pro Mandant"""
id: str # UUID
mandateId: str # FK → Mandate (UNIQUE)
billingModel: str # PREPAY_MANDATE | PREPAY_USER | CREDIT_POSTPAY | UNLIMITED
# Konfiguration
defaultUserCredit: float # Startguthaben in CHF für neue User (bei PREPAY_USER)
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
notifyOnWarning: bool # Email bei Unterschreitung Warnschwelle
class UsageStatistics(BaseModel):
"""Aggregierte Nutzungsstatistiken (für schnellen Abruf)"""
id: str # UUID
accountId: str # FK → BillingAccount
periodType: str # DAY | MONTH | YEAR
periodStart: date # Beginn der Periode
# Aggregierte Werte
totalCostCHF: float # Gesamtkosten in CHF
transactionCount: int # Anzahl Transaktionen
# Aufschlüsselung nach Provider
costByProvider: Dict[str, float] # {'anthropic': 12.50, 'openai': 8.30}
# Aufschlüsselung nach Feature
costByFeature: Dict[str, float] # {'playground': 15.00, 'automation': 5.80}
Erweiterung bestehender Modelle
# Erweiterung: ChatWorkflow (datamodelChat.py)
class ChatWorkflow(BaseModel):
# ... bestehende Felder ...
# NEU: Mandanten-Kontext für Billing
mandateId: Optional[str] # FK → Mandate
Hinweis: Die Provider-Steuerung erfolgt NICHT über Datenbank-Attribute, sondern über das RBAC-System (siehe Abschnitt "LLM-Provider als RBAC Resources").
ER-Diagramm
┌─────────────────┐ ┌─────────────────────┐
│ Mandate │────<│ BillingSettings │
│ │ 1:1│ │
│ id │ │ mandateId │
│ name │ │ billingModel │
└────────┬────────┘ │ billingAddress │
│ └─────────────────────┘
│
│ 1:n
▼
┌─────────────────────┐ ┌─────────────────────┐
│ BillingAccount │────<│ BillingTransaction │
│ │ 1:n │ │
│ mandateId │ │ accountId │
│ userId (optional) │ │ amount (CHF) │
│ balance (CHF) │ │ transactionType │
│ accountType │ │ workflowId │
└─────────────────────┘ └─────────────────────┘
│
│ 1:n RBAC Resources
▼ ┌─────────────────────┐
┌─────────────────────┐ │ resource.aicore.* │
│ UsageStatistics │ │ │
│ │ │ .anthropic │
│ accountId │ │ .openai │
│ periodType │ │ .perplexity │
│ totalCostCHF │ │ .tavily │
│ costByProvider │ │ .internal │
└─────────────────────┘ └─────────────────────┘
LLM-Provider als RBAC Resources
Die Steuerung, welche LLM-Provider ein Benutzer verwenden darf, erfolgt über das RBAC-System:
Registrierte Resources:
resource.aicore.anthropicresource.aicore.openairesource.aicore.perplexityresource.aicore.tavilyresource.aicore.internal
Bootstrap-Berechtigungen (Mandate ROOT):
- SysAdmin: Alle Provider (USE)
- Admin: Alle Provider (USE)
- User: Alle Provider (USE)
Vorteile des RBAC-Ansatzes:
- Zentrale Verwaltung über bestehende Rollen-Struktur
- Granulare Steuerung pro Mandant möglich
- Keine zusätzlichen Datenbank-Attribute erforderlich
- Konsistent mit bestehendem Berechtigungssystem
Backend-Integration
1. Billing Service
# modules/services/serviceBilling/mainServiceBilling.py
class BillingService:
"""Zentrale Service-Klasse für Billing-Operationen"""
def __init__(self, currentUser: User, mandateId: str):
self.currentUser = currentUser
self.mandateId = mandateId
self._loadSettings()
def _loadSettings(self):
"""Lädt BillingSettings für den Mandanten"""
self.settings = getBillingSettings(self.mandateId)
def getAccount(self) -> BillingAccount:
"""Gibt das relevante Account zurück (Mandate oder User)"""
if self.settings.billingModel == 'PREPAY_USER':
return getOrCreateUserAccount(self.mandateId, self.currentUser.id)
else:
return getMandateAccount(self.mandateId)
def checkBalance(self, estimatedCost: float) -> BillingCheckResult:
"""Prüft ob genug Guthaben vorhanden ist"""
account = self.getAccount()
if self.settings.billingModel == 'UNLIMITED':
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=True, currentBalance=account.balance)
def recordUsage(self, priceCHF: float, context: UsageContext) -> BillingTransaction:
"""Erfasst Nutzungskosten und erstellt Transaktion"""
account = self.getAccount()
transaction = BillingTransaction(
id=generateUuid(),
accountId=account.id,
transactionType='DEBIT',
amount=priceCHF,
description=f"AI Usage: {context.process}",
referenceType='WORKFLOW',
referenceId=context.workflowId,
workflowId=context.workflowId,
featureInstanceId=context.featureInstanceId,
aicoreProvider=context.provider
)
# Account-Balance aktualisieren (atomar in DB)
account.balance -= priceCHF
# Warnung prüfen
self._checkWarningThreshold(account)
# Statistiken aktualisieren
self._updateStatistics(transaction)
return transaction
def isProviderAllowed(self, provider: str) -> bool:
"""Prüft ob User Berechtigung für Provider hat via RBAC"""
resourceKey = f"resource.aicore.{provider}"
return self.rbacService.hasPermission(
user=self.currentUser,
mandateId=self.mandateId,
objectKey=resourceKey,
action='USE'
)
2. Integration in AICore
# modules/aicore/aicoreModelSelector.py - Erweiterung
class ModelSelector:
def selectModel(
self,
operationType: str,
promptBytes: int,
processingMode: str = 'BASIC',
priority: str = 'BALANCED',
allowedProviders: Optional[List[str]] = None # NEU
) -> Optional[AiModel]:
"""
Wählt das beste Model basierend auf Kriterien.
NEU: Filtert nach erlaubten Providern.
"""
availableModels = self._getAvailableModels()
# NEU: Provider-Filter anwenden
if allowedProviders:
availableModels = [
m for m in availableModels
if m.connectorType in allowedProviders
]
# ... bestehende Selektionslogik ...
3. Integration in Workflow-Verarbeitung
# modules/workflows/processing/workflowProcessor.py - Erweiterung
class WorkflowProcessor:
def __init__(self, ...):
# ... bestehende Initialisierung ...
# NEU: Billing Service initialisieren
self.billingService = BillingService(
currentUser=self.currentUser,
mandateId=self.mandateId
)
async def _processAiCall(self, ...):
"""Erweitert um Billing-Checks"""
# Provider-Check
if not self.billingService.isProviderAllowed(selectedModel.connectorType):
raise ProviderNotAllowedException(
f"Provider '{selectedModel.connectorType}' not allowed for this mandate"
)
# Balance-Check (mit Kostenschätzung)
estimatedCost = self._estimateCost(selectedModel, inputBytes)
balanceCheck = self.billingService.checkBalance(estimatedCost)
if not balanceCheck.allowed:
raise InsufficientBalanceException(
f"Insufficient balance. Current: {balanceCheck.currentBalance}, "
f"Required: {balanceCheck.requiredAmount}"
)
# AI-Aufruf durchführen
response = await self._callAi(...)
# NEU: Kosten erfassen
if response.priceCHF:
self.billingService.recordUsage(
priceCHF=response.priceCHF,
context=UsageContext(
workflowId=self.workflowId,
featureInstanceId=self.featureInstanceId,
provider=selectedModel.connectorType,
process=response.process
)
)
return response
4. Bootstrap: Root-Mandant Setup
# modules/interfaces/interfaceBootstrap.py - Erweiterung
def _setupRootMandate():
"""Setup für Root-Mandant mit PREPAY_USER Modell"""
rootMandate = getOrCreateMandate(
name="ROOT",
description="System Root Mandate"
)
# Billing Settings für Root
billingSettings = BillingSettings(
id=generateUuid(),
mandateId=rootMandate.id,
billingModel='PREPAY_USER',
defaultUserCredit=10.0, # 10 CHF Startguthaben
warningThresholdPercent=20,
blockOnZeroBalance=True,
notifyOnWarning=True
)
saveBillingSettings(billingSettings)
# RBAC: LLM-Provider Berechtigungen für alle Rollen
_setupAicoreRbacPermissions(rootMandate.id)
def _setupAicoreRbacPermissions(mandateId: str):
"""Erstellt RBAC-Berechtigungen für LLM-Provider"""
providers = ['anthropic', 'openai', 'perplexity', 'tavily', 'internal']
roles = ['sysadmin', 'admin', 'user']
for role in roles:
for provider in providers:
createAccessRule(
mandateId=mandateId,
roleLabel=role,
context='RESOURCE',
item=f'resource.aicore.{provider}',
use=True
)
def _setupNewUserInRoot(user: User):
"""Erstellt Billing-Account für neuen User im Root-Mandanten"""
rootMandate = getRootMandate()
settings = getBillingSettings(rootMandate.id)
# User-Account erstellen
account = BillingAccount(
id=generateUuid(),
mandateId=rootMandate.id,
userId=user.id,
accountType='USER',
balance=settings.defaultUserCredit, # 10 CHF
warningThreshold=settings.defaultUserCredit * 0.2, # 20%
enabled=True
)
# Initiale Gutschrift-Transaktion
transaction = BillingTransaction(
id=generateUuid(),
accountId=account.id,
transactionType='CREDIT',
amount=settings.defaultUserCredit,
description='Initial credit for new user registration',
referenceType='SYSTEM'
)
saveAccount(account)
saveTransaction(transaction)
API-Endpoints
Billing Management (Admin)
# Mandanten-Billing-Einstellungen
GET /api/admin/billing/mandates/{mandateId}/settings
PUT /api/admin/billing/mandates/{mandateId}/settings
POST /api/admin/billing/mandates/{mandateId}/credit # Guthaben aufladen
# Transaktionen einsehen
GET /api/admin/billing/mandates/{mandateId}/transactions
GET /api/admin/billing/mandates/{mandateId}/accounts
User-Billing (Self-Service)
# Eigenes Guthaben
GET /api/billing/balance # Aktueller Stand pro Mandant
GET /api/billing/balance/{mandateId} # Stand für spezifischen Mandanten
# Eigene Transaktionen
GET /api/billing/transactions # Alle eigenen Transaktionen
GET /api/billing/transactions/{mandateId} # Transaktionen pro Mandant
# Statistiken
GET /api/billing/statistics # Aggregierte Stats
GET /api/billing/statistics/{mandateId} # Stats pro Mandant
GET /api/billing/statistics/{mandateId}/chart # Chart-Daten (Tag/Monat/Jahr)
Provider-Konfiguration
# Erlaubte Provider für Workflows
GET /api/billing/providers # Alle verfügbaren Provider
GET /api/billing/providers/{mandateId} # Erlaubte Provider für Mandant
# Workflow-spezifische Provider-Einstellung
PUT /api/chat/playground/{workflowId}/providers # Provider für Workflow setzen
PUT /api/automations/{automationId}/providers # Provider für Automation setzen
Response-Beispiele
// GET /api/billing/balance
{
"balances": [
{
"mandateId": "root-mandate-id",
"mandateName": "ROOT",
"billingModel": "PREPAY_USER",
"balance": 6.50,
"currency": "CHF",
"warningThreshold": 2.00,
"isWarning": false
},
{
"mandateId": "soha-mandate-id",
"mandateName": "SOHA Treuhand AG",
"billingModel": "PREPAY_MANDATE",
"balance": 299.25,
"currency": "CHF",
"warningThreshold": 50.00,
"isWarning": false
}
]
}
// GET /api/billing/statistics/root-mandate-id/chart?period=month&year=2026
{
"mandateId": "root-mandate-id",
"period": "month",
"year": 2026,
"currency": "CHF",
"data": [
{"label": "Jan", "totalCost": 12.50, "byProvider": {"anthropic": 10.00, "openai": 2.50}},
{"label": "Feb", "totalCost": 8.30, "byProvider": {"anthropic": 8.30}},
// ...
],
"totals": {
"totalCost": 45.80,
"byProvider": {"anthropic": 38.30, "openai": 7.50}
}
}
UI-Seiten
Billing wird als separater Navigation-Container gerendert:
Static Block: BILLING
├── page.billing.dashboard → /billing
├── page.billing.transactions → /billing/transactions
├── page.billing.statistics → /billing/statistics/{mandateId}
└── page.billing.admin → /billing/admin (nur SysAdmin)
1. Billing Dashboard (User)
Pfad: /billing
Funktionen:
- Übersicht aller Mandanten mit Guthaben
- Restbudget für ROOT-Mandant prominent anzeigen
- Quick-Stats: Ausgaben heute/diese Woche/diesen Monat
┌─────────────────────────────────────────────────────────┐
│ MEIN GUTHABEN │
├─────────────────────────────────────────────────────────┤
│ ┌──────────────────┐ ┌──────────────────────────────┐ │
│ │ ROOT │ │ Chart: Ausgaben pro Tag │ │
│ │ ────────────────│ │ ┌─┐ ┌─┐ ┌─┐ │ │
│ │ CHF 6.50 │ │ │█│ │█│ ┌─┐ │█│ ┌─┐ │ │
│ │ [████████░░] 65% │ │ │█│ │█│ │█│ │█│ │█│ ┌─┐ │ │
│ │ │ │ Mo Di Mi Do Fr Sa So │ │
│ │ Warnschwelle: 2 │ └──────────────────────────────┘ │
│ └──────────────────┘ │
│ │
│ MANDANTEN-ÜBERSICHT │
│ ┌──────────────────────────────────────────────────┐ │
│ │ SOHA Treuhand AG CHF 299.25 ████████████░░ │ │
│ │ Partner AG CHF 150.00 ██████████░░░░ │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
2. Detailansicht pro Mandant
Pfad: /billing/statistics/{mandateId}
Funktionen:
- Detaillierte Kostengrafik (Tag/Monat/Jahr umschaltbar)
- Aufschlüsselung nach Provider
- Aufschlüsselung nach Feature
- Transaktionshistorie
┌─────────────────────────────────────────────────────────┐
│ SOHA Treuhand AG - Kostenübersicht │
├─────────────────────────────────────────────────────────┤
│ Zeitraum: [Tag ▼] [Monat] [Jahr] Feb 2026 │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ [Bar-Chart: Kosten pro Tag im Februar] │ │
│ │ 25 ─┬─ (CHF) │ │
│ │ │ ██ │ │
│ │ 20 ─┤ ██ ██ │ │
│ │ │ ██ ██ ██ ██ │ │
│ │ 10 ─┤ ██ ██ ██ ██ ██ ██ │ │
│ │ │──██──██───██───██───██──██─────────────│ │
│ │ 1 5 10 15 20 25 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ AUFSCHLÜSSELUNG │
│ ┌────────────────┐ ┌────────────────┐ │
│ │ Nach Provider │ │ Nach Feature │ │
│ │ Anthropic 70% │ │ Playground 60% │ │
│ │ OpenAI 25% │ │ Automation 30% │ │
│ │ Internal 5% │ │ Chatbot 10% │ │
│ └────────────────┘ └────────────────┘ │
│ │
│ LETZTE TRANSAKTIONEN │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 04.02.2026 14:32 AI Usage: Document Analysis │ │
│ │ anthropic -CHF 0.15 │ │
│ │ 04.02.2026 14:28 AI Usage: Chat Response │ │
│ │ anthropic -CHF 0.08 │ │
│ │ ... │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
3. Provider-Auswahl im Chat Playground
Integration in: /workflows/playground
Funktionen:
- Dropdown/Checkboxen für erlaubte Provider
- Zeigt nur Provider an, für die der User RBAC-Berechtigung hat
- Auswahl gilt für den nächsten AI-Call
┌─────────────────────────────────────────────────────────┐
│ Chat Playground │
├─────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────┐ │
│ │ EINSTELLUNGEN │ │
│ │ │ │
│ │ AI-Provider für nächsten Call: │ │
│ │ ○ Anthropic (Claude) ~CHF 0.020/1k tokens │ │
│ │ ● OpenAI (GPT-4) ~CHF 0.015/1k tokens │ │
│ │ ○ Perplexity (Web) ~CHF 0.008/1k tokens │ │
│ │ ○ Internal (Document) ~CHF 0.002/1k tokens │ │
│ │ │ │
│ │ Geschätzte Kosten: ~CHF 0.02 pro Nachricht │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ [Chat-Interface...] │
└─────────────────────────────────────────────────────────┘
4. Automation Editor (Modal mit Tabs)
Integration in: /automations/{id}/edit
Das Automation-Editor Modal wird in drei Tabs aufgeteilt:
Tab-Struktur:
- General - Name, Beschreibung, Schedule, Status
- Modellierung - Template, Placeholders, Preview
- LLM Modelle - Provider-Auswahl basierend auf RBAC-Berechtigungen
┌─────────────────────────────────────────────────────────┐
│ Automation bearbeiten: "Täglicher Report" │
├─────────────────────────────────────────────────────────┤
│ [General] [Modellierung] [LLM Modelle] │
│ ───────────────────────────────────────────────────── │
│ TAB: LLM Modelle │
│ │
│ Verfügbare Provider (basierend auf Ihren Berechtigungen):
│ ○ Anthropic (Claude) CHF 0.020/1k tokens │
│ ● OpenAI (GPT-4) CHF 0.015/1k tokens │
│ ○ Perplexity (Web) CHF 0.008/1k tokens │
│ ○ Internal (Document) CHF 0.002/1k tokens │
│ │
│ Geschätzte Kosten pro Ausführung: ~CHF 0.05 │
│ Bei täglicher Ausführung: ~CHF 1.50/Monat │
│ │
│ [Speichern] [Abbrechen] │
└─────────────────────────────────────────────────────────┘
5. Admin: Billing-Verwaltung (nur SysAdmin)
Pfad: /billing/admin
Funktionen:
- Ü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
┌─────────────────────────────────────────────────────────┐
│ BILLING ADMINISTRATION │
├─────────────────────────────────────────────────────────┤
│ MANDANTEN │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Mandant Modell Balance Status │ │
│ │ ──────────────────────────────────────────────── │ │
│ │ ROOT PREPAY_USER n/a ✓ │ │
│ │ SOHA Treuhand PREPAY_MANDATE CHF 299 ✓ │ │
│ │ Enterprise AG CREDIT_POSTPAY CHF -450 ✓ │ │
│ │ Test GmbH PREPAY_MANDATE CHF 5 ⚠ │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ [+ Neuer Mandant] [Export Monatsreport] │
│ │
│ MANDANT BEARBEITEN: SOHA Treuhand AG │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 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 ] │ │
│ │ │ │
│ │ [Speichern] [Guthaben aufladen] │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
Hinweis: Provider-Berechtigungen werden über das RBAC-System verwaltet (Administration → Rollen).
Email-Reports
Monatsende-Report
Trigger: Automatisch am 1. jeden Monats (Cronjob)
Empfänger: BillingSettings.notifyEmails pro Mandant
Template:
Betreff: PowerOn Kostenreport Januar 2026 - SOHA Treuhand AG
Sehr geehrte Damen und Herren,
anbei erhalten Sie den monatlichen Kostenreport für Ihren Mandanten
"SOHA Treuhand AG" für den Zeitraum Januar 2026.
ZUSAMMENFASSUNG
───────────────────────────────────────────────────────
Gesamtkosten: $ 156.80 USD
Anzahl AI-Operationen: 423
Durchschnitt pro Operation: $ 0.37 USD
AUFSCHLÜSSELUNG NACH PROVIDER
───────────────────────────────────────────────────────
Anthropic (Claude) $ 112.50 USD (72%)
OpenAI (GPT-4) $ 38.30 USD (24%)
Internal $ 6.00 USD (4%)
AUFSCHLÜSSELUNG NACH FEATURE
───────────────────────────────────────────────────────
Chat Playground $ 95.00 USD (61%)
Automation $ 45.80 USD (29%)
Chatbot (Feature) $ 16.00 USD (10%)
TOP 5 NUTZER (nach Verbrauch)
───────────────────────────────────────────────────────
1. Max Mustermann $ 48.50 USD
2. Anna Schmidt $ 35.20 USD
3. Peter Weber $ 28.90 USD
4. Lisa Müller $ 22.10 USD
5. Thomas Meier $ 12.30 USD
KONTOSTAND
───────────────────────────────────────────────────────
Vorheriger Stand: $ 456.05 USD
Verbrauch: -$ 156.80 USD
───────────────────────────────────────────────────────
Aktueller Stand: $ 299.25 USD
Bei Fragen stehen wir Ihnen gerne zur Verfügung.
Freundliche Grüsse
PowerOn Platform
Warnung bei niedriger Balance
Trigger: Balance unterschreitet warningThreshold
Empfänger: BillingSettings.notifyEmails
Betreff: ⚠️ PowerOn - Niedriges Guthaben für SOHA Treuhand AG
Ihr Guthaben für den Mandanten "SOHA Treuhand AG" hat die
Warnschwelle unterschritten.
Aktuelles Guthaben: $ 45.00 USD
Warnschwelle: $ 50.00 USD (10%)
Bitte laden Sie Ihr Guthaben auf, um Unterbrechungen zu vermeiden.
[Guthaben aufladen →]
Freundliche Grüsse
PowerOn Platform
Implementierungsschritte
Phase 1: Datenmodell & Grundstruktur
-
Datenbank-Schema erstellen
BillingAccount,BillingTransaction,BillingSettings,UsageStatisticsTabellen- Migration für bestehende Mandanten (Default:
UNLIMITED)
-
Erweiterung bestehender Modelle
ChatWorkflow.mandateIdhinzufügenChatWorkflow.allowedProvidershinzufügenAutomationDefinition.allowedProvidershinzufügen
-
BillingService implementieren
- Balance-Prüfung
- Transaktions-Erfassung
- Provider-Validierung
Phase 2: Integration in Workflows
-
ChatWorkflow erweitern
mandateIdbei Start speichern- Provider-Filter bei Model-Selection
-
Billing-Checks einbauen
- Vor jedem AI-Call: Balance prüfen
- Nach jedem AI-Call: Kosten erfassen
-
Error Handling
InsufficientBalanceExceptionProviderNotAllowedException- User-freundliche Fehlermeldungen
Phase 3: API-Endpoints
-
User-Billing APIs
- Balance-Abfrage
- Transaktions-Historie
- Statistiken mit Chart-Daten
-
Admin-Billing APIs
- Settings verwalten
- Guthaben aufladen
- Report-Export
Phase 4: UI-Integration
-
Billing Dashboard
- Neue Seite:
/billing - Chart-Komponenten für Statistiken
- Neue Seite:
-
Provider-Auswahl
- Chat Playground erweitern
- Automation-Editor erweitern
-
Admin-Seiten
- Billing-Verwaltung
- Mandanten-Konfiguration
Phase 5: Bootstrap & Migration
-
Root-Mandant Setup
PREPAY_USERModell- 10 USD Startguthaben für neue User
-
Bestehende User migrieren
- Accounts erstellen
- Initiales Guthaben gutschreiben
-
Bestehende Mandanten migrieren
- Default-Settings erstellen
UNLIMITEDoderPREPAY_MANDATE
Phase 6: Email-Reports
-
Email-Templates erstellen
- Monatsreport
- Balance-Warnung
-
Scheduler einrichten
- Monatsende-Cronjob
- Warnschwellen-Check
-
Email-Service integrieren
- Template-Rendering
- Email-Versand
Offene Fragen und Entscheidungen
Beantwortet
-
Soll Chat Playground über Mandanten laufen? → Ja, dies ist für konsistente Verrechnung notwendig.
ChatWorkflow.mandateIdwird aus dem Request-Context übernommen. -
Welche Services laufen noch nicht über Mandanten? →
data.chat.*unddata.files.*sind aktuellUSER_OWNED. Für Billing mussChatWorkflowden Mandanten-Kontext speichern. -
Läuft Automation bereits über Mandanten? → Ja,
AutomationDefinition.mandateIdexistiert bereits. Erweiterung nur umallowedProviders.
Noch zu klären
-
Payment-Integration
- Wie laden Kunden Guthaben auf? (Stripe, Rechnung, manuell)
- Self-Service oder nur Admin?
-
Preisgestaltung
- Aufschlag auf Provider-Kosten? (z.B. +20%)
- Fixkosten pro Mandant/User?
-
Währung
- Nur USD intern, Anzeige in CHF/EUR?
- Wechselkurs-Handling?
-
Datenhaltung
- Wie lange Transaktions-Historie aufbewahren?
- Archivierung alter Daten?
-
GDPR/Datenschutz
- User-Löschung: Was passiert mit Billing-Daten?
- Anonymisierung nach X Jahren?
Glossar
| Begriff | Beschreibung |
|---|---|
| AICore | Plugin-System für LLM-Provider Integration |
| Billing Account | Konto für Guthaben/Saldo (pro Mandant oder User) |
| Connector | AICore-Plugin für einen spezifischen Provider |
| Kostenstelle | Mandant, dem Kosten zugeordnet werden |
| Mandate | Mandant/Tenant in der Multi-Tenant-Architektur |
| Provider | LLM-Anbieter (anthropic, openai, etc.) |
| priceCHF | Berechneter Preis einer AI-Operation in USD |
Anhang: Wichtige Dateien
Bestehend (zu erweitern)
gateway/modules/
├── aicore/
│ ├── aicoreModelSelector.py # Provider-Filter hinzufügen
│ └── aicoreBase.py # connectorType für Billing
├── datamodels/
│ ├── datamodelChat.py # ChatWorkflow erweitern
│ └── datamodelUam.py # Mandate-Referenz
├── features/automation/
│ └── datamodelFeatureAutomation.py # allowedProviders hinzufügen
├── interfaces/
│ └── interfaceBootstrap.py # Root-Mandant Billing Setup
├── workflows/
│ ├── automation/mainWorkflow.py # Billing-Integration
│ └── processing/workflowProcessor.py # Balance-Checks
└── routes/
└── routeChat.py # mandateId im Workflow speichern
Neu zu erstellen
gateway/modules/
├── datamodels/
│ └── datamodelBilling.py # Neue Billing-Modelle
├── interfaces/
│ └── interfaceDbBilling.py # Billing CRUD-Operationen
├── services/
│ └── serviceBilling/
│ ├── mainServiceBilling.py # Zentrale Billing-Logik
│ └── subBillingReports.py # Report-Generierung
└── routes/
└── routeBilling.py # Billing API-Endpoints