Дополнительное поле в письме о заказе: 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 дней гарантии