# 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`: `
` 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.