События корзины - подарки, ограничения, пересчёт
Вмешиваемся в корзину из кода: запрещаем лишнее, добавляем подарки и разбираемся, почему обработчик срабатывает чаще, чем ожидалось.
Решение
Ловим добавление позиции:
use Bitrix\Main\EventManager;
EventManager::getInstance()->addEventHandler('sale', 'OnSaleBasketItemBeforeSaved', ['\\Vendor\\Basket', 'beforeSaved']);// событие срабатывает и при добавлении, и при смене количестваСобытие позиции срабатывает при любом её сохранении в корзине. Добавление, смена количества и пересчёт корзины поднимают его одинаково, поэтому код внутри должен переживать повторные вызовы.
Запрещаем добавление товара:
public static function beforeSaved(\Bitrix\Main\Event $event): \Bitrix\Main\EventResult{ $item = $event->getParameter('ENTITY'); if ($item->getQuantity() > 10) { return new \Bitrix\Main\EventResult(\Bitrix\Main\EventResult::ERROR, [ 'ERROR' => new \Bitrix\Main\Error('Больше десяти штук - оформите заявку'), ]); } return new \Bitrix\Main\EventResult(\Bitrix\Main\EventResult::SUCCESS);}Отказ возвращают именно результатом самого события. Тогда сообщение доходит до покупателя понятным текстом, а не превращается в пустую страницу с ошибкой выполнения.
Кладём подарок в корзину:
$item = $basket->createItem('catalog', $giftId);$item->setFields([ 'QUANTITY' => 1, 'CUSTOM_PRICE' => 'Y', // цену задаём сами 'PRICE' => 0, 'PRODUCT_PROVIDER_CLASS' => \Bitrix\Catalog\Product\Basket::getDefaultProviderName(),]);Подарок кладут со своей ценой и с признаком ручной цены. Без этого признака первый же пересчёт вернёт позиции обычную цену из каталога, и подарок станет платным.
Проверяем пересчёт:
$basket->refresh(); // пересчёт цен и скидокprintf("позиций=%d сумма=%.2f\n", count($basket), $basket->getPrice());// каждый пересчёт снова поднимает события позиций// поэтому добавление подарка проверяют на повтор перед каждой записьюКорзина пересчитывается при каждом её изменении, и обработчик выполняется заново. Код, добавляющий подарок без проверки, положит второй подарок при первом же пересчёте, а потом третий.
Состав корзины стоит проверять ещё раз прямо при оформлении самого заказа. Между добавлением товара и оплатой проходят часы, за которые остаток успевает кончиться, а цена - поменяться после ночного обмена.
Тяжёлые проверки в событиях корзины бьют по каждому клику самого живого покупателя. Запрос к внешней системе внутри обработчика превращает добавление в корзину в ожидание чужого ответа.
Пустую корзину покупателя стоит помнить между заходами. Товары, добавленные с телефона и пропавшие на компьютере, воспринимаются как поломка магазина, хотя это всего лишь разные сеансы одного человека.
Правила, которые можно выразить скидкой или ограничением доставки, лучше и делать ими. Код в обработчике - последнее средство, а не первое: его не видно менеджеру и не поменять без разработчика.
Типичные проблемы
Подарок дублируется в корзине.
Обработчик добавляет его заново при каждом пересчёте корзины. Перед добавлением обязательно проверяют, нет ли этого подарка в корзине уже.
Подарок стал платным.
У позиции не выставлен признак ручной цены. Пересчёт корзины вернул ей обычную цену из каталога.
Покупатель видит пустую страницу вместо запрета.
В обработчике брошено исключение вместо возврата ошибки. Текст сообщения доходит до покупателя только через возвращённый результат этого события.
Добавление в корзину стало медленным.
В обработчике идёт обращение к внешней системе. Оно выполняется на каждое изменение позиции корзины.
Ограничение срабатывает не всегда.
Проверка стоит только в обработчике добавления позиции. Количество меняют и в самой корзине, и при пересчёте.
Частые вопросы
Чем событие корзины отличается от события заказа?
Корзина живёт до оформления и меняется на каждый клик. Событие заказа срабатывает при сохранении оформленного заказа.
Как положить подарок правилом, а не кодом?
Правилом работы с корзиной: у него есть действие с подарком. Код нужен там, где условие не выражается настройками.
Можно ли менять цену позиции из кода?
Да, с признаком ручной цены. Без него правила корзины и пересчёт вернут цену из каталога.
Где проверять остаток перед покупкой?
В событии позиции и повторно при оформлении заказа. Между добавлением в корзину и оплатой товар успевают купить.
Смежное
- Корзина - оглавление подтемы
- Работа с корзиной из кода: добавление, количество, пересчёт - работа с корзиной без событий
- События оформления заказа: свои проверки, запрет и допданные - следующий шаг после корзины
- Правила работы с корзиной: условия, порядок, отладка - скидки и подарки настройками
- Каталог и продажи - устройство магазина целиком