From 0044306f172c024bfb810c769e7fd06fd8bf1e42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Gla=CC=88ser?= Date: Wed, 12 Aug 2026 16:58:25 +0200 Subject: [PATCH] Dokumentiere Shopify Datenmodell und n8n Vertrag --- .../shopify-datenmodell-und-n8n-konzept.md | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 docs/modules/erp/import-integration/shopify-datenmodell-und-n8n-konzept.md diff --git a/docs/modules/erp/import-integration/shopify-datenmodell-und-n8n-konzept.md b/docs/modules/erp/import-integration/shopify-datenmodell-und-n8n-konzept.md new file mode 100644 index 0000000..471259d --- /dev/null +++ b/docs/modules/erp/import-integration/shopify-datenmodell-und-n8n-konzept.md @@ -0,0 +1,146 @@ +# Shopify-Bestellmodell und n8n-Kompatibilitaetskonzept + +Stand: 2026-08-12 + +Status: Entscheidungsentwurf – vor DB-Umsetzung abnehmen + +## 1. Zielbild + +Shopify ist die fachlich führende Quelle für Shop-Bestellungen. Das ERP hält einen nachvollziehbaren, erweiterbaren Spiegel dieser Bestellungen und ihrer Positionen, Kundenbezüge, Zahlungen, Erstattungen, Fulfillments und Tags. Shopify wird ausschliesslich gelesen. + +Die bestehenden 93 ERP-Bestellungen bleiben erhalten. Die 536 Shopify-Bestellungen mit dem Tag `wix-import` werden ohne Duplikate zugeordnet oder als fachliche Klärfälle ausgewiesen. Historische Wix-Referenzen bleiben dauerhaft sichtbar. + +Die zwei bestehenden n8n-Schnittstellen behalten ihre jeweiligen Payload-Verträge. Historische Migrationen senden an keinen der beiden Endpunkte und lösen weder Kundenkommunikation noch Klaviyo, Labels oder Lagerbewegungen aus. + +## 2. Belegte Ausgangslage + +- `sales_order.external_ref` ist heute gleichzeitig externe Wix-Referenz, Direktverkaufsreferenz und die an Excel weitergegebene Bestellnummer. +- `order_source` erlaubt aktuell nur `wix` und `direct`. +- Zahlungsstatus erlaubt aktuell nur `paid`; Fulfillment, Teilrefunds und Shopify-Quellversionen sind nicht eigenständig modelliert. +- Shopify `name` und Shopify Order-GID sind verschiedene Identitäten. Der Stichprobenbestand zeigt beispielsweise `name = #1001`, Tag `wix-import` und die historische Referenz in der Notiz `Migriert von Wix (Bestellnummer 10559)`. +- Der Label-Endpunkt erhält heute den vollständigen Wix-Eingangspayload. Der Excel-Endpunkt erhält ausschliesslich `{"Bestellnummer": ""}`. Die Endpunkte erhalten damit heute bewusst unterschiedliche JSON-Formate. + +## 3. Verbindliche Datenidentitaeten + +| Fachliche Bedeutung | Zielattribut | Regel | +| --- | --- | --- | +| ERP-interne Bestellung | `sales_order.id` | Unveraenderlicher Primärschlüssel. | +| Shopify-Bestellung | `sales_order.shopify_order_gid` | Eindeutige, stabile Shopify-Identität; genau einmal je synchronisierter Shopify-Bestellung. | +| Shopify numerische ID | `sales_order.shopify_order_legacy_id` | Technische Hilfsidentität für API- und Audit-Zwecke; keine fachliche Zuordnung über diese ID. | +| Shopify-Anzeigenummer | `sales_order.shopify_order_name` | Zum Beispiel `#1001`; separate Darstellung, nie als alleinige technische Identität. | +| Historische Wix-Nummer | `sales_order_reference` mit Typ `wix_order_number` | Aus Shopify-Notiz nur über den definierten Parser übernommen und unverändert gespeichert. | +| Bestehende operative Referenz | `sales_order.external_ref` | Bleibt für bestehende Wix- und Direktverkaufsbestellungen unverändert. Für neue Shopify-Bestellungen wird sie einmalig auf die Shopify-Anzeigenummer gesetzt und danach nicht umgedeutet. | + +Eine Zuordnung bestehender ERP-Bestellungen erfolgt ausschliesslich in dieser Reihenfolge: + +1. vorhandene `shopify_order_gid`; +2. eindeutige gespeicherte Wix-Referenz; +3. eindeutig normalisierte historische Wix-Nummer aus der Shopify-Notiz; +4. manuell bestätigter Klärfall. + +Ein Treffer allein über Name, E-Mail-Adresse, Betrag oder Datum ist keine automatische Zuordnung. Fehlt eine eindeutige Referenz, bleibt die Bestellung unverändert und wird als Klärfall protokolliert. + +## 4. Ziel-Datenmodell und Ownership + +Die Erweiterung erfolgt additiv. Bestehende Tabellen werden nicht umbenannt oder geleert. + +### `erp/bestellungen` + +`sales_order` wird um folgende fachliche Shopify-Felder ergänzt: + +- `shopify_order_gid`, `shopify_order_legacy_id`, `shopify_order_name` +- `source_created_at`, `source_updated_at`, `source_closed_at` +- `financial_status`, `fulfillment_status` +- `shopify_cancelled_at`, `shopify_cancel_reason` +- `shopify_tags_version` und `last_shopify_snapshot_id` + +`order_source` wird auf `wix`, `direct` und `shopify` erweitert. Die Status-Constraints werden so erweitert, dass Shopify-Zustände nicht in den bisherigen Einzelwert `paid` oder in einen unzutreffenden Bestellstatus gezwungen werden. + +Neue Owned Tabellen des Bestellmoduls: + +- `sales_order_reference`: mehrere unveränderliche externe Referenzen je Bestellung; eindeutig nach `(source_system, reference_type, reference_value)`. +- `sales_order_sync_state`: fachlicher Projektionstand mit letzter erfolgreicher Shopify-Version, letztem Fehler und Reconciliation-Zeitpunkt. +- `sales_order_tag`: aktuelle Shopify-Tags mit Quelle und Beobachtungszeitpunkt. +- `sales_order_adjustment`: Rabatte und sonstige monetäre Anpassungen auf Bestell- oder Positionsebene. +- `sales_order_refund` und `sales_order_refund_line`: Refunds und ihre Positionen ohne Rückschreiben nach Shopify. +- `sales_order_fulfillment`, `sales_order_fulfillment_line` und `sales_order_tracking`: Fulfillment-, Teilversand- und Trackingdaten. + +`sales_order_line` wird additiv um `shopify_line_item_gid`, `shopify_product_gid`, `shopify_variant_gid`, `sku`, `fulfillable_qty` und `fulfilled_qty` ergänzt. Jede Shopify-Line-Item-GID darf innerhalb einer Shopify-Bestellung nur einmal vorkommen. + +### `erp/kontakte` + +Neue Tabelle `party_external_identity` mit `party_id`, `source_system`, `external_gid` und `observed_at`. Die Shopify-Customer-GID wird ausschliesslich dort geführt. E-Mail bleibt ein Suchmerkmal, aber keine automatische Merge-Regel für unterschiedliche Parties. + +### `erp/artikel-mapping` + +`external_item_alias.source_system` wird um `shopify` erweitert. Shopify-Varianten-GID und Shopify-Produkt-GID werden als explizite externe Aliase gespeichert; SKU oder Titel sind nur kontrollierte Fallback-Suchwerte. Nicht gemappte Varianten erzeugen einen Mapping-Klärfall und keine automatische Lagerbewegung. + +### `erp/import-integration` + +Neue append-only Tabelle `shopify_order_snapshot` für die read-only bezogene Shopify-Rohantwort: Order-GID, API-Version, Payload-Hash, Shopify-`updatedAt`, Abrufzeitpunkt und Rohpayload. Sie ist technischer Nachweis, nicht fachliche Bestellwahrheit. + +`shopify_webhook_event` und `process_runs` bleiben unverändert technische Eingangs- und Laufartefakte. Das Bestellmodul bezieht Snapshots ausschliesslich über die definierte Schnittstelle des Import-Integrationsmoduls, nie durch direkte Fremdtabellenzugriffe. + +### `system` + +Die zwei n8n-Zustellungen werden als getrennte, idempotente Outbox-Deliveries modelliert. Jede Delivery hat Ziel `label` oder `excel`, Vertragsversion, Payload-Hash, Ereignisschlüssel, Versuchszähler und Lieferstatus. Ein Erfolg oder Fehler des einen Endpunkts ändert den Zustand des anderen nicht. + +Das verhindert, dass eine erneute Shopify-Synchronisation einen bereits versendeten Label- oder Excel-Aufruf wiederholt. + +## 5. Shopify-zu-ERP-Projektion + +| Shopify-Bereich | ERP-Ziel | Regel | +| --- | --- | --- | +| Order-ID, Name, Zeitstempel | `sales_order` | GID ist technisch führend; Name ist Anzeige- und n8n-Referenz. | +| Customer und Adressen | `party_external_identity`, `party`, `address` | Shopify-GID verbindet; Adressen werden als Snapshot der Bestellung gespeichert. | +| Line Items, Produkt, Variante, SKU | `sales_order_line`, `external_item_alias` | GID/Variante zuerst, SKU danach, manueller Klärfall zuletzt. | +| Preise, Steuer, Versand, Rabatte | `sales_order`, `sales_order_adjustment` | Shopify-Geldwerte werden mit Währung und Originalbeträgen gespeichert. | +| Finanzstatus und Transaktionen | `sales_order.financial_status` sowie spätere Transaktionsprojektion | Kein Status wird aus einer Annahme als `paid` gesetzt. | +| Cancel/Refund | `sales_order`, Refund-Tabellen | Kein automatischer Lager-Rollback im Initialimport. | +| Fulfillment/Tracking | Fulfillment-Tabellen | Reine Spiegelung; Labelerzeugung ist davon getrennt. | +| Tags und relevante Metafelder | `sales_order_tag`, definierte Metafelder | `wix-import` ist Migrationsklassifikation, kein laufender Auslöser. | + +## 6. n8n-Kompatibilitaetsvertrag + +Die bestehende Semantik wird nicht vereinheitlicht. Beide Zielsysteme behalten ihren bereits produktiv verwendeten Vertrag. + +| Ziel | Transport und Body | Ausloesung | +| --- | --- | --- | +| Label-n8n | HTTP POST, vollständiges Legacy-Bestelldokument mit den bisherigen Feldnamen, inklusive `BestellungNr` und `lineItems` | Nur explizit freigegebene neue operative Shopify-Bestellung; nie historische Migration, Update, Refund oder Reconciliation. | +| Excel-n8n | HTTP POST mit exakt `{"Bestellnummer":""}` | Derselbe explizit freigegebene operative Auslöser; eigene idempotente Delivery. | + +Für den Label-Adapter wird aus der ERP-Projektion ein versionsgebundenes Legacy-Dokument erzeugt. Die bereits vom Wix-Eingang verwendeten Feldnamen bleiben erhalten, insbesondere Bestellnummer, Kunden-, Rechnungs- und Lieferadressfelder, Zahlungs- und Liefermethode, Summen sowie `lineItems` mit Artikelnummer, Titel, Menge und Einzelpreis. + +Vor der Umsetzung wird je Endpoint ein anonymisiertes Payload-Fixture aus einem erfolgreichen bisherigen Lauf archiviert. Dieses Fixture ist der byte- und strukturgenaue Regressionstest. Header, Authentifizierung, Content-Type, Endpoint-URL und Retry-Verhalten bleiben ebenfalls unverändert. + +Falls mit „gleiche Daten“ stattdessen identische JSON-Bodies für beide n8n-Endpunkte gemeint sind, ist das ein separater n8n-API-Change und nicht Teil dieses Konzepts. + +## 7. Prozessgrenzen + +1. `erp/import-integration` nimmt Webhooks an, prüft sie, speichert das technische Ereignis und lädt den Shopify-Snapshot read-only. +2. `erp/bestellungen` übernimmt nur Snapshot-ID beziehungsweise Order-GID, projiziert die Fachdaten, führt die Zuordnung durch und entscheidet fachlich über den Sync-Status. +3. `erp/artikel-mapping` liefert ausschliesslich Artikel- und Varianten-Zuordnung. +4. `erp/lager` wird nur von ausdrücklich definierten fachlichen Folgeschritten aufgerufen; historische Projektionen haben keinen Lager-Effekt. +5. `system` stellt die n8n-Outbox-Deliveries bereit. Adapter lesen die erforderlichen Daten über die Bestellmodul-Schnittstelle und versenden nur den eingefrorenen Vertrags-Payload. + +Die Übergaben enthalten nur stabile IDs. Weder Shopify-Rohpayloads noch vorbereitete Bestell-, Kunden- oder Positionsdaten werden zwischen Modulen übertragen. + +## 8. Migrations- und Betriebsregeln + +- Der Initiallauf verarbeitet ausschliesslich Shopify-Bestellungen mit `wix-import` und läuft read-only gegenüber Shopify. +- Für jede Bestellung wird zuerst ein Snapshot gespeichert, dann eine Zuordnung vorgeschlagen und erst nach Eindeutigkeit fachlich projiziert. +- Migration und Reconciliation erzeugen keine n8n-Deliveries, keine Klaviyo-Ereignisse, keine Labels und keine Kundenmails. +- Ein technischer Fehler beim Shopify-Abruf oder bei der DB-Transaktion beendet den betroffenen Batch hart. Fachliche Einzelprobleme – fehlende Wix-Referenz, Variant-Mapping oder unklare Kundenidentität – werden als Klärfall isoliert und der Batch läuft weiter. +- Wiederholte Verarbeitung derselben Shopify-Version ist idempotent. Eine neuere Shopify-Version aktualisiert nur die Shopify-projizierbaren Felder und überschreibt keine ERP-eigenen Entscheidungen ohne ausdrücklich definierten Fachprozess. +- Ein periodischer Reconciliation-Prozess gleicht alle seit dem letzten erfolgreichen Wasserzeichen geänderten Shopify-Bestellungen ab und kompensiert verpasste Webhooks. + +## 9. Umsetzungsetappen und Abnahme + +1. Datenmodell-Migration und Constraints erstellen; vorher DEV-Entwicklerbackup, danach Schema- und Regressionstests. +2. Snapshot- und Projektion-Schnittstelle im Bestellmodul implementieren; nur Fixtures und read-only Shopify-Tests. +3. Historischen Lauf zunächst als Dry-Run durchführen: 536 Shopify-Bestellungen, Zuordnungsergebnis, Klärfälle, Summen- und Positionsvergleich. +4. Erst nach fachlicher Abnahme die Projektion in kontrollierten Batches ausführen. Dabei bleiben alle n8n-Deliveries gesperrt. +5. n8n-Kompatibilitätsadapter gegen die zwei archivierten Fixtures testen; keine echten n8n-Aufrufe im Test. +6. Laufenden Webhook- und Reconciliation-Sync aktivieren; n8n-Deliveries erst nach separatem End-to-End-Test mit freigegebenem Testauftrag einschalten. + +Abnahme ist erreicht, wenn die Zuordnungsregeln, alle neuen Tabellen und Ownerships, die beiden n8n-Verträge sowie die Auslösekriterien für operative neue Bestellungen fachlich bestätigt sind.