wiki/c-work/1-plan/2026-06-umsystem-integration-connectors-und-datenquellen-seiten.md
2026-06-04 17:48:24 +02:00

222 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- status: plan -->
<!-- started: 2026-06-04 -->
<!-- component: gateway | ui-nyla | platform -->
# Umsystem-Integration: generische Connectors + kundenspezifische Workflows + Datenquellen-Seiten
## Beschreibung und Kontext
Ein Kunde schreibt seine Rechnungen in einem externen System (**SelectLine**, REST-API vorhanden) und möchte, dass diese Rechnungen **ins RMA (Run My Accounts)** geladen werden. Die Frage des Kunden: «Kann PowerOn dafür einfach eine Schnittstelle machen?»
Der konkrete Fall ist ein Spezialfall eines generischen Musters: PowerOn soll **proprietäre Umsysteme** (ERP, Fakturierung, Lohn, …) anbinden. Wir haben die Logik dafür im Prinzip schon im Feature **Trustee** sind Buchhaltungssysteme (ABACUS, Bexio, Banana, **RMA**) über das **AccountingBridge-Connector-Framework** angebunden (`platform-core/modules/features/trustee/accounting/`). RMA ist bereits ein vollwertiger Ziel-Connector (inkl. `pushBooking`, `pushInvoice`, Beleg-Upload).
**Kern-Architekturvorgabe (Auftrag):** Es soll **keine kundenspezifische Logik im Code** entstehen. Wir trennen sauber in:
1. **Generische Bausteine** (Code, wiederverwendbar, ohne Kundennamen): Connectors, kanonische Datenmodelle, generische Workflow-Nodes.
2. **Kundenspezifische Logik** (als **Daten** = Workflow-Graph + Config, nicht als Code): die konkrete Verdrahtung «SelectLine → Mapping → RMA» pro Mandant/Instanz im Graphical Editor.
Business-Treiber:
- Konkreter Kundenwunsch (Termin nächste Woche).
- Wiederverwendbares Muster für jedes weitere Umsystem → Sales-Asset und skalierbare Onboarding-Story.
- Vermeidung von Wartungs-Hölle durch `if kunde == X`-Code.
**Abgrenzung — NICHT als Personal Source einbinden.** Personal Sources (UDB-`DataSource`-Familie: SharePoint, Drive, Infomaniak) sind **user-privat** und für **unstrukturierte RAG-Daten** gedacht (`wiki/b-reference/platform/unified-data-bar.md`). Eine Debitorenrechnung von SelectLine nach RMA ist ein **transaktionaler Buchungs-Vorgang auf Mandant-/Feature-Ebene** mit Dedup, Sync-Audit und verschlüsselten Credentials pro Mandant. Das gehört in das **Trustee-Connector-Framework** (`featureInstanceId`-scoped), nicht in Personal Sources.
## Fokus und kritische Details
- **Trennung Baustein ↔ Kundenlogik ist das eigentliche Ziel.** Jede Versuchung, Kunden-/Systemspezifika fest zu verdrahten (Konten-Mappings, Filter, Belegart-Auswahl), muss in **Config/Workflow-Graph** wandern, nicht in den Connector.
- **Connector = reiner API-Adapter.** Auth, Lesen/Schreiben von Ressourcen, Mapping zwischen nativem API-Format und kanonischem Modell. Keine Geschäftsregeln, keine Kundennamen, keine Konto-Zuordnungen.
- **Richtung.** Bestehende AccountingBridge ist heute **push-orientiert** (lokale `TrusteePosition` → extern). SelectLine ist eine **Quelle** (lesen). Wir brauchen einen sauberen **Import-/Source-Pfad** und eine **Rolle** (`source` / `target`) auf der Config, weil eine Instanz künftig *eine Quelle* **und** *ein Ziel* hat.
- **On-Prem-Erreichbarkeit (grösstes Risiko, extern abhängig).** Die SelectLine-API läuft typischerweise beim Kunden (Doku zeigt `http://localhost/...`). Unsere Cloud muss sie erreichen → VPN / Reverse-Tunnel / fester Endpoint / On-Site-Agent. Klärt Aufwand und Sicherheit.
- **Datenschutz/Neutralisierung.** Rechnungen enthalten Personendaten. Pfade, die durch das LLM laufen (z. B. KI-gestütztes Konten-Mapping), müssen über das Neutralisierungs-Gate (`wiki/b-reference/platform/neutralization.md`).
- **Fragile Stellen:** `accountingBridge.py` (Orchestrierung, Dedup, Sync-Records) ist heute auf Position→Push zugeschnitten; der Source-Pfad muss additiv ergänzt werden, ohne den bestehenden Push-Pfad zu brechen. `TrusteeAccountingConfig` erlaubt heute nur **eine aktive** Config pro Instanz (siehe `getActiveConfig`).
## Ziel und Nicht-Ziele
- **Ziel:** Generisches, plugin-basiertes **Umsystem-Connector-Framework** (Erweiterung des bestehenden AccountingBridge-Musters), mit dem ein neues System = **eine Connector-Datei + Config**, ohne Architekturänderung.
- **Ziel:** **SelectLine-Connector** (Quelle): Login, Lesen von Ausgangsrechnungen (`Document`/`DocumentKind`), Kunden, Konten, Steuerschlüssel.
- **Ziel:** **Source→Target-Import** als **generische Workflow-Nodes**, sodass «SelectLine → RMA» ein **Workflow-Graph** ist (kundenspezifisch als Daten), kein Code.
- **Ziel:** **UI-Muster «Datenquelle = Seite»** im Feature Trustee: die Seite «Buchhaltungssystem» existiert; das Muster wird generisch, sodass weitere Kategorien (z. B. «Saläre», «Fakturierung») als eigene Seiten dazukommen können.
- **Explizit NICHT:** kundenspezifische Logik im Code (`if kunde == …`).
- **Explizit NICHT:** SelectLine als Personal Source.
- **Explizit NICHT:** Aufbau eines eigenen ERP/Faktura-Systems wir konsumieren/transferieren nur.
- **Explizit NICHT (vorerst):** voll generisches «Integrations-Framework für alles». Wir bleiben im Trustee-Kontext und verallgemeinern erst, wenn die zweite Kategorie (Saläre) real wird (YAGNI siehe «Meine Gedanken»).
## Architektur: generische Bausteine vs. kundenspezifische Logik
Vier Schichten. Schicht 13 sind **Code (generisch)**, Schicht 4 ist **Daten (kundenspezifisch)**.
```
┌─ Schicht 4: WORKFLOW-GRAPH + CONFIG (Daten, pro Instanz, im Graphical Editor) ─┐
│ "SelectLine → mappe Konto 1100→1100, Belegart=AR, seit lastSync → RMA (GL)" │
│ Konto-Mappings · Filter · Zeitplan · Quelle/Ziel-Auswahl → KUNDENSPEZIFISCH │
└─────────────────────────────────────────────────────────────────────────────────┘
▲ konsumiert generische Nodes, enthält KEINEN Code
┌─ Schicht 3: WORKFLOW-NODES (generische Typed Actions) ─────────────────────────┐
│ source.fetchInvoices · transform.mapAccounts · target.pushBooking · attachDoc │
└─────────────────────────────────────────────────────────────────────────────────┘
▲ arbeiten ausschliesslich auf kanonischen Modellen
┌─ Schicht 2: KANONISCHE MODELLE ─────────────────────────────────────────────────┐
│ AccountingBooking / AccountingBookingLine / Invoice / Contact / Chart … │
│ (bereits vorhanden in accountingConnectorBase.py) │
└─────────────────────────────────────────────────────────────────────────────────┘
▲ Connectors übersetzen natives API-Format ⇄ kanonisch
┌─ Schicht 1: CONNECTORS (generische API-Adapter, plugin-discovery) ─────────────┐
│ accountingConnectorRma.py · accountingConnectorSelectline.py · …bexio/abacus │
│ nur Auth + Lesen/Schreiben + Mapping. KEINE Kundenlogik. │
└─────────────────────────────────────────────────────────────────────────────────┘
```
### Schicht 1 — Connectors (neu: SelectLine)
- Neue Datei `platform-core/modules/features/trustee/accounting/connectors/accountingConnectorSelectline.py`, implementiert `BaseAccountingConnector` (`accountingConnectorBase.py`). Auto-Discovery über `accountingRegistry.py` (Pattern `accountingConnector*.py`) → **keine Registrierungs-Änderung nötig**.
- Methoden: `testConnection` (Login + GET), `getChartOfAccounts` (Konten), `getCustomers`/`getVendors`, und die Lese-Seite für Belege (siehe «Source-Capability» unten).
- Config-Felder (`getRequiredConfigFields`): `baseUrl`, `username`, `password` (secret), optional `mandant`. Verschlüsselt wie alle Configs (`_decryptConfig`).
- **Source-Capability:** Da die Base heute primär Push/Read-Journal kennt, ergänzen wir eine schlanke, optionale Lesemethode für Belege, z. B. `getOutgoingInvoices(config, dateFrom, sinceCursor) -> List[Invoice]` (Default `[]` in der Base; nur Source-Connectors überschreiben). Alternativ Wiederverwendung von `getJournalEntries` Entscheidung siehe «Offene Fragen».
### Schicht 2 — kanonische Modelle (vorhanden)
`AccountingBooking`, `AccountingBookingLine`, `AccountingChart`, `SyncResult` etc. in `accountingConnectorBase.py`. Für reine Debitoren-Fakturen ggf. ein kanonisches `Invoice`-Modell ergänzen (Header + Lines + Kunde + Steuer), falls RMA als **AR-Rechnung** (offener Posten) statt GL-Buchung beschrieben werden soll.
### Schicht 3 — Workflow-Nodes (generisch, Typed Actions)
Analog zu den bestehenden Trustee-Actions (`trustee.extractFromFiles` → `processDocuments``syncToAccounting`). Neue, **system-agnostische** Nodes:
- `integration.fetchFromSource` — zieht Belege/Buchungen aus dem **konfigurierten Quell-Connector** (Quelle = Config, nicht hartcodiert) → kanonische Liste.
- `integration.mapAccounts` — wendet eine **Mapping-Tabelle** (Config/Daten) auf Konten/Steuercodes an.
- `integration.pushToTarget` — schreibt über den **konfigurierten Ziel-Connector** (`pushBooking`/`pushInvoice` + Beleg-Upload, bereits in RMA vorhanden).
Diese Nodes erscheinen über den Editor-Adapter (`graphicalEditor/nodeDefinitions/`) als Bausteine im Graphical Editor. Die Pipeline `trigger → fetchFromSource(SelectLine) → mapAccounts → pushToTarget(RMA)` ersetzt damit für strukturierte Quellen den KI-Extraktionsschritt (günstiger + zuverlässiger).
### Schicht 4 — Workflow-Graph + Config (kundenspezifisch, als Daten)
- Der konkrete Kundenfluss ist ein **Workflow-Template/Graph** pro Feature-Instanz (persistiert), gebaut im Graphical Editor. Konten-Mappings, Filter (nur Ausgangsrechnungen, nur «seit letztem Sync»), Buchungsart, Zeitplan = **Config-Datensätze**.
- Ergebnis: zwei Kunden mit SelectLine→RMA, aber unterschiedlichem Kontenplan-Mapping = **zwei Graphen/Configs, ein Code**.
### Datenmodell-Änderung
`TrusteeAccountingConfig` um **`role`** (`source` | `target`) erweitern, sodass pro Instanz mehrere Connector-Configs koexistieren (eine Quelle + ein Ziel). `getActiveConfig` / `_resolveConnectorAndConfig` entsprechend um `role` filtern. Optional: `connectorCategory` (`accounting` | `payroll` | `invoicing`) für die UI-Seitenzuordnung.
## SelectLine-API Befund (Kurz)
Geprüft anhand hilfe.selectline.ch und der Demo-API (demo.slmobile.de):
- REST, JSON/XML. Auth: `POST /Login` mit `{username,password}``AccessToken`/`LoginId`; danach Header `Authorization: LoginId <token>` (Session-Token, Re-Login bei Ablauf).
- Relevante Ressourcen: **`Document` / `DocumentKind`** (Belegkette inkl. Ausgangsrechnungen), `Customers`/`Vendors` (Geschäftspartner), `Konten` + `Kontensalden`, `Journale`, `Offene Posten`, `Steuerschlüssel`, `Kostenrechnung`, `Bankassistent` (camt.053-Import).
- Betriebsmodell: i. d. R. **on-premise** beim Kunden → Erreichbarkeit klären (Risiko oben).
- Swagger-/OpenAPI-Beschreibung pro Installation herunterladbar → exaktes Beleg-Schema beim Kunden verifizieren.
## UI-Konzept: «Datenquelle = Seite»
Heute ist «Buchhaltungs-Einstellungen» eine eigene Seite im Trustee-Feature:
- Registriert als UI-Object `ui.feature.trustee.settings` (`meta.area="settings"`, `admin_only`) in `mainTrustee.py`.
- Gemappt auf `TrusteeAccountingSettingsView` in `ui-nyla/src/pages/FeatureView.tsx` (`VIEW_COMPONENTS.trustee.settings`).
- Innen bereits Tabs: «Verbindungseinstellungen» + «Buchhaltungsdaten importieren» (Connector wählen → Credentials → Test → Import).
**Vorschlag:** dieses Muster zu einem **generischen «Datenquellen-Seiten»-Pattern** verallgemeinern eine Seite pro **Kategorie** von Umsystem:
| Seite (UI-Area) | Kategorie | Connectors (Beispiele) | Kanonisches Modell |
|---|---|---|---|
| Buchhaltungssystem (existiert) | `accounting` | RMA, Bexio, Abacus, **SelectLine (Quelle)** | AccountingBooking |
| **Saläre** (Idee) | `payroll` | SwissSalary, Abacus Lohn, Swissdec-konform | PayrollEntry (neu) |
| Fakturierung (Idee) | `invoicing` | SelectLine, Bexio | Invoice |
Pro Seite identischer Aufbau (**eine generische React-Komponente, parametrisiert über `category`**):
1. **Verbindungen** — Connectors der Kategorie listen, Rolle (Quelle/Ziel) wählen, Credentials, Test (wiederverwendet die bestehende `TrusteeAccountingSettingsView`-Mechanik, generalisiert).
2. **Datenfluss/Import** — Workflow-Template wählen/starten (= Schicht-4-Graph), Zeitplan, Status/Audit.
3. **Daten** — eingelesene Datensätze (read-only Spiegel).
Konkret:
- Generische `IntegrationSettingsView` (aus `TrusteeAccountingSettingsView` extrahiert), die `category` als Prop bekommt.
- Neue UI-Areas in `mainTrustee.py` (z. B. `ui.feature.trustee.payroll`) + Mapping in `FeatureView.tsx`**gleiche** Komponente, andere `category`.
- «Saläre» ist bewusst **nur ein Platzhalter-Beispiel**, um zu zeigen, dass das Muster skaliert; nicht Teil der ersten Umsetzung.
## Betroffene Module
- **Gateway (platform-core):**
- `features/trustee/accounting/connectors/accountingConnectorSelectline.py` (neu).
- `accounting/accountingConnectorBase.py` (optionale Source-Lesemethode, Default `[]`).
- `accounting/accountingBridge.py` (additiver Import-/Source-Pfad; `role`-Auflösung).
- `datamodelFeatureTrustee.py` (`TrusteeAccountingConfig.role` [+ optional `connectorCategory`]) → **DB-Migration: ja**.
- `workflows/methods/methodTrustee/actions/` + `graphicalEditor/nodeDefinitions/` (generische Nodes `integration.fetchFromSource` / `mapAccounts` / `pushToTarget`).
- `mainTrustee.py` (UI-Areas, RBAC-Objekte, Workflow-Templates).
- **Frontend (ui-nyla):**
- `TrusteeAccountingSettingsView` → generische `IntegrationSettingsView` (`category`-Prop).
- `FeatureView.tsx` Registry + `mainTrustee` UI-Areas für weitere Seiten.
- `trusteeApi.ts` (Connector-Listing nach Kategorie, Rolle source/target).
- **DB-Migration:** ja (`role`, optional `connectorCategory`).
- **Andere:** Neutralisierung (falls KI-Mapping), Audit/Billing (Sync-Volumen).
## Entscheidungen
| Datum | Entscheidung | Begründung |
|-------|-------------|------------|
| 2026-06-04 | Umsystem als Trustee-Connector, **nicht** als Personal Source | Transaktional, mandant-scoped, Dedup/Audit/verschlüsselte Credentials bereits in AccountingBridge |
| 2026-06-04 | Kundenlogik als Workflow-Graph + Config, nicht im Code | Auftrag: keine kundenspezifische Logik im Code; nutzt bestehende Typed-Action-/Editor-Architektur |
| 2026-06-04 | UI-Muster «Datenquelle = Seite» pro Kategorie, eine generische Komponente | Skaliert auf Saläre/Fakturierung ohne Copy-Paste |
## Umsetzungs-Checkliste
- [ ] DB: `TrusteeAccountingConfig.role` (+ optional `connectorCategory`) + Migration
- [ ] Bridge: Source-Auflösung nach `role`, Import-Pfad (additiv, Push-Pfad unverändert)
- [ ] Connector: `accountingConnectorSelectline.py` (Login/Test/Konten/Kunden/Belege lesen)
- [ ] Kanonisch: `Invoice`-Modell prüfen/ergänzen (falls AR-Rechnung in RMA)
- [ ] Workflow-Nodes: `integration.fetchFromSource` / `mapAccounts` / `pushToTarget` + Editor-Adapter
- [ ] Workflow-Template «Source→Target Import» (parametrisierbar)
- [ ] UI: `IntegrationSettingsView` generalisieren (`category`), Quelle/Ziel-Auswahl
- [ ] RBAC / Permissions (neue UI-Areas, admin_only)
- [ ] Neutralisierung betroffen? (nur falls KI-Mapping)
- [ ] Navigation / Routing (UI-Areas + FeatureView-Registry)
- [ ] Billing-Impact? (Sync-Läufe/Volumen)
## Akzeptanzkriterien
| # | Kriterium (Given-When-Then) | Prio |
|---|---------------------------|------|
| 1 | Given konfigurierter SelectLine-Source + RMA-Target, When der Import-Workflow läuft, Then erscheinen die SelectLine-Ausgangsrechnungen als Buchungen in RMA (inkl. Beleg-Verlinkung) | must |
| 2 | Given zwei Mandanten mit unterschiedlichem Konten-Mapping, When beide SelectLine→RMA nutzen, Then unterscheiden sie sich nur in Workflow-Graph/Config **kein** kundenspezifischer Code | must |
| 3 | Given ein neues Umsystem, When ein Connector-File + Config ergänzt wird, Then ist es ohne Änderung an Registry/Bridge/Editor verfügbar | must |
| 4 | Given erneuter Import, When Belege bereits gebucht sind, Then werden sie über die bestehende Dedup-Logik nicht doppelt gebucht | must |
| 5 | Given das UI-Muster, When eine neue Kategorie-Seite («Saläre») registriert wird, Then nutzt sie dieselbe generische Komponente | should |
## Testplan
| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
|----|----|-----|--------------|-----------|--------|
| T1 | 1 | unit | ja | platform-core/tests/unit/features/trustee/test_accountingConnectorSelectline.py | pending |
| T2 | 1,4 | integration | ja | platform-core/tests/integration/trustee/test_source_target_import_e2e.py | pending |
| T3 | 3 | unit | ja | platform-core/tests/unit/.../test_adapter_validator.py (Editor-Node-Drift) | pending |
| T4 | 2 | unit | ja | platform-core/tests/unit/.../test_integration_nodes.py (Mapping aus Config) | pending |
## Offene Fragen (für den Kundentermin)
1. **Erreichbarkeit:** Wo läuft die SelectLine-API (on-prem/Cloud)? Welcher Zugriffsweg (VPN/Tunnel/Agent)?
2. **Zielart in RMA:** Debitoren-Rechnung (AR, offener Posten) **oder** GL-Buchung? → bestimmt `pushInvoice`-Ausbau vs. `pushBooking`.
3. **Mapping:** Liefert SelectLine pro Rechnung Konto + Steuerschlüssel, oder Kontenplan-Mapping SelectLine→RMA nötig?
4. **Beleg-PDF:** Rechnungs-PDF mit nach RMA (Beleg-Upload vorhanden)?
5. **Delta/Frequenz:** On-demand, Zeitplan oder Event? Cursor = Belegnummer/Datum?
6. **Alternative:** SelectLine `Bankassistent` (camt.053) relevant oder nur Ausgangsrechnungen?
## Meine Gedanken / Empfehlungen
- **Saubere Trennung ist erreichbar, weil die Plattform sie schon vorlebt.** Trustee nutzt bereits Typed Actions + Graphical Editor + Templates. «Kundenlogik als Graph» ist kein neues Paradigma, sondern Anwendung des Bestehenden. Das ist der grösste Hebel gegen kundenspezifischen Code.
- **Nicht zu früh über-generalisieren.** Ein einziges «BaseConnector für alles» mit Capability-Mixins (`AccountingCapable`, `PayrollCapable`) ist elegant, aber bevor «Saläre» real ist, würde ich beim bestehenden `BaseAccountingConnector` bleiben und nur die **Source-Rolle** ergänzen. Die Abstraktion ziehen wir, wenn die zweite Kategorie konkret wird sonst bauen wir ein Framework für hypothetische Fälle (YAGNI).
- **Namensgebung zur Abgrenzung:** «Personal Sources» = user-privat/RAG. Diese hier sind **«Umsystem-Anbindungen» / «Integrationen»** auf Mandant-Ebene. Klare Begriffe verhindern, dass die zwei Welten wieder vermischt werden.
- **Source vs. Target ist die wichtigste neue Achse.** Sie macht aus dem heutigen Push-Framework ein bidirektionales Integrations-Framework und ist die Voraussetzung für «System A → PowerOn → System B» generell.
- **«Saläre»-Seite** ist ein gutes nächstes Beispiel: Lohnsystem (Swissdec-konform) → Lohnbuchungen → Buchhaltung. Gleicher Mechanismus, neues kanonisches Modell `PayrollEntry`. Bestätigt, dass das Seiten-Muster trägt.
- **Risiko bleibt extern:** On-Prem-Erreichbarkeit von SelectLine ist der kritische Pfad das ist eher Infrastruktur/Netzwerk als Code. Im Kundentermin zuerst klären.
## Links
- Vorgelagerte Analyse (dieser Chat): SelectLine-API-Prüfung + erste Empfehlung
- Referenz Trustee: `wiki/b-reference/platform-core/features/trustee.md`
- Referenz UDB/Personal Sources: `wiki/b-reference/platform/unified-data-bar.md`
- Code: `platform-core/modules/features/trustee/accounting/` (Bridge, Base, Registry, Connectors)
- PR: —
- Issue: —
## Abschluss
- [ ] b-reference/ aktualisiert (`features/trustee.md` Abschnitt «Source-Connectors / Integrationen»; ggf. neue `platform/integrations.md`)
- [ ] TOPICS.md aktualisiert (neues Thema «Umsystem-Integrationen»)
- [ ] Dieses Dokument → c-work/2-build/ (bei Umsetzungsbeginn), am Ende → z-archive/