Technische Eigenschaften (Shopware Property Groups) in ERPNext abbilden und synchronisieren #37
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
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Frappe-Projekte/ecommerce_integrations#37
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?
Ziel
Shopwares natives Eigenschaften-Modul (
property_group/property_group_option, Storefront-Tab "Eigenschaften") in ERPNext abbildbar machen. Konkretes Beispiel aus der Praxis: "Gerbung" mit mehreren gleichzeitig ausgewählten Werten (z. B. "Gerbung zertifiziert nach ISO 9001:2015", "Naturleder", "chromfrei", "vegetabile Nachgerbung").Wichtig: das ist nicht dasselbe wie die varianten-treibenden Optionen aus #14/#19 (
configuratorSettings) — Shopware trennt "Eigenschaften" (filterbar, mehrere Werte gleichzeitig möglich, erzeugen keine Variante) klar von "Konfigurator-Optionen" (erzeugen jeweils eine eigene Variante). Dieses Issue betrifft nur Erstere.Design: zwingend zwei Phasen, in dieser Reihenfolge
Explizite Nutzer-Entscheidung: da in bestehenden Shopware-Shops schon Eigenschaftswerte gepflegt sind, darf ERPNext erst dann zum führenden System werden, wenn es Shopwares bestehende Daten vollständig gespiegelt hat — nicht andersherum. Ein reiner Push (ERPNext → Shopware) von Anfang an würde bestehende Shopware-Werte, die noch nicht in ERPNext erfasst sind, per Full-Replace stillschweigend löschen (siehe Kommentar unten).
Phase 1 — Pull: Shopwares bestehende Eigenschaften nach ERPNext lesen, wiederholbar (kein einmaliges Einweg-Skript)
product.propertieslive aus Shopware lesen (m2m-Relation zuproperty_group_option, inkl. Namensauflösung überassociations: {"properties": {"associations": {"group": {}}}}).Item Ecommerce Property-Zeilen in ERPNext anlegen bzw. aktualisieren — Dedup über (Artikel,property_group,property_value), damit ein wiederholter Lauf keine Duplikate erzeugt.Phase 2 — Push: ERPNext → Shopware, erst NACHDEM Phase 1 für den jeweiligen Artikel abgeschlossen und geprüft ist
properties: [{id: ...}], IDs überget_or_create_property_group()/get_or_create_property_option()(Live-Suche nach Name/GroupId+Name vor Neuanlage, keine Duplikate; Neuanlage nur bei echtem Fehlen, mit deterministischer MD5-Hash-ID).Referenzcode (Marcel-Fork)
Drei zusammenspielende DocTypes:
Ecommerce Property Group(ecommerce_integrations/ecommerce_integrations/doctype/ecommerce_property_group/) — Stammdaten: Eigenschaftsname, Anzeigename DE/EN,filterable-Flag,sync_to_shopware-Flag, Kind-Tabelle mit den möglichen Werten.Ecommerce Property Option— Kind-Tabelle der möglichen Werte je Gruppe (Wert + Anzeige DE/EN).Item Ecommerce Property(Kind-Tabelle aufItem) — eine Zeile pro ausgewähltem Wert. Mehrere Zeilen mit derselbenproperty_group= mehrere gleichzeitig ausgewählte Werte (genau das "Gerbung"-Beispiel), pro Zeile eigenessync_to_shopware-Flag.Push-Logik (Phase 2):
ecommerce_integrations/product_sync/engine/adapters/shopware.py(push_product_properties()/_push_properties_impl()) plusget_or_create_property_group()/get_or_create_property_option()inecommerce_integrations/shopware6/export/property_handler.py.Pull-Logik (Phase 1) muss neu gebaut werden — geprüft, ob Marcels vorhandener
PropertyImporter(shopware6/import_handlers/property_importer.py) das schon löst: nein, der importiert Shopwaresproperty_group/property_group_optionin ERPNextsItem Attribute(das varianten-treibende System aus #14), nicht inItem Ecommerce Property, aus der die Push-Logik liest. Import- und Push-Richtung zielen bei Marcel auf unterschiedliche DocTypes — vermutlich Altlast aus mehreren Überarbeitungsstufen seines eigenen Codes (es existiert zusätzlich noch eine ältere, per SQL migrierteItem Shopware Property-Tabelle). Kein direkt wiederverwendbarer Rückweg gefunden — Phase 1 braucht eigene Import-Logik, analog zu #13s "one-time backfill vor Produktimport"-Muster, aber wiederholbar statt einmalig.Deterministische IDs (kein Sync-/Speicherbedarf):
generate_uuid(name)(shopware6/export/utils.py, MD5-Hash über einen stabilen Namens-String, z. B.f"property_group_{name}") berechnet die Shopware-UUID bei Neuanlage deterministisch — ERPNext muss keine Shopware-IDs speichern oder synchron halten.Zu klären im Umsetzungs-Plan
sync_to_shopwarerelevant,sync_to_medusanicht (kein Medusa-Anschluss in diesem Projekt).Explizit außerhalb des Scopes
Wichtige Ergänzung nach genauerer Prüfung — Pflicht-Vorbedingung für die Umsetzung:
Duplikate sind kein Problem:
get_or_create_property_group()/get_or_create_property_option()(shopware6/export/property_handler.py) suchen bei jedem Push zuerst live in Shopware nach Name (search/property-groupnachname,search/property-group-optionnachgroupId+name) und legen nur an, wenn nichts gefunden wird — ein bereits in Shopware vorhandener Wert wird wiederverwendet, egal ob manuell angelegt oder aus einem früheren Push.Überschreiben ist dagegen ein echtes, ungelöstes Risiko: Der Push (
_push_properties_impl) ist ein Full-Replace (PATCH product/{id}mitproperties: [{id: ...}, ...]) und baut die Liste ausschließlich aus dem, was aktuell in ERPNextsItem Ecommerce Propertysteht. Wenn ein Artikel in Shopware bereits Eigenschaftswerte hat, die noch nicht als Zeilen in ERPNext existieren (z. B. weil sie nie migriert wurden), würde ein Push diese in Shopware stillschweigend löschen.Geprüft, ob Marcels Fork das über einen Rückweg (Shopware → ERPNext) löst: es gibt einen
PropertyImporter(shopware6/import_handlers/property_importer.py), der importiert Shopwaresproperty_group/property_group_optionaber in ERPNextsItem Attribute(das varianten-treibende System aus #14) — nicht inItem Ecommerce Property, aus der der Push tatsächlich liest. Import- und Push-Richtung zielen bei Marcel also auf unterschiedliche DocTypes — vermutlich Altlast aus mehreren Überarbeitungsstufen (es existiert zusätzlich noch eine ältere, per SQL-Migration abgelösteItem Shopware Property-Tabelle). Kein sauberer, fertiger Rückweg gefunden, der das für uns löst.Konsequenz für den Umsetzungs-Plan: Ein einmaliger Rückimport (Shopwares bestehende
product.propertiesje Artikel live auslesen, inkl.associations: {"properties": {"associations": {"group": {}}}}, und alsItem Ecommerce Property-Zeilen in ERPNext anlegen) ist eine Pflicht-Vorbedingung, bevor der Push (ERPNext → Shopware) für einen Artikel aktiviert wird — sonst gehen bestehende Shopware-Daten beim ersten Push verloren. Analog zu #13s "one-time backfill vor Produktimport"-Muster in diesem Projekt.Weitere wichtige Korrektur am Design — Marcels 3-DocType-Ansatz wird NICHT 1:1 übernommen:
Zu Recht angemerkt: Shopware nutzt
property_group/property_group_optionals eine einzige Entität sowohl für Varianten-Optionen (configuratorSettings, #14/#19) als auch für reine, nicht-varianten-treibende Eigenschaften (properties). Ob eine Gruppe bei einem Produkt varianten-treibend ist, hängt nicht an der Gruppe selbst, sondern an der Verwendung durch das jeweilige Produkt — dieselbe Gruppe (z. B. "Farbe") kann bei Artikel A Variante sein und bei Artikel B nur Filter-Eigenschaft.Marcels Ansatz baut dafür zwei komplett getrennte Stammdaten-Systeme (
Item Attributefür Varianten vs.Ecommerce Property Group/Ecommerce Property Optionfür Eigenschaften) — das würde bei uns dazu führen, dass z. B. "Farbe" an zwei Stellen parallel gepflegt werden müsste und auseinanderlaufen könnte, je nachdem ob ein Artikel sie als Variante oder als reine Eigenschaft nutzt.Korrigiertes Design für diesen Fork:
Item Attribute/Item Attribute Value(bereits aus #14 vorhanden) bleibt die einzige Stammdatenquelle für Attributname + mögliche Werte — entspricht 1:1 Shopwaresproperty_group/property_group_option. Keine zweite parallele Stammdaten-Tabelle. Pro Artikel entscheidet nicht die Gruppe, sondern wo sie referenziert wird:Item.attributes(bestehende #14-Kind-Tabelle) → varianten-treibend, läuft über #19s bereits gebautenconfiguratorSettings-Push.Item Ecommerce Property, aber mit Link-Feldern aufItem Attribute/Item Attribute Valuestatt Freitext) → nur Eigenschaft (Shopwaresproperties-Relation, Phase 1/2 wie oben beschrieben).Damit landet ein Attribut nie in zwei getrennten, potenziell widersprüchlichen Stammdatensätzen — nur die Zuordnung (welcher Artikel nutzt es wie) unterscheidet sich, die Werte-Liste bleibt an einer Stelle gepflegt. Bei der Umsetzungsplanung zu klären: exakte Feldtypen der neuen Kind-Tabelle (Link vs. weitere Details), und ob/wie ein Artikel dasselbe Attribut gleichzeitig varianten-treibend UND als sichtbare Eigenschaft führen können soll.