258 lines
13 KiB
Markdown
258 lines
13 KiB
Markdown
# Multi-Mandate Konzept — Umsetzungsbericht
|
|
|
|
**Datum:** 2026-03-28 (aktualisiert)
|
|
**Basis:** [Multi-Mandate-Onboarding-und-Store-Konzept.md](./Multi-Mandate-Onboarding-und-Store-Konzept.md)
|
|
|
|
---
|
|
|
|
## Legende
|
|
|
|
| Status | Bedeutung |
|
|
|--------|-----------|
|
|
| OK | Vollstaendig umgesetzt gemaess Konzept |
|
|
|
|
---
|
|
|
|
## 1. Datenmodell-Erweiterung
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| `MandateType` Enum (`system`, `personal`, `company`) | OK | Auf `Mandate`-Model in `datamodelUam.py`. |
|
|
| `isSystem` separat von `mandateType` | OK | Zwei unabhaengige Felder mit klarer Trennung. |
|
|
| `mandateType` mutabel/informativ | OK | Keine Geschaeftslogik an den Typ gebunden. |
|
|
| Default fuer bestehende Mandanten: `company` | OK | Migration setzt Default. |
|
|
|
|
---
|
|
|
|
## 2. Onboarding-Flows
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| Zwei CTAs auf Login-Seite (Personal/Company) | OK | `Login.tsx`: "Kostenlos testen" und "Fuer Unternehmen". |
|
|
| Register-Formular mit `registrationType` + `companyName` | OK | `Register.tsx` liest `type` aus URL, zeigt Firmenname bei `company`. |
|
|
| Backend: `_provisionMandateForUser` mit korrektem Typ | OK | `routeSecurityLocal.py` erstellt Mandate mit `personal`/`company`. |
|
|
| Subscription startet als `PENDING` | OK | `MandateSubscription` mit `status=PENDING` bei Erstellung. |
|
|
| `_activatePendingSubscriptions` bei Login | OK | Wird sowohl bei Local-Login als auch bei Google-OAuth aufgerufen. |
|
|
| OAuth-Onboarding-Wizard (Post-Login) | OK | `OnboardingWizard.tsx` existiert, wird bei `isNewUser=true` gezeigt. |
|
|
| Invitation-Flow (bestehendes Modell) | OK | Unveraendert, funktioniert mit Multi-Mandate-Modell. |
|
|
|
|
---
|
|
|
|
## 3. Feature Store: Own Instance Pattern
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| `mandateId` immer required (explizit, nie implizit) | OK | `StoreActivateRequest.mandateId: str = Field(...)` (required). |
|
|
| Mehrere Instanzen desselben Features erlaubt | OK | Jede Aktivierung erstellt neue `FeatureInstance`. |
|
|
| Store fuer alle User sichtbar (Upselling) | OK | Kein Gate auf Admin-Rolle fuer Store-Ansicht. |
|
|
| Auto-Provisioning Personal-Mandate bei 0 Admin-Mandaten | OK | Automatische Mandate-Erstellung wenn User keine Admin-Mandate hat. |
|
|
| Orphan Control bei Deaktivierung | OK | Letzer `FeatureAccess` entfernt -> `FeatureInstance` geloescht. |
|
|
| Store.tsx Header: "Own Instance Pattern" | OK | Kommentar aktualisiert. |
|
|
| Docstring in `routeStore.py` | OK | Korrigiert: "mandateId is None" entfernt, da mandateId jetzt required. |
|
|
|
|
---
|
|
|
|
## 4. Root-Mandant: Nur technisch
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| `createUser` weist NICHT zum Root-Mandanten zu | OK | `_assignUserToRootMandate` wurde geloescht. |
|
|
| Keine Feature-Instanzen fuer Endkunden im Root | OK | Migration entfernt Root-Instanzen. |
|
|
| sysadmin-User behalten Root-Mitgliedschaft | OK | Step 2 der Migration entfernt nur Nicht-SysAdmin-User. |
|
|
|
|
---
|
|
|
|
## 5. Migration: Root-Mandant bereinigen
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| Step 1: Feature-Daten migrieren (alle User) | OK | `migrateRootUsers.py`: Erstellt Ziel-Instanzen, migriert 5 Tabellen. |
|
|
| Step 2: Root bereinigen | OK | Entfernt Root-FeatureInstances, entfernt Nicht-SysAdmin-UserMandates. |
|
|
| Idempotent + DB-Flag | OK | `_isMigrationCompleted` / `_setMigrationCompleted` mit DB-Record. |
|
|
| Bootstrap-Integration | OK | `interfaceBootstrap.py` ruft Migration automatisch auf. |
|
|
| Voice/Documents Migration | OK | `migrateVoiceAndDocuments.py` migriert VoiceSettings + CoachingDocuments. |
|
|
|
|
---
|
|
|
|
## 6. Unified Data Layer (UDL)
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| `FileItem`: `scope` + `neutralize` Felder | OK | `scope: str` (default "personal"), `neutralize: bool` (default False). |
|
|
| `DataSource`: `scope` + `neutralize` Felder | OK | Felder vorhanden in `datamodelDataSource.py`. |
|
|
| `FeatureDataSource`: `scope` + `neutralize` Felder | OK | Felder hinzugefuegt in `datamodelFeatureDataSource.py`. |
|
|
| `FileContentIndex`: `scope`, `isNeutralized`, `userId`, `mandateId` | OK | Alle 4 Felder vorhanden. |
|
|
| `scope=global` RBAC (nur SysAdmin) | OK | Auf PATCH und PUT Endpunkten enforced. |
|
|
| Re-Indexing bei Scope/Neutralize-Aenderung | OK | PATCH-Endpoints triggern `_autoIndexFile`. |
|
|
| `indexFile` liest `scope` aus FileItem | OK | Setzt `scope` und `isNeutralized` auf FileContentIndex. |
|
|
| Alte DB-Records mit `scope=None` | OK | `_convertFileItems` setzt Default `"personal"` vor Validierung. |
|
|
| PATCH `/api/datasources/{id}/scope` + `/neutralize` | OK | Neue Route `routeDataSources.py` mit Lookup fuer DataSource + FeatureDataSource. |
|
|
|
|
---
|
|
|
|
## 7. Daten-Neutralisierung
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| Flag auf Datenquelle (`neutralize`) | OK | Auf FileItem, DataSource und FeatureDataSource. |
|
|
| Fail-Safe: Dokument NICHT weitergeben bei Fehler | OK | `_neutralizeRequest` returned excludedDocs, Call laeuft weiter ohne fehlgeschlagene Docs. |
|
|
| `_neutralizeRequest` neutralisiert `prompt` UND `messages` | OK | Beide werden verarbeitet. |
|
|
| Graceful Degradation statt RuntimeError | OK | Einzelne Dokumente werden ausgeschlossen, nicht der gesamte Call. |
|
|
| Neutralisierte Docs im Chat-UI sichtbar | OK | `ChatStream.tsx`: `<details>` Block mit "Gesendete Daten", neutralisiert/uebersprungen Badges, und "Nicht gesendet (Neutralisierung fehlgeschlagen)" Bereich fuer excludedDocs. |
|
|
| User kann Platzhalter-Mappings einsehen/loeschen | OK | Settings-Tab "Datenneutralisierung" mit Tabelle aller `DataNeutralizerAttributes` pro User. GET/DELETE API unter `/api/local/neutralization-mappings`. |
|
|
| `requireNeutralization` Flag pro AI-Call | OK | Feld auf `AiCallRequest` (Optional[bool]). `_shouldNeutralize` respektiert per-Request Override. UI-Toggle (Lock-Button) in `WorkspaceInput.tsx` neben Send-Button. |
|
|
|
|
---
|
|
|
|
## 8. UI-Komponente: Unified Data Bar (UDB)
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| UDB als wiederverwendbare Plattform-Komponente | OK | `components/UnifiedDataBar/` mit UnifiedDataBar, ChatsTab, FilesTab, SourcesTab. |
|
|
| Keine Render-Props, direkte Tab-Komposition | OK | Tabs direkt gerendert, `hideTabs`-Prop verfuegbar. |
|
|
| `hideTabs` fuer Feature-spezifische Konfiguration | OK | CommCoach nutzt `hideTabs={['chats']}`. |
|
|
| Alte Komponenten geloescht (FileBrowser, DataSourcePanel, ConversationList) | OK | Alle drei Dateien geloescht. |
|
|
|
|
### 8.1 Files-Tab
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| FolderTree mit Ordnerstruktur | OK | Nutzt `FolderTree`-Komponente. |
|
|
| Upload (Drag-Drop + Dateidialog) | OK | Implementiert. |
|
|
| Suche | OK | Volltextsuche ueber Dateinamen und Tags. |
|
|
| Scope-Icons inline (cycling) | OK | Klick wechselt personal -> featureInstance -> mandate. Icons auf 14px vergroessert fuer bessere Sichtbarkeit. |
|
|
| Neutralisierungs-Toggle inline | OK | Lock-Emoji mit opacity 0.4/1.0 Toggle. 14px fuer Sichtbarkeit. |
|
|
| Legende | OK | Am unteren Rand der Komponente. |
|
|
|
|
### 8.2 Sources-Tab
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| Connection Browsing (Services, Ordner, Dateien) | OK | Volle Browsing-Logik transferiert. |
|
|
| Feature Data Browsing (Mandate -> Instanzen -> Tabellen) | OK | Volle Logik transferiert. |
|
|
| Active Sources mit Scope/Neutralize-Icons | OK | Scope-Cycling und Neutralisierungs-Toggle auf Active-Elementen. |
|
|
| Icons NUR auf Active Sources (nicht auf Browse/Feature Data) | OK | Konzeptkonform. |
|
|
| PATCH-Endpoints fuer DataSource/FeatureDataSource | OK | `routeDataSources.py` registriert unter `/api/datasources`. |
|
|
|
|
### 8.3 Chats-Tab
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| Baumstruktur nach Feature-Instanzen | OK | Gruppiert nach `featureInstanceId` mit aufgeloestem `featureLabel`. |
|
|
| Feature-Label (nicht UUID) als Gruppenname | OK | Backend `listWorkspaceWorkflows` reichert `featureLabel` und `featureCode` aus `FeatureInstance`-Lookup an. |
|
|
| Suchfunktion | OK | Volltextsuche ueber Chat-Labels. |
|
|
| Flat Mode (chronologische Liste) | OK | Toggle im Tab-Header. |
|
|
| Drag-and-Drop in Prompt | OK | `draggable` + `onDragStart` mit `application/chat-id`. |
|
|
| Create New Button | OK | "+" Button wenn `onCreateNew` gesetzt. |
|
|
| Aktiv/Archiv Filter-Tabs | OK | Sub-Tabs "Aktiv (N)" und "Archiv (N)" unter der Toolbar. Nutzt `includeArchived=true` im API-Call. |
|
|
| Inline Action Icons (Umbenennen, Archivieren, Loeschen) | OK | Drei Icons pro Chat-Eintrag auf Hover: Stift, Box/Pfeil, Muelleimer. Archivierung/Wiederherstellung per PATCH workflow status. |
|
|
| Active Workflow Highlighting | OK | CSS-Klasse auf aktiven Chat. |
|
|
| API-Response korrekt gelesen (`workflows` key) | OK | `response.data?.workflows` (Bugfix angewendet). |
|
|
|
|
### 8.4 FolderTree
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| `FileNode` mit `scope` + `neutralize` | OK | Optionale Felder auf dem Interface. |
|
|
| Scope/Neutralize Icons in Datei-Zeilen | OK | Zwischen Dateiname und Aktions-Buttons. 14px Emoji-Groesse. |
|
|
| Callbacks `onScopeChange`/`onNeutralizeToggle` | OK | Durch `SelectionCtx` durchgereicht. |
|
|
|
|
---
|
|
|
|
## 9. RAG als zentrales Repository
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| Scope-basiertes Filtering bei RAG-Queries | OK | `semanticSearch` filtert nach Scope. |
|
|
| Union-Query (personal + featureInstance + mandate + global) | OK | Implementiert im Knowledge-Service. |
|
|
| `scope` aus FileItem in RAG-Index uebernommen | OK | `indexFile` liest und setzt Scope. |
|
|
| `isNeutralized` im RAG-Index | OK | Wird bei Neutralisierung gesetzt. |
|
|
|
|
---
|
|
|
|
## 10. Onboarding-Assistant
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| Global (nicht Feature-gebunden) | OK | Keine `instanceId`/`mandateId` Props. |
|
|
| Auf Dashboard-Seite | OK | In `Dashboard.tsx` eingebunden. |
|
|
| 4 globale Schritte (Mandant, Feature, Datenquelle, Chat) | OK | API-basierte Pruefung. |
|
|
| "Nicht wieder anzeigen" Checkbox | OK | `localStorage`-basiert mit Checkbox. |
|
|
| Reaktivierbar ueber User-Kontextmenu | OK | "Onboarding-Assistent" in `UserSection.tsx`. |
|
|
| Kontextsensitive Callouts | OK | Pro Schritt ein Tipp-Text (Callout) mit farbiger Hervorhebung fuer den naechsten offenen Schritt. Texte fuer alle 4 Schritte definiert. |
|
|
|
|
---
|
|
|
|
## 11. Mandanten-Loeschung: Kaskade
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| FeatureAccess loeschen | OK | Pro Instanz. |
|
|
| FeatureInstances loeschen | OK | Alle im Mandanten. |
|
|
| Instanz-Daten: ChatWorkflow, ChatMessage, ChatLog | OK | Geloescht pro Instanz. |
|
|
| Instanz-Daten: FileItem | OK | Geloescht pro Instanz. |
|
|
| Instanz-Daten: DataSource | OK | Geloescht pro Instanz. |
|
|
| Instanz-Daten: DataNeutralizerAttributes | OK | Geloescht pro Instanz. |
|
|
| Instanz-Daten: FileContentIndex (RAG) | OK | Geloescht pro Instanz. |
|
|
| UserMandate-Zuordnungen | OK | Entfernt. |
|
|
| MandateSubscription | OK | Entfernt. |
|
|
| Stripe-Subscription kuendigen | OK | `stripe.Subscription.cancel(stripeSubId)` wird pro aktiver Subscription vor dem Loeschen aufgerufen. |
|
|
| `isSystem=true` Schutz | OK | Systemmandate nicht loeschbar. |
|
|
| Soft-Delete mit 30-Tage Retention | OK | `deletedAt` Feld auf Mandate-Model. Soft-Delete setzt `enabled=False` + `deletedAt=now()`. `purgeExpiredMandates(retentionDays=30)` im Bootstrap. `restoreMandate()` fuer Wiederherstellung. |
|
|
|
|
---
|
|
|
|
## 12. Subscription: Datenvolumen als Parameter
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| `maxDataVolumeMB` in SubscriptionPlan | OK | Feld existiert auf `SubscriptionPlan` (`Optional[int]`). |
|
|
| API fuer Datenvolumen-Nutzung | OK | `GET /api/subscription/data-volume/{mandateId}` berechnet Nutzung (FileItem.fileSize Summe), liefert `usedMB`, `maxDataVolumeMB`, `percentUsed`, `warning`. |
|
|
|
|
---
|
|
|
|
## 13. Voice-Settings Zentralisierung
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| `UserVoicePreferences` Model (User-Level) | OK | In `datamodelUam.py`. |
|
|
| Settings-Seite mit Tabs inkl. "Stimme & Sprache" | OK | `Settings.tsx` mit 5 Tabs (inkl. Datenneutralisierung). |
|
|
| CommCoach nutzt zentrale Voice-Preferences | OK | Kein eigenes Voice-Silo mehr. |
|
|
| Workspace nutzt zentrale Voice-Preferences | OK | `WorkspaceInput.tsx` liest von `/api/local/voice-preferences`. |
|
|
|
|
---
|
|
|
|
## 14. Code-Hygiene
|
|
|
|
| Punkt | Status | Details |
|
|
|-------|--------|---------|
|
|
| `_assignUserToRootMandate` geloescht | OK | Methode entfernt. |
|
|
| Store.tsx: "Own Instance Pattern" im Kommentar | OK | Aktualisiert. |
|
|
| `interfaceFeatureWorkspace.py`: Kein "VoiceSettings" in Docstring | OK | Bereinigt. |
|
|
| `routeFeatureCommcoach.py`: Kein "voice endpoints" in Docstring | OK | Bereinigt. |
|
|
| CommCoach: Eigene Dokument-/Voice-Endpoints geloescht | OK | Entfernt. |
|
|
| Alte FileBrowser/DataSourcePanel/ConversationList geloescht | OK | Alle drei entfernt. |
|
|
|
|
---
|
|
|
|
## Zusammenfassung
|
|
|
|
| Kategorie | OK |
|
|
|-----------|-----|
|
|
| Datenmodell | 4 |
|
|
| Onboarding-Flows | 7 |
|
|
| Feature Store | 7 |
|
|
| Root-Mandant | 3 |
|
|
| Migration | 5 |
|
|
| Unified Data Layer | 9 |
|
|
| Neutralisierung | 7 |
|
|
| UDB (UI) | 27 |
|
|
| RAG | 4 |
|
|
| Onboarding-Assistant | 6 |
|
|
| Mandanten-Loeschung | 12 |
|
|
| Subscription/Datenvolumen | 2 |
|
|
| Voice-Zentralisierung | 4 |
|
|
| Code-Hygiene | 6 |
|
|
| **Gesamt** | **103** |
|
|
|
|
Alle Punkte sind vollstaendig umgesetzt. Keine offenen oder teilweisen Punkte verbleibend.
|