Перейти к содержимому

События корзины - подарки, ограничения, пересчёт

Вмешиваемся в корзину из кода: запрещаем лишнее, добавляем подарки и разбираемся, почему обработчик срабатывает чаще, чем ожидалось.

Решение

Ловим добавление позиции:

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());
// каждый пересчёт снова поднимает события позиций
// поэтому добавление подарка проверяют на повтор перед каждой записью

Корзина пересчитывается при каждом её изменении, и обработчик выполняется заново. Код, добавляющий подарок без проверки, положит второй подарок при первом же пересчёте, а потом третий.

Состав корзины стоит проверять ещё раз прямо при оформлении самого заказа. Между добавлением товара и оплатой проходят часы, за которые остаток успевает кончиться, а цена - поменяться после ночного обмена.

Тяжёлые проверки в событиях корзины бьют по каждому клику самого живого покупателя. Запрос к внешней системе внутри обработчика превращает добавление в корзину в ожидание чужого ответа.

Пустую корзину покупателя стоит помнить между заходами. Товары, добавленные с телефона и пропавшие на компьютере, воспринимаются как поломка магазина, хотя это всего лишь разные сеансы одного человека.

Правила, которые можно выразить скидкой или ограничением доставки, лучше и делать ими. Код в обработчике - последнее средство, а не первое: его не видно менеджеру и не поменять без разработчика.

Типичные проблемы

Подарок дублируется в корзине.

Обработчик добавляет его заново при каждом пересчёте корзины. Перед добавлением обязательно проверяют, нет ли этого подарка в корзине уже.

Подарок стал платным.

У позиции не выставлен признак ручной цены. Пересчёт корзины вернул ей обычную цену из каталога.

Покупатель видит пустую страницу вместо запрета.

В обработчике брошено исключение вместо возврата ошибки. Текст сообщения доходит до покупателя только через возвращённый результат этого события.

Добавление в корзину стало медленным.

В обработчике идёт обращение к внешней системе. Оно выполняется на каждое изменение позиции корзины.

Ограничение срабатывает не всегда.

Проверка стоит только в обработчике добавления позиции. Количество меняют и в самой корзине, и при пересчёте.

Частые вопросы

Чем событие корзины отличается от события заказа?

Корзина живёт до оформления и меняется на каждый клик. Событие заказа срабатывает при сохранении оформленного заказа.

Как положить подарок правилом, а не кодом?

Правилом работы с корзиной: у него есть действие с подарком. Код нужен там, где условие не выражается настройками.

Можно ли менять цену позиции из кода?

Да, с признаком ручной цены. Без него правила корзины и пересчёт вернут цену из каталога.

Где проверять остаток перед покупкой?

В событии позиции и повторно при оформлении заказа. Между добавлением в корзину и оплатой товар успевают купить.

Смежное

Первоисточник