Yandex Metrika
sanches.free

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 дней гарантии