
- MODX 3
- PHP 8.2
- miniShop3


ms3PromoCode — один MODX-плагин, который слушает события MiniShop3 и MODX.
| Событие | Когда | Что делает плагин |
|---|---|---|
OnLoadWebDocument | Вывод страницы на фронте | Регистрирует JS/CSS по пресету frontend_assets_preset |
msOnAddToCart | Покупатель добавил товар в корзину | syncAfterCartChange — пересчитать или снять код |
msOnChangeInCart | Изменено количество позиции | То же |
msOnChangeOptionInCart | Изменены или удалены опции уже добавленной позиции | То же |
msOnRemoveFromCart | Удалена позиция из корзины | То же |
msOnCreateOrderProduct | Менеджер добавил позицию в заказ через прежнюю админку | То же (новая Vue-админка использует JS-перехватчик) |
msOnUpdateOrderProduct | То же — изменение позиции | То же |
msOnRemoveOrderProduct | То же — удаление позиции | То же |
msOnCreateOrder | Покупатель оформил заказ | recordApplication — фиксация в ms3_promo_code_usages |
msOnChangeOrderStatus | Изменён статус заказа | Откат или восстановление применения по cancel_statuses |
msOnManagerCustomCssJs | Загрузка страницы редактирования заказа | Подключение order-tab.js — вкладка «Промо-код» |
События изменения корзины (msOnAddToCart, msOnChangeInCart, msOnChangeOptionInCart, msOnRemoveFromCart) и правки позиций заказа из админки (msOnCreateOrderProduct, msOnUpdateOrderProduct, msOnRemoveOrderProduct) идут через ApplicationService::syncAfterCartChange($order):
properties.promo_code нет применённого кода — ничего не делает.apply($order, $code) повторно с тем же кодом. Проверка (жизненный цикл, min_order_amount, правила) запускается заново, уценка пересчитывается по текущему составу корзины. Внутренние шаги apply — на странице Сервисы.apply возвращает ошибку, плагин вызывает remove($order) и снимает применение. Фронтовое событие ms3promocode:auto-removed формирует само headless-ядро — оно сравнивает кэш с ответом GET /api/v1/promo/current после ms3:cart:updated и посылает событие, если код исчез.Сохранённая цена позиции и внешние плагины
Между apply и следующей синхронизацией цену позиции может переписать сторонний плагин — например, ms3Variants при смене опции. Тогда properties.ms3promocode.original_price устаревает: для повторного применения берётся текущая price, а не сохранённое значение. При remove в этой же ситуации сохранённая цена чистится без «восстановления» прежней — текущая остаётся как есть.
При автоматическом снятии кода с заказа (события из админки) в msOrderLog пишется запись:
{
"operation": "ms3promocode_auto_remove",
"reason": "min_order",
"message": "Сумма заказа меньше требуемого минимума (5 000 ₽)"
}msOnGetCartCost — не используется. Сумма корзины уже считается из msOrderProduct.cost (модель «уценка позиций»); вычитать скидку ещё раз из агрегата — двойная скидка.msOnGetProductPrice — не используется. Скидки не применяются к отдельным товарам вне контекста корзины.Headless-ядро на фронтенде посылает на document DOM-события (см. JS API):
| Событие | Когда |
|---|---|
ms3promocode:ready | Headless-ядро инициализировано |
ms3promocode:applied | Код успешно применён |
ms3promocode:removed | Код успешно снят |
ms3promocode:auto-removed | Код автоматически снят при изменении корзины |
ms3promocode:error | Ошибка операции |
Подписка:
document.addEventListener('ms3promocode:applied', (e) => {
console.log(e.detail.code, e.detail.discount);
});Чтобы реагировать на применение кода, подпишитесь на стандартные события MiniShop3 — их данные уже содержат всё нужное.
// elements/plugins/myplugin.php
$eventName = $modx->event->name;
if ($eventName === 'msOnCreateOrder') {
/** @var \MiniShop3\Model\msOrder $order */
$order = $scriptProperties['msOrder'];
$properties = $order->get('properties');
if (!empty($properties['promo_code'])) {
$code = $properties['promo_code']['code'];
$discount = $properties['promo_code']['discount_amount'];
// ... отправить в Telegram, метрику и т.п.
myNotifier()->send("Применён код {$code}, скидка {$discount}");
}
}// Проверка перед применением на уровне плагина
if ($eventName === 'msOnCreateOrder') {
$order = $scriptProperties['msOrder'];
$properties = $order->get('properties');
$code = $properties['promo_code']['code'] ?? null;
if ($code === 'BANNED-CODE') {
// Снять код принудительно
/** @var \ms3PromoCode\Services\ApplicationService $svc */
$svc = $modx->services->get('ms3promocode_application_service');
$svc->remove($order);
}
}Внимание
Своего обработчика перед применением с возможностью отмены ms3PromoCode не даёт — только события после факта. Если нужна отмена до применения — заведите задачу в issue tracker.