Bestellungen aus Shopware als Sales Order in ERPNext importieren #27
Labels
No labels
API
Artikel
B2B
B2C
Bestandssync
Bestellungen
DACH
DocType
Dokumentation
Grundgerüst
Import
Kategorien
Kunden
Lager
Mapping
Mehrsprachigkeit
Performance
Preise
Push
Scheduler
Schweiz
Steuern
Storefront
Sync
Varianten
Verbindung
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Frappe-Projekte/ecommerce_integrations#27
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Shopware-Bestellungen über die Admin API abrufen und als ERPNext Sales Orders anlegen:
GET /api/order(nur neue, noch nicht importierte Bestellungen)shopware6/api/order.pyBeim Recherchieren für #8 (Sales Channel Mapping) ist mir Julians (TubaApollo) separates Plugin aufgefallen:
shopware6-erpnext-webhook— ein eigenständiges Shopware-6-PHP-Plugin (Composer-Paket, MIT-lizenziert, kein Teil dieses Repos), das bei Bestell-Events (order.placed,order.state.changed,order_transaction.state.changed,customer.written) signierte (HMAC-SHA256) Webhooks verschickt.Relevant für dieses Issue: Das Plugin bringt bereits ein fertiges ERPNext-Preset mit, das exakt folgenden Endpoint erwartet:
D.h. Julian hat die Gegenstelle auf unserer Seite schon mitgedacht (Payload-Format
{event, data, timestamp, source}, Signaturprüfung per HMAC-SHA256).Aktuell ist dieses Issue auf Polling (
GET /api/order) gescoped. Bevor wir #27 umsetzen, sollten wir bewusst entscheiden: Polling beibehalten, oder auf dieses Webhook-Plugin als Trigger umsteigen (ggf. auch kombiniert — Webhook für Echtzeit-Import, Polling als Fallback/Backfill). Details dazu auch inProjekt.mdunter "Reference Code → Julian's Webhook Plugin".Keine Entscheidung an dieser Stelle, nur Referenz für später.
Umgesetzt in
shopware6/api/order.py(import_orders()) +shopware6/sync/order.py(sync_orders_hourly()).Architektur-Entscheidung (mit Nutzer abgestimmt): reines Polling, kein Webhook. Abgewogen gegen Marcels bereits validiertes Hybrid-Muster (Webhook + Scheduler-Fallback, siehe sein
order/scheduled_sync.py) und gegen diesen Repos eigenen, rein webhook-getriebenen Shopify-Connector (shopify/order.py) — Shopify nutzt Webhooks, weil Shopifys eigenes App-Modell das verlangt, das ist keine Frappe/ERPNext-Designentscheidung. Polling bleibt näher am Frappe/ERPNext-Standardmuster (scheduler_events, wie schon #21/#22 für den Bestandssync), braucht keine zusätzliche Komponente (Julians separatesshopware6-erpnext-webhook-Plugin, HMAC-Signaturprüfung, öffentlicher Endpoint) und ist von den drei Optionen am robustesten: idempotent, selbstheilend bei einem verpassten Lauf.Kernspannung:
Sales Order.customer/.companysind ERPNext-Pflichtfelder, aber Kundenanlage ist explizit #28s und Storefront-Härtung #29s Scope. Gelöst über das etablierte "fail loud, nie raten"-Prinzip (#18/#20/#25): #27 importiert nur Bestellungen mit eindeutig auflösbarer Storefront (über die bereits bestehendeShopware Storefront, #7) und eindeutig auflösbarem, bereits existierendem Kunden (E-Mail-Abgleich, keine Neuanlage — bleibt #28). Alles andere wird übersprungen und im Error Log dokumentiert, nicht geraten.Design-Details:
shopware_order_idCustom Field aufSales Order— kein eigenes "last sync"-Tracking nötig.taxStatusnetto übernommen oder über dieselbe unrundende Formel aus #18 (gross / (1+tax_rate/100)) umgerechnet.Shopware Account.auto_submit_orders-Feld (Default aus, Nutzerentscheidung): steuert pro Account, ob importierte Bestellungen automatisch eingereicht (docstatus=1) oder als Entwurf angelegt werden.conversion_rate=1bei gleicher Währung, sonst über ERPNext eigeneget_exchange_rate()— nicht auflösbar heißtfailed, nicht stillschweigend1."Shipping"-Item.Live-Fund während der Verifikation: eine echte Testbestellung besteht komplett aus Shopwares Musterbestellungs-Feature — Positionen mit
type: "product"(nicht vom Typ unterscheidbar!), aberpayload.isSample: trueund Preis 0,referencedIdzeigt auf das echte Produkt. #27s bestehende Item-Auflösung verarbeitet das bereits korrekt (Preis 0, kein Crash), aber ob solche Positionen so importiert oder gesondert markiert werden sollen, ist eine offene Frage — als neues, noch nicht eingeplantes Issue #33 festgehalten statt hier inline entschieden.Live verifiziert: Erfolgspfad (korrekte Company/Preisliste/Währung/Kunde/Items/Rates/Versand/Custom Fields) gegen eine konstruierte Test-Storefront + Test-Kunde; zweimaliger Lauf ohne Duplikat (Dedup); alle vier Skip-Pfade (unbekannte Storefront, unbekannter Kunde, storniert, nicht auflösbare Position) mit korrektem Zähler + Error-Log-Eintrag;
auto_submit_orders=0/1beide getestet;taxStatus="net"-Pfad bestätigt. Alle Testdaten gelöscht und Löschung unabhängig bestätigt.Details:
docs/architecture.md→ "Issue 27: Import Shopware orders as Sales Orders — pure polling".