Warenwirtschaft: AP-34 — Varianten-Artikelnummer korrekt zuordnen
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>
This commit is contained in:
parent
190713c21b
commit
8c938eed0a
4 changed files with 65 additions and 14 deletions
|
|
@ -722,14 +722,27 @@ class ShoppingUserController extends Controller
|
|||
$error[] = 'article parameter is missing';
|
||||
} else {
|
||||
|
||||
// Variations-SKUs aus dem Shop (z. B. „10042-1") sind dasselbe Produkt:
|
||||
// für die Zuordnung zählt nur der Teil vor dem ersten „-". Das Original
|
||||
// AP-34 (26.08.2026): Jede Produktvariante ist im Shop eine eigene
|
||||
// Bestellposition mit eigener Artikel-Nr. — in der DB eine eigene
|
||||
// products-Zeile mit eigener wp_number (kein main_product_id, keine
|
||||
// „-"-Suffixe). Der Connector sendet ab v1.6 die Variations-SKU statt
|
||||
// der Eltern-SKU. Zuordnung deshalb primär über die VOLLE SKU gegen
|
||||
// wp_number; der frühere „-"-Split bleibt nur optionaler Fallback für
|
||||
// evtl. alte Variations-SKUs im Format „basisnr-suffix". Das Original
|
||||
// bleibt in $order->article, damit das API-Protokoll die echte SKU zeigt.
|
||||
$baseArticle = trim(explode('-', trim((string) $order->article), 2)[0]);
|
||||
$article = trim((string) $order->article);
|
||||
|
||||
$product = $baseArticle !== ''
|
||||
? Product::whereWpNumber($baseArticle)->first()
|
||||
$product = $article !== ''
|
||||
? Product::whereWpNumber($article)->first()
|
||||
: null;
|
||||
|
||||
// Legacy-Fallback: alte Variations-SKU „basisnr-suffix" → Basisnummer
|
||||
if (! $product && str_contains($article, '-')) {
|
||||
$baseArticle = trim(explode('-', $article, 2)[0]);
|
||||
if ($baseArticle !== '') {
|
||||
$product = Product::whereWpNumber($baseArticle)->first();
|
||||
}
|
||||
}
|
||||
if (! $product) {
|
||||
$error[] = 'article not found';
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
/**
|
||||
* Plugin Name: PARTNERCENTER API Connector
|
||||
* Description: Connect WooCommerce to PARTNERCENTER API
|
||||
* Version: 1.5.0
|
||||
* Version: 1.6.0
|
||||
* Author: Cross Media Cloud
|
||||
* Author URI: https://www.cross-media-cloud.de
|
||||
* License: GNU General Public License v2 or later
|
||||
|
|
|
|||
|
|
@ -237,11 +237,21 @@ trait PARTNERCENTER_API_Order
|
|||
// Loop all order items
|
||||
foreach ($this->order->get_items() as $order_item) {
|
||||
|
||||
// Get product ID of order item
|
||||
$product_id = $order_item->get_product_id();
|
||||
// AP-34: Bei Produktvarianten hat jede Variante ihre eigene Artikel-Nr.
|
||||
// (SKU). get_product() liefert für variable Produkte das Variations-Objekt
|
||||
// (nutzt get_variation_id(), sonst get_product_id()), also die richtige
|
||||
// Variations-SKU. Nur wenn an der Variation keine eigene SKU gepflegt ist,
|
||||
// fallen wir auf das Elternprodukt zurück.
|
||||
$product = $order_item->get_product();
|
||||
|
||||
// Get product object
|
||||
$product = wc_get_product($product_id);
|
||||
// Get variation-aware SKU with fallback to the parent product SKU
|
||||
$sku = $product ? $product->get_sku() : '';
|
||||
if ($sku === '') {
|
||||
$parent_product = wc_get_product($order_item->get_product_id());
|
||||
if ($parent_product) {
|
||||
$sku = $parent_product->get_sku();
|
||||
}
|
||||
}
|
||||
|
||||
// Get sum of total and taxes
|
||||
$line_total = $order_item->get_total() + $order_item->get_total_tax();
|
||||
|
|
@ -254,7 +264,7 @@ trait PARTNERCENTER_API_Order
|
|||
|
||||
// Build array item
|
||||
$cart[] = [
|
||||
'article' => $product->get_sku(),
|
||||
'article' => $sku,
|
||||
'qty' => intval($order_item->get_quantity()),
|
||||
'price' => $item_price_converted,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -150,9 +150,37 @@ test('bekannte sku ohne menge wird als fehler markiert (position fehlt in der be
|
|||
->and(ShoppingOrderItem::query()->where('product_id', $product->id)->count())->toBe(0);
|
||||
});
|
||||
|
||||
test('variations-sku wird dem basisprodukt zugeordnet', function () {
|
||||
// Shop-Variationen (z. B. „10042-1") sind dasselbe Produkt: für die
|
||||
// Zuordnung zählt nur der Teil vor dem ersten „-".
|
||||
test('AP-34: jede variante wird über ihre eigene volle SKU dem richtigen produkt zugeordnet', function () {
|
||||
// Ab Connector v1.6 sendet der Shop die Variations-Artikel-Nr. selbst. In der DB
|
||||
// ist jede Variante eine eigene products-Zeile mit eigener wp_number (kein
|
||||
// main_product_id, kein „-"-Suffix). Eine Bestellung der MINI-Variante (100026)
|
||||
// darf NICHT beim 50-ml-Basisprodukt (100006) landen.
|
||||
Mail::fake();
|
||||
Passport::actingAs(apilogMakeUser());
|
||||
$parent = apilogMakeProduct('Deocreme Angekommen 50 ml', 100006);
|
||||
$mini = apilogMakeProduct('Deocreme Angekommen MINI 5 ml', 100026);
|
||||
apilogMakeShoppingUser(4730);
|
||||
|
||||
$this->postJson('https://api.'.config('app.api_domain').'/wp/order', [
|
||||
'wp_order_number' => 4730,
|
||||
'wp_order' => [
|
||||
['article' => 100026, 'qty' => 1, 'price' => 1990],
|
||||
],
|
||||
])->assertOk();
|
||||
|
||||
$log = ApiOrderLog::query()->sole();
|
||||
expect($log->has_errors)->toBeFalse()
|
||||
->and($log->payload['lines'][0]['product_id'])->toBe($mini->id)
|
||||
->and($log->payload['lines'][0]['product_id'])->not->toBe($parent->id)
|
||||
->and($log->payload['lines'][0]['failed'])->toBeFalse();
|
||||
|
||||
expect(ShoppingOrderItem::query()->where('shopping_order_id', $log->shopping_order_id)->where('product_id', $mini->id)->count())->toBe(1)
|
||||
->and(ShoppingOrderItem::query()->where('shopping_order_id', $log->shopping_order_id)->where('product_id', $parent->id)->count())->toBe(0);
|
||||
});
|
||||
|
||||
test('variations-sku im legacy-format „basisnr-suffix" wird als fallback zugeordnet', function () {
|
||||
// Rückwärtskompatibilität: sollte der Shop noch eine alte Variations-SKU im
|
||||
// Format „10042-1" senden, greift der „-"-Split-Fallback auf die Basisnummer.
|
||||
Mail::fake();
|
||||
Passport::actingAs(apilogMakeUser());
|
||||
$product = apilogMakeProduct('Apilog Variante', 10042);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue