Skip to content

feat(snippet): ms3_cart — итоги из status.total_cost и передача status в чанк#197

Merged
biz87 merged 2 commits intobetafrom
feat/ms3-cart-chunk-status-and-total-cost
Apr 13, 2026
Merged

feat(snippet): ms3_cart — итоги из status.total_cost и передача status в чанк#197
biz87 merged 2 commits intobetafrom
feat/ms3-cart-chunk-status-and-total-cost

Conversation

@Ibochkarev
Copy link
Copy Markdown
Member

@Ibochkarev Ibochkarev commented Apr 13, 2026

Описание

Сниппет ms3_cart после события msOnGetStatusCart может получать в $data['status'] агрегаты (total_cost, total_count и т.д.), уже скорректированные плагинами. При этом сумма по строкам корзины ($total['cost'] из позиций) не всегда совпадает с этим итогом.

Изменения:

  1. Итоги для чанка и return=data: если в status есть числовой total_cost, в total.cost подставляется именно он, а cost_formatted пересчитывается от этого значения. Так подпись «к оплате» в Fenom-чанке совпадает с тем, что отдаёт Cart::get() после плагинов.
  2. Данные для шаблона: в ранние выходы (пустой статус / пустая корзина) и в основной $outputData добавлена передача status, чтобы в чанке ms3_cart (и при return=data) были доступны те же поля статуса, что и при полной корзине — без дублирования запросов к API корзины.

Зачем это нужно

  • Плагины на msOnGetStatusCart часто меняют итоговую сумму (скидки, округления, доставка в превью и т.п.). Раньше чанк мог показывать сумму по строкам, а Cart::get() — уже «официальный» total_cost из статуса; поведение расходилось.
  • Шаблоны корзины иногда должны вывести {$status.total_cost}, флаги доставки или текст из статуса — без полного набора status в Fenom это было невозможно на пустой корзине.

Как использовать

Fenom (чанк корзины, &return=tpl``):

  • Итог к оплате: {$total.cost} / {$total.cost_formatted} — после правки это согласовано с status.total_cost, если он числовой.
  • Доступ к статусу в любом сценарии: {$status.total_cost}, {$status.total_count}, и т.д. (структура та же, что в $data['status'] у сниппета).

JSON / &return=data``:

  • В массиве ответа есть ключ status рядом с total и products.
  • Для интеграций с фронтом используйте total.cost как основной итог для отображения, если полагаетесь на плагины статуса.

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

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

Связанные Issues

NA

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

  • Ручное тестирование
  • Автоматические тесты (PHPStan, ESLint)
  • Тестирование на разных версиях PHP/MODX

Скриншоты (если применимо)

До После

Чеклист

  • Код соответствует стилю проекта
  • Добавлены/обновлены комментарии в сложных местах
  • Изменения не ломают существующую функциональность
  • Лексиконы добавлены на двух языках (ru/en)
  • PHPStan проходит без новых ошибок
  • ESLint проходит без ошибок (для JS/Vue изменений)
  • Обновлён CHANGELOG.md (для значимых изменений)

Дополнительные заметки

  • Обратная совместимость: для чанков добавился ключ status в плейсхолдеры; старые шаблоны, которые его не используют, не ломаются.

…tus to chunk

- After msOnGetStatusCart, data.status.total_cost may differ from the sum of line items; use it for total.cost and cost_formatted when numeric.
- Pass status into Fenom chunk data (and return=data) including empty-cart early exits so templates match cart->get() API.
@Ibochkarev Ibochkarev marked this pull request as ready for review April 13, 2026 08:53
@Ibochkarev Ibochkarev requested a review from biz87 April 13, 2026 08:53
Copy link
Copy Markdown
Member

@biz87 biz87 left a comment

Choose a reason for hiding this comment

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

Ревью

PR решает реальную проблему — рассинхронизация total.cost в чанке с status.total_cost после плагинов. Изменение точечное, обратную совместимость не ломает, описание подробное. Но есть несколько вопросов.


