From fd00a851ed93a49e2c97b7d5670fe7cdbb8a6dec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Gla=CC=88ser?= Date: Wed, 12 Aug 2026 17:31:37 +0200 Subject: [PATCH] Erweitere Schema fuer Shopify Delta Import --- .../0010_shopify_delta_order_schema.sql | 68 +++++++++++ .../database/module_db_ownership.md | 1 + ...-integration.shopify_delta_order_import.md | 113 ++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 db/migrations/0010_shopify_delta_order_schema.sql create mode 100644 docs/modules/erp/import-integration/processes/main/erp.import-integration.shopify_delta_order_import.md diff --git a/db/migrations/0010_shopify_delta_order_schema.sql b/db/migrations/0010_shopify_delta_order_schema.sql new file mode 100644 index 0000000..38d6a3f --- /dev/null +++ b/db/migrations/0010_shopify_delta_order_schema.sql @@ -0,0 +1,68 @@ +BEGIN; + +-- Additive Shopify delta-sync identities. Existing Wix and direct orders, +-- their lines and their lot allocations remain unchanged. + +ALTER TABLE sales_order + ADD COLUMN IF NOT EXISTS shopify_order_gid TEXT, + ADD COLUMN IF NOT EXISTS shopify_order_name TEXT, + ADD COLUMN IF NOT EXISTS shopify_created_at TIMESTAMP, + ADD COLUMN IF NOT EXISTS shopify_updated_at TIMESTAMP, + ADD COLUMN IF NOT EXISTS shopify_financial_status TEXT, + ADD COLUMN IF NOT EXISTS shopify_fulfillment_status TEXT; + +ALTER TABLE sales_order_line + ADD COLUMN IF NOT EXISTS shopify_line_item_gid TEXT, + ADD COLUMN IF NOT EXISTS shopify_variant_gid TEXT, + ADD COLUMN IF NOT EXISTS shopify_product_gid TEXT, + ADD COLUMN IF NOT EXISTS sku TEXT; + +ALTER TABLE external_item_alias + ADD COLUMN IF NOT EXISTS shopify_product_gid TEXT, + ADD COLUMN IF NOT EXISTS shopify_variant_gid TEXT; + +CREATE TABLE IF NOT EXISTS party_external_identity ( + id BIGSERIAL PRIMARY KEY, + party_id BIGINT NOT NULL REFERENCES party(id) ON DELETE CASCADE, + source_system TEXT NOT NULL, + external_id TEXT NOT NULL, + observed_at TIMESTAMP NOT NULL DEFAULT NOW(), + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW(), + CONSTRAINT uq_party_external_identity_source_external UNIQUE (source_system, external_id) +); + +ALTER TABLE sales_order + DROP CONSTRAINT IF EXISTS chk_sales_order_source; + +ALTER TABLE sales_order + ADD CONSTRAINT chk_sales_order_source + CHECK (order_source IN ('wix', 'direct', 'shopify')); + +ALTER TABLE sales_order + DROP CONSTRAINT IF EXISTS chk_sales_order_payment_status; + +ALTER TABLE sales_order + ADD CONSTRAINT chk_sales_order_payment_status + CHECK (payment_status IN ('pending', 'authorized', 'paid', 'partially_refunded', 'refunded', 'voided', 'unknown')); + +CREATE UNIQUE INDEX IF NOT EXISTS uq_sales_order_shopify_order_gid + ON sales_order(shopify_order_gid) + WHERE shopify_order_gid IS NOT NULL; + +CREATE UNIQUE INDEX IF NOT EXISTS uq_sales_order_line_shopify_line_item_gid + ON sales_order_line(shopify_line_item_gid) + WHERE shopify_line_item_gid IS NOT NULL; + +CREATE UNIQUE INDEX IF NOT EXISTS uq_external_item_alias_shopify_variant_gid + ON external_item_alias(shopify_variant_gid) + WHERE shopify_variant_gid IS NOT NULL; + +CREATE INDEX IF NOT EXISTS idx_party_external_identity_party + ON party_external_identity(party_id); + +CREATE INDEX IF NOT EXISTS idx_sales_order_shopify_updated_at + ON sales_order(shopify_updated_at DESC) + WHERE shopify_order_gid IS NOT NULL; + +COMMIT; diff --git a/docs/architektur/database/module_db_ownership.md b/docs/architektur/database/module_db_ownership.md index 047e48e..e322cc0 100644 --- a/docs/architektur/database/module_db_ownership.md +++ b/docs/architektur/database/module_db_ownership.md @@ -13,6 +13,7 @@ Diese Referenz ordnet die aktuellen Tabellen, Views und technischen DB-Artefakte - `party` - `address` - `contact` +- `party_external_identity` ### `bestellungen` diff --git a/docs/modules/erp/import-integration/processes/main/erp.import-integration.shopify_delta_order_import.md b/docs/modules/erp/import-integration/processes/main/erp.import-integration.shopify_delta_order_import.md new file mode 100644 index 0000000..4ce81e1 --- /dev/null +++ b/docs/modules/erp/import-integration/processes/main/erp.import-integration.shopify_delta_order_import.md @@ -0,0 +1,113 @@ +# erp.import-integration.shopify_delta_order_import +Stand: 2026-08-12 +Status: Verbindliche Prozess-Spezifikation + +## 1. Zweck + +Read-only-Abgleich von Shopify-Bestellungen nach dem ERP-Stichtag und identifier-only Übergabe qualifizierter Bestellungen an `erp/bestellungen`. + +## 2. Prozess-Einbettung + +Manuell startbarer Initial-Deltalauf und später wiederverwendbarer Reconciliation-Prozess im Submodul `erp/import-integration`. Er ist vom Webhook-Empfang getrennt. + +## 3. Input + +- `cutoff_timestamp` +- optional `dry_run` + +Es werden keine vorbereiteten Shopify-Geschäftsdaten an nachgelagerte Module übergeben. + +## 4. Exakter Prozessablauf + +1. `cutoff_timestamp` und `dry_run` validieren. +2. Read-only Shopify-Bestellungen mit `createdAt` nach dem Stichtag in stabiler Reihenfolge laden. +3. Stornierte oder vollständig erstattete Bestellungen als nicht lagerwirksam klassifizieren und nur technisch protokollieren. +4. Für jede qualifizierte Bestellung die Shopify Order-GID idempotent gegen `sales_order.shopify_order_gid` prüfen. +5. Nur die Shopify Order-GID an die Bestell-Schnittstelle übergeben; diese lädt und projiziert die erforderlichen Shopify-Daten selbst. +6. Bei `dry_run` keine fachlichen DB-Writes, Lagerbewegungen oder externen Aufrufe ausführen. +7. Keine Shopify-Mutation, keine n8n-Zustellung, kein Klaviyo-Ereignis, keine Kundenmail und keine Label-Aktion ausführen. +8. Den technischen Lauf in `process_runs` abschliessen. + +Read sources: + +- Shopify Admin API, ausschliesslich read-only +- `sales_order` für die Idempotenzprüfung +- `shopify_webhook_event` und `process_runs` als technische Eingangs-/Laufdaten + +Write targets: + +- `process_runs` +- technische Ergebnisdaten im owning Submodul +- Bestelldaten ausschliesslich über die Schnittstelle von `erp/bestellungen` + +## 5. Batch, Betriebsmodell und Einbettung + +Der Initiallauf verarbeitet den festen Zeitraum nach dem bestehenden ERP-Stichtag seriell. Spätere Reconciliation-Läufe nutzen denselben Prozess mit einem definierten Wasserzeichen. Keine Parallelisierung. + +## 6. Output + +Kompaktes Ergebnis mit geprüfter, übersprungener, bereits vorhandener und übergebener Bestellanzahl. + +## 7. Erfolgskriterien + +- nur Bestellungen nach dem Stichtag werden berücksichtigt +- stornierte/erstattete Bestellungen sind nicht lagerwirksam +- jede Shopify Order-GID wird höchstens einmal als ERP-Bestellung angelegt +- der Dry-Run verändert keine fachlichen Daten +- n8n, Klaviyo, Mail, Labels und Shopify bleiben unbeeinflusst + +## 8. Fehlerschranke + +Ein Shopify-API- oder technischer DB-Fehler beendet den Lauf hart. Eine fachlich nicht abbildbare Einzelbestellung wird als Klärfall gezählt; weitere Bestellungen werden verarbeitet. + +## 9. Fachliche Betriebsregel + +Die bestehenden 93 ERP-Bestellungen inklusive ihrer Chargen- und Lagerrelationen bleiben erhalten. Nur neuere, qualifizierte Shopify-Bestellungen erzeugen neue Bestellpositionen und Lagerbewegungen. + +## 10. Sub-Prozess-Referenzen + +- `erp.bestellungen.shopify_order_projection` + +## 11. End-to-End Sub-Prozess-Kette + +Delta-Scope bestimmen → Shopify read-only laden → Qualifikation → identifier-only Bestellübergabe → technischer Abschluss. + +## 12. Sub-Prozess-Wiederverwendung + +Der bestehende Shopify-API-Client und die vorhandene technische Laufpersistenz werden wiederverwendet. Die Bestell- und Lagerfachlichkeit bleibt in ihren owning Modulen. + +## 13. Step-Liste + +1. `validate_delta_scope` +2. `load_shopify_delta` +3. `classify_order_for_inventory` +4. `shopify_order_projection` +5. `finalize_delta_run` + +## 14. Parallelisierung + +Nicht anwendbar. Der aktuelle Delta-Scope ist klein und wird seriell, nachvollziehbar verarbeitet. + +## 15. Output-Payload + +```json +{ + "status": "done|partial_success|failed", + "cutoff_timestamp": "ISO-8601 timestamp", + "dry_run": true, + "checked_orders": 0, + "skipped_cancelled_or_refunded": 0, + "already_imported": 0, + "handed_off": 0, + "clarification_order_gids": [], + "technical_run_id": 0 +} +``` + +## 16. Done-Kriterien + +- Stichtag und Qualifikationsregel sind umgesetzt und testbar. +- Shopify-Identitäten sind im Datenmodell eindeutig speicherbar. +- Bestehende Bestellungen und Chargenrelationen bleiben unverändert. +- Der Dry-Run ist nebenwirkungsfrei. +- Externe Kommunikation bleibt in diesem Prozess ausgeschlossen.