From 22c5490f5307cb0a88f1bebb6230cc9c5ffbf9b4 Mon Sep 17 00:00:00 2001 From: Ivan Bochkarev Date: Thu, 16 Jul 2026 01:00:27 +0600 Subject: [PATCH] feat(order): wire EmptyOrder/GetOrderCost events and set errors (#343) Fire registered msOnBeforeEmptyOrder/msOnEmptyOrder on draft clean and msOnBeforeGetOrderCost/msOnGetOrderCost on getTotalCost. Order::set aggregates per-field add() failures. Draft create stays on SaveOrder; recalculate stays an internal write without inventing new event names. --- .../minishop3/src/Controllers/Order/Order.php | 25 ++++++++++++--- .../Services/Order/OrderCostCalculator.php | 31 +++++++++++++++++++ .../src/Services/Order/OrderDraftManager.php | 28 ++++++++++++----- 3 files changed, 72 insertions(+), 12 deletions(-) diff --git a/core/components/minishop3/src/Controllers/Order/Order.php b/core/components/minishop3/src/Controllers/Order/Order.php index f8e8b5db..b52819d0 100644 --- a/core/components/minishop3/src/Controllers/Order/Order.php +++ b/core/components/minishop3/src/Controllers/Order/Order.php @@ -347,21 +347,33 @@ public function remove(string $key): bool /** * Set multiple order fields at once + * + * No batch msOn*SetOrder events in MS2/MS3: each field goes through add() and + * msOnBeforeAddToOrder / msOnAddToOrder (with returnedValues). This method only + * aggregates per-field failures for the API response. */ public function set(array $order): array { $this->initDraft(); $this->ensureOrderLoaded(); - // TODO: Event before set - // TODO: Collect array of possible validation errors + $errors = []; foreach ($order as $key => $value) { - $this->add($key, $value); + $response = $this->add($key, $value); + if (!$response['success']) { + $errors[$key] = $response['message']; + } } - // TODO: Event on set $this->order = $this->draftManager->toArray($this->draft); + if (!empty($errors)) { + return $this->error('ms3_order_err_validation', [ + 'order' => $this->order, + 'errors' => $errors, + ]); + } + return $this->success('ms3_order_set_success', ['order' => $this->order]); } @@ -401,7 +413,10 @@ public function clean(): array return $this->success('ms3_order_clean_success'); } - $this->draftManager->clean($this->draft); + $result = $this->draftManager->clean($this->draft); + if ($result !== true) { + return $this->error($result !== '' ? $result : 'ms3_err_unknown'); + } return $this->success('ms3_order_clean_success'); } diff --git a/core/components/minishop3/src/Services/Order/OrderCostCalculator.php b/core/components/minishop3/src/Services/Order/OrderCostCalculator.php index b497e978..3f35b30c 100644 --- a/core/components/minishop3/src/Services/Order/OrderCostCalculator.php +++ b/core/components/minishop3/src/Services/Order/OrderCostCalculator.php @@ -227,6 +227,17 @@ public function getTotalCost( string $ctx = 'web', bool $onlyCost = false ): array { + $response = $this->ms3->utils->invokeEvent('msOnBeforeGetOrderCost', [ + 'calculator' => $this, + 'draft' => $draft, + 'cart' => $this->ms3->cart, + 'with_cart' => true, + 'only_cost' => $onlyCost, + ]); + if (!$response['success']) { + return $this->error($response['message']); + } + $cartCostResponse = $this->getCartCost($draft, $token, $ctx); $cartCost = $cartCostResponse['success'] ? $cartCostResponse['data']['cost'] : 0; @@ -245,6 +256,26 @@ public function getTotalCost( (float) $paymentCost ); + $response = $this->ms3->utils->invokeEvent('msOnGetOrderCost', [ + 'calculator' => $this, + 'draft' => $draft, + 'cart' => $this->ms3->cart, + 'with_cart' => true, + 'only_cost' => $onlyCost, + 'cost' => $cost, + 'delivery_cost' => $deliveryCost, + 'cart_cost' => $cartCost, + 'payment_cost' => $paymentCost, + ]); + if (!$response['success']) { + return $this->error($response['message']); + } + + $cost = $response['data']['cost'] ?? $cost; + $deliveryCost = $response['data']['delivery_cost'] ?? $deliveryCost; + $cartCost = $response['data']['cart_cost'] ?? $cartCost; + $paymentCost = $response['data']['payment_cost'] ?? $paymentCost; + if ($onlyCost) { return $this->success('ms3_order_getcost_success', ['cost' => $cost]); } diff --git a/core/components/minishop3/src/Services/Order/OrderDraftManager.php b/core/components/minishop3/src/Services/Order/OrderDraftManager.php index f7450608..3dadf812 100644 --- a/core/components/minishop3/src/Services/Order/OrderDraftManager.php +++ b/core/components/minishop3/src/Services/Order/OrderDraftManager.php @@ -102,7 +102,8 @@ public function createDraft(string $token, string $ctx = 'web'): msOrder ]; $msOrder->fromArray($data); - // TODO: Event before creating draft + // No draft-specific events in MS3 registry: msOrder::save() already fires + // msOnBeforeSaveOrder / msOnSaveOrder with MODE_NEW. $saved = $msOrder->save(); if ($saved) { @@ -113,7 +114,6 @@ public function createDraft(string $token, string $ctx = 'web'): msOrder 'order_id' => $msOrder->get('id') ]); $msOrderAddress->save(); - // TODO: Event after creating draft } return $msOrder; @@ -146,10 +146,13 @@ public function getOrCreateDraft(string $token, string $ctx = 'web', ?int $custo /** * Recalculate order costs (cart, delivery, total) + * + * Persists line aggregates after cart mutations. Cost hooks for plugins live on + * OrderCostCalculator::getTotalCost via msOnBeforeGetOrderCost / msOnGetOrderCost + * (MS2 getCost parity), not on this internal write path. */ public function recalculate(msOrder $draft): void { - // TODO: event before recalculating order $products = $draft->getMany('Products'); $cart_cost = 0; $weight = 0; @@ -166,7 +169,6 @@ public function recalculate(msOrder $draft): void $delivery_cost = (float) $draft->get('delivery_cost'); $cost = $orderService->clampComputedTotal($draft, (float) $cart_cost, $delivery_cost, 0.0); - // TODO: event on recalculating order $draft->set('updatedon', time()); $draft->set('cart_cost', $cart_cost); $draft->set('cost', $cost); @@ -205,10 +207,17 @@ public function toArray(?msOrder $draft): array /** * Clean order data (reset all fields to null) + * + * @return true|string True on success, plugin error message otherwise */ - public function clean(msOrder $draft): bool + public function clean(msOrder $draft): bool|string { - // TODO: Event before clean + $response = $this->ms3->utils->invokeEvent('msOnBeforeEmptyOrder', [ + 'draft' => $draft, + ]); + if (!$response['success']) { + return $response['message']; + } // Clean address fields if ($draft->Address) { @@ -232,7 +241,12 @@ public function clean(msOrder $draft): bool $draft->set('updatedon', time()); $draft->save(); - // TODO: event on clean + $response = $this->ms3->utils->invokeEvent('msOnEmptyOrder', [ + 'draft' => $draft, + ]); + if (!$response['success']) { + return $response['message']; + } return true; }