1. Early returns не синхронизируют total.cost с status.total_cost

В early returns (строки 53-64) status теперь передаётся, но логика перезаписи total.cost из status.total_cost есть только в основном пути (после строки ~198).

Если плагин на msOnGetStatusCart выставит total_cost при пустой корзине (например, фиксированная стоимость доставки) — total.cost останется 0, а status.total_cost будет другим. Та же рассинхронизация, которую PR исправляет.

Если это осознанное решение — стоит добавить комментарий. Если нет — нужно применить ту же логику перед early returns.

2. Только total_cost, но не другие агрегаты

Синхронизируется только cost. Если плагины могут корректировать total_count, total_weight, total_discount в статусе — аналогичная проблема остаётся. Стоит либо пояснить, почему только cost, либо покрыть и остальные поля.

3. cost vs discount — противоречие

Если total_cost из статуса уже включает скидку/доставку, то total.discount (посчитанный по строкам) становится несогласованным: cost != sum(products) - discount. Разработчики чанков могут получить противоречивые данные.

4. Мутация $outputData после создания

Массив создаётся, а потом total.cost перезаписывается отдельно. Читаемее было бы сделать перезапись до формирования $outputData:

if (isset($status['total_cost']) && is_numeric($status['total_cost'])) {
    $total['cost'] = (float) $status['total_cost'];
}

$outputData = [
    'total' => $total,
    'products' => $products,
    'status' => $status,
];

$outputData['total']['cost_formatted'] = $ms3->format->price($outputData['total']['cost'], true);

Линейный data flow без промежуточной мутации.


В целом направление правильное, но хотелось бы услышать мнение по пунктам 1-3 перед мержем.

…near outputData

- Apply status totals (cost/count/weight/discount/positions) before early returns
  so plugins can set e.g. total_cost on empty cart (review #197)
- Document partial-plugin override vs line-level discount
- Format cost/weight for chunk on all paths; build outputData without post-mutation
- CHANGELOG: note #197 follow-up
@Ibochkarev
Copy link
Copy Markdown
Member Author

@biz87 Спасибо за ревью. Учёл все пункты во втором коммите: на ранних выходах перед return вызывается та же синхронизация $total из $status, что и после цикла по корзине; в маппинг добавлены все числовые агрегаты из CartItemManager::calculateStatus() (total_cost, total_count, total_weight, total_discount, total_positions). Сборка $outputData линейная — сначала финальный $total и форматирование, потом массив без последующей мутации. По расхождению cost и discount при частичной подмене статуса плагином добавлен комментарий у замыкания: для шапки корзины каноничны поля из $status, по строкам — продуктовые поля.

CHANGELOG обновлён краткой строкой про #197. Если что-то ещё покажется спорным — напиши, поправлю.

@Ibochkarev Ibochkarev requested a review from biz87 April 13, 2026 09:14
Copy link
Copy Markdown
Member

@biz87 biz87 left a comment

Choose a reason for hiding this comment

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

Все замечания из предыдущего ревью учтены:

  1. Early returns$applyStatusToTotal и $formatTotalForDisplay вызываются перед обоими ранними выходами
  2. Все агрегаты — покрыты total_cost, total_count, total_weight, total_discount, total_positions через маппинг с правильными типами (int/float)
  3. cost vs discount — добавлен docblock, явно объясняющий, что status-поля каноничны для header totals, а row-level discount_* остаётся per-position
  4. Нет мутации $outputData$total модифицируется до сборки $outputData, data flow линейный

Код чистый: замыкания static, маппинг расширяем, is_numeric() проверка на месте, CHANGELOG обновлён.

LGTM 👍

@biz87 biz87 merged commit de68c16 into beta Apr 13, 2026
@Ibochkarev Ibochkarev deleted the feat/ms3-cart-chunk-status-and-total-cost branch April 13, 2026 10:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants