Dokumentiere Shopify Datenmodell und n8n Vertrag
This commit is contained in:
@@ -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": "<external_ref>"}`. 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":"<external_ref>"}` | 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.
|
||||||
Reference in New Issue
Block a user