From 3c5ce4413fbc89b8297aa49ae409acbe48a32b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=93=D1=83=D0=BB=D0=BE=D0=BC=D0=BE=D0=B2=20=D0=91=D0=B0?= =?UTF-8?q?=D1=85=D1=82=D0=BE=D0=B2=D0=B0=D1=80?= Date: Sun, 27 Sep 2026 17:49:11 +0500 Subject: [PATCH] fix(delivery): take weight from order products in default delivery cost Delivery::getCost() read the weight from the cart controller guarded by empty($this->ms3->cart). MiniShop3 exposes cart through __get() without __isset(), so the check was always true, the weight stayed 0 and weight_price never affected the delivery cost on checkout, order/cost and submit. The manager recalculation used the real order weight, so the two paths disagreed. The weight is now summed from the order products (weight x count) via OrderService::aggregateProductsTotals(), the same source as ManagerOrderCostRecalculator. --- .../src/Controllers/Delivery/Delivery.php | 28 ++---- .../tests/DeliveryWeightCostTest.php | 91 +++++++++++++++++++ 2 files changed, 97 insertions(+), 22 deletions(-) create mode 100644 core/components/minishop3/tests/DeliveryWeightCostTest.php diff --git a/core/components/minishop3/src/Controllers/Delivery/Delivery.php b/core/components/minishop3/src/Controllers/Delivery/Delivery.php index 14113e3a3..b1147fd70 100644 --- a/core/components/minishop3/src/Controllers/Delivery/Delivery.php +++ b/core/components/minishop3/src/Controllers/Delivery/Delivery.php @@ -6,6 +6,7 @@ use MiniShop3\Model\msDelivery; use MiniShop3\Model\msOrder; use MiniShop3\Services\Order\OrderCostEngine; +use MiniShop3\Services\Order\OrderService; use MODX\Revolution\modX; /** @@ -65,7 +66,7 @@ public function __construct(MiniShop3 $ms3, array $config = []) * Calculate delivery cost (default implementation) * * Standard calculation logic includes: - * - Cost by weight (weight_price * order weight) + * - Cost by weight (weight_price * weight of the order products) * - Free delivery when threshold exceeded (free_delivery_amount) * - Fixed cost or percentage of order amount * @@ -79,32 +80,15 @@ public function __construct(MiniShop3 $ms3, array $config = []) */ public function getCost(msOrder $order, msDelivery $delivery, float $cost): float { - // Get cart data for weight calculation - $cart = [ - 'total_weight' => 0, - 'total_cost' => 0 - ]; - - // Load controllers if not loaded yet - if (empty($this->ms3->cart)) { - $this->ms3->services->load($this->ms3->config['ctx'] ?? 'web'); - } - - // Get cart data if controller is available - if (!empty($this->ms3->cart)) { - $response = $this->ms3->cart->status(); - if ($response['success']) { - $cart = $response['data']; - } - } - - $cartWeight = (float) ($cart['total_weight'] ?? 0); + // Weight of the order's own products (per-unit weight × count). The cart controller + // is not used: outside a storefront session it has no customer token. + $orderWeight = OrderService::aggregateProductsTotals($order->getMany('Products') ?? [])['weight']; return OrderCostEngine::calculateDefaultDeliveryCost( $this->modx, $delivery, $cost, - $cartWeight + $orderWeight ); } diff --git a/core/components/minishop3/tests/DeliveryWeightCostTest.php b/core/components/minishop3/tests/DeliveryWeightCostTest.php new file mode 100644 index 000000000..f1348d55b --- /dev/null +++ b/core/components/minishop3/tests/DeliveryWeightCostTest.php @@ -0,0 +1,91 @@ + 0.000001) { + $fail($case . ': expected ' . $expected . ', got ' . $actual); + } +}; + +$ref = new ReflectionClass(MiniShop3::class); +/** @var MiniShop3 $ms3 */ +$ms3 = $ref->newInstanceWithoutConstructor(); +$ms3->modx = new modX(); + +$delivery = static function (array $fields): msDelivery { + return new class($fields) extends msDelivery { + public function __construct(private array $fields) + { + } + + public function get($key, $format = null, $formatTemplate = null): mixed + { + return $this->fields[$key] ?? null; + } + }; +}; + +$product = static function (float $weight, int $count): msOrderProduct { + return new class(['weight' => $weight, 'count' => $count]) extends msOrderProduct { + public function __construct(private array $fields) + { + } + + public function get($key, $format = null, $formatTemplate = null): mixed + { + return $this->fields[$key] ?? null; + } + }; +}; + +$order = static function (array $products): msOrder { + return new class($products) extends msOrder { + public function __construct(private array $products) + { + } + + public function getMany($alias, $criteria = null, $cacheFlag = true) + { + return $alias === 'Products' ? $this->products : []; + } + }; +}; + +$controller = new DefaultDelivery($ms3); +$post = $delivery(['id' => 4, 'price' => '390', 'weight_price' => 60, 'free_delivery_amount' => 0]); + +// 0.4 kg × 2 + 1 kg × 1 = 1.8 kg → 390 + 60 × 1.8 +$assertFloat(498.0, $controller->getCost($order([$product(0.4, 2), $product(1.0, 1)]), $post, 2000.0), 'weight × count'); +$assertFloat(390.0, $controller->getCost($order([]), $post, 0.0), 'empty order: base price only'); + +$courier = $delivery(['id' => 2, 'price' => '350', 'weight_price' => 60, 'free_delivery_amount' => 5000]); +$assertFloat(0.0, $controller->getCost($order([$product(3.0, 1)]), $courier, 6000.0), 'free delivery threshold wins over weight'); +$assertFloat(530.0, $controller->getCost($order([$product(3.0, 1)]), $courier, 1000.0), 'below threshold: price + weight'); + +$percent = $delivery(['id' => 5, 'price' => '10%', 'weight_price' => 100, 'free_delivery_amount' => 0]); +$assertFloat(150.0, $controller->getCost($order([$product(0.5, 1)]), $percent, 1000.0), 'percent price + weight'); + +echo "OK DeliveryWeightCostTest\n";