MiniShop3
ms3PromoCode — глубоко интегрированный плагин MS3. Эта страница объясняет, как именно компонент встраивается в жизненный цикл заказа и почему сделан именно так.
Учёт скидки в позициях
Главное архитектурное решение: при применении кода скидка распределяется по подпадающим позициям и записывается в msOrderProduct.price (а оригинальная цена сохраняется в properties.ms3promocode.original_price).
Что это даёт
| Что | Как работает |
|---|---|
| Чек 54-ФЗ | ОФД-агенты формируют чек по позициям, считывая msOrderProduct.price — суммы автоматически совпадают с оплаченной |
| Email-уведомления | Стандартные email-шаблоны MS3 показывают price каждой позиции — после применения кода они уценённые |
| Личный кабинет | Аналогично — без правок шаблонов покупатель видит реальные оплаченные цены |
| Аналитика MS3 | msOrder.cart_cost вычисляется как SUM(msOrderProduct.cost) — и в submit, и в finalize заказа |
Альтернатива, которую мы НЕ выбрали
Можно было бы выводить скидку отдельной строкой в итогах:
Товары: 5000 ₽
Скидка SALE10: −500 ₽
Итого: 4500 ₽Так делают крупные маркетплейсы (Ozon, WB, Я.Маркет) — но у них своя фискализация, и расхождение между «общая сумма позиций» и «оплачено» не вызывает проблем.
Для типичного MS3-магазина такой подход потребовал бы:
- Доработки email-шаблонов (показать строку скидки).
- Доработки шаблонов личного кабинета.
- Кастомизации формирования чека ОФД (отдельная позиция-скидка).
Подход «уценка позиций» решает все эти проблемы автоматически, без правки шаблонов.
Точки интеграции
События MS3, на которые подписан плагин
См. Развёрнутый список событий.
Lifecycle применения кода
1. Покупатель заходит в корзину
└─→ Сниппет ms3PromoCodeForm рендерит SSR-форму
2. Покупатель применяет код
└─→ POST /api/v1/promo/apply
└─→ ApplicationService::apply()
├─ ValidationService::validateCode → lifecycle OK
├─ ValidationService::validateForOrder → min_order OK
├─ findMatchingItems (по rules)
├─ DiscountCalculator::calculate → breakdown
├─ applyToLineItems (уценка позиций + properties)
├─ saveToOrder (msOrder.properties.promo_code)
└─ recalculateOrderTotals (cart_cost, cost)
3. Покупатель меняет состав корзины
└─→ msOnAdd/Change/RemoveFromCart
└─→ syncAfterCartChange
├─ restoreLineItems (восстановить оригиналы)
├─ findMatchingItems (на новом составе)
├─ DiscountCalculator (новый breakdown)
└─ applyToLineItems (или remove если стало невалидно)
4. Покупатель оформляет заказ
└─→ msOnCreateOrder
└─→ UsageTracker::recordApplication
├─ INSERT в ms3_promo_code_usages
└─ UPDATE ms3_promo_codes.used_count + 1
5. Менеджер переводит заказ в "Отменён"
└─→ msOnChangeOrderStatus
└─→ UsageTracker::cancelApplication
├─ UPDATE usage SET cancelled_at = NOW()
└─ UPDATE ms3_promo_codes.used_count - 1Менеджер изменяет состав уже созданного заказа
Vue-админка MS3 не вызывает MODX-процессоры при изменении позиций (см. issue #207). Поэтому события msOnCreateOrderProduct / msOnUpdateOrderProduct / msOnRemoveOrderProduct, на которые подписан наш плагин, не срабатывают.
Решение
В order-tab.js установлен JS-перехватчик window.fetch:
- Перехватывает каждый успешный
POST/PUT/DELETEк/api/mgr/orders/{id}/products*. - До возврата response клиенту дёргает
Mgr/Order/Resyncпроцессор. - Vue-админка получает ответ только после нашего
await resync(...)— и затем перезапрашивает данные заказа, видя уже пересчитанные цены.
После мерджа PR в MS3 этот обходной механизм можно убрать — встроенные подписки на события заработают.
Что НЕ модифицирует ms3PromoCode
Чтобы не конфликтовать с другими плагинами и оставаться прозрачным:
- Не модифицирует
msProduct(карточка товара в каталоге остаётся нетронутой). - Не модифицирует
msOrderProduct.options,weight,name— толькоpriceи связанныеcost. - Не модифицирует настройки доставки и оплаты.
- Не подписан на
msOnGetProductPrice— скидки не применяются на уровне отдельного товара вне контекста корзины. - Не подписан на
msOnGetCartCost— это бы дало двойную скидку при модели «уценка позиций».
Совместимость с версиями MS3
| Версия MS3 | Поддержка ms3PromoCode |
|---|---|
| 1.10+ | Полная — фронтенд, новая Vue-админка, манагерская вкладка работают |
| 1.8 – 1.9 | Не тестировалось, но скорее всего работает (минорные изменения схемы) |
| < 1.8 | Не поддерживается |
Для 1.10+ требуется применение PR на события msOn*OrderProduct (#207) для полноценной работы манагерского редактирования заказа без JS-перехватчика. Без PR — работает через JS-перехватчик.
