Yandex Metrika
sanches.free 20 просмотров

Дополнительное поле в письме о заказе: OnBeforeAdd почтового события Sale

Задача

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

Где перехватить данные

В главном модуле для сущности почтового события объявлено событие OnBeforeAdd. Подписка идёт на класс \Bitrix\Main\Mail\Internal\Event — в обработчик приходит стандартный объект события сущности с параметром fields, внутри которого лежат EVENT_NAME и уже сформированный массив C_FIELDS с макросами вроде #ORDER_REAL_ID#.

Достаточно отфильтровать нужный тип (в примере — уведомление об отгрузке SALE_ORDER_DELIVERY), дописать свой ключ (например, PICKUP_NOTICE для макроса #PICKUP_NOTICE# в шаблоне), вернуть EventResult с modifyFields — ядро подхватит изменённые C_FIELDS.

Пример: текст с склада в макрос письма

Ниже модуль подключает sale, поднимает заказ по ORDER_REAL_ID, проходит по отгрузкам, берёт первый связанный склад и читает UF‑поле (замените UF_PICKUP_RULES на код из своего инфоблока полей каталога). При отсутствии склада или поля макрос остаётся пустой строкой — шаблон не ломается.

namespace RetailMail;

use Bitrix\Main\Entity\Event as EntityEvent;
use Bitrix\Main\Entity\EventResult;
use Bitrix\Main\EventManager;
use Bitrix\Main\Loader;

EventManager::getInstance()->addEventHandler(
    'main',
    '\Bitrix\Main\Mail\Internal\Event::OnBeforeAdd',
    [DeliveryLetterFields::class, 'onBeforeMailEventAdd']
);

final class DeliveryLetterFields
{
    public static function onBeforeMailEventAdd(EntityEvent $event)
    {
        $fields = $event->getParameter('fields');

        if (($fields['EVENT_NAME'] ?? '') !== 'SALE_ORDER_DELIVERY') {
            return;
        }

        $macros = $fields['C_FIELDS'] ?? [];
        $macros['PICKUP_NOTICE'] = '';

        $orderId = (int) ($macros['ORDER_REAL_ID'] ?? 0);
        if (
            $orderId > 0
            && Loader::includeModule('sale')
            && ($order = \Bitrix\Sale\Order::load($orderId))
        ) {
            foreach ($order->getShipmentCollection() as $shipment) {
                $storeId = (int) $shipment->getStoreId();
                if ($storeId <= 0) {
                    continue;
                }
                $row = \Bitrix\Catalog\StoreTable::getList([
                    'filter' => ['=ID' => $storeId],
                    'select' => ['*', 'UF_PICKUP_RULES'],
                    'limit'  => 1,
                ])->fetch();
                if (!$row) {
                    continue;
                }
                $macros['PICKUP_NOTICE'] = (string) ($row['UF_PICKUP_RULES'] ?? '');
            }
        }

        $result = new EventResult();
        $result->modifyFields(['C_FIELDS' => $macros]);

        return $result;
    }
}

Шаблон в административной части

В типе почтового события с кодом SALE_ORDER_DELIVERY добавьте в тему или тело вставку #PICKUP_NOTICE# (имя поля в C_FIELDS без решёток). Текст подставится только для этого типа письма; для остальных событий обработчик не срабатывает.

На что обратить внимание

  • Пользовательское поле склада должно существовать в БД и быть выбрано в select, иначе значение не приедет.
  • Если у заказа несколько отгрузок со складами, цикл перезаписывает макрос последним найденным — при необходимости соберите строку явно (например, только для отгрузки с самовывозом).
  • Производительность: обработчик выполняется в момент постановки письма; тяжёлые запросы лучше не разворачивать, для отладки смотрите фактическую цепочку вызовов отправки у своей версии sale.

См. также обзор событий почтовой подсистемы в том же сборнике сниппетов.

Не хотите копаться сами?

Починю за 1-3 дня. Без предоплаты — оплата по результату.

15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии