Bestandssynchronisation
Wie Flowkom Bestandsänderungen zu Shopify und Amazon pusht — und wie der Sync gegen Silent-Failures abgesichert ist.
Diese Seite ist für Admins und Power-User, die Sync-Drift analysieren oder Logs interpretieren wollen. Für reguläre Nutzung ist sie nicht relevant.
Überblick
Flowkom ist die Source-of-Truth für Bestände. Drei Dinge ändern den verfügbaren Bestand:
- Physischer Stock-Change — Wareneingang, Versand, Umlagerung, manuelle Buchung
- Reservierung — Eine Bestellung im Status
offen,in Bearbeitungodergepicktreserviert ihre Positionen - Bundle-Cascade — Wenn ein Bundle-Bestand sich ändert, ändert sich auch der seiner Komponenten (und umgekehrt)
Jede dieser Änderungen muss zu allen verbundenen Plattformen propagiert werden — sonst überverkauft Shopify oder Amazon Bestand, der eigentlich anderswo reserviert ist.
Architektur
Reservierungs-Modell
Eine Bestellposition zählt als reserviert, solange ihr übergeordneter Auftrag in einem dieser Status ist:
| Status | Reserviert? | Bedeutung |
|---|---|---|
open | ✅ | Auftrag importiert, noch nicht in Bearbeitung |
in_progress | ✅ | In Pickliste eingeplant |
picked | ✅ | Gepickt, wartet auf Versand |
shipped | ❌ | Versendet — Bestand wurde abgebucht |
completed | ❌ | Abgeschlossen |
cancelled | ❌ | Storniert — Bestand ist wieder frei |
Beim Push zu Shopify rechnet Flowkom: available = physischer_stock − reserviert. Amazon erhält den gleichen Wert pro FBM-Listing.
Was triggert einen Sync?
Es gibt zwei parallele Mechanismen, die das Produkt in die Sync-Queue schreiben:
1. DB-Trigger (primär)
Direkt in der PostgreSQL-Transaktion. Kann nicht silent failen — wenn der Trigger fehlschlägt, schlägt auch der Order-Insert/-Update fehl.
| Trigger | Wann | Was wird enqueued |
|---|---|---|
trg_inventory_queue_on_line_item | INSERT/DELETE/UPDATE OF (product_id, quantity) auf order_line_items | betroffenes Produkt, sofern Order in Reservierungs-Status |
trg_inventory_queue_on_order_status | UPDATE OF status auf orders, Übergang in/aus Reservierungs-Set | alle Line-Items der Order |
trg_inventory_queue_on_order_delete | BEFORE DELETE auf orders mit Reservierungs-Status | alle Line-Items vor CASCADE |
Schreibt source = 'reservation' in inventory_sync_queue.
Trigger gibt es nur für die Shopify-Queue. Die Amazon-Queue (amazon_inventory_dirty_queue) wird vom App-Layer befüllt, weil sie pro Eintrag über amazon_listings, bundle_items und amazon_fba_inventory filtern muss — zu komplex für reinen SQL-Trigger.
2. App-Layer-Hooks (Defense-in-Depth)
In allen Order-Lifecycle-Routen wird zusätzlich ein Helper aufgerufen, der beide Queues füllt: Shopify (über markProductsDirty) und Amazon (über enqueueAmazonDirtyBatch). Beide haben einen internen Retry mit 500 ms Backoff.
Wenn der DB-Trigger und der App-Hook beide feuern, fängt das ON CONFLICT (workspace_id, product_id)-Constraint die doppelte Schreibung auf — idempotent.
Reconciliation: Safety-Sync
Selbst wenn beide oben genannten Mechanismen versagen, fängt ein Voll-Abgleich alle Drift ein:
- Shopify:
inventory-safety-syncläuft pro Integration alle 60 Minuten und pusht alle gemappten Produkte komplett neu. Reservierungen werden frisch ausorder_line_itemsberechnet und abgezogen. - Amazon:
amazon-inventory-safety-sync(Status: separate Baustelle) macht Analoges.
Das Intervall ist pro Integration konfigurierbar via integrations.settings.safety_sync_interval_minutes.
Drift-Diagnose
Wenn Shopify einen falschen Bestand zeigt:
- Queue-Eintrag prüfen —
select * from inventory_sync_queue where product_id = '...'. Wenn leer: Trigger und App-Hook haben nicht gegriffen. - Letzten Push prüfen —
select * from integration_sync_logs where sync_type='inventory_push' order by created_at desc limit 5. - Reservierungen prüfen —
select oli.product_id, oli.quantity, o.status from order_line_items oli join orders o on o.id=oli.order_id where oli.product_id='...' and o.status in ('open','in_progress','picked'). - Logs nach
[PROJ-265]-Marker filtern — alle Hook-Failures sind damit getaggt. - Backup-Trigger — manuell
select mark_product_dirty(workspace_id, product_id, 'manual')aufrufen, dann den Sync abwarten.
Quellen
- Spec:
features/PROJ-265-inventory-queue-db-triggers.md(DB-Trigger + Härtung) - Spec:
features/PROJ-235-inventory-sync-reliability.md(App-Hook-Verdrahtung) - Spec:
features/PROJ-55-bestandssync-wms-to-shop.md(Shopify-Sync-Engine) - Spec:
features/PROJ-64-amazon-fbm-bestandssync.md(Amazon-FBM-Sync-Engine)