Bitrix Sale: события OnSaleOrderSaved, OnSaleOrderBeforeSaved и OnSaleOrderEntitySaved
Текст по мотивам шпаргалки по событиям сохранения заказа. Три точки входа дополняют (но не заменяют) события на изменение отдельных полей заказа и связанных сущностей.
OnSaleOrderSaved — после записи
Параметр IS_NEW позволяет выполнить код только при первом появлении заказа в базе. ENTITY — объект \Bitrix\Sale\Order.
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'sale',
'OnSaleOrderSaved',
[\Acme\Sale\OrderSavedPulse::class, 'onSaved']
);
final class OrderSavedPulse
{
public static function onSaved(\Bitrix\Main\Event $signal): void
{
if (!$signal->getParameter('IS_NEW')) {
return;
}
$purchase = $signal->getParameter('ENTITY');
/* логика для нового заказа */
}
}
OnSaleOrderBeforeSaved — до записи, можно отменить
Чтобы запретить создание (например, не хватает обязательных данных), верните EventResult::ERROR. Имеет смысл вешать обработчик с большой сортировкой и просматривать чужие $signal->getResults(), чтобы не перетирать уже зафиксированную ошибку. Для кода «только при создании» отсекайте записи с непустым $order->getId().
public static function onBeforeSaved(\Bitrix\Main\Event $signal)
{
foreach ($signal->getResults() as $prior) {
if ($prior->getType() !== \Bitrix\Main\EventResult::SUCCESS) {
return null;
}
}
$purchase = $signal->getParameter('ENTITY');
if ($purchase->getId()) {
return null;
}
return new \Bitrix\Main\EventResult(
\Bitrix\Main\EventResult::ERROR,
\Bitrix\Sale\ResultError::create(
new \Bitrix\Main\Error('Сообщение для оформления', 'ACME_BLOCK')
)
);
}
Изменить заказ и платёж до первой записи
Типичный пример — пометить заказ и внешний платёж оплаченными при заранее известной оплате (аккуратно с бизнес-правилами и чеками):
$purchase = $signal->getParameter('ENTITY');
if ($purchase->getId()) {
return null;
}
$external = null;
foreach ($purchase->getPaymentCollection() as $onePayment) {
if ((int)$onePayment->getPaymentSystemId() !== (int)\Bitrix\Sale\PaySystem\Manager::getInnerPaySystemId()) {
$external = $onePayment;
break;
}
}
if (!$external) {
return null;
}
$purchase->setField('PAYED', 'Y');
$external->setField('PAID', 'Y');
Заполнить свойство по корзине
В обработчике до сохранения можно пройти позиции корзины, подтянуть данные из инфоблока и записать значение свойства заказа (в примере — список акций в свойство с кодом OFFERS):
if (!\Bitrix\Main\Loader::includeModule('iblock')) {
return;
}
$purchase = $signal->getParameter('ENTITY');
$labels = [];
foreach ($purchase->getBasket() as $line) {
$pid = (int) $line->getProductId();
if ($pid <= 0) {
continue;
}
$rs = \CIBlockElement::GetList(
[],
['=ID' => $pid],
false,
['nTopCount' => 1],
['ID', 'IBLOCK_ID', 'PROPERTY_SPECIALOFFERS']
);
if (!$el = $rs->GetNextElement()) {
continue;
}
$prop = $el->GetProperty('SPECIALOFFERS');
if (empty($prop['VALUE'])) {
continue;
}
foreach ((array)$prop['VALUE'] as $name) {
if ($name !== '' && !in_array($name, $labels, true)) {
$labels[] = $name;
}
}
}
foreach ($purchase->getPropertyCollection() as $valueRow) {
if ($valueRow->getField('CODE') === 'OFFERS') {
$valueRow->setValue($labels);
break;
}
}
Реакция только на смену поля
Второй параметр «старых значений» — VALUES — сравниваем с текущим полем, чтобы не дублировать работу:
$prior = $signal->getParameter('VALUES') ?? [];
if (!$purchase->getId()) {
return;
}
if (!array_key_exists('DEDUCTED', $prior) || $prior['DEDUCTED'] === $purchase->getField('DEDUCTED')) {
return;
}
/* поле DEDUCTED реально изменилось */
OnSaleOrderEntitySaved
Вызывается после прохождения проверок полей, но до финальной фиксации в рамках цепочки сущности — удобно для последних правок, когда валидация уже прошла.
См. также материалы про оплату заказа, кастомизацию sale.order.ajax и смену статусов — ссылки в разделе интернет-магазина на сайте.
Не хотите копаться сами?
Починю за 1-3 дня. Без предоплаты — оплата по результату.
15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии