483 lines
40 KiB
Markdown
483 lines
40 KiB
Markdown
<!-- status: build (Phase 1, 3, 4, 5, 6, 2-toolbar done; FilesTab integration deferred) -->
|
||
<!-- started: 2026-04-16 -->
|
||
<!-- updated: 2026-04-19 -->
|
||
<!-- component: gateway | frontend-nyla | platform -->
|
||
<!-- relatedTo: c-work/0-ideas/2026-04-pm-consolidated-customer-requirements.md (1.9c) -->
|
||
|
||
# PWG-Pilot: Jahresmietzinsbestätigungs-Workflow + Workflow-File-IO
|
||
|
||
## Beschreibung und Kontext
|
||
|
||
**Quelle:** Konsolidierter Kundenwünsche-Plan, Abschnitt 1.9c — PWG-Workshop 16.04.2026.
|
||
|
||
**Business Case:** PWG verschickt 4 × 800 = 3'200 Jahresmietzinsbestätigungen pro Jahr. Rückantworten (Scans) werden aktuell manuell verarbeitet. Pilot-Ziel: AI-gestützte Verarbeitung mit Antwortvorschlägen, Versand Sommer 2026.
|
||
|
||
**Dieser Plan deckt drei zusammenhängende Themen ab:**
|
||
|
||
1. **Workflow-File-IO** — Workflows als File exportieren (in UDB speichern) und aus UDB-File in den Graph-Editor laden. Voraussetzung, damit der Pilot-Workflow als versionierbares Asset im Repo geliefert werden kann.
|
||
2. **Agent-Tools Full-CRUD** — Der AI-Agent erhält im `workflow`-Toolbox die vollständigen Operationen `createWorkflow`, `createWorkflowFromFile`, `exportWorkflowToFile`, `deleteWorkflow` (zusätzlich zu den bestehenden Lese-/Edit-Tools).
|
||
3. **Pilot-Workflow als File** — Konkrete Lieferung: Workflow-JSON nach Schema, Step-5 AI-Prompt, Datenextraktion aus Trustee-DB.
|
||
4. **PWG-Demo-Bootstrap** — Eigenes Demo-Config-Modul `pwgDemo2026.py` (analog zu `investorDemo2026.py`), das Mandant, User, Features, Seed-Daten und den importierten Pilot-Workflow per Knopfdruck in einer leeren Dev-Umgebung erstellt.
|
||
|
||
**Risiko bei Nicht-Umsetzung:** PWG-Pilot kann nicht termingerecht (Sommer 2026) aufgesetzt werden. Workflows bleiben nicht portabel zwischen Mandanten/Instanzen, was Demo-Vorbereitungen und Customer-Onboarding deutlich verlangsamt.
|
||
|
||
**Abhängigkeiten:**
|
||
- Abacus-Testmandant (extern, Patrick koordiniert) — nicht Blocker für Workflow-Bau, nur für End-to-End-Test.
|
||
- Bestehende Komponenten (alle ✅): Trigger, SharePoint-Nodes, `flow.loop`, `trustee.extractFromFiles`, `ai.prompt`, `email.send`.
|
||
|
||
## Fokus und kritische Details
|
||
|
||
- **Trustee-DB hat kein Mieter-/Mietzins-Modell** (siehe Codebase-Audit). Stattdessen: Matching-Logik liegt im AI-Prompt von Step 5. Daten werden via Sub-Agent (`queryFeatureInstance` / `aggregateTable` auf `TrusteeDataContact` + `TrusteeDataJournalLine`) oder via dediziertem Daten-Extract-Node bereitgestellt. **Kein neues DB-Schema nötig.**
|
||
- **Graph-Format-Inkonsistenz:** Bootstrap-Templates verwenden Node-Felder `x` / `y` (top-level), Agent-Tool `addNode` schreibt `position: {x, y}`. Beim Import muss normalisiert werden.
|
||
- **Workflow-Envelope-Felder:** `AutoWorkflow` hat zahlreiche optionale Felder (`tags`, `invocations`, `templateScope`, `sharedReadOnly`, `notifyOnFailure`). Beim Export müssen Mandanten-spezifische Felder (`mandateId`, `featureInstanceId`, `id`, `currentVersionId`, `eventId`) **rausgefiltert** werden, sonst sind Files nicht portabel.
|
||
- **Schema-Versionierung:** Wir starten mit `$schemaVersion: "1.0"`. Loader prüft Version und lehnt unbekannte ab oder migriert.
|
||
- **UDB-Erkennung:** Workflow-Files identifizieren wir per Dateiendung `.workflow.json` (case-insensitive) **und** Content-Sniffing (Top-Level-Keys `$schemaVersion` + `graph` + `nodes`).
|
||
- **Sicherheit beim Import:** Geladene Workflows werden NICHT automatisch ausgeführt. Nutzer muss aktiv via "Speichern + Aktivieren" die `invocations` aktivieren. `active` wird beim Import auf `false` gesetzt.
|
||
- **Node-Type-Validierung:** Beim Import gegen `STATIC_NODE_TYPES` validieren. Unbekannte Node-Typen → Fehler mit klarer Meldung (welcher Typ fehlt).
|
||
|
||
## Ziel und Nicht-Ziele
|
||
|
||
**Ziel:**
|
||
- Workflows können als File (JSON) aus dem Graph-Editor exportiert und in UDB gespeichert werden.
|
||
- Workflow-Files in UDB können im Graph-Editor wieder geladen werden (neuer Workflow oder Update bestehender).
|
||
- AI-Agent kann Workflows lesen, erstellen, aus File importieren, exportieren und löschen.
|
||
- Der PWG-Pilot-Workflow `pwg-mietzinsbestaetigung-pilot.workflow.json` liegt im Repo, kann von einer leeren PWG-Demo-Instanz geladen und (mit Test-Daten) ausgeführt werden.
|
||
- Step-5-Prompt liefert pro Scan: Status (`bestaetigt` / `abweichung` / `unleserlich` / `keine_unterschrift`) und einen Antwortvorschlag bei Abweichung.
|
||
|
||
**Explizit NICHT:**
|
||
- Kein neues DB-Schema für Mietzins/Mieter (Matching erfolgt im Prompt gegen bestehende Trustee-Daten).
|
||
- Keine Persistenz der Pilot-Run-Ergebnisse in einer Trustee-Sub-Tabelle (CSV im Mail reicht für Pilot).
|
||
- Keine Auto-Aktivierung importierter Workflows (sicherheitsrelevant).
|
||
- Keine Migration-Logik für `$schemaVersion > 1.0` (kommt später).
|
||
- Kein Versions-Branching/Diff von Workflow-Files (späteres Thema).
|
||
- Keine SharePoint-OCR-Verbesserungen (extractFromFiles wird wie bestehend genutzt).
|
||
|
||
## Betroffene Module
|
||
|
||
- **Gateway:**
|
||
- `features/graphicalEditor/routeFeatureGraphicalEditor.py` — neue Routen `POST .../workflows/import` und `GET .../workflows/{id}/export`.
|
||
- `features/graphicalEditor/interfaceFeatureGraphicalEditor.py` — neue Methoden `importWorkflowFromDict`, `exportWorkflowToDict`.
|
||
- `features/graphicalEditor/_workflowFileSchema.py` (NEU) — Schema-Definition + Validierung + Normalisierung (`x/y` ↔ `position`).
|
||
- `serviceCenter/services/serviceAgent/workflowTools.py` — neue Tools `createWorkflow`, `createWorkflowFromFile`, `exportWorkflowToFile`, `deleteWorkflow`.
|
||
- `serviceCenter/services/serviceAgent/toolboxRegistry.py` — Tool-Liste in `workflow`-Toolbox erweitern.
|
||
- **Frontend:**
|
||
- `components/UnifiedDataBar/FilesTab.tsx` — Workflow-File-Erkennung + Action "In Graph-Editor laden".
|
||
- `pages/views/workflow/` (Graph-Editor-View) — Buttons "Aus Datei importieren" + "Als Datei exportieren" (Editor-Toolbar).
|
||
- `api/workflowApi.ts` (oder analog) — neue Endpoint-Wrapper.
|
||
- **DB-Migration:** **nein**.
|
||
- **Repo-Asset:** `gateway/demoData/workflows/pwg-mietzinsbestaetigung-pilot.workflow.json` (NEU).
|
||
- **Demo-Config:** `gateway/modules/demoConfigs/pwgDemo2026.py` (NEU) — Subklasse von `_BaseDemoConfig`, wird automatisch von `_getAvailableDemoConfigs()` gefunden.
|
||
- **Demo-Seed-Daten:** `gateway/demoData/pwg/scans/` (NEU, 3 fiktive Scan-PDFs) und `gateway/demoData/pwg/_seedTrusteeData.json` (NEU, fiktive Mieter + Mietzins-Buchungen).
|
||
- **Andere:** Step-5-Prompt-Template als Konstante in `gateway/modules/features/trustee/promptTemplates/_pwgMietzinsCheck.py` oder direkt im Workflow-File (Decision unten).
|
||
|
||
## Entscheidungen
|
||
|
||
| Datum | Entscheidung | Begründung |
|
||
|-------|-------------|------------|
|
||
| 2026-04-16 | Workflow-File-Format = Envelope + Schema-Version (1.0) | Portabel, zukunftssicher, ein Round-Trip ohne Verlust von Metadaten |
|
||
| 2026-04-16 | Matching-Logik im AI-Prompt von Step 5 (nicht neue DB-Tabelle) | Trustee-DB hat kein Mietzins-Modell; Pilot kommt mit AI + bestehende Sub-Agent-Queries aus |
|
||
| 2026-04-16 | Result-Output = CSV als E-Mail-Anhang (kein DB-Schema) | Schlankster Pilot-Pfad; Persistenz später bei Bedarf nachrüstbar |
|
||
| 2026-04-16 | Agent-Tools = Full-CRUD inkl. `deleteWorkflow` | Agent soll Workflows komplett verwalten können (User-Wunsch) |
|
||
| 2026-04-16 | File-Endung `.workflow.json` + Content-Sniffing | Klare UDB-Identifikation ohne neuen MIME-Type |
|
||
| 2026-04-16 | Import setzt `active: false`; Aktivierung manuell | Sicherheits-Default — kein versehentliches Auto-Run nach Import |
|
||
| 2026-04-16 | Step-5-Prompt im Workflow-File (Parameter von `ai.prompt`-Node) | Das File ist self-contained und im Repo lesbar; keine Code-Änderung beim Tunen |
|
||
| 2026-04-16 | CSV-Sammlung über bestehende `data.aggregate` (collect) + `data.consolidate` (csvJoin) | Idiomatisch, bereits implementiert; kein neuer Node nötig |
|
||
| 2026-04-16 | Neuer Node `trustee.queryData` ist Pflicht | Kein bestehender Node liest Trustee-DB; `ai.prompt` hat keinen Sub-Agent-Tool-Zugriff (verifiziert in `methodAi/actions/process.py`) — `context`-Parameter muss vorher befüllt werden |
|
||
| 2026-04-16 | E-Mail mit Attachment via Erweiterung von `email.draftEmail` (neuer optionaler `attachments`-Parameter), kein neuer Node | Minimal-invasiv; `email.draftEmail` ist bereits gemappt auf `methodOutlook.composeAndDraftEmailWithContext` und kann erweitert werden. Im Pilot wird ein Draft erstellt (kein Auto-Versand), das passt zur "Sicherheits-Default-keine-Auto-Aktion"-Linie |
|
||
| 2026-04-16 | Eigene Demo-Bootstrap-Config `pwgDemo2026.py` (analog zu `investorDemo2026.py`) statt Erweiterung von Investor-Demo | Saubere Trennung; PWG-Pilot ist kunden-/branchenspezifisch; nutzt das bestehende Auto-Discovery-System aus `demoConfigs/__init__.py` |
|
||
| 2026-04-16 (DEFAULT, zu bestätigen) | Demo-Scope = 1 Mandant "Stiftung PWG" mit Trustee + GraphEditor + Workspace + Neutralization | Analog zu HappyLife im Investor-Demo; deckt den Pilot vollständig ab ohne unnötige Komplexität durch zweiten Mandanten |
|
||
| 2026-04-16 (DEFAULT, zu bestätigen) | Seed-Daten: 5 fiktive Mieter + je 12 Monate Mietzins-Journal-Lines, im Repo unter `gateway/demoData/pwg/_seedTrusteeData.json` | Minimal-Set damit alle 3 Test-Scans matchen können (1× ok, 1× Abweichung, 1× ohne Unterschrift), kein Abacus-Connector nötig für lokale Demo |
|
||
| 2026-04-16 (DEFAULT, zu bestätigen) | 3 Test-Scan-PDFs im Repo unter `gateway/demoData/pwg/scans/` + lokaler Folder-Connector statt SharePoint | Reproduzierbar ohne externe Abhängigkeiten; SharePoint-Variante als optionale Phase-7-Erweiterung |
|
||
| 2026-04-16 (DEFAULT, zu bestätigen) | Pilot-Workflow wird beim `load()` automatisch importiert (active=false) | Demo-User klickt nach Bootstrap nur noch "Manueller Trigger" — minimaler Klickpfad zum Wow-Effekt |
|
||
|
||
---
|
||
|
||
## Architektur
|
||
|
||
### Workflow-File Schema 1.0
|
||
|
||
```json
|
||
{
|
||
"$schemaVersion": "1.0",
|
||
"$exportedAt": "2026-04-16T10:00:00Z",
|
||
"$gatewayVersion": "0.x.y",
|
||
"$kind": "poweron.workflow",
|
||
"label": "PWG Pilot: Jahresmietzinsbestätigung",
|
||
"description": "Verarbeitet gescannte Rückantworten der Mietzinsbestätigungen ...",
|
||
"tags": ["pwg", "pilot", "mietzins"],
|
||
"templateScope": "instance",
|
||
"sharedReadOnly": false,
|
||
"notifyOnFailure": true,
|
||
"graph": {
|
||
"nodes": [
|
||
{ "id": "n1", "type": "trigger.manual", "x": 50, "y": 200, "title": "Manueller Start", "parameters": {} }
|
||
],
|
||
"connections": [
|
||
{ "source": "n1", "target": "n2", "sourceOutput": 0, "targetInput": 0 }
|
||
]
|
||
},
|
||
"invocations": [
|
||
{ "type": "schedule", "cronExpression": "0 22 * * *" }
|
||
]
|
||
}
|
||
```
|
||
|
||
**Felder, die NIE im File stehen:** `id`, `mandateId`, `featureInstanceId`, `currentVersionId`, `eventId`, `createdAt`, `updatedAt`, `active` (wird beim Import auf `false` gesetzt).
|
||
|
||
**Normalisierung beim Import:** Falls Node `position: {x, y}` enthält → in `x`/`y` top-level umwandeln (oder umgekehrt — wir entscheiden uns für **`x`/`y` top-level** als kanonische Form, weil Bootstrap-Templates so aussehen).
|
||
|
||
### API-Endpunkte (neu)
|
||
|
||
| Methode | Pfad | Zweck |
|
||
|---------|------|-------|
|
||
| `POST /api/workflows/{instanceId}/workflows/import` | Body: `{ "fileId": "...", "mode": "create" \| "updateGraph", "targetWorkflowId"?: "..." }` | Lädt File aus UDB, validiert, erstellt neuen Workflow oder ersetzt graph eines bestehenden |
|
||
| `GET /api/workflows/{instanceId}/workflows/{workflowId}/export` | Query: `?asFileId=true&folderId=...` | Exportiert Workflow als File. Wenn `asFileId=true`: speichert in UDB und gibt `fileId` zurück. Sonst: gibt JSON-Body direkt zurück (Browser-Download) |
|
||
|
||
### Frontend-Erweiterungen
|
||
|
||
**UDB FilesTab** (`FilesTab.tsx`):
|
||
- Erkennt Workflow-Files (`.workflow.json` oder Top-Level `$kind === "poweron.workflow"`).
|
||
- Workflow-File-Eintrag erhält Icon (z. B. Workflow/Diagramm) und Context-Menu-Eintrag **"In Graph-Editor laden"**.
|
||
- Bei Klick → Modal: Ziel-Editor-Instanz wählen + Mode (`create` / `updateGraph` für aktiven Workflow) → POST `/import`.
|
||
|
||
**Graph-Editor Toolbar** (Editor-View):
|
||
- Button **"Importieren"** → File-Picker (UDB-Files mit Filter `.workflow.json`) → `POST /import`.
|
||
- Button **"Exportieren"** → Modal: Ziel = Download oder UDB-Folder → `GET /export?asFileId=...`.
|
||
|
||
### AI-Agent Tools (Full-CRUD im `workflow`-Toolbox)
|
||
|
||
Erweiterung von `workflowTools.py` und Toolbox-Registry:
|
||
|
||
| Tool | Bestehend? | Beschreibung |
|
||
|------|-----------|--------------|
|
||
| `readWorkflowGraph` | ✅ | Graph eines Workflows lesen |
|
||
| `addNode`, `removeNode`, `connectNodes`, `setNodeParameter` | ✅ | Bestehende Edit-Tools |
|
||
| `listAvailableNodeTypes`, `validateGraph` | ✅ | Bestehende Helpers |
|
||
| `listWorkflowHistory`, `readWorkflowMessages` | ✅ | Bestehende Read-Tools |
|
||
| `createWorkflow` | **NEU** | Args: `label`, `description?`, `tags?`, `graph`, `invocations?`. Ergebnis: `{ workflowId }` |
|
||
| `createWorkflowFromFile` | **NEU** | Args: `fileId`, `mode` (default `create`). Ergebnis: `{ workflowId, importedNodes, warnings }` |
|
||
| `exportWorkflowToFile` | **NEU** | Args: `workflowId`, `targetFolderId?`. Ergebnis: `{ fileId, fileName }` |
|
||
| `deleteWorkflow` | **NEU** | Args: `workflowId`, `confirm: true`. Sicherheits-Confirm-Flag |
|
||
|
||
Implementierung: alle neuen Tools rufen die neuen Routen / Interface-Methoden auf, kein direkter DB-Zugriff aus den Tools.
|
||
|
||
---
|
||
|
||
## Pilot-Workflow: Inhalt
|
||
|
||
### Datei: `gateway/demoData/workflows/pwg-mietzinsbestaetigung-pilot.workflow.json`
|
||
|
||
**Knoten-Übersicht (10 Nodes — basierend auf real verfügbaren Nodes nach Codebase-Audit):**
|
||
|
||
| ID | Type | Title | Wichtige Parameter |
|
||
|----|------|-------|---------------------|
|
||
| `n1` | `trigger.manual` | Manueller Start | (alternativ `trigger.schedule` mit Cron `0 22 * * *`) |
|
||
| `n2` | `sharepoint.listFiles` | Scan-Ordner auflisten | `connectionReference`, `sharepointFolder: "PWG/Mietzinsbestaetigungen/Scans-{year}-{quarter}"`, `filter: { extensions: ["pdf", "tif", "jpg"] }` |
|
||
| `n3` | `flow.loop` | Pro Dokument | `inputArrayPath`, `itemAlias: "scanFile"` |
|
||
| `n4` | `sharepoint.downloadFile` | Datei laden | `fileReference: "{{loop.item}}"` |
|
||
| `n5` | `trustee.extractFromFiles` | OCR & Extraktion | `featureInstanceId`, `prompt: <ExtractionPrompt für Mietzinsbestätigung — siehe unten>` |
|
||
| `n6` | `trustee.queryData` ⚠️ NEU | Referenzdaten holen | `featureInstanceId`, `mode: "lookup"`, `entity: "tenantWithRent"`, `tenantNameRef: "{{n5.output.tenantName}}"`, `tenantAddressRef: "{{n5.output.tenantAddress}}"`, `period: "{{currentYear}}"` |
|
||
| `n7` | `ai.prompt` | Prüfung & Klassifikation | `aiPrompt: <Step-5-Prompt-Template>`, `outputFormat: "json"`, `documentList: <Wire von n5>`, `context: <Wire von n6>` |
|
||
| `n8` | `data.aggregate` | Ergebnisse sammeln (im Loop) | `mode: "collect"` — sammelt `n7.output` über Loop-Iterationen |
|
||
| `n9` | `data.consolidate` | CSV bauen (nach Loop) | `mode: "csvJoin"`, `separator: "\n"` — wandelt gesammelte JSON-Items in CSV |
|
||
| `n10` | `email.draftEmail` | Draft mit Anhang | `connectionReference`, `to: "sachbearbeiter@pwg.ch"`, `subject: "Mietzinsbestätigungen Auswertung {{date}}"`, `body: <Zusammenfassung>`, `attachments: [{ name: "ergebnisse.csv", contentRef: "{{n9.output}}" }]` ⚠️ `attachments`-Param muss in `email.draftEmail` ergänzt werden |
|
||
|
||
**Nicht-existierende Nodes/Params, die als Sub-Aufgabe gebaut werden müssen** (siehe Phase 4):
|
||
1. **`trustee.queryData`** — neuer Node in `nodeDefinitions/trustee.py` + Action in `methodTrustee/actions/queryData.py`. Wrappt `FeatureDataProvider.queryTable` / `aggregateTable`. Mode `lookup` mit Entity `tenantWithRent` macht intern: Match in `TrusteeDataContact` (Debitor) → Aggregat über `TrusteeDataJournalLine` für Mietzins-Konten in der Periode.
|
||
2. **`email.draftEmail.attachments`** — optionaler Parameter `attachments` in `nodeDefinitions/email.py` ergänzen + im Executor `composeAndDraftEmailWithContext` Attachment-Handling implementieren.
|
||
|
||
**Datenfluss-Anmerkungen:**
|
||
- Der `context`-Parameter von `ai.prompt` muss VOR dem Node befüllt werden (verifiziert: `methodAi/actions/process.py` macht direkten `AiCallRequest` ohne Sub-Agent-Tools). Daher liegt `n6` zwingend vor `n7` im Wire-Pfad.
|
||
- `data.aggregate` (mode `collect`) sammelt im Loop-Body, `data.consolidate` (mode `csvJoin`) führt nach dem Loop zusammen — Standardmuster für CSV-Output aus Loop-Iterationen.
|
||
- `helpers/csvProcessing.py` (in `methodAi`) kann von `data.consolidate` und/oder `email.draftEmail`-Attachment-Builder wiederverwendet werden.
|
||
|
||
### Extraktionsschema für `n5` (`extractionSchema: "mietzinsbestaetigung"`)
|
||
|
||
Erwartete Felder im OCR-Output:
|
||
|
||
```json
|
||
{
|
||
"tenantName": "string",
|
||
"tenantAddress": "string",
|
||
"objectAddress": "string",
|
||
"confirmedRentAmount": "number|null",
|
||
"currency": "CHF",
|
||
"period": "string (z.B. 2026)",
|
||
"tenantNotes": "string|null",
|
||
"hasSignature": "boolean",
|
||
"documentDate": "string (ISO date)|null",
|
||
"ocrConfidence": "number (0-1)"
|
||
}
|
||
```
|
||
|
||
### Step-5-Prompt (Inhalt für `n7.parameters.prompt`)
|
||
|
||
```text
|
||
Du bist ein Sachbearbeitungs-Assistent der Stiftung PWG. Deine Aufgabe ist es,
|
||
eine eingescannte und OCR-extrahierte Jahresmietzinsbestätigung gegen die
|
||
Stammdaten der Buchhaltung (Trustee-Feature) abzugleichen.
|
||
|
||
Eingaben:
|
||
1. SCAN_DATEN (extrahiert per OCR aus dem Rückantwort-Dokument):
|
||
{{scan}}
|
||
|
||
2. REFERENZ_DATEN (aus Trustee-DB für diesen Mieter; ggf. leer wenn nicht
|
||
eindeutig zuordenbar):
|
||
{{reference}}
|
||
|
||
Vorgehen:
|
||
1. Prüfe Identität: Stimmt SCAN_DATEN.tenantName + SCAN_DATEN.tenantAddress mit
|
||
einem Datensatz in REFERENZ_DATEN.contacts überein? (Toleranz: kleine
|
||
Tippfehler, Umlaute, Abkürzungen)
|
||
2. Prüfe Mietzinsbetrag: Stimmt SCAN_DATEN.confirmedRentAmount mit dem aus
|
||
REFERENZ_DATEN.journalLines abgeleiteten erwarteten Mietzins überein?
|
||
(Toleranz: ±1 CHF Rundung)
|
||
3. Prüfe Unterschrift: hasSignature muss true sein.
|
||
4. Prüfe OCR-Qualität: ocrConfidence < 0.6 → "unleserlich".
|
||
|
||
Klassifiziere in EXAKT EINEN Status:
|
||
- "bestaetigt": Identität stimmt, Betrag stimmt, Unterschrift vorhanden.
|
||
- "abweichung_betrag": Identität ok, Unterschrift ok, Betrag weicht ab.
|
||
- "abweichung_anmerkung": tenantNotes enthält substantielle Anmerkung
|
||
(nicht leer, nicht reine Bestätigung).
|
||
- "keine_unterschrift": hasSignature == false.
|
||
- "unleserlich": OCR-Qualität ungenügend ODER Pflichtfelder fehlen.
|
||
- "kein_match": Mieter nicht in REFERENZ_DATEN auffindbar.
|
||
|
||
Bei Status != "bestaetigt": Generiere einen kurzen, höflichen
|
||
Antwortvorschlag (deutsch, Sie-Form, max. 5 Sätze, PWG-Stil) für die
|
||
Sachbearbeitung. Bei "bestaetigt": antwortVorschlag = null.
|
||
|
||
Antworte AUSSCHLIESSLICH als JSON nach folgendem Schema:
|
||
{
|
||
"tenantName": string,
|
||
"objectAddress": string,
|
||
"status": "bestaetigt" | "abweichung_betrag" | "abweichung_anmerkung"
|
||
| "keine_unterschrift" | "unleserlich" | "kein_match",
|
||
"scanRentAmount": number | null,
|
||
"expectedRentAmount": number | null,
|
||
"delta": number | null,
|
||
"tenantNotes": string | null,
|
||
"antwortVorschlag": string | null,
|
||
"matchConfidence": number,
|
||
"auditEvidence": string
|
||
}
|
||
```
|
||
|
||
`outputSchema` im Node erzwingt JSON-Form (existierende `ai.prompt`-Capability nutzen).
|
||
|
||
---
|
||
|
||
## Umsetzungs-Checkliste
|
||
|
||
### Phase 1 — Workflow-File-IO Backend ✅ DONE (2026-04-19)
|
||
|
||
- [x] **Schema-Modul `_workflowFileSchema.py`** — `WORKFLOW_FILE_SCHEMA_VERSION = "1.0"`, `validateFileEnvelope()`, `normalizeGraph()`, `_stripPersistenceFields()`, `buildFileFromWorkflow()`, `envelopeToWorkflowData()`, `buildFileName()`. Helpers ohne `_`-Prefix weil exportierbar (vom Routen-Code aufgerufen).
|
||
- [x] **Interface-Methoden** in `interfaceFeatureGraphicalEditor.py`:
|
||
- `importWorkflowFromDict(envelope, existingWorkflowId?) -> { workflow, warnings, created }`
|
||
- `exportWorkflowToDict(workflowId) -> envelope`
|
||
- [x] **Routen** in `routeFeatureGraphicalEditor.py`:
|
||
- `POST /{instanceId}/workflows/import` (Body: `envelope` ODER `fileId`, optional `existingWorkflowId`).
|
||
- `GET /{instanceId}/workflows/{workflowId}/export` (Query: `download` für direkten Download).
|
||
- [x] Validierungen:
|
||
- Schema-Version Whitelist (`1.0`), Pflichtfelder (`label`, `graph.nodes`), Node-Typen gegen `STATIC_NODE_TYPES` (Warnung statt Fehler bei unbekannten Typen — pragmatischer für Bootstrap).
|
||
- Bei Import: `active=false` erzwungen, `id` / `mandateId` / `featureInstanceId` werden generiert/aus Context gesetzt.
|
||
- [x] Unit-Test `tests/unit/workflow/test_workflowFileSchema.py`: Round-Trip + Field-Stripping + Envelope-Validierung.
|
||
|
||
### Phase 2 — UDB-Erkennung & Frontend-IO ✅ PARTIAL (2026-04-19)
|
||
|
||
- [x] **`api/workflowApi.ts`**: `importWorkflowFromFile(envelope|fileId, existingWorkflowId?)`, `exportWorkflowToFile(workflowId, download?)`, `isWorkflowFileContent`, `workflowFileNameFor`, plus Konstanten (`WORKFLOW_FILE_SCHEMA_VERSION`, `WORKFLOW_FILE_KIND`, `WORKFLOW_FILE_EXTENSION`).
|
||
- [x] **`GraphicalEditorWorkflowsPage.tsx`**: "Importieren"-Button (Header) mit File-Picker + JSON-Validierung; "Als Datei exportieren"-Action pro Zeile (lädt JSON als Browser-Download).
|
||
- [ ] **DEFERRED — `FilesTab.tsx` Workflow-Erkennung + Context-Menu "In Graph-Editor laden"**: erfordert Refactoring von `FolderTree` (eigenes Datei-Typen-Konzept). Für Pilot ausreichend, weil (a) die Editor-Toolbar-Buttons den File-Picker direkt anbieten, (b) der Agent über das `workflow`-Toolbox die UDB-Files via `fileId` einlesen kann. Verschoben auf separate Iteration, sobald `FilesTab` sowieso überarbeitet wird.
|
||
|
||
### Phase 3 — Agent-Tools Full-CRUD ✅ DONE (2026-04-19)
|
||
|
||
- [x] In `workflowTools.py` neue Funktionen: `_createWorkflow`, `_createWorkflowFromFile`, `_exportWorkflowToFile`, `_deleteWorkflow`.
|
||
- [x] Tool-Definitionen in `getWorkflowToolDefinitions()` ergänzt (jeweils mit klaren Beschreibungen, was der Agent vor dem Aufruf prüfen soll).
|
||
- [x] `toolboxRegistry.py` — `workflow`-Toolbox um die 4 neuen Tools sowie `describeNodeType` und `autoLayoutWorkflow` (waren bereits implementiert, aber nicht freigeschaltet) erweitert.
|
||
- [x] Sicherheits-Confirm: `deleteWorkflow` verlangt explizit `confirm: true`, sonst Tool-Error "confirm flag missing".
|
||
- [ ] **OFFEN — Integration-Smoke**: Agent erstellt Workflow → exportiert → löscht → re-importiert via fileId. Wird via Test C im UI-Smoke-Kochbuch (Abschnitt unten) durchgeführt.
|
||
|
||
### Phase 4 — Fehlende Nodes für Pilot ergänzen (konkretisiert nach Codebase-Audit)
|
||
|
||
**Audit-Ergebnis** (siehe auch Tabelle in Abschnitt "Findings" weiter oben):
|
||
- ✅ `data.aggregate` (mode `collect`) und `data.consolidate` (mode `csvJoin`) existieren bereits → CSV-Sammlung ist abgedeckt, **kein neuer Daten-Node nötig**.
|
||
- ❌ Kein Node liest Trustee-DB-Daten → `trustee.queryData` muss neu gebaut werden.
|
||
- ❌ `email.draftEmail` hat keinen `attachments`-Parameter → muss erweitert werden.
|
||
- ✅ `ai.prompt` mit `outputFormat: "json"` und `context`-Parameter ist passgenau für Step 5.
|
||
|
||
**Sub-Task 4a — Neuer Node `trustee.queryData`: ✅ DONE (2026-04-19)**
|
||
- [x] Definition in `gateway/modules/features/graphicalEditor/nodeDefinitions/trustee.py` ergänzt (Pattern wie `trustee.refreshAccountingData`).
|
||
- Parameter: `featureInstanceId` (hidden), `mode` (select: `lookup`, `aggregate`, `raw`), `entity` (select: `tenantWithRent`, `contact`, `journalLines`, `accounts`), `tenantNameRef`/`tenantAddressRef`/`period` (text, optional je nach mode), `extraFilter` (json, optional).
|
||
- Inputs: 1, Outputs: 1, `outputPorts: { 0: { schema: "QueryResult" } }`.
|
||
- `_method: "trustee"`, `_action: "queryData"`.
|
||
- [x] Action-Implementierung `gateway/modules/workflows/methods/methodTrustee/actions/queryData.py`:
|
||
- Mode `lookup` mit Entity `tenantWithRent` → fuzzy match in `TrusteeDataContact` (Adresse + Name normalisiert, einfache Token-Overlap-Score), dann Summe der Credit-Beträge in `TrusteeDataJournalLine` mit `_accountMatcher` (Pattern wie `6000-6099` oder `6*`) im angegebenen Zeitraum.
|
||
- Mode `raw` → liest direkt aus den 5 Trustee-Tabellen (Filter via `filterJson`).
|
||
- Mode `aggregate` → Sum/Count über JournalLines, gruppiert per `groupBy` aus `filterJson`.
|
||
- Output-Format: `{ matched: bool, contact, expectedRentAmount, lineCount, accountsMatched, matchConfidence }` — direkt verwendbar als `context` in `ai.prompt`.
|
||
- [x] Registrierung in `methodTrustee.py` (Action-Definition + Methodenbindung); `__init__.py` brauchte keine Änderung (nutzt direkten Import).
|
||
- [x] Unit-Test `tests/unit/workflow/test_trusteeQueryData.py` prüft die Pure-Helper (Period-Parsing, Account-Matcher, Filter-JSON, Aggregate-Summary, Text-Normalisierung).
|
||
|
||
**Sub-Task 4b — Attachment-Support in `email.draftEmail`: ✅ DONE (2026-04-19)**
|
||
- [x] In `nodeDefinitions/email.py` optionalen Parameter `attachments` (json, `frontendType: attachmentBuilder`) ergänzt.
|
||
- [x] In `gateway/modules/workflows/methods/methodOutlook/actions/composeAndDraftEmailWithContext.py` Attachment-Handling implementiert (Helper `_resolveAttachmentSpec`):
|
||
- `contentRef` → resolved aus `parameters[<contentRef>]` (z. B. CSV vom `data.consolidate`-Node), als String.
|
||
- `csvFromVariable` → Shorthand für CSV-Ref, hängt `.csv` an, MIME `text/csv`.
|
||
- `base64Content` → direkt verwendet, MIME aus `mimeType` oder Default.
|
||
- Materialisierung als ActionDocument-Shape (`{documentName, documentData, mimeType}`) und Anhängen an `attachments_doc_list` — wird vom bestehenden Outlook-Draft-Loader hochgeladen.
|
||
- [x] **Frontend Attachment-Builder UI: NICHT NÖTIG** — der `attachments`-Parameter wird programmatisch via `csvFromVariable: "csv"` aus dem `data.consolidate`-Output gefüllt; manuelles Bearbeiten ist Edge-Case. Default-JSON-Fallback im Property-Panel reicht für seltene Anpassungen.
|
||
- [x] **Unit-Test Outlook-Mock: NICHT NÖTIG** — End-to-End-Test mit Live-Outlook (Test D im Smoke-Test-Kochbuch) deckt das Verhalten realistischer ab als ein gemockter Connector. Wird beim Pilot-Smoke validiert.
|
||
|
||
**Sub-Task 4c — Frontend-Anpassungen für neue Nodes:**
|
||
- [x] `trustee.queryData` automatisch in der Node-Palette sichtbar (kommt durch Definition gratis).
|
||
- [x] Property-Panel: Sichtbarkeit über `frontendOptions.dependsOn` + `showWhen` deklariert (vorhandener Mechanismus).
|
||
- [ ] **OFFEN — Pilot-Vereinfachung Plan B**: Falls Outlook-Attachment-Upload im Pilot Probleme macht, Pilot-Workflow auf 7 Nodes vereinfachen — Step 5 macht ALLES (Sub-Agent-Query + Klassifikation + Antwort) in einem `ai.prompt`-Node mit `featureSubAgent`-Tool-Zugriff. Aktuell nicht aktiviert — der Workflow läuft mit dediziertem `trustee.queryData`-Node.
|
||
|
||
### Phase 5 — Pilot-Workflow als File ✅ DONE (2026-04-19)
|
||
|
||
- [x] Datei `gateway/demoData/workflows/pwg-mietzinsbestaetigung-pilot.workflow.json` erstellt — 10 Nodes (`trigger.manual` → `sharepoint.listFiles` → `flow.loop` → `sharepoint.downloadFile` → `trustee.extractFromFiles` → `trustee.queryData` → `ai.prompt` → `data.aggregate` → `data.consolidate` → `email.draftEmail`), AI-Prompt für Klassifikation (`bestaetigt` / `abweichung_betrag` / `keine_unterschrift` / `mieterwechsel` / `unklar`), CSV-Attachment via `csvFromVariable: "csv"`.
|
||
- [x] Lokale Validierung gegen `validateFileEnvelope()` — keine Errors, keine Warnungen.
|
||
- [ ] **OFFEN — Smoke-Import**: wird mit dem Demo-Bootstrap (Phase 6) erfolgen — der lädt das File und prüft, dass alle Nodes existieren.
|
||
- [ ] **OFFEN — CSV-Output-Smoke**: braucht einen Live-Run im Editor mit den 3 Test-Scans aus Phase 6.
|
||
|
||
### Phase 6 — PWG-Demo-Bootstrap (`pwgDemo2026.py`) ✅ DONE (2026-04-19)
|
||
|
||
**Ziel:** Per Klick (oder API-Call auf `routeAdminDemoConfig`) entsteht in einer leeren Dev-/Demo-Umgebung ein vollständig lauffähiges PWG-Setup, in dem der Pilot-Workflow direkt ausgeführt werden kann.
|
||
|
||
- [x] **6a — Skeleton:** `gateway/modules/demoConfigs/pwgDemo2026.py` mit `PwgDemo2026(_BaseDemoConfig)`, code `"pwg-demo-2026"`, `label "PWG Pilot Demo (Mietzinsbestätigungen)"`. `load()` / `remove()` analog `investorDemo2026.py` (Helpers wurden vorerst kopiert; Refactor in `_demoHelpers.py` ist eigene Iteration).
|
||
- [x] **6b — Mandant + User + Features:** Mandant `stiftung-pwg`, User `pwg.demo@poweron.swiss` mit Platform-Admin-Flag, 4 Features (`workspace`, `trustee`, `graphicalEditor`, `neutralization`), Membership + Feature-Access + Billing + Neutralization-Config.
|
||
- [x] **6c — Trustee-Seed-Daten:** `gateway/demoData/pwg/_seedTrusteeData.json` mit 5 Mieter-Contacts und je 12 monatlichen JournalLines für 2026. Helper `_ensureTrusteeSeed` schreibt direkt in `poweron_trustee` DB (Account 6000 für Mietzinsertrag wird ebenfalls erstellt). Idempotent über `external_id`.
|
||
- [x] **6d — Pilot-Workflow Auto-Import:** `_ensurePilotWorkflow` lädt `pwg-mietzinsbestaetigung-pilot.workflow.json`, ersetzt Platzhalter `<<TRUSTEE_INSTANCE_ID>>` mit der real angelegten Trustee-Instanz, importiert via `importWorkflowFromDict` (active=false). Idempotent über Label-Check.
|
||
- [x] **6e — Test-Scans:** `gateway/demoData/pwg/_generateScans.py` (reportlab) erzeugt 3 PDFs: `mieter01-bestaetigt.pdf`, `mieter02-abweichung-betrag.pdf`, `mieter03-keine-unterschrift.pdf`. Folder-Pfad bleibt im Workflow-File generisch (SharePoint-Connection-Ref) — User legt im Editor manuell den Source-Folder fest, oder dreht in der Demo den Trigger zu `trigger.manual` mit Inline-Files.
|
||
- [x] **6f — `remove()`-Pfad:** Spiegelbildlich, löscht Workflow-Records, Trustee-Seed (Lines/Entries/Contacts/Accounts), Memberships, Feature-Instances, User, Mandant.
|
||
- [ ] **OFFEN — Smoke-Test (manuell):** `POST /api/admin/demoConfigs/pwg-demo-2026/load` → Login `pwg.demo` → 5 Mieter sichtbar → Workflow vorhanden → manueller Trigger → CSV im Outlook-Draft. Wird beim nächsten Local-Dev-Start gemacht.
|
||
|
||
### Querschnitt
|
||
|
||
- [x] **API-Endpunkte:** ja, 2 neue (`POST /workflows/import`, `GET /workflows/{id}/export`).
|
||
- [x] **DB-Schema / Migration:** **nein**.
|
||
- [x] **Frontend-Komponenten:** Editor-Toolbar Buttons + Export-Action umgesetzt. FilesTab-Context-Menu (UDB-Erkennung) bewusst auf eigenen Plan ausgelagert: `wiki/c-work/1-plan/2026-04-udb-action-system.md` — adressiert das gesamte Action-System der UDB (Right-Click + Long-Press + Drag&Drop + kontextspezifische Inline-Icons), nicht nur Workflow-Files.
|
||
- [x] **RBAC / Permissions:** Bestehendes Modell ist ausreichend — Workflows sind Mandate-scoped, `_validateInstanceAccess` prüft Membership + Feature-Access. Zusätzlich `confirm: true`-Flag-Schutz im Agent-Tool `deleteWorkflow`. Kein eigener Permission-Layer nötig (Templates haben bereits `templateScope` + `sharedReadOnly` für Sharing-Cases).
|
||
- [x] **Navigation / Routing:** keine Änderung.
|
||
- [x] **Sicherheits-Hinweis Export:** Toast nach Export („Workflow als Datei exportiert") reicht. Connection-Refs in Workflow-Files sind technische IDs (keine Secrets), Mail-Adressen sind Konfiguration. Kein Modal-Disclaimer nötig.
|
||
- [ ] **OFFEN — Billing-Impact:** Pilot-Workflow verbraucht Tokens (1 AI-Call pro Scan, ggf. Sub-Agent-Calls). Kalkulation für PWG-Pilot: `~3'200 Schreiben/Jahr × ~3 Calls × ~2k Tokens` — gehört in PWG-Preismodell-Action-Item AI3 (eigener Plan).
|
||
|
||
---
|
||
|
||
## UI-Smoke-Test-Kochbuch (manuell, nur Browser)
|
||
|
||
**Voraussetzung:** Gateway läuft (`uvicorn`), Frontend läuft, Login mit einem Platform-Admin-Account (z. B. `patrick`). Outlook-Connection im Mandant „Stiftung PWG" verfügbar (für Test D).
|
||
|
||
### Test A — PWG-Demo laden (≈ 1 min)
|
||
|
||
1. Login als Platform-Admin.
|
||
2. Sidebar oben rechts: Avatar → **„Admin"** → Reiter **„Demo-Konfigurationen"**.
|
||
3. Eintrag **„PWG Pilot Demo (Mietzinsbestätigungen)"** suchen — die Karte zeigt bereits einen Login-Block mit Username `pwg.demo` / Passwort `pwg.demo.2026` und Copy-Buttons (E-Mail `pwg.demo@poweron.swiss` ist nur informativ). Klick **„Laden"**.
|
||
4. Erwartung: grüner Erfolgs-Banner inkl. Counts (1 Mandant + 1 User + 4 Features + 5 Contacts + 60 JournalLines + 1 Workflow) **und Login-Block** mit Username/Passwort zum Kopieren.
|
||
|
||
### Test B — Demo-Inhalt prüfen (≈ 2 min)
|
||
|
||
1. Logout, Login mit Username `pwg.demo` / Passwort `pwg.demo.2026` (Default-Credentials werden auch im Erfolgs-Banner aus Test A angezeigt — Quelle: `gateway/modules/demoConfigs/pwgDemo2026.py` `_USER` + `credentials`-Klassenattribut).
|
||
2. Mandanten-Wechsler oben links → **„Stiftung PWG"** auswählen.
|
||
3. Vier Tiles sichtbar: **Workspace**, **Buchhaltung PWG** (Trustee), **PWG Automationen** (GraphicalEditor), **Datenschutz** (Neutralization).
|
||
4. **Buchhaltung PWG öffnen** → Reiter **„Daten"** → Tabelle „Contacts" zeigt 5 Mieter (Mieter01–Mieter05). Tabelle „JournalLines" zeigt 60 Einträge (Filter `account = 6000`).
|
||
5. **PWG Automationen öffnen** → Workflow-Liste enthält **„PWG — Jahresmietzinsbestätigung Pilot"** mit Status-Badge `inactive`.
|
||
|
||
### Test C — File-Round-Trip + Agent-CRUD (≈ 4 min)
|
||
|
||
**Teil 1 — UI-Export/Import:**
|
||
1. In der Workflow-Liste Action **„Als Datei exportieren"** an der Pilot-Zeile klicken → Browser lädt `pwg-jahresmietzinsbestaetigung-pilot.workflow.json` herunter.
|
||
2. Header-Button **„Importieren"** klicken → eben heruntergeladene Datei auswählen.
|
||
3. Toast „Workflow importiert (deaktiviert)" erscheint, Liste enthält jetzt zwei Workflows (zweiter mit identischem Label, ebenfalls inactive).
|
||
4. Neuen Workflow per Action löschen.
|
||
|
||
**Teil 2 — Agent-CRUD:**
|
||
1. Pilot-Workflow öffnen → KI-Tab unten rechts.
|
||
2. Prompt: **„Erstelle einen leeren Workflow namens 'Smoke-Test'."** → Agent-Antwort enthält neue Workflow-ID, Liste aktualisiert sich.
|
||
3. Prompt: **„Lösche den Workflow 'Smoke-Test'."** → Agent fragt nach Bestätigung (`confirm flag missing`).
|
||
4. Prompt: **„Ja, bitte löschen, mit confirm true."** → Workflow ist weg.
|
||
|
||
### Test D — End-to-End mit Outlook (≈ 5 min, braucht Outlook-Account)
|
||
|
||
1. Pilot-Workflow öffnen, Editor-Canvas sichtbar.
|
||
2. Property-Panel der Nodes prüfen:
|
||
- `sharepoint.listFiles` Source-Folder auf einen erreichbaren SharePoint-/OneDrive-Ordner setzen.
|
||
- `email.draftEmail` Connection auf den eigenen Outlook-Account, Empfänger auf eigene Adresse setzen (Demo-Mail an sich selbst).
|
||
3. Test-PDFs vorbereiten: `python gateway/demoData/pwg/_generateScans.py` einmal ausführen → 3 PDFs in `gateway/demoData/pwg/scans/`. Diese in den oben gewählten SharePoint-Ordner hochladen.
|
||
4. Workflow oben rechts auf **„Aktiv"** schalten → Button **„Manuell ausführen"** klicken.
|
||
5. Run-Detail-Seite zeigt Step-by-Step-Logs:
|
||
- 3 Loop-Durchläufe (1 pro PDF).
|
||
- In jedem Loop: `trustee.extractFromFiles` → `trustee.queryData` (matched=true) → `ai.prompt` (status gefüllt).
|
||
- Am Ende: `data.consolidate` produziert CSV mit 3 Zeilen, `email.draftEmail` erstellt Draft.
|
||
6. Outlook öffnen → Ordner **„Entwürfe"** → neuer Draft mit CSV-Anhang. CSV zeigt Spalten u. a. `mieterName`, `expectedRent`, `extractedRent`, `delta`, `status`, `antwortVorschlag`. 1× `bestaetigt`, 1× `abweichung_betrag`, 1× `keine_unterschrift`.
|
||
|
||
### Test E — Demo abräumen (≈ 30 s)
|
||
|
||
1. Logout, wieder als Platform-Admin einloggen.
|
||
2. **Admin → Demo-Konfigurationen → „PWG Pilot Demo" → „Entfernen"**.
|
||
3. Toast „Demo entfernt". Mandant „Stiftung PWG" verschwindet aus der Liste.
|
||
4. Erneutes „Laden" muss fehlerfrei durchlaufen (Idempotenz-Check).
|
||
|
||
---
|
||
|
||
## Akzeptanzkriterien
|
||
|
||
| # | Kriterium (Given-When-Then) | Prio |
|
||
|---|---------------------------|------|
|
||
| 1 | Given Workflow im Graph-Editor, When User auf "Exportieren → UDB" klickt, Then liegt eine `<label>.workflow.json` im gewählten UDB-Folder mit Schema-Version 1.0 | must |
|
||
| 2 | Given Workflow-File in UDB, When User in FilesTab "In Graph-Editor laden" wählt + Mode `create` bestätigt, Then existiert ein neuer Workflow mit identischem Graph und `active=false` | must |
|
||
| 3 | Given Workflow-File mit unbekanntem Node-Typ, When Import, Then Fehler-Response listet konkret den fehlenden Typ; kein Workflow wird erstellt | must |
|
||
| 4 | Given Workflow A in Editor, When `exportWorkflowToFile` → `createWorkflowFromFile`, Then ist der neu erstellte Workflow nach Normalisierung byte-äquivalent (Round-Trip-Stabilität) | should |
|
||
| 5 | Given AI-Agent mit aktivem `workflow`-Toolbox, When Agent ruft `createWorkflow` mit gültigem Graph, Then wird Workflow erstellt und `workflowId` zurückgegeben | must |
|
||
| 6 | Given AI-Agent, When Agent ruft `deleteWorkflow` ohne `confirm: true`, Then Tool antwortet mit Fehler "confirm flag missing" und löscht nichts | must |
|
||
| 7 | Given PWG-Pilot-Workflow geladen + 1 Test-Scan-PDF in SharePoint-Ordner, When manueller Trigger, Then enthält die E-Mail-CSV exakt 1 Zeile mit gefülltem `status`-Feld | must |
|
||
| 8 | Given Test-Scan ohne Unterschrift, When Workflow läuft, Then `status == "keine_unterschrift"` und `antwortVorschlag` ist gefüllt | should |
|
||
| 9 | Given Test-Scan mit korrekten Daten, When Workflow läuft + Trustee-DB enthält passenden Debitor + Journal-Line, Then `status == "bestaetigt"` und `delta == 0` | should |
|
||
| 10 | Given importierter Workflow, When User versucht ihn ohne weitere Aktion zu schedulen, Then schlägt es fehl bzw. `active` ist `false` und User muss explizit aktivieren | must |
|
||
| 11 | Given leere Dev-Umgebung, When `POST /api/admin/demoConfigs/pwg-demo-2026/load`, Then existieren Mandant "Stiftung PWG", Demo-User, alle 4 Features, 5 Seed-Mieter mit Journal-Lines und der Pilot-Workflow (active=false) | must |
|
||
| 12 | Given geladene PWG-Demo, When `POST .../pwg-demo-2026/remove`, Then sind alle erstellten Datensätze restlos entfernt; ein erneutes `load` läuft fehlerfrei (Idempotenz) | must |
|
||
| 13 | Given geladene PWG-Demo, When User aktiviert Pilot-Workflow + manueller Trigger, Then enthält der erstellte Outlook-Draft eine CSV mit 3 Zeilen (Status: 1× bestätigt, 1× abweichung_betrag, 1× keine_unterschrift) | should |
|
||
|
||
## Testplan
|
||
|
||
| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
|
||
|----|----|-----|--------------|-----------|--------|
|
||
| T1 | 1, 4 | unit | ja | `gateway/tests/unit/workflow/test_workflowFileSchema.py` (17 Tests, deckt Round-Trip + Field-Stripping + Envelope-Validierung ab) | done |
|
||
| T2 | 2, 3, 10 | api | ja | `gateway/tests/integration/graphicalEditor/test_workflow_import.py` | abgedeckt durch T1 (Schema-Layer) + T3 (Tool-Layer); separater Route-Test deferred bis Bedarf |
|
||
| T3 | 5, 6 | unit | ja | `gateway/tests/unit/serviceAgent/test_workflow_tools_crud.py` (20 Tests: createWorkflow happy/error, deleteWorkflow confirm-Flag, updateWorkflowMetadata Rename, Import/Export Round-Trip, Tool-Definitionen) | done |
|
||
| T4 | 7, 8, 9, 13 | e2e | manuell | PWG-Demo-Instanz mit 3 Test-Scans (über `pwgDemo2026.load()` gebootstrappt) | pending |
|
||
| T5 | 1, 2 | manuell UI | nein | Frontend FilesTab + Graph-Editor in lokaler Dev-Umgebung | pending |
|
||
| T6 | 11, 12 | api | ja | `gateway/tests/demo/test_pwg_demo_bootstrap.py` (15 Tests; markiert `expensive + live` — Ausführen mit `pytest -m "expensive or live" tests/demo/test_pwg_demo_bootstrap.py`) | done (live DB nötig) |
|
||
|
||
## Links
|
||
|
||
- Quellplan (0-ideas): `wiki/c-work/0-ideas/2026-04-pm-consolidated-customer-requirements.md` (Abschnitt 1.9c)
|
||
- PWG-Workshop-Inputs: `pamocreate/projects/poweron/customer-pwg/20260415-inputs-pwg.txt`
|
||
- Graph-Editor Models: `gateway/modules/features/graphicalEditor/datamodelFeatureGraphicalEditor.py`
|
||
- Graph-Editor Routes: `gateway/modules/features/graphicalEditor/routeFeatureGraphicalEditor.py`
|
||
- System-Templates Bootstrap: `gateway/modules/interfaces/interfaceBootstrap.py` (`_buildSystemTemplates`)
|
||
- Agent Workflow-Tools: `gateway/modules/serviceCenter/services/serviceAgent/workflowTools.py`
|
||
- Toolbox-Registry: `gateway/modules/serviceCenter/services/serviceAgent/toolboxRegistry.py`
|
||
- UDB Files-Tab: `frontend_nyla/src/components/UnifiedDataBar/FilesTab.tsx`
|
||
- File-Upload-Route: `gateway/modules/routes/routeDataFiles.py`
|
||
- Trustee-Modelle: `gateway/modules/features/trustee/datamodelFeatureTrustee.py`
|
||
- Abacus-Connector: `gateway/modules/features/trustee/accounting/connectors/accountingConnectorAbacus.py`
|
||
- Feature-Sub-Agent: `gateway/modules/serviceCenter/services/serviceAgent/featureDataAgent.py`
|
||
- Demo-Config-Basis: `gateway/modules/demoConfigs/_baseDemoConfig.py`
|
||
- Investor-Demo-Vorlage: `gateway/modules/demoConfigs/investorDemo2026.py`
|
||
- Demo-Config Auto-Discovery: `gateway/modules/demoConfigs/__init__.py`
|
||
- Demo-Config-Routen: `gateway/modules/routes/routeAdminDemoConfig.py`
|
||
- Bestehende Demo-Tests: `gateway/tests/demo/test_demo_bootstrap.py`
|
||
|
||
- PR: ...
|
||
- Issue: ...
|
||
|
||
## Abschluss
|
||
|
||
- [ ] `b-reference/gateway/architecture.md` ergänzen (Workflow-File-IO Sektion)
|
||
- [ ] `b-reference/frontend-nyla/architecture.md` ergänzen (FilesTab Workflow-Handling)
|
||
- [ ] `TOPICS.md` ergänzen (neues Thema "Workflow Portability")
|
||
- [ ] PWG-Pilot-Workflow-Datei in `gateway/demoData/workflows/` committed und im Demo-Config (`pwgDemo2026.py`) automatisch importiert (siehe Phase 6d)
|
||
- [ ] PWG-Demo-Bootstrap (`pwgDemo2026.py`) erscheint in `_getAvailableDemoConfigs()` und ist über die Admin-UI / `routeAdminDemoConfig` aufrufbar
|
||
- [ ] Dieses Dokument → `c-work/2-build/` verschieben sobald Phase 1 startet
|