Kunde hat den eigentlichen Varianten-Fall präzisiert (28.08.): Kopiert ein
Anwender ein Produkt für eine Aktion / ein neues Bild, verlangt WooCommerce eine
eindeutige SKU. Konvention: Kernnummer + Suffix (100500, 100500-a, 100500-b) —
derselbe physische Artikel, der auf die Kernnummer gebucht werden muss.
Der bestehende Importer macht das bereits korrekt (volle SKU zuerst, sonst
„-"-Split auf die Kernnummer). Da wp_number eine Integer-Spalte ist, kann keine
echte Artikelnummer einen „-" enthalten — dashed SKUs fallen also immer sauber
auf die Kernnummer zurück, eigenständige Nummern treffen ihr eigenes Produkt.
Nur Doku korrigiert: Kommentare im ShoppingUserController und der Testname waren
als „Legacy-Fallback" fehletikettiert — es ist der beabsichtigte Mechanismus für
Shop-Duplikate. Kommentar erklärt jetzt beide Fälle; Test umbenannt auf
„shop-duplikat kernnummer-suffix bucht dasselbe kern-produkt". Keine Logikänderung.
Suite 338 grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bestellungen mit Produktvarianten (z. B. Deocreme MINI) wurden im Versand nicht
automatisch zugeordnet und mussten manuell nachgepflegt werden. Zwei
zusammenwirkende Fehler:
1. Connector (partnercenter-api v1.6.0): compose_cart_content() las per
get_product_id() das Elternprodukt und sendete dessen SKU — nie die eigene
Artikel-Nr. der Variation. Jetzt liest get_product() (variations-aware) die
Variations-SKU, Fallback auf Eltern-SKU nur wenn leer. Plugin muss neu in den
Shop ausgerollt werden.
2. Importer (ShoppingUserController::prepareOrder): matchte nur den Teil vor dem
ersten „-" gegen wp_number. In der Live-DB ist jede Variante eine eigene
products-Zeile mit eigener wp_number (kein main_product_id, keine „-"-Suffixe).
Jetzt Zuordnung primär über die VOLLE SKU gegen wp_number; der „-"-Split bleibt
nur Legacy-Fallback für alte Variations-SKUs „basisnr-suffix".
Tests: neue AP-34-Prüfung (MINI 100026 landet beim MINI-Produkt, nicht beim
50-ml-Basisprodukt 100006) + Legacy-Fallback-Test. Suite 327 grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review 26.08.2026 (Testphase). Änderungen in ShoppingUserController::order():
- Bestellanlage läuft transaktional (DB::transaction um makeShoppingOrder) —
schlägt das Anlegen der Positionen fehl, bleibt keine halbe Bestellung zurück.
- Bestätigungsmail hinter den Commit verschoben und fehlertolerant (try/catch,
Log-Warnung): ein SMTP-Fehler lässt die bereits gespeicherte Übertragung nicht
mehr auf HTTP 500 laufen. Vorher meldete der Shop dann Fehlschlag, und der
Retry scheiterte an der „Bestellung existiert bereits"-Sperre → verwaiste
Bestellung.
- Preis-Abgleich als Integer-Cent (int) round(price*100) statt Gleitkomma-!=,
das sonst falsche „different price"-Meldungen erzeugte (19,99 * 100 = 1998.99…).
- Auskommentierte API-Zugangsdaten (Mail/Passwort) aus dem Quelltext entfernt —
Passwort bitte rotieren, falls je real genutzt.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Shop-Variationen (z. B. „10042-1") sind dasselbe Produkt mit derselben
WP-Nummer. Bei der Bestellannahme zählt für die Zuordnung nur der Teil
vor dem ersten „-"; die Original-SKU bleibt im API-Protokoll erhalten.
Leere Basis (z. B. „-1") wird nicht zugeordnet statt auf 0 zu matchen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
recordTransfer() löste die SKU erneut auf und markierte nur unbekannte
Artikel als Fehler — Zeilen mit gültiger SKU aber fehlender Menge/Preis
wurden verworfen und trotzdem grün protokolliert. prepareOrder() reicht
jetzt included/product_id/product_name durch; failed = Position nicht
übernommen. Entfernt zugleich die N+1-Produkt-Queries je Position.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die Brücke hätte live nicht funktioniert: Connector 1.4.0 sendete die
label_url aus dem per 'deny from all' geschützten DHL-Ordner (403), dazu
SSRF-Risiko durch serverseitigen Abruf einer Client-URL und Timeout-
Mismatch (20 s Download vs. 5 s WordPress-Default). Der Connector liest
die PDF jetzt vom Shop-Dateisystem (realpath-Pinning auf wp-uploads) und
sendet sie Base64; storeWpLabel() validiert und speichert ohne Http::get;
tote Shop-Link-Fallbacks aus den Views entfernt; Connector-Timeout 30 s.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bestell-Übertragung (wp/order) akzeptiert wp_label_url + wp_tracking_number:
Label-PDF wird als lokale Kopie im privaten Storage gespeichert (Shop-URL ist
öffentlich erratbar; Kopie überlebt Löschung im Shop), Sendungsnummer landet
in shopping_orders.tracking. Neuer Endpoint wp/label zum Nachreichen
(Connector-Hook pr_save_dhl_label_tracking), wp/status liefert die
Sendungsnummer an den Shop zurück. Abwicklungs-Board + Bestell-Details zeigen
Label-Download (copyreader-Dokumentroute type=label, Fallback Shop-Link) und
Sendungsnummer. Download-Fehler blockieren die Übertragung nie.
Doku: Connector-Update-Anleitung (dev/product management), Hinweise,
Testleitfaden §11, Plan-Protokoll. pint.json schließt dev/ (Fremdcode) aus.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Neuer Navigationspunkt „API-Bestellungen": jede wp/order-Übertragung wird
protokolliert (api_order_logs) — auch abgelehnte (Kunde unbekannt, Bestellung
existiert bereits, Speicherfehler). Positionen ohne Produktzuordnung
(unbekannte WP-Nummer) markieren den Eintrag rot; das Modal zeigt den Inhalt
der Übertragung je Position (SKU, Produkt, Menge, Preis, Meldung übersetzt).
Dazu SKU-Konsistenzprüfung auf der Seite: doppelte WP-Nummern aktiver Produkte
(Warnung) und aktive Produkte ohne WP-Nummer (aufklappbar). Behebt die stille
Lücke, dass „article not found" nur in der API-Antwort unterging und die
Position kommentarlos in Bestellung und Bestandsabzug fehlte (Plan §4).
ShoppingUserController per Pint formatiert.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>