События OnBeforeSale*SetField в Bitrix: заказ, корзина, отгрузка, оплата
Зачем это нужно
В интернет-магазине на D7 сущности заказа, корзины, отгрузки и оплаты — это объекты с набором полей. Ядро при каждом присвоении значения через штатные сеттеры порождает события до фиксации в хранилище и после неё. Так можно перехватить смену статуса, флагов отгрузки, оплаты или пользовательских свойств и либо скорректировать поведение, либо вернуть понятную ошибку вместо «тихого» сохранения.
Общая схема
Модуль первым аргументом — строка sale. Имена событий строятся по шаблону OnBefore…SetField и On…SetField, где вместо многоточия подставляется имя класса сущности без префикса Sale, например Order, BasketItem, Shipment, ShipmentItem, Payment или PropertyValue (значение свойства заказа).
В обработчик передаётся \Bitrix\Main\Event. Типичные параметры: ENTITY — изменяемый объект, NAME — символьный код поля, VALUE — новое значение. Перед своей логикой полезно просматривать уже существующие EventResult предыдущих обработчиков: если кто-то вернул неуспех, дальше лучше не продолжать.
Пример: запрет финального статуса заказа
Ниже обработчик срабатывает, когда поле STATUS_ID пытаются сменить на «финальный» вариант F. Пока заказ ещё не сохранён в базе, у него нет числового идентификатора — в таком состоянии часто разумно выйти и не выполнять проверки, завязанные на реквизиты заказа в БД.
namespace Acme\Sale;
use Bitrix\Main\Error;
use Bitrix\Main\Event;
use Bitrix\Main\EventManager;
use Bitrix\Main\EventResult;
use Bitrix\Sale\ResultError;
final class StatusSentinel
{
public static function guardTerminalStatus(Event $event)
{
foreach ($event->getResults() as $prior) {
if ($prior->getType() !== EventResult::SUCCESS) {
return;
}
}
/** @var \Bitrix\Sale\Order $order */
$order = $event->getParameter('ENTITY');
$fieldName = (string) $event->getParameter('NAME');
$newValue = $event->getParameter('VALUE');
if (!$order || !$order->getId()) {
return;
}
$current = (string) $order->getField($fieldName);
$replacement = (string) $newValue;
if (
$fieldName === 'STATUS_ID'
&& $current !== 'F'
&& $replacement === 'F'
/* && здесь ваше бизнес-условие */
) {
return new EventResult(
EventResult::ERROR,
ResultError::create(
new Error('Нельзя перевести заказ в финальный статус', 'ACME_ORDER_STATUS')
)
);
}
return;
}
}
// local/php_interface/init.php (после автозагрузки класса)
EventManager::getInstance()->addEventHandler(
'sale',
'OnBeforeSaleOrderSetField',
[StatusSentinel::class, 'guardTerminalStatus']
);Пример: отгрузка — блокировка разрешения доставки или факта списания
Здесь важно брать идентификатор у переменной отгрузки, а не заказа: в оригинальном сниппете по ошибке фигурировала несуществующая в методе переменная $order.
namespace Acme\Sale;
use Bitrix\Main\Error;
use Bitrix\Main\Event;
use Bitrix\Main\EventManager;
use Bitrix\Main\EventResult;
use Bitrix\Sale\ResultError;
final class ShipmentSentinel
{
public static function guardShipmentFlags(Event $event)
{
foreach ($event->getResults() as $prior) {
if ($prior->getType() !== EventResult::SUCCESS) {
return;
}
}
/** @var \Bitrix\Sale\Shipment $shipment */
$shipment = $event->getParameter('ENTITY');
$fieldName = (string) $event->getParameter('NAME');
$newValue = $event->getParameter('VALUE');
if (!$shipment || !$shipment->getId()) {
return;
}
$current = (string) $shipment->getField($fieldName);
$replacement = (string) $newValue;
if (
($fieldName === 'ALLOW_DELIVERY' || $fieldName === 'DEDUCTED')
&& $current === 'N'
&& $replacement === 'Y'
/* && ваше условие */
) {
return new EventResult(
EventResult::ERROR,
ResultError::create(
new Error('Нельзя завершить отгрузку', 'ACME_SHIPMENT_BLOCK')
)
);
}
return;
}
}
EventManager::getInstance()->addEventHandler(
'sale',
'OnBeforeSaleShipmentSetField',
[ShipmentSentinel::class, 'guardShipmentFlags']
);Регистрацию обработчиков оформите в init.php или bootstrap вашего модуля; классы разнесите по автозагрузке PSR-4, чтобы не дублировать статические строки событий по проекту.
См. также (источники идеи)
Не хотите копаться сами?
Починю за 1-3 дня. Без предоплаты — оплата по результату.
15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии