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

20 KiB
Raw Blame History

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.extractFromFilesprocessDocumentssyncToAccounting). 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.tsxgleiche 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.
  • 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/