Wiki source code of BMD_Rimo_Invoices
Last modified by Patrizia Gurschka on 2026/10/05 13:48
Hide last authors
| author | version | line-number | content |
|---|---|---|---|
| |
13.1 | 1 | |(% style="width:772px" %)((( |
| |
22.1 | 2 | {{box cssClass="floatinginfobox" title="**Auf dieser Seite**"}} |
| 3 | {{toc/}} | ||
| 4 | {{/box}} | ||
| 5 | |||
| |
20.2 | 6 | = **🧾**BMD → Rimo Invoices (Rechnungsautomatisierung) = |
| |
5.1 | 7 | |
| |
19.1 | 8 | {{info}} |
| |
41.1 | 9 | **Zweck: **Automatisierte Übertragung von BMD-Eingangsrechnungen nach Rimo — Download, Upload und Freigabe (Approval), vollständig zustandsbehaftet. Die Lösung läuft stündlich über eine Fabric-Pipeline und meldet fachliche Fehler an die Buchhaltung, technische Auffälligkeiten an die IT. |
| |
19.1 | 10 | |
| 11 | **Zielgruppe: **Buchhaltung/Fibu-Team und IT/Fabric-Betrieb. | ||
| 12 | {{/info}} | ||
| |
20.1 | 13 | ))) |
| |
19.1 | 14 | |
| |
5.1 | 15 | == Überblick == |
| 16 | |||
| 17 | **BMD → Rimo Invoices** ist eine Fabric-Notebook-gesteuerte Automatisierung mit zwei begleitenden Data-Factory-Pipelines. Die Lösung besteht aus: | ||
| 18 | |||
| 19 | * **Notebook: **Python/PySpark (Microsoft Fabric) | ||
| 20 | * **Datenquelle: **Fabric Lakehouse (Spark SQL) | ||
| 21 | * **Orchestrierung: **2 Data-Factory-Pipelines | ||
| 22 | * **Zielsystem: **Rimo REST-API (Test/Dev) | ||
| 23 | |||
| 24 | == Architektur == | ||
| 25 | |||
| |
39.2 | 26 | [[image:1791200276107-433.png]] |
| |
5.1 | 27 | |
| 28 | == Technologiestack == | ||
| 29 | |||
| |
40.2 | 30 | Seit dem 25.09.2026 läuft das Notebook auf Fabric Runtime 2.0 (Spark 4, Python 3.13); die SQL-Abfragen wurden dafür von parametrisierten Abfragen auf f-Strings umgestellt. |
| |
5.1 | 31 | |
| |
40.2 | 32 | * **Notebook/Backend:** Python, PySpark (Spark SQL), requests, notebookutils, Python-Logging mit eigenem Logger |
| 33 | * **Datenplattform:** Fabric Lakehouse, Delta-Tabellen bmd.fibu_erb_buchungen, bmd.fibu_erk_konto, rimo.sdmcompany, rimo.sdmcreditor, rimo.sdmaccount, rimo.sdmsalesorder, rimo.sdmprojectpurchaseorder, spl_gold_lh.bmd.rimo_processed_invoices_log | ||
| 34 | * **Orchestrierung:** Fabric Data Factory (Copy-Data, Notebook-Aktivität, If-Condition, Office-365-Outlook-Aktivität) | ||
| 35 | * **Externe API:** Rimo REST-API (multipart/form-data für Upload, JSON für Approvals) | ||
| |
5.1 | 36 | |
| |
40.2 | 37 | == Hauptfunktionen == |
| |
5.1 | 38 | |
| |
40.2 | 39 | Jeder Lauf durchläuft nach den Health-Checks drei fachliche Phasen; jede Rechnung trägt ihren Stand in der State-Datei, sodass ein abgebrochener Lauf beim nächsten Mal genau dort weitermacht. |
| |
5.1 | 40 | |
| 41 | === 1. Datenerfassung === | ||
| 42 | |||
| |
40.2 | 43 | Buchungszeilen werden aus bmd.fibu_erb_buchungen gelesen: alle Buchungen ohne „Spesen“ im Buchungstext, einschließlich Buchungen **ohne** Buchungstext, mit Erstelldatum ab 27.08.2026, 10:00 Uhr (offenes Zeitfenster). Bereits verarbeitete Zeilen werden per LEFT JOIN gegen spl_gold_lh.bmd.rimo_processed_invoices_log ausgeschlossen; bei mehreren Dateien zum selben Beleg wird die .pdf-Variante bevorzugt. Jede Zeile erhält den Schlüssel //intBelegNr_Firmennr// und wird in die Warteschlange der State-Datei übernommen. |
| |
5.1 | 44 | |
| |
40.2 | 45 | Buchungen ohne Buchungstext werden ohne userLabel an Rimo übergeben; das Feld fehlt dann im Request. |
| 46 | |||
| |
5.1 | 47 | === 2. Download-Phase (Belegdokument) === |
| 48 | |||
| |
40.2 | 49 | Notebooks können den On-Premises Data Gateway nicht nutzen. Deshalb kopiert die vorgelagerte Copy-Data-Aktivität die Dateien vom Dateiserver (\atwotbmd01\BMDDocs\0\2...) gesammelt ins Lakehouse, anhand eines Abgleichs von bmd.fibu_erb_buchungen mit der Log-Tabelle. Die Download-Phase prüft nur, ob die erwartete Datei im Lakehouse-Zielordner angekommen ist. |
| |
5.1 | 50 | |
| 51 | === 3. Upload-Phase (Rechnungsdaten) === | ||
| 52 | |||
| |
40.2 | 53 | BMD-Felder werden auf Rimo-Felder gemappt, Firmen- und Kreditor-IDs per Lakehouse-Lookup ergänzt und Beleg plus Daten per multipart/form-data an POST /v1/invoices übergeben. Ein erfolgreicher Upload liefert die Rimo-externalId. |
| |
5.1 | 54 | |
| 55 | === 4. Approval-Phase (Freigaben) === | ||
| 56 | |||
| |
40.2 | 57 | Pro Buchungszeile in bmd.fibu_erk_konto (verknüpft über ErfassNr) wird eine eigene Freigabe an Rimo gesendet, mit mehrstufigem Fallback für die Bestellnummer- und Sales-Order-Zuordnung (siehe Besondere Geschäftslogik). Die Approval-Phase läuft im Modus „all“ mit. |
| |
5.1 | 58 | |
| |
40.2 | 59 | == Health-Checks und technische Fehlererkennung == |
| |
5.1 | 60 | |
| |
40.2 | 61 | Vor jeder Verarbeitung prüft das Notebook Verbindungen, Tabellen und die eigenen SQL-Abfragen. Ein kritischer Fehler bricht den Lauf ab, bevor eine Rechnung angefasst wird; so landen bei Systemumstellungen (z. B. Runtime-Wechsel) keine Rechnungen fälschlich in dead_letter. |
| |
5.1 | 62 | |
| |
40.2 | 63 | |Check|Schwere|Prüft |
| 64 | |Rimo-Token|kritisch|Token-Abruf liefert HTTP 200 und einen Token | ||
| 65 | |Rimo-API erreichbar|kritisch|API antwortet, keine 401/403/5xx | ||
| 66 | |State-Datei|kritisch|Datei lesbar und vollständig; fehlt sie bei befüllter Log-Tabelle → Abbruch (Schutz vor Doppelverarbeitung) | ||
| 67 | |Lakehouse schreibbar|kritisch|Testdatei im Zielordner schreiben und löschen | ||
| 68 | |Stammdatentabellen|kritisch|rimo.sdmcompany, sdmcreditor, sdmaccount, sdmsalesorder, sdmprojectpurchaseorder, bmd.fibu_erk_konto nicht leer | ||
| 69 | |bmd.fibu_erb_buchungen|Warnung|nicht leer; leer ist zulässig, wenn keine Rechnungen anfallen | ||
| 70 | |Schlüsselformat|kritisch|Schlüssel entsprechen //Ziffern_Ziffern// (erkennt Typänderungen im DWH) | ||
| 71 | |BMD-Buchungen aktuell|Warnung|neueste Buchung höchstens 4 Tage alt | ||
| 72 | |Lookup Company|kritisch|echte Lookup-Funktion findet die bekannte Firma 70 | ||
| |
5.1 | 73 | |
| |
40.2 | 74 | **Während des Laufs** gilt zusätzlich: |
| |
5.1 | 75 | |
| |
40.2 | 76 | * **Technische Fehler:** Scheitert eine Lookup-Abfrage technisch (SQL-Fehler statt „nicht gefunden“), wird das als technischer Fehler gesammelt und an die IT gemeldet. Ab 5 technischen Fehlern bricht der Lauf ab. |
| 77 | * **Lookup-Trefferquote:** Finden Company-, Kreditor-, Account-, Sales-Order- oder Purchase-Order-Lookups bei mindestens 3 Versuchen zu 80 % oder mehr nichts, wird das als vermutliches Daten- oder Abfrageproblem gemeldet. | ||
| 78 | * **Caches:** Lookup-Caches werden zu Beginn jedes Laufs geleert, damit bei wiederverwendeten Sessions keine veralteten Stammdaten verwendet werden. | ||
| |
5.1 | 79 | |
| |
40.2 | 80 | == Fehler-Orchestrierung und Benachrichtigungen == |
| |
5.1 | 81 | |
| |
40.2 | 82 | Die Pipeline trennt seit Ende September 2026 fachliche Fehler (Mail an die Buchhaltung) von technischen Auffälligkeiten (Mail an die IT); bricht das Notebook ab, geht ein eigener Alarm an die IT. |
| |
5.1 | 83 | |
| |
40.2 | 84 | [[image:1791200539745-771.png]] |
| |
5.1 | 85 | |
| |
40.2 | 86 | Fehler-Orchestrierung · drei Mails, zwei Empfängergruppen |
| |
5.1 | 87 | |
| |
40.2 | 88 | Beide If-Conditions hängen parallel am Erfolgspfad der Notebook-Aktivität und lesen Flags aus dem exitValue. |
| 89 | |||
| 90 | |Mail|Auslöser|Inhalt | ||
| 91 | |ErrorMailing (Buchhaltung)|hasFailures = 1|Download-, Upload-, Approval-Fehler und „Lieferperiode ungültig“, je als eigene Tabelle (Feld emailBodyHtml) | ||
| 92 | |TechnicalAlert (IT)|hasTechnicalIssues = 1|Technische Fehler, Health-Check-Warnungen, auffällige Lookups, unklare Uploads (Feld itEmailBodyHtml) | ||
| 93 | |NotebookStopAlert (IT)|Notebook-Aktivität fehlgeschlagen|Extrahierter Fehlertext aus errorValue, Pipeline, Zeitpunkt, Run-ID | ||
| 94 | |||
| 95 | * **Keine Doppelmeldungen:** Gemeldete Fehler werden in der State-Datei markiert (notified_at) und nur einmal gemailt. | ||
| 96 | * **Health-Warnungen mit Schwelle:** Eine leere bmd.fibu_erb_buchungen wird erst nach 3 Läufen in Folge gemeldet, einmal pro Serie; der Zähler steht in der State-Datei und zählt über Tagesgrenzen. Andere Warnungen werden beim ersten Auftreten gemeldet. | ||
| 97 | * **Lieferperiode ungültig:** bleibt in pending statt dead_letter und wird nach Korrektur in BMD automatisch neu versucht. | ||
| 98 | * **Download-Fehler** nach Erreichen der Retry-Grenze erhalten eine verständliche Meldung, z. B. „Datei nach Pipeline-Lauf nicht im Lakehouse gefunden“. | ||
| 99 | |||
| 100 | == Logging, State-Datei und Backups == | ||
| 101 | |||
| 102 | Jeder Lauf wird als eigener Block oben in rimo_pipeline.log geschrieben, der neueste Lauf steht damit immer am Anfang der Datei. | ||
| 103 | |||
| 104 | === Logging === | ||
| 105 | |||
| 106 | * Das Notebook nutzt einen eigenen Logger (rimo_pipeline), unabhängig vom Root-Logger der Fabric-Runtime. Hintergrund: Runtime 2.0 konfiguriert den Root-Logger selbst, wodurch seit 25.09.2026 nach 07:08 Uhr keine Einträge mehr in der Logdatei landeten. | ||
| 107 | * Während des Laufs wird lokal nach /tmp geloggt; am Ende fügt das Notebook den Lauf als Block (Kopfzeile //LAUF Start bis Ende//) oben in die Lakehouse-Logdatei ein, innerhalb des Blocks chronologisch. | ||
| 108 | * Das Log wird auch bei einem Abbruch geschrieben (try/finally um den Hauptlauf). | ||
| 109 | * Zeitstempel im Log in Wiener Zeit (Europe/Vienna); Zeitstempel in der State-Datei in UTC. | ||
| 110 | |||
| 111 | === Dateien im Lakehouse === | ||
| 112 | |||
| 113 | |Datei / Ordner|Pfad|Inhalt | ||
| 114 | |State-Datei|Files/bmd/import/BMD_Invoices/rimo_processed_state.json|Warteschlange (pending), erledigt (succeeded), dead_letter, Zähler der Health-Warnungen | ||
| 115 | |Logdatei|Files/bmd/import/BMD_Invoices/rimo_pipeline.log|Läufe als Blöcke, neuester oben | ||
| 116 | |State-Backups|Files/bmd/import/BMD_Invoices_state_backups/|Tägliche Kopie der State-Datei, 5 Tage aufbewahrt | ||
| 117 | |Log-Tabelle|spl_gold_lh.bmd.rimo_processed_invoices_log|Erledigte und dead_letter-Einträge; Basis für den Ausschluss bei der Datenerfassung | ||
| 118 | |||
| 119 | Die State-Datei wird atomar geschrieben (Temp-Datei, Prüfung, Umbenennen), damit ein Abbruch keine halbe Datei hinterlässt. Die Backups liegen bewusst außerhalb von BMD_Invoices, weil das Cleanup am Ende jedes Laufs dort alle Unterordner löscht; .log- und .json-Dateien bleiben dabei erhalten. | ||
| 120 | |||
| 121 | == Pipelines und Konfiguration == | ||
| 122 | |||
| 123 | Die wichtigsten Einstellungen stehen als Konstanten in der Konfig-Zelle des Notebooks; Filter und Zugangsdaten sind noch nicht parametrisiert. | ||
| 124 | |||
| 125 | |Bereich|Details | ||
| 126 | |Copy-Pipeline „BMD_Invoices“|Kopiert die benötigten Dateien gesammelt vom Dateiserver ins Lakehouse, vor der Notebook-Aktivität | ||
| 127 | |Orchestrierungs-Pipeline „BMD_Invoices_Error_Handling“|Notebook → If Condition (hasFailures) → ErrorMailing/FallbackAlert; parallel If Condition (hasTechnicalIssues) → IT-Mail; bei Abbruch NotebookStopAlert | ||
| 128 | |Rimo-API-Endpunkt|rimo-api.d-prod.spl-tele.com/v1/invoices | ||
| |
25.2 | 129 | |State-Datei|/lakehouse/default/Files/bmd/import/BMD_Invoices/rimo_processed_state.json |
| |
40.2 | 130 | |Retry-Grenzen|Download/Upload/Approval je max. 5 Versuche; HTTP-Aufrufe je 3 Versuche mit Backoff |
| 131 | |Technische Fehler|Abbruch ab 5 technischen Fehlern pro Lauf (MAX_TECH_ERRORS) | ||
| 132 | |Health-Warnung leere Buchungstabelle|Mail erst nach 3 Läufen in Folge, einmal pro Serie | ||
| 133 | |Exit-Details|max. 50 Einträge pro Kategorie im Exit-Wert (MAX_EXIT_DETAILS) | ||
| 134 | |Aktuelle Filterung|Buchungen ohne „Spesen“ (inkl. leerem Buchungstext) + Erstelldatum ab 27.08.2026, 10:00 Uhr; fest im Code. Kein Firmennr-Filter | ||
| |
5.1 | 135 | |
| 136 | == Sicherheit == | ||
| 137 | |||
| |
40.2 | 138 | Die Rimo-Zugangsdaten stehen weiterhin im Klartext im Notebook; die Umstellung auf einen Key Vault ist vorbereitet, aber noch nicht umgesetzt. |
| 139 | |||
| |
5.1 | 140 | === Authentifizierung === |
| 141 | |||
| |
40.2 | 142 | * **Rimo-API-Token:** wird per Benutzer/Passwort über /v1/auth/fetch-token einmal pro Lauf geholt. Der Health-Check bricht den Lauf ab, wenn der Abruf fehlschlägt. Eine automatische Token-Erneuerung bei HTTP 401 gibt es noch nicht. |
| 143 | * **Zugangsdaten:** Benutzer und Passwort stehen im Klartext im Notebook und damit auch im Versionsverlauf. Empfehlung: über notebookutils.credentials.getSecret aus einem Key Vault beziehen und das Passwort ändern. | ||
| 144 | * **Fabric-API-Token:** wird nicht mehr verwendet, da der Kopiervorgang vollständig in der vorgelagerten Copy-Data-Aktivität läuft. | ||
| |
5.1 | 145 | |
| 146 | === Datenschutz === | ||
| 147 | |||
| |
40.2 | 148 | Zugriff auf Buchungs- und Stammdaten ausschließlich über die Fabric-Lakehouse-Berechtigungen. Das Notebook greift nicht direkt auf den On-Premises-Dateiserver zu; das läuft ausschließlich über die Gateway-Pipeline. |
| |
5.1 | 149 | |
| 150 | == Besondere Geschäftslogik == | ||
| 151 | |||
| |
40.2 | 152 | Vier fachliche Sonderregeln weichen bewusst von einer 1:1-Übernahme der BMD-Daten ab. |
| |
5.1 | 153 | |
| |
40.2 | 154 | * **Vorzeichen-Korrektur beim Upload:** Ist der Bruttobetrag (grossAmount) positiv, der Nettobetrag (netAmount) aber negativ, wird der Betrag ohne Vorzeichen als netAmount übergeben. Alle anderen Kombinationen bleiben unverändert. |
| 155 | * **Mehrstufiger Fallback bei Approvals:** | ||
| 156 | *1. Bestellnummer-Kandidaten (Purchase-/Sales-Order-Paare) einzeln probieren | ||
| 157 | *1. bei Scheitern ein firmenbasierter Sales-Order-Kandidat ohne Bestellung | ||
| 158 | *1. sonst endgültig gescheitert (approval_dead) | ||
| 159 | * **Sales-Order-Ausnahme:** Gibt es für eine Firma keinen Sales Order mit getsalesordernumber = '0000 {4 Ziffern}', wird ersatzweise nach dem Muster „{Firmennummer}000 0001“ gesucht (Firma 68 → „68000 0001“). Dieser Fallback greift nur, wenn die reguläre Suche keine Kandidaten liefert. | ||
| 160 | * **Ungültige Lieferperiode:** Ist eine falsch formatierte Lieferperiode der einzige Fehler, bleibt die Rechnung in pending statt in dead_letter und wird nach Korrektur in BMD automatisch erneut versucht. | ||
| |
5.1 | 161 | |
| 162 | == Server / Umgebung == | ||
| 163 | |||
| |
40.2 | 164 | Notebook und Pipelines laufen im Microsoft Fabric Workspace auf Runtime 2.0; ausgelöst wird stündlich über die Orchestrierungs-Pipeline oder manuell. |
| |
5.1 | 165 | |
| |
40.2 | 166 | * **Workspace:** wegen des Kapazitätschecks laufen Pipeline und Notebook derzeit über den Fabic_test_workspace, angehängt an das im Fabric Workspace liegende bmd_invoice_execution. |
| 167 | * **Runtime:** Fabric Runtime 2.0 seit 25.09.2026. Bei künftigen Runtime-Wechseln vorab in einer Kopie testen; die Health-Checks fangen grundlegende Probleme ab. | ||
| 168 | * **Erreichbarkeit Rimo-API:** über die im Notebook hinterlegten Zugangsdaten. | ||
| 169 | * **Belegquelle:** \atwotbmd01\BMDDocs\0\2..., nur über die On-Premises-Gateway-Pipeline erreichbar. | ||
| 170 | |||
| 171 | == Offene Punkte und bekannte Einschränkungen == | ||
| 172 | |||
| 173 | Das größte offene Risiko sind doppelte Rechnungen in Rimo bei Timeouts; die Lösung ist ausgearbeitet, aber noch nicht eingebaut. | ||
| 174 | |||
| 175 | * ☐ **Schutz vor doppelten Uploads:** Bei Timeout oder 5xx wiederholt upload_invoice den Upload, obwohl Rimo die Rechnung womöglich schon angelegt hat. Geplant: Status upload_uncertain, kein automatischer Retry, Meldung an die IT, Hilfsfunktion resolve_upload_uncertain. | ||
| 176 | * ☐ **Token-Erneuerung bei HTTP 401** für Upload und Approval, damit lange Läufe nicht abbrechen. | ||
| 177 | * ☐ **Zugangsdaten in Key Vault** verschieben und Passwort ändern. | ||
| 178 | * ☐ **Leere Firmennr im Schlüssel:** CONCAT mit NULL ergibt NULL; COALESCE in der Abfrage ergänzen. | ||
| 179 | * ☐ **Copy-Data-Abfrage angleichen:** Buchungen ohne Buchungstext müssen auch dort mitkopiert werden, sonst scheitert ihr Download. | ||
| 180 | * ☐ **Startdatum parametrisieren** statt fest im SQL. | ||
| 181 | * ☐ **Gleichnamige PDFs** aus verschiedenen BMD-Ordnern überschreiben sich im flachen Lakehouse-Ordner; Copy-Pipeline sollte den relativen Pfad oder den Schlüssel als Dateinamen verwenden. | ||
| 182 | * ☐ **Atomares Schreiben prüfen:** Erscheint im Log „Atomares Ersetzen der State-Datei nicht möglich“, unterstützt der OneLake-Mount os.replace nicht. | ||
| 183 | * ☐ **Health-Check-Werte ergänzen:** bekannten Kreditor und Kontonummer in KNOWN_LOOKUPS eintragen. | ||
| 184 | * ☐ **Approvals bei Timeout:** submit_approval wiederholt bei Timeout und 5xx; im schlimmsten Fall entsteht eine Freigabezeile doppelt. |