312 lines
36 KiB
Markdown
312 lines
36 KiB
Markdown
<!-- status: plan -->
|
||
<!-- started: 2026-06-04 -->
|
||
<!-- component: gateway | ui-nyla | platform -->
|
||
<!-- replaces: 2026-06-STEP2-lawyer-feature.md, 2026-06-STEP2-umsystem-integration-connectors-und-datenquellen-seiten.md (Code-Teile), 2026-06-STEP2-pm-consolidated-customer-requirements.md (Feature/Plattform-Teile) -->
|
||
|
||
# Plan: Features & Plattform-Bausteine — der Code hinter den Solutions
|
||
|
||
> **Baut auf:** `2026-06-CustomerCases-step1-architecture.md` (Schichten L1–L4, Feature-vs-Solution-Grenze).
|
||
> **Schwester-Plan:** `2026-06-CustomerCases-step3-solutions-plan.md` (die konfigurierten Use Cases, die diesen Code konsumieren).
|
||
> **Konsolidiert/ersetzt:** Lawyer-Feature-Plan + die Code-/Plattform-Teile aus Umsystem-Integration und konsolidiertem Kundenwünsche-Plan.
|
||
|
||
## Beschreibung und Kontext
|
||
|
||
Ein **Feature** ist **Code**: eigenes Datenmodell und/oder tiefe, opinionated UI / Domänenlogik, die ein Graph nicht ausdrücken kann (step1-architecture, Entscheidungsmatrix). Dazu zählen auch die **Plattform-Bausteine**, die die Solutions erst möglich machen — sie sind generischer Code (L1/L2/L3/L4), **ohne Kundennamen**.
|
||
|
||
Dieser Plan bündelt allen **Code**, der zu bauen ist, in zwei Teilen:
|
||
|
||
- **Teil A — Plattform-Enabler:** die Solution-Schicht selbst (L3/L4), generische Workflow-Bausteine, Connectors, das «Datenquelle = Seite»-Muster. Voraussetzung für *alle* Solutions.
|
||
- **Teil B — Vertikale Code-Features:** Module mit eigenem Datenmodell/UI (`lawyer`), bestehende Features (`commcoach`, `trustee`, `neutralization`) und ihre Erweiterungen.
|
||
|
||
**Leitsatz (step1-architecture):** Solution-first. Code bauen wir nur, wenn (a) es ein generischer Baustein ist, den viele Solutions teilen, oder (b) der Use Case nachweislich nicht als Solution trägt.
|
||
|
||
## Fokus und kritische Details
|
||
|
||
- **Generisch, nie kundenspezifisch.** Connectors = reine API-Adapter (Auth + Lesen/Schreiben + Mapping auf kanonische Modelle), keine Geschäftsregeln, keine Kundennamen. Kundenlogik lebt als Solution (Daten).
|
||
- **Eine Workflow-Wahrheit.** Die Solution-Schicht ist eine **dünne Erweiterung der bestehenden `graphicalEditor`-Welt** (`AutoWorkflow`/`AutoVersion`/`AutoRun`/`WorkflowScheduler`), keine Parallel-Engine. Die tragenden Architektur-Entscheide dazu stehen in **A0** (Ownership = Mandant+Run-as-Principal, native Multi-Feature/Multi-Instanz, Settings-Injektion, `graphicalEditor`-Einordnung) — Voraussetzung für alles Weitere.
|
||
- **Source vs. Target** ist die wichtigste neue Connector-Achse (macht aus dem Push-Framework ein bidirektionales Integrations-Framework).
|
||
- **RBAC strikt feature-instance-scoped**, nicht mit Mandate-Rollen vermischen (`.cursor/rules/rbac-role-separation.mdc`). Admin-Match immer `roleLabel.endswith("-admin")`, nie `"admin" in label`.
|
||
- **i18n-Pflicht:** jeder neue UI-Text via `t('Deutscher Klartext')` (nur String-Literale), kundensichtbare Inhalte (Solution-Name/-Beschreibung, kuratierte Settings-Labels) als `TextMultilingual` — Details in **A5**.
|
||
- **Adapter-Drift-Invariante:** jeder neue Node muss `test_adapter_validator.py` (`_KNOWN_ADAPTER_DRIFTS = frozenset()`) grün halten.
|
||
- **Neutralisierung + Private LLM** zwingend für jeden LLM-Pfad mit Personendaten (Trustee, Lawyer/Berufsgeheimnis StGB 321).
|
||
- **YAGNI bei Connector-Abstraktion:** beim bestehenden `BaseAccountingConnector` bleiben und nur die **Source-Rolle** ergänzen; das generische «Capability-Mixin»-Framework erst ziehen, wenn die zweite Kategorie (Saläre) real wird.
|
||
|
||
## Teil A — Plattform-Enabler (macht Solutions möglich)
|
||
|
||
### A0 — Architektur-Leitentscheide (die kritischen Punkte)
|
||
|
||
> **Korrektur ggü. erstem Entwurf (Code-verifiziert, 2026-06-05).** Eine Code-Analyse von `executionEngine.py`, `mainScheduler.py`, `routeFeatureGraphicalEditor.py`, `datamodelFeatureGraphicalEditor.py` und den Trustee-Actions zeigt: Die Plattform modelliert Workflows **nicht** als Eigentum eines Host-Features, sondern als **mandatsweite, identitätsgebundene Orchestrierung über beliebige Feature-Instanzen**. Der frühere Entwurf «Owner = `hostFeatureRef`» (instanz-genaues Billing, Fan-out als Ausnahme) war **falsch geschnitten**. A0.1/A0.2 sind unten korrigiert; A0.3 (Settings-Injektion) bleibt gültig; A0.4 ordnet `graphicalEditor` ein.
|
||
|
||
#### A0.1 — Ownership-Achse: **Mandant + Run-as-Principal**, nicht Host-Feature
|
||
|
||
**Befund (Code):** `AutoWorkflow` trägt `mandateId` (Tenant-Grenze), `featureInstanceId` (GE-Instanz = nur RBAC-/Authoring-Eintritt) und `targetFeatureInstanceId` (Default-Daten-Scope). `getWorkflows()` filtert **nur nach `mandateId`** («cross-instance»). Ein Workflow läuft unter `services.user` — interaktiv der eingeloggte User, im Scheduler der **`event`-Service-Account** (`isSysAdmin=True`). `AutoRun.ownerId` = auslösender User. Es gibt **keinen** Host-Feature-Owner; ein Graph referenziert legitim **mehrere** Features (`trustee.*`, `redmine.*`, …) je per eigenem `FeatureInstanceRef`.
|
||
|
||
**Konsequenz — die richtige Achse:** Eine **Solution** ist ein **mandatsweit gescopter, konfigurierter Workflow**, der unter einem **Run-as-Principal** läuft; Zugriff auf Feature-Daten richtet sich nach der **RBAC dieses Principals**, über Feature-Grenzen hinweg. «Im Trustee angezeigt» ist **Präsentation**, nicht Eigentum.
|
||
|
||
```
|
||
Solution {
|
||
id
|
||
mandateId # Tenant-Grenze (Owner-Achse #1)
|
||
workflowRef -> AutoWorkflow (Orchestrierungs-Substrat)
|
||
runAsPrincipal # in wessen Namen ausgeführt wird (Owner-Achse #2):
|
||
# - interaktiv: auslösender User
|
||
# - geplant: definierte Automations-Identität (NICHT global sysadmin)
|
||
surfaceFeatureRef? # NUR UI-Platzierung (z.B. trustee-Seite), kein Governance-Kontext
|
||
settingsValues # Daten (A0.3), Secrets verschlüsselt
|
||
instanceSelection? # Menge der zu verarbeitenden Feature-Instanzen (A0.2)
|
||
customerFacing, outputBinding
|
||
}
|
||
```
|
||
|
||
- **RBAC = Principal-RBAC, nicht Host-Rollen.** Verwaltung der Solution wird über die bestehende **Workflow-/Automation-Permission** autorisiert; Host-Features exponieren das wie heute Trustee via RESOURCE-Item, das auf die Workflow-API zeigt (`resource.feature.trustee.workflows.manage` → `/api/workflows/{instanceId}/…`). Kein Bedarf, einen «Host-Owner» zu erfinden.
|
||
- **Run-as-Principal explizit machen (Code-Lücke schliessen):** Heute laufen geplante Runs als globaler `event`-Sysadmin → effektiv RBAC-Bypass. Sauber: pro Solution eine **definierte Automations-Identität** (Ersteller oder dedizierter Service-Principal mit *genau* den nötigen Feature-Rollen), in deren Namen der Scheduler ausführt. «Der Ersteller bestimmt, in wessen Namen es läuft» (deine Vorgabe) wird damit ein First-Class-Feld.
|
||
- **Daten-Lesezugriff RBAC-gleichziehen (Code-Lücke):** Trustee-**Writes** prüfen `checkCombinedPermission`, **Reads** filtern heute nur nach `featureInstanceId` ohne RBAC. Für «Daten sind gemäss RBAC verfügbar» müssen Lesepfade dieselbe Principal-RBAC anwenden (sonst sieht der Principal mehr als er dürfte).
|
||
|
||
#### A0.2 — Multi-Feature-/Multi-Instanz-Zugriff ist **nativ**, die echte Schranke ist der Mandant
|
||
|
||
**Befund (Code):** Pro-Node-`FeatureInstanceRef` ist der etablierte Mechanismus; der Instanz-Picker ist **mandats-gescoped** (`getFeatureInstancesByMandate`, filtert nach `featureCode`) und «leakt nie fremde Mandanten». Ein Graph über 5 Trustee-Instanzen **eines** Mandanten ist also kein Sonderfall, sondern der Normalfall (Pling = 5 Trustee-Instanzen eines Mandanten — bestätigt).
|
||
|
||
**Konsequenz:**
|
||
|
||
- **Kein Sonder-`instanceSet` als Ausnahme.** Multi-Instanz-Verarbeitung ist die native Fähigkeit (`flow.loop` über eine mandatsinterne Instanz-Auswahl, je Iteration ein `FeatureInstanceRef`). `feature-instance-scoping.mdc` braucht **keine** Fan-out-Ausnahme, sondern eine **Klarstellung**: *Cross-Instanz-Verarbeitung innerhalb eines Mandanten ist erlaubt, sofern der Run-as-Principal auf den Instanzen berechtigt ist; Cross-Mandate ist verboten.*
|
||
- **Laufzeit-Schranke schliessen (Code-Lücke):** Die Mandatsgrenze wird heute nur **design-time** (Picker) und am API-Start (`_validateTargetInstance`) geprüft, **nicht** pro Node zur Laufzeit. Ein handgebauter Graph könnte eine fremd-mandatige Instanz-UUID einschleusen. → **Runtime-Validierung:** beim Auflösen jedes `FeatureInstanceRef` prüfen, dass die Instanz zum `mandateId` des Runs gehört **und** der Run-as-Principal `FeatureAccess` hat.
|
||
- **Billing korrekt attribuieren (Code-Lücke, ersetzt den alten Vorschlag):** Heute bucht **jede** Nutzung auf `featureCode="graphicalEditor"` + GE-Instanz (aus dem Service-Hub), **unabhängig** von der berührten Trustee-Instanz. Mein früherer Satz «instanz-genaues Billing existiert» war falsch. Sauber: `recordUsage` soll, wenn ein Node eine `FeatureInstanceRef` trägt, **diese** `featureInstanceId`/`featureCode` mitschreiben (per-Node-Attribution); Mandate bleibt korrekt (`mandateId`). So werden Store-Kosten real dem Store, Holding-Overhead der Holding zugeordnet — über die bestehende `recordUsage`-Signatur, ohne neue Billing-Achse.
|
||
- **RBAC der Empfänger:** `rbac.queryUsersByRole` löst **pro iterierter Instanz** auf (Store-Empfänger je Store).
|
||
|
||
#### A0.3 — Settings-Injektion statt Graph-Mutation (löst die Versions-Invariante)
|
||
|
||
**Problem:** «Settings = Daten, kein Deploy» darf den **immutablen, publizierten** `AutoVersion`-Graph nicht verändern (Invariante: max. eine PUBLISHED-Version; Scheduler bindet an Published).
|
||
|
||
**Lösung — Settings werden zur Laufzeit als Inputs injiziert, nie in den Graph geschrieben:**
|
||
|
||
- Das Template deklariert seine konfigurierbare Oberfläche über einen **`trigger.form`**-Node (bzw. als «exposed» markierte Params). `settingsSchema` wird daraus abgeleitet (`parse_graph_defined_output_schema` / `{kind:"fromGraph"}` existiert real).
|
||
- Die `Solution` speichert nur **`settingsValues`** (Daten). Beim Run-Start baut der Solution-Service das **`runEnvelope`** aus `settingsValues` und speist sie als `trigger.form`-Output ein. Die Graph-Nodes lesen sie via **DataRef** (Pick-not-Push, bestehender Mechanismus). **Der publizierte Graph bleibt unangetastet.**
|
||
- **Ein Template-Graph, N Solutions:** N Kunden = N `settingsValues`-Records auf **einer** publizierten Version. Keine Graph-Kopien pro Kunde, kein Re-Publish bei Settings-Änderung.
|
||
- **Secrets** (Connector-Credentials) liegen verschlüsselt in `settingsValues` (wie `TrusteeAccountingConfig`) und werden nur ins `runEnvelope` injiziert, nie in den Graph persistiert.
|
||
- **Template-Versionierung entkoppelt:** Solutions referenzieren die `PUBLISHED`-Version des Templates; optional `pinnedVersionId` zum Einfrieren. Bei neuem `settingsSchema` werden bestehende `settingsValues` beim Laden gegen das neue Schema validiert (Migrations-Hinweis in der UI).
|
||
|
||
#### A0.4 — `graphicalEditor` ist ein **Orchestrierungs-Substrat** (System-Komponente) im Feature-Mantel
|
||
|
||
**Befund (Code):** `graphicalEditor` ist ein **Hybrid**. Formal ist es ein Feature (Discovery wie Trustee, `TEMPLATE_ROLES` `graphicalEditor-viewer/user/admin`, Store-Eintrag `resource.store.graphicalEditor`, `FeatureInstance` pro Mandant). Architektonisch ist es **System-Infrastruktur**:
|
||
|
||
- **Kein `getDataObjects()`** — keine End-Kundendaten/Domäne; `AutoWorkflow`/`AutoRun` sind Orchestrierungs-Metadaten. Es ist das einzige Feature **ohne** DATA-Katalog.
|
||
- **Eigene DB `poweron_graphicaleditor`** hält Graphen/Runs **aller** Features; Trustee-System-Templates werden bei Trustee-Anlage **dorthin** kopiert (`targetFeatureInstanceId` = Trustee-Instanz).
|
||
- **Einziges Feature mit `onStart`/`onStop`** — bootet beim App-Start den **globalen** Scheduler + Email-Poller (für alle Mandanten), unabhängig davon, ob ein Mandant eine GE-Instanz hat.
|
||
- **Scheduler scannt mandatsübergreifend** (`getAllWorkflowsForScheduling()` ohne Mandatsfilter); System-Dashboard `/api/system/workflow-runs` ist eine **Ops-Sicht** über alle Mandanten.
|
||
- Engine + Scheduler liegen ohnehin unter `modules/workflows/` (System), nicht im Feature.
|
||
|
||
→ Es ist «n8n in PORTA», kein Domänen-Feature wie Trustee. Vergleich: Scheduler/RBAC/`system` sind reine Infrastruktur; `neutralization` ist das echte Feature-Analogon (DATA, kein `onStart`).
|
||
|
||
**Entscheid (pragmatisch, kein Big-Bang):**
|
||
|
||
- **Konzeptionell als Plattform-/System-Komponente führen** («Automation/Workflow-Plattform»), nicht als Domänen-Feature. Der **Feature-Mantel bleibt** als RBAC-Eintritt + Store-Packaging + Navigation — das ist legitime Wiederverwendung der Feature-Mechanik, kein Modellfehler.
|
||
- **Solutions nicht an einen GE-Owner hängen** (siehe A0.1): Solution = mandatsweit + Run-as-Principal; die GE-Instanz ist nur Authoring-/RBAC-Eintritt.
|
||
- **Doku/Mentales Modell schärfen:** `b-reference` soll `graphicalEditor` explizit als **«Orchestrierungs-Substrat, als Feature paketiert»** beschreiben, damit künftige Designs nicht erneut einen «Host-Feature-Owner» erfinden.
|
||
- **Roadmap (optional, nicht jetzt):** Scheduler-Boot vom Feature-`onStart` entkoppeln (echter System-Service) und GE-Instanz rein als Authoring-Surface führen. Erst ziehen, wenn es weh tut (YAGNI).
|
||
|
||
> Netto (korrigiert): **A0.1** verankert Ownership an **Mandant + Run-as-Principal** (statt erfundenem Host-Owner); **A0.2** behandelt Multi-Feature/Multi-Instanz als **native** Fähigkeit und benennt die 3 echten Code-Lücken (Runtime-Mandatscheck, Read-RBAC, per-Node-Billing); **A0.3** hält «Config statt Code» mit der Versions-Invariante vereinbar; **A0.4** ordnet `graphicalEditor` als Orchestrierungs-Substrat ein. Alles auf bestehenden Mechanismen (`mandateId`-Scoping, `FeatureInstanceRef`, `runEnvelope`/`trigger.form`, `recordUsage`).
|
||
|
||
### A1 — Solution-Schicht (L3/L4)
|
||
|
||
- **`Solution`-Modell** in `graphicalEditor` (siehe A0.1): `mandateId`, `workflowRef`, `runAsPrincipal`, `surfaceFeatureRef?` (nur UI-Platzierung), `settingsValues` (Secrets verschlüsselt), `instanceSelection?`, `customerFacing`, `outputBinding`, optional `pinnedVersionId`. **DB-Migration: ja (Greenfield, additiv).** `AutoWorkflow` selbst bleibt schlank; konfigurierbar über `trigger.form` (A0.3) — **kein** Settings-Schreiben in den Graph.
|
||
- **`settingsSchema` automatisch ableiten** aus `trigger.form` + als «exposed» markierten Node-Params (`parse_graph_defined_output_schema`); kuratierte, mehrsprachige Label-Overrides via A5.
|
||
- **Run-Start = Settings-Injektion** (A0.3): Solution-Service baut `runEnvelope` aus `settingsValues`, Nodes lesen via DataRef. Ausführung unter `runAsPrincipal` (A0.1). **Billing per-Node** auf die berührte `FeatureInstanceRef` (A0.2), Mandate = `mandateId`.
|
||
- **Solution-Routes** (dünn über bestehende Editor-/Run-Routes): CRUD Settings, Test-Run (`testMode`), Activate/Deactivate. Verwaltungs-RBAC = **Workflow-/Automation-Permission** (vom Host-Feature via RESOURCE-Item auf die Workflow-API exponiert, bestehendes Trustee-Muster `resource.feature.trustee.workflows.manage`), **nicht** ein erfundener Host-Owner.
|
||
- **`SolutionsView`** (ui-nyla): **eine** generische «Lösungen»-Seite, pro Host-Feature als **Surface eingebettet** (Präsentation, nicht Ownership) — Liste · Katalog/Neu · Detail mit Tabs (Einstellungen · Testlauf · Läufe · Ausgabe). Folgt dem Tab-Pattern (`?tab=`/`?solutionId=`-Deep-Links, Lazy-Mount, Flex-Chain) wie `TrusteeDataTablesView`. Settings-Formular aus `settingsSchema` via `FRONTEND_TYPE_RENDERERS`/FormGenerator (Felder **nie** hardcodiert). Aktionen über `useConfirm()`/`usePrompt()`. Registriert via `FeatureView.tsx` + UI-Areas in `main<Feature>.py`. Alle Texte i18n (A5).
|
||
- **Use-Case-Katalog** = `templateScope=system`-Workflows mit `customerFacing=true`, gefiltert nach Host-`featureCode`/Tag (bestehende System-Template-Mechanik in `mainGraphicalEditor._bootstrapSystemTemplates` / `interfaceBootstrap.py`).
|
||
- **AI: Use Case → Workflow** über bestehenden Workflow-Agent (Toolbox `workflow`), Draft + Review vor Aktivierung.
|
||
- **Output-Renderer** für `outputBinding.kind` = `file` (Datenraum/`TrusteeDocument`), `table` (FormGenerator/Data-Tables, Spalten aus Node-Output-Schema), `summary` (`AutoRun`/`AutoStepLog`). `dashboard` später (AI-Report/Canvas-Pipeline).
|
||
|
||
### A2 — Generische Workflow-Bausteine (Nodes/Actions)
|
||
|
||
> **Node-ID-Abgleich gegen `nodeDefinitions/` (verifiziert).** Vorhanden: `trustee.queryData` (echter Node), `data.consolidate`, `data.aggregate`, `data.filter`, `ai.prompt`, `ai.generateDocument`, `ai.consolidate`, `file.create`, `sharepoint.listFiles`/`sharepoint.readFile`/`sharepoint.upload`, `flow.loop`, `trigger.*`. **Es gibt KEINEN Versand-Node** — nur `email.checkEmail`/`email.searchEmail`/`email.draftEmail` (Senden existiert nur als Methode `outlook.sendDraftEmail`). **Es gibt KEIN `data.writeToTable`.** Beides muss gebaut werden (unten).
|
||
|
||
| Baustein | Zweck | Status |
|
||
|---|---|---|
|
||
| `email.sendEmail` (Versand-Node) | Mail tatsächlich **versenden** (heute nur `draftEmail`); kapselt `outlook.sendDraftEmail`. **Blocker** für S2/S3/S6. | **neu** |
|
||
| `data.writeToTable` | strukturierte Ergebnistabelle (PWG-Übersicht S3) als Run-Output | **neu** |
|
||
| `rbac.queryUsersByRole` | rollenbasierte Empfänger-Auflösung, **feature-instance-scoped** (`FeatureAccessRole→FeatureAccess→User`, Admin-Match `endswith("-admin")`); bei Fan-out pro iterierter Instanz | **neu** |
|
||
| Trigger-`accessControl.requiredRoles` (in `triggerExecutor`) | nur erlaubte Rolle darf manuell auslösen — **Prior Art:** Quick Actions in `mainTrustee.py` tragen bereits `requiredRoles`; Mechanik wiederverwenden | **neu (auf Basis Bestehendem)** |
|
||
| run-level `testMode` | Testlauf real, Seiteneffekt-Nodes (`email.sendEmail`/`sharepoint.upload`/`integration.pushToTarget`) unterdrücken Versand (loggen «würde senden …»); Wechselwirkung mit `input.emailWait` beachten | **neu** |
|
||
| `integration.fetchFromSource` / `integration.mapAccounts` / `integration.pushToTarget` | system-agnostischer Source→Target-Import (Quelle/Ziel = Config) | **neu** |
|
||
| `data.consolidate` (sumByKey) | Aggregation mehrerer Instanzen (Pling Holding-Konsolidierung) | **vorhanden** ✓ |
|
||
| `trigger.event` / DB-Change-Detection | ereignisgetriggerte Solutions (Notification) | **neu (Roadmap)** |
|
||
|
||
Alle **neuen** Nodes erscheinen über den Editor-Adapter (`graphicalEditor/nodeDefinitions/` + `registerNodeWithMethod`) und müssen `test_adapter_validator.py` (Drift = `frozenset()`) grün halten.
|
||
|
||
### A3 — Connectors (L1, generische API-Adapter)
|
||
|
||
- **SelectLine-Connector** (Quelle): `accountingConnectorSelectline.py`, implementiert `BaseAccountingConnector`, Auto-Discovery via `accountingRegistry.py`. Methoden: `testConnection`, `getChartOfAccounts`, `getCustomers`/`getVendors`, Lese-Seite für Belege (`getOutgoingInvoices`, Default `[]` in Base). Config: `baseUrl`, `username`, `password` (secret), optional `mandant`.
|
||
- **`TrusteeAccountingConfig.role`** (`source` | `target`) → mehrere Connector-Configs pro Instanz (eine Quelle + ein Ziel); `getActiveConfig`/`_resolveConnectorAndConfig` um `role` filtern. Optional `connectorCategory` (`accounting` | `payroll` | `invoicing`). **DB-Migration: ja.**
|
||
- **Kanonisches `Invoice`-Modell** prüfen/ergänzen (falls RMA als AR-Rechnung statt GL-Buchung).
|
||
- **Source-Pfad in `accountingBridge.py`** additiv (Push-Pfad unverändert).
|
||
- **Roadmap-Connectors** (generisch, je 1 Datei + Config): Xero (1.1.11), FileMaker (PWG 1.9.10), Kassensystem/Gastro (1.6, wartet auf Input), Lohn/Swissdec (4.2), Bank-Statement-Import (4.1.2). Je neuer Connector schaltet neue Solutions frei, ohne Architekturänderung.
|
||
|
||
### A4 — «Datenquelle = Seite» (generische Connector-UI)
|
||
|
||
- `TrusteeAccountingSettingsView` → generische **`IntegrationSettingsView`** (`category`-Prop): Verbindungen (Connector + Rolle Quelle/Ziel + Credentials + Test), Datenfluss/Import, Daten-Spiegel.
|
||
- Neue UI-Areas in `mainTrustee.py` (z. B. `ui.feature.trustee.payroll`) + Mapping in `FeatureView.tsx` → **gleiche** Komponente, andere `category`.
|
||
- «Buchhaltungssystem» existiert; «Saläre»/«Fakturierung» als Folge-Kategorien (Platzhalter, nicht erste Umsetzung).
|
||
|
||
### A5 — i18n & Lokalisierung (Pflicht, regelkonform)
|
||
|
||
`ui-nyla` ist DB-backed i18n (`LanguageContext.t()`); Key-Konvention **deutscher Klartext = Key**. Regeln (`b-reference/ui-nyla/architecture.md`):
|
||
|
||
- **Statische UI-Texte** (`SolutionsView`, Katalog, Tabs, Buttons, Toasts, Empty-States, Fehlermeldungen) **müssen** `t('Deutscher Klartext')` nutzen — **nur String-Literale**, nie `t(variable)`. Status-Labels (z. B. «bestätigt»/«Abweichung») via `switch` mit Literalen, kein Map-Lookup.
|
||
- **Kundensichtbare Inhaltsdaten** = `TextMultilingual` (`{xx, en, de, …}`, `xx`=Quelltext), damit sie bereits übersetzt vom Backend kommen und direkt gerendert werden:
|
||
- **Solution-Name + Beschreibung** (Katalog/Liste/Detail).
|
||
- **Kuratierte Settings-Labels/Hilfetexte**: das auto-abgeleitete `settingsSchema` liefert technische Param-Namen — darüber liegt eine **mehrsprachige Label-Overlay-Ebene** (`TextMultilingual` pro Setting), die im Formular gerendert wird. Übersetzung via bestehendem `POST /api/i18n/translate-field`.
|
||
- **System-Template-Katalog**: Template-Titel/-Beschreibung als `TextMultilingual`.
|
||
- **Backend-Werte** (Rollen-`description`, Feature-Labels) sind bereits übersetzt → direkt rendern, **nicht** durch `t()`.
|
||
- **Akzeptanz:** kein hardcodierter deutscher String im neuen JSX; fehlende Keys erscheinen als `[key]` (sichtbar-machen).
|
||
|
||
## Teil B — Vertikale Code-Features
|
||
|
||
### B1 — `lawyer` (Mandatsvorbereitung + Dashboards) — NEU
|
||
|
||
Echtes Feature (eigene Modelle + opinionated UI), das die Plattform-Bausteine bündelt; **konsumiert** die Solution-Schicht (Matter-Prep/Dashboard-Refresh als interne Solutions/Actions).
|
||
|
||
- **Datenmodell** (`datamodelLawyer.py`): `LawyerMatter`, `LawyerPreparation`, `LawyerDashboardSnapshot`, `LawyerKpi`, `LawyerSourceMap`. **Migration: ja.**
|
||
- **Feature-Modul** `mainLawyer.py` + Template-Rollen `lawyer-admin` / `lawyer-user` / `lawyer-viewer` (feature-instance-scoped).
|
||
- **Actions** (`methodLawyer/`): `lawyer.prepareMatter`, `lawyer.refreshDashboard`, `lawyer.queryData`.
|
||
- **UI** (`ui-nyla/src/components/Lawyer/`): Dashboard-View (Tabs HR/Business/IT/Finance) + Matter-Preparation-View (opinionated, analog Workspace).
|
||
- **Connectors:** DMS (SharePoint vorhanden, iManage neu?), E-Mail (Outlook/Exchange), KYC/AML, Konflikt-Check — als generische L1-Bausteine (Teil A3-Muster).
|
||
- **Neutralisierung zwingend** (Berufsgeheimnis); Sprache UI primär Englisch (WW/Investor).
|
||
- **Demo-Asset:** synthetic Matter `M-DEMO-001` für WW-Präsentation (statisches Briefing-JSON, Backend-frei).
|
||
|
||
> Begründung Feature statt Solution: eigene Datenmodelle (`LawyerMatter`…), tiefe opinionated UI (Matter-Preparation), Domänenlogik. Dashboards/Matter-Prep dürfen intern Solutions/Actions nutzen — das Feature ist der opinionated Rahmen.
|
||
|
||
### B2 — `commcoach` (bestehend)
|
||
|
||
- Feature komplett (10+ Personas inkl. 4 PWG-Immobilien-Personas, Gamification, Seeding). **Erweiterungen:** kundenspezifische Persona-Sets als Daten (z. B. PWG-Knowledge-Set). Kein Neubau.
|
||
|
||
### B3 — `trustee` (Core, bestehend) — Erweiterungen
|
||
|
||
- Engine + Accounting-Bridge + Pipeline (`extractFromFiles`/`processDocuments`/`syncToAccounting`) + Connectors (RMA/Bexio/Abacus) bestehen.
|
||
- **Erweiterungen als Bausteine** (nicht als Kundenlogik): Firmen-/Lieferanten-Mapping-UI (1.1.4), Buchungsregeln-Engine (1.1.6), Vorsteuer-Automatik (1.1.7), Bank-Statement-Matching (4.1).
|
||
- **Exponieren statt migrieren:** Die Analyse-Templates existieren in `mainTrustee.py` **bereits als System-Workflow-Templates** (`trustee-budget-comparison`, `trustee-kpi-dashboard`, `trustee-cashflow`, `trustee-forecast`, `trustee-year-end-check`) **mit `requiredRoles`/`tags`**. Aufgabe ist nicht «aus Prompt-Templates migrieren», sondern sie als **customer-facing Solutions exponieren** (`customerFacing=true`, `settingsSchema` via `trigger.form`, mehrsprachige Labels) → solutions-plan S5. Trustee bleibt Daten-/Connector-Heimat.
|
||
|
||
### B4 — Plattform-Features (bestehend, Querschnitt)
|
||
|
||
- `neutralization` (PII-Masking, Private-LLM, Compliance-Audit) — vorhanden; pro Solution/Feature nur konfigurieren.
|
||
- Knowledge/RAG, Billing, Multi-Tenancy, Workspace — vorhanden.
|
||
- **UI/UX-Härtung** (aus altem Kundenplan Teil 2, plattformweit, nicht Solution/Feature-spezifisch): Empty-State, DE-Placeholder/i18n, Zwischen-Breakpoint 1025–1280px, Trust-Strip, Billing-Discovery, Connector-Onboarding-Copy. → separate UX-Charge, hier nur referenziert.
|
||
|
||
## Feature-vs-Solution — Recap (Entscheidungsmatrix)
|
||
|
||
| Kriterium | → Solution (Daten) | → Feature (Code) |
|
||
|---|---|---|
|
||
| Eigenes Datenmodell? | nein | ja (`LawyerMatter`) |
|
||
| Tiefe opinionated UI? | nein | ja (Matter-Preparation) |
|
||
| Aus Bausteinen komponierbar? | ja | nein (Domänenlogik) |
|
||
| Beispiel | SelectLine→RMA, Pling, PWG, Analysen | `lawyer`, `commcoach`, `trustee`-Core |
|
||
|
||
## Betroffene Module
|
||
|
||
- **Gateway (platform-core):**
|
||
- `features/graphicalEditor/` — neues `Solution`-Modell (`mandateId`/`workflowRef`/`runAsPrincipal`/`surfaceFeatureRef`/`settingsValues`/`instanceSelection`, A0.1) + Solution-Service + Run-Envelope-Injektion (A0.3).
|
||
- `workflows/automation2/executionEngine.py` + `scheduler/mainScheduler.py` — **Run-as-Principal** statt globalem `event`-Sysadmin (A0.1); **Runtime-Mandatsvalidierung** beim `FeatureInstanceRef`-Auflösen (A0.2, `graphUtils.py`). **Migration: ja.**
|
||
- `serviceCenter/services/serviceAi`/`serviceBilling` (`recordUsage`) — **per-Node-Attribution** auf berührte `featureInstanceId`/`featureCode` statt fix `graphicalEditor` (A0.2).
|
||
- Trustee-Read-Pfade (`methodTrustee/actions/queryData.py` u. a.) — **Read-RBAC** gleichziehen mit Write-Pfad (`checkCombinedPermission`, A0.1).
|
||
- `workflows/methods/` + `graphicalEditor/nodeDefinitions/` — neue Nodes (A2): `email.sendEmail`, `data.writeToTable`, `rbac.queryUsersByRole`, `integration.*` (+ `registerNodeWithMethod`, Drift-Test).
|
||
- `workflows/.../triggerExecutor` — `accessControl.requiredRoles` (Prior Art: Trustee Quick-Actions) + `testMode` (A2).
|
||
- `features/trustee/accounting/connectors/accountingConnectorSelectline.py` (neu), `accountingConnectorBase.py` (Source-Methode), `accountingBridge.py` (Source-Pfad), `datamodelFeatureTrustee.py` (`role` [+ `connectorCategory`]) — **Migration: ja** (A3).
|
||
- `features/lawyer/` (neu, B1) — `mainLawyer.py`, `datamodelLawyer.py`, `methodLawyer/`, Routes. **Migration: ja.**
|
||
- **RBAC-Items**: Verwaltungs-Permission = Workflow-/Automation-Permission; Host-Feature exponiert ein RESOURCE-Item auf die Workflow-API (Muster `resource.feature.trustee.workflows.manage`) + UI-Item `ui.feature.<host>.solutions` für die Surface. DATA-AccessRules für `Solution`/Settings (`feature.graphicalEditor`-Namespace).
|
||
- **Deletion-Cascade**: `Solution`/Settings in `deleteMandate(force=True)` mit abräumen (feature-instance-scoping-Checkliste).
|
||
- `serviceCenter/services/serviceAgent/` (Toolbox `workflow`) — Use-Case→Graph (A1).
|
||
- **Frontend (ui-nyla):**
|
||
- `SolutionsView` (A1, Tab-Pattern + FormGenerator + `useConfirm`/`usePrompt`, i18n A5), `IntegrationSettingsView` (A4), `Lawyer/`-Komponenten (B1), `FeatureView.tsx`-Registry + UI-Areas. Alle Texte `t()`, Inhaltsdaten `TextMultilingual`.
|
||
- **DB-Migration:** ja (`Solution`-Modell + Settings, `TrusteeAccountingConfig.role`, Lawyer-Modelle).
|
||
- **Andere:** RBAC (Workflow-/Automation-Permission via Host-RESOURCE-Item + Principal-RBAC), Neutralisierung (selektiv via `FeatureDataSource`-Flags, feature-admin-gated), Billing (Mandate-Pool; **per-Node-Attribution** auf berührte Feature-Instanz, A0.2), i18n-Keys/Sprachsets.
|
||
|
||
## Entscheidungen
|
||
|
||
| Datum | Entscheidung | Begründung |
|
||
|-------|-------------|------------|
|
||
| 2026-06-05 | **A0.1 (korrigiert)** Ownership = **Mandant + Run-as-Principal**, nicht Host-Feature; RBAC = Principal-RBAC | Code-Befund: Workflows sind mandatsweit + identitätsgebunden, kein Host-Owner |
|
||
| 2026-06-05 | **A0.1** Scheduled Runs unter **definierter Automations-Identität** statt globalem `event`-Sysadmin | RBAC-Bypass schliessen; «Ersteller bestimmt Run-as» |
|
||
| 2026-06-05 | **A0.2 (korrigiert)** Multi-Feature/Multi-Instanz ist **nativ** (per-Node `FeatureInstanceRef`); Schranke = Mandant | Code-Befund: Picker mandats-gescoped, kein Sonder-Fan-out nötig |
|
||
| 2026-06-05 | **A0.2** 3 Code-Lücken schliessen: Runtime-Mandatscheck, Read-RBAC, **per-Node-Billing** | heute Billing fix auf `graphicalEditor`; Reads ohne RBAC; Mandat nur design-time geprüft |
|
||
| 2026-06-05 | **A0.3** Settings als **Run-Envelope-Injektion** (`trigger.form`/DataRef), nie Graph-Mutation | hält Published-Version-Invariante; 1 Template-Graph, N Settings |
|
||
| 2026-06-05 | **A0.4** `graphicalEditor` als **Orchestrierungs-Substrat** führen (Feature-Mantel bleibt) | Code-Befund: kein DATA, eigene DB für alle Features, globaler Scheduler-Boot |
|
||
| 2026-06-05 | Versand als **neuer `email.sendEmail`-Node** (heute nur `draftEmail`) | Send-Node fehlt real → Blocker für Reporting-Solutions |
|
||
| 2026-06-05 | i18n: UI via `t()`, kundensichtbare Inhalte als `TextMultilingual` | `ui-nyla`-Regel; Kataloge/Settings lokalisierbar |
|
||
| 2026-06-04 | Plattform-Bausteine + Solution-Schicht als **generischer Code**, nie kundenspezifisch | macht alle Solutions möglich, hält «kein Kunden-Code» real |
|
||
| 2026-06-04 | Connectors = reine Adapter; `source`/`target`-Rolle als neue Achse | bidirektionales Integrations-Framework, additive Erweiterung |
|
||
| 2026-06-04 | `lawyer` bleibt **Feature** (eigene Modelle + opinionated UI), nutzt Solution-Schicht intern | Entscheidungsmatrix; Domänenlogik nicht als Graph abbildbar |
|
||
| 2026-06-04 | Trustee-Analyse-Templates als **Solutions exponieren** (existieren als System-Templates) | kundentauglich machen; Trustee bleibt Daten-/Connector-Heimat |
|
||
| 2026-06-04 | Connector-Abstraktion erst bei zweiter Kategorie (Saläre) generalisieren | YAGNI |
|
||
|
||
## Umsetzungs-Checkliste
|
||
|
||
**Teil A0 (Architektur-Leitentscheide, zuerst festzurren):**
|
||
- [ ] A0.1 `Solution`-Modell (`mandateId`/`workflowRef`/`runAsPrincipal`/`surfaceFeatureRef`/`settingsValues`) + Solution-Service
|
||
- [ ] A0.1 **Run-as-Principal** in Engine + Scheduler (statt globalem `event`-Sysadmin) + Read-RBAC gleichziehen
|
||
- [ ] A0.2 **Runtime-Mandatsvalidierung** beim `FeatureInstanceRef`-Auflösen + **per-Node-Billing** (`recordUsage` mit berührter Instanz) + Klarstellung in `feature-instance-scoping.mdc`
|
||
- [ ] A0.3 Run-Envelope-Injektion aus `settingsValues` (kein Graph-Schreiben) + `settingsSchema`-Drift-Validierung + optional `pinnedVersionId`
|
||
- [ ] A0.4 `b-reference`: `graphicalEditor` als Orchestrierungs-Substrat dokumentieren (Roadmap: Scheduler-Boot entkoppeln — später)
|
||
|
||
**Teil A (Enabler):**
|
||
- [ ] Solution-Routes **am Host-Feature** (CRUD/Test/Activate) + Host-RBAC + `SolutionsView` (Tab-Pattern, FormGenerator, `useConfirm`/`usePrompt`, i18n)
|
||
- [ ] RBAC-Items: `ui.feature.<host>.solutions`, `resource.feature.<host>.solutions.execute`, DATA-AccessRules in `TEMPLATE_ROLES`
|
||
- [ ] Output-Renderer `file`/`table`/`summary` (Spalten aus Node-Output, nicht hardcodiert)
|
||
- [ ] **`email.sendEmail`-Node** (Versand) + **`data.writeToTable`-Node** + Adapter-Drift-Test grün
|
||
- [ ] `rbac.queryUsersByRole` (feature-instance-scoped, `endswith("-admin")`), Trigger-`accessControl` (Prior Art Quick-Actions), run-level `testMode`
|
||
- [ ] Integration-Nodes `fetchFromSource`/`mapAccounts`/`pushToTarget` + Editor-Adapter
|
||
- [ ] `data.consolidate` vorhanden ✓ (kein Neubau)
|
||
- [ ] SelectLine-Connector + `TrusteeAccountingConfig.role` (+ optional `connectorCategory`) + Migration
|
||
- [ ] kanonisches `Invoice`-Modell prüfen; Source-Pfad in `accountingBridge` (additiv)
|
||
- [ ] `IntegrationSettingsView` (`category`) generalisieren
|
||
- [ ] Use-Case-Katalog (`templateScope=system` + `customerFacing`) + Workflow-Agent-Pfad
|
||
- [ ] **i18n (A5):** alle UI-Texte `t()`, Solution-Name/-Beschreibung + Settings-Labels als `TextMultilingual`
|
||
- [ ] **Deletion-Cascade:** `Solution`/Settings in `deleteMandate(force=True)`
|
||
|
||
**Teil B (Features):**
|
||
- [ ] `lawyer`: Datenmodell (5 Modelle) + `mainLawyer.py` + 3 Actions + UI (Dashboard/Matter-Prep) + RBAC + Neutralisierung + Demo-Asset
|
||
- [ ] `trustee`: Analyse-Templates → Solution-Templates; optional Mapping-UI/Buchungsregeln/Vorsteuer/Bank-Matching
|
||
- [ ] `commcoach`: kundenspezifische Persona-/Knowledge-Sets als Daten
|
||
- [ ] Roadmap-Connectors nach Bedarf (Xero, FileMaker, Lohn, Kassensystem, Bank-Import)
|
||
|
||
## Akzeptanzkriterien
|
||
|
||
| # | Kriterium (Given-When-Then) | Prio |
|
||
|---|---|---|
|
||
| 1 | Given ein neues Umsystem, When ein Connector-File + Config ergänzt wird, Then ohne Änderung an Registry/Bridge/Editor verfügbar | must |
|
||
| 2 | Given `testMode`, When eine Solution testweise läuft, Then erzeugt sie Artefakte, sendet aber keine Kommunikation | must |
|
||
| 3 | Given `rbac.queryUsersByRole`, When mit Rollen-Set + Instanz aufgerufen, Then dedupliziert die korrekten Empfänger **dieser Instanz** | must |
|
||
| 4 | Given Anwalt mit `lawyer-user`, When Matter-Preparation getriggert, Then strukturiertes Briefing in <90s, PII vor LLM neutralisiert | must |
|
||
| 5 | Given Mandate-Rolle ohne `lawyer-*`, When Lawyer-Feature geöffnet, Then Access Denied | must |
|
||
| 6 | Given Settings-Änderung, When gespeichert, Then ohne Re-Publish/Code-Deploy beim nächsten Run wirksam (Injektion, kein Graph-Schreiben) | must |
|
||
| 7 | Given ein Run mit Nodes über mehrere Trustee-Instanzen, When er läuft, Then bucht `recordUsage` je Node die **berührte** `featureInstanceId` (nicht fix `graphicalEditor`), Mandate = `mandateId` (A0.2) | must |
|
||
| 8 | Given ein Node mit Instanz-UUID aus fremdem Mandant, When der Ref aufgelöst wird, Then Laufzeit-Fehler (Mandatsgrenze, A0.2) | must |
|
||
| 9 | Given ein geplanter Run, When er ausgeführt wird, Then unter der definierten Automations-Identität (nicht globaler Sysadmin); Daten-Reads nur im RBAC-Umfang des Principals (A0.1) | must |
|
||
| 10 | Given neuer Node (`email.sendEmail`/`data.writeToTable`/…), When Adapter-Drift-Test läuft, Then `report.errors == []` | must |
|
||
|
||
## Offene Fragen
|
||
|
||
> A0.1–A0.4 sind als Leitentscheide **entschieden** (oben). Offen bleiben Detail-/Externfragen:
|
||
|
||
1. **Run-as-Principal-Modell:** Ersteller-Identität vs. dedizierter Service-Principal pro Solution — wie verwaltet/rotiert man die Rollen dieser Identität? Audit-Anforderungen?
|
||
2. **Per-Node-Billing-Migration:** `recordUsage`-Signatur trägt `featureInstanceId`/`featureCode` bereits — reicht ein Durchreichen der Node-Ref, oder braucht es Aggregations-/Reporting-Anpassungen?
|
||
3. **`outputBinding`-Schema:** Artefakt-Referenz (Run-Output-Key vs. Document-Query); `table`-Spalten aus Node-Output-Schema ableiten — Detailformat.
|
||
4. **SelectLine:** on-prem-Erreichbarkeit (VPN/Tunnel/Agent); Zielart RMA (AR vs. GL).
|
||
5. **Lawyer-Connectors:** iManage/KYC/Konflikt-Check-APIs — welche mandantenspezifisch, welche generisch?
|
||
6. **Connector-Generalisierung:** wann Capability-Mixins (`AccountingCapable`/`PayrollCapable`) ziehen?
|
||
7. **`email.sendEmail`-Scope:** eigener Node vs. `email.draftEmail` + Auto-Send-Flag — und Verhältnis zu `input.emailWait`/`testMode`.
|
||
8. **`graphicalEditor`-Entkopplung:** Scheduler-Boot vom Feature-`onStart` lösen — jetzt oder als spätere Roadmap (A0.4)?
|
||
|
||
## Links
|
||
|
||
- Architektur: `c-work/0-ideas/2026-06-CustomerCases-step1-architecture.md`
|
||
- Solutions: `c-work/0-ideas/2026-06-CustomerCases-step3-solutions-plan.md`
|
||
- Bausteine: `b-reference/platform-core/workflow.md`, `b-reference/platform-core/automation.md`, `b-reference/platform-core/features/trustee.md`
|
||
- RBAC/Neutralisierung: `b-reference/platform/rbac.md`, `b-reference/platform/neutralization.md`, `.cursor/rules/rbac-role-separation.mdc`
|
||
- Lawyer-Kontext (WW): `pamocreate/projects/poweron/customer-walderwyss/`
|
||
- Code: `platform-core/modules/features/trustee/accounting/` (Bridge/Base/Registry/Connectors), `features/graphicalEditor/`
|
||
|
||
## Abschluss
|
||
|
||
- [ ] Bei Annahme → Build in `c-work/2-build/` (Teil A vor Teil B)
|
||
- [ ] `b-reference/` Kanon-Seiten: «Solutions / Customer Workflows», `features/lawyer.md`, Trustee «Source-Connectors»
|
||
- [ ] `TOPICS.md` + `_CHANGELOG.md` pro Phase
|