Skip to content

Цена доставки за вес не учитывалась при оформлении заказа - #810

Open
GulomovCreative wants to merge 1 commit into
modx-pro:betafrom
GulomovCreative:fix/delivery-weight-cost
Open

GulomovCreative wants to merge 1 commit into
modx-pro:betafrom
GulomovCreative:fix/delivery-weight-cost

Conversation

@GulomovCreative

Copy link
Copy Markdown
Member

Описание

Цена доставки за единицу веса (weight_price) не учитывалась при оформлении заказа, в /api/v1/order/cost, /api/v1/order/cost/delivery и Order::submit(): в заказ записывалась стоимость без веса.

Delivery::getCost() брал вес из контроллера корзины под проверкой empty($this->ms3->cart). У MiniShop3 свойство cart отдаётся через __get(), а __isset() нет, поэтому empty() всегда возвращал true, блок с cart->status() не выполнялся и вес оставался 0. При этом пересчёт в менеджере (ManagerOrderCostRecalculator) передаёт реальный вес заказа, и суммы на витрине и после пересчёта в админке расходились.

Теперь вес считается по товарам самого заказа — вес × количество через OrderService::aggregateProductsTotals(), так же, как в ManagerOrderCostRecalculator. Расчёт больше не зависит от того, инициализирован ли контроллер корзины токеном покупателя (Web API, программное создание заказа).

msOrderProduct.weight — вес одной штуки: так его записывает CartItemManager и так же суммируют CartItemManager::getStatus() и OrderService::getOrderStatistics().

Пример: доставка price = 390, weight_price = 60, в корзине товар весом 0,4 кг — было delivery_cost = 390, стало 414.

Тип изменений

  • Исправление бага (non-breaking change)
  • Новая функциональность (non-breaking change)
  • Breaking change (изменение, ломающее обратную совместимость)
  • Рефакторинг (без изменения функциональности)
  • Документация
  • Другое (опишите):

Связанные Issues

—

Как это было протестировано?

php core/components/minishop3/tests/DeliveryWeightCostTest.php
# OK DeliveryWeightCostTest (на beta тест падает: вес не доходит до расчёта)

cd core/components/minishop3
php tests/run-smoke.php
# все smoke-тесты OK, как и на beta

phpunit --testsuite Unit
# Tests: 609, Assertions: 2179 — OK, как и на beta

Вручную на MODX 3.2.4 + MiniShop3 1.14.1-beta2: доставка 390 + 60 ₽/кг, в корзине 0,4 кг — order/cost вернул delivery_cost = 414 (без исправления — 390). Порог free_delivery_amount по-прежнему обнуляет доставку, процентная цена складывается с весовой.

  • Ручное тестирование
  • Автоматические тесты (composer ci:php / composer test, npm run lint:ci, composer stan / GitHub Actions CI)

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.
@Ibochkarev
Ibochkarev requested a review from biz87 September 27, 2026 12:59
@Ibochkarev Ibochkarev added the bug Something isn't working label Sep 27, 2026

@Ibochkarev Ibochkarev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вердикт: APPROVE

Claim из описания сходится с кодом: MiniShop3 отдаёт cart через __get() без __isset(), поэтому empty($this->ms3->cart) всегда был true, вес оставался нулём и weight_price на витрине молча не применялся. Менеджерский пересчёт шёл другим путём и получал реальный вес, суммы расходились.

Что сделано хорошо

  • Правка удаляет слой, а не перекладывает его. Старый getCost() поднимал cart-контроллер и сессию ради одного числа. Теперь одна строка через канонический OrderService::aggregateProductsTotals(), который уже зовут OrderDraftManager и ManagerOrderCostRecalculator. Метод сжался с ~28 строк с ветвлениями до 6 строк без условий.
  • Слой правильный. Controllers/Delivery здесь доменный провайдер, не HTTP. Зависимость от msOrder и статических хелперов в Services/Order повторяет рисунок Payment::getCost() → OrderCostEngine. Старый вариант лез в $this->ms3->cart через services->load(), это и была утечка границы.
  • BC не сломан. Сигнатура getCost() не менялась, подклассы с собственным расчётом не затронуты. Кто звал parent::getCost(), начнёт получать надбавку за вес. Это и есть фикс.
  • Тест гоняет настоящий путь DefaultDelivery::getCost → aggregateProductsTotals → OrderCostEngine. На beta падает, на ветке проходит. Пять кейсов: вес × количество, пустой заказ, порог бесплатной доставки, процент + вес.
  • Побочный эффект старого кода ушёл: первый empty() всегда вызывал services->load(), второй никогда не доходил до cart->status().

Замечания (не блокеры)

  1. getMany против getIterator (Delivery.php:86). Формула общая с менеджерским пересчётом, источник выборки разный: там getIterator, здесь getMany. Репозиторий уже знает про кэш связей xPDO (CartItemManager::loadItems обходит getMany). На штатных путях checkout и submit это не ломается: фасады Cart и Order держат разные экземпляры msOrder, первый getMany на свежем объекте идёт в БД. Для программного создания заказа getMany даже лучше, потому что видит addMany на несохранённом объекте. Follow-up: общий accessor «товары заказа» рядом с calculateProductTotals, чтобы aggregateProductsTotals никогда не получал кэш связей. Не этот PR.
  2. ?? [] мёртвый (Delivery.php:86). getMany() в xPDO возвращает массив, не null. Та же идиома уже стоит в OrderDraftManager::recalculate:172. Сносить стоит вместе с accessor'ом из пункта 1, не точечно в багфиксе.
  3. Тест закрывает формулу, не выборку (DeliveryWeightCostTest.php:64-74). getMany подменён анонимным классом, реальный путь через xPDO тест не видит. Регрессию «источник снова стал пустым» он не поймает. Для двухфайлового фикса хватает. Интеграцию через SqliteDraftCartHarnessTrait можно добавить отдельно.

Поведенческое предупреждение для релиз-нот

У магазинов с настроенным weight_price доставка на витрине вырастет (пример из PR: 390 → 414 при 0,4 кг и 60 ₽/кг). Это исправление молчаливого бага, но цифра в заказе изменится. Стоит упомянуть при подготовке релиза.

Проверки

php -l ок, smoke 122 ок, PHPUnit 738/2625 (3 падения HeadlessStorefrontCors* одинаковы на beta и ветке, к доставке не относятся). High-находок нет. Мержить можно.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants