1С Битрикс · 8 мин чтения

CEvent::Send: отправка почтовых событий Битрикса из кода

CEvent::Send - метод в 1С-Битрикс для отправки писем прямо из кода: из обработчика формы, из агента, из вебхука или из кастомного компонента. Я подключаю его каждый раз, когда стандартной привязки почтового события к действию вроде оформления заказа не хватает и письмо нужно отправить по своей логике, например после подтверждения оплаты через вебхук платёжной системы или при смене статуса лида в CRM. В статье разберу, как устроен метод CEvent::Send, какие параметры он принимает, что нужно подготовить в админке перед вызовом и на каких ошибках спотыкаются чаще всего.

Как работает отправка почтовых событий в Битриксе

В основе лежит связка из трёх сущностей. Тип события, EVENT_NAME, это условный идентификатор рассылки, например ORDER_NEW или FEEDBACK_FORM. Шаблон сообщения привязан к конкретному сайту и содержит тему, отправителя и тело письма, где переменные вида #EMAIL# или #NAME# заменяются на реальные значения. И сам вызов CEvent::Send, который передаёт в этот шаблон конкретные данные и запускает отправку.

Письмо не обязательно уходит в тот же момент, когда отработал код. В настройках почты в админке есть опция очереди для email-рассылок: если она включена, письма попадают в таблицу очереди и рассылаются агентом по расписанию, а не мгновенно при вызове. Частоту обработки очереди агентом можно посмотреть и изменить в списке агентов Битрикса, обычно это интервал в несколько минут, поэтому даже при включённой очереди задержка между вызовом кода и фактической отправкой обычно не превышает нескольких минут, а не часов.

Каждый тип события существует в своём экземпляре на каждом сайте отдельно. Если в мультисайтовом проекте письмо должно уходить с трёх разных доменов с разным брендингом, шаблон создаётся для каждого сайта персонально, а не один на всех.

Синтаксис и параметры CEvent::Send

Метод принимает семь параметров, из которых обязательны первые три.

<?php
CEvent::Send(
    $event_name,
    $site_id,
    $arFields,
    $duplicate,
    $message_id,
    $files,
    $language_id
);

Параметр event_name задаёт код типа события из справочника почтовых событий, например ORDER_NEW. Параметр site_id принимает ID сайта строкой или массив ID сайтов, если письмо должно уйти по нескольким шаблонам сразу. Массив arFields содержит данные, которые подставятся в переменные шаблона: ключи массива должны совпадать с именами полей, заданными в типе события. Четвёртый параметр называется duplicate и к профилю пользователя отношения не имеет: он управляет тем, отправлять ли копию письма на адрес, указанный в настройках главного модуля как адрес для дублирования всех исходящих сообщений, по умолчанию Y. Пятый, message_id, это идентификатор почтового шаблона, по которому уйдёт письмо: если он не задан или равен пустой строке, письма отправятся по всем шаблонам, привязанным к типу события и к сайту. Шестой, files, принимает массив идентификаторов файлов, которые используются классом CFile, либо массив абсолютных путей до файлов. Седьмой, language_id, задаёт идентификатор языковой версии.

Метод существует в Битриксе очень давно и исторически вызывался как метод объекта: New CEvent, затем Send. Сейчас в актуальных версиях ядра его вызывают статически, CEvent::Send, оба варианта рабочие, но новый код проще писать через статический вызов без создания лишнего объекта. Возвращает метод идентификатор созданного почтового события, то есть число, а не булево значение. И означает это только то, что событие создано, а не то, что письмо долетело до почтового ящика получателя, это разные вещи и путать их не стоит.

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

Бесплатный материал

🎁 Полезный скрипт в подарок

Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.

Без спама. Отписка в 1 клик.

Как подготовить почтовое событие перед вызовом из кода

Прежде чем звать CEvent::Send, в админке нужно завести две вещи.

Сначала тип события: раздел Настройки, Почта, Почтовые события, вкладка Типы событий. Здесь задаётся код события и список полей, которые он умеет принимать, скажем EMAIL, NAME, ORDER_ID.

Затем шаблон сообщения для нужного сайта: та же секция, вкладка Почтовые события. В шаблоне прописывается тема письма, отправитель, ответный адрес и тело письма с переменными в виде #ORDER_ID# или #NAME#, которые должны буква в букву совпадать с ключами массива arFields в коде. Разойдётся регистр или название поля, и в письме на месте переменной останется пустое место или сам текст плейсхолдера.

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

Вызов CEvent::Send из агентов и вебхуков

CEvent::Send одинаково хорошо работает и в обработчике, который выполняется в рамках обычного запроса, и в фоновом агенте Битрикса. Разница только в контексте: в агенте, который проверяет брошенные корзины раз в час, важно не отправить одно и то же письмо повторно при следующем запуске, поэтому перед вызовом CEvent::Send стоит отмечать заказ или лид отдельным флагом, что письмо уже отправлено, и проверять этот флаг перед следующей отправкой.

В обработчике вебхука, например при подтверждении оплаты от Т‑Банка, письмо логично отправлять сразу после проверки подписи запроса и смены статуса заказа, не откладывая до следующего запуска агента. Здесь же, если процесс завязан на СДЭК или другую службу доставки, тем же вызовом можно отправить письмо с номером трек-кода, как только он появился в заказе.

Если помимо письма нужно продублировать событие в Telegram-бота на aiogram или передать его во внешний сценарий на n8n для дальнейшей маршрутизации, я обычно развожу эти вызовы: CEvent::Send отвечает только за письмо, а уведомление в мессенджер или запрос во внешний сервис уходит отдельным вызовом сразу после него, чтобы падение одного канала не тянуло за собой другой.

Примеры отправки писем через CEvent::Send

Базовая отправка после обработки формы обратной связи выглядит так.

<?php
$arEventFields = array(
    "EMAIL" => $email,
    "NAME" => $name,
    "MESSAGE" => $message,
);

CEvent::Send("FEEDBACK_FORM", SITE_ID, $arEventFields);

Отправка с вложением, например с готовым PDF-отчётом, требует предварительно сохранить файл через CFile и передать его ID шестым параметром, а пятый, message_id, оставить пустой строкой.

<?php
$file = CFile::MakeFileArray($_SERVER["DOCUMENT_ROOT"]."/upload/reports/report.pdf");
$fileID = CFile::SaveFile($file, "mail_attachments");

$arEventFields = array(
    "EMAIL" => $email,
    "NAME" => $name,
);

CEvent::Send("REPORT_READY", SITE_ID, $arEventFields, "N", "", array($fileID));

Такой вызов встраивается в любую точку кода: в обработчик вебхука от Т‑Банка после подтверждения оплаты, в агент, который раз в час проверяет статусы заказов, или в обработчик события OnSaleOrderSaved.

Если письмо должно завязываться на нестандартную бизнес-логику, которой в модулях Битрикса из коробки нет, такими доработками занимается доработка и поддержка 1С-Битрикс, у меня эта услуга стоит от 3 000 рублей в час.

Типичные ошибки при работе с CEvent::Send

Почти все проблемы с CEvent::Send сводятся к рассинхрону между кодом и настройками в админке, а не к ошибкам самого метода.

  • Нет шаблона для нужного сайта. Метод отработает без исключений, но письмо никуда не уйдёт, потому что Битрикс просто не найдёт активный шаблон, привязанный к переданному site_id.
  • Поля в arFields не совпадают с переменными в шаблоне. Если в коде передано ORDER_NUM, а в шаблоне написано ORDER_ID, в письме останется пустое место или буквально текст плейсхолдера.
  • Передан не тот site_id. В мультисайтовых проектах легко перепутать константу SITE_ID с ID сайта, для которого реально создан шаблон, особенно если код вызывается из общего модуля, подключённого сразу к нескольким сайтам.
  • Шаблон настроен как обычный текст, а тело письма содержит HTML-разметку. Тогда получатель увидит теги вместо оформленного письма.
  • Отправка больших вложений при выключенной очереди. Без очереди файл готовится и уходит синхронно в момент вызова CEvent::Send, и если вложение тяжёлое, пользователь на фронте ждёт ответ от сервера дольше, чем нужно.
  • Повторная отправка из-за отсутствия защиты от дублей. Если вызов CEvent::Send стоит внутри обработчика, который может сработать дважды на одном и том же событии, скажем, вебхук от платёжной системы прилетел повторно, письмо уйдёт получателю два раза. Спасает простая проверка статуса или отдельного флага перед вызовом.
  • Пустой EMAIL в arFields. Метод не проверяет валидность адреса и не бросает ошибку на пустое значение, он просто передаст пустую строку в шаблон, и письмо в итоге может уйти в никуда или вовсе не сформироваться корректно на уровне почтового сервера.

Как проверить, что письмо действительно отправлено

Первым делом смотрю историю самого события в админке: в разделе Почтовые события у каждого типа события есть список отправленных писем с датой постановки в очередь и датой фактической отправки. Если запись есть, а письмо не дошло, дело почти всегда в почтовом сервере хостинга или в том, что письмо осело в спаме у получателя.

Второй шаг, если записи в истории вообще нет, вернуться к предыдущему разделу и проверить, что шаблон для нужного типа события и сайта существует и активен. Метод CEvent::Send не бросает исключение и не пишет ошибку в лог, если подходящего шаблона просто нет, он молча ничего не отправляет.

И третий шаг, актуальный на shared-хостинге, проверить настройки исходящей почты сервера: SPF и DKIM для домена отправителя. Без них письма от корректно настроенного почтового события всё равно будут падать в спам у Яндекса и Mail.ru, и это уже не имеет отношения к коду.

Проверять доставку удобно на реальном почтовом ящике на стороннем домене, а не только на корпоративной почте: некоторые почтовые серверы внутри организации доверяют своему же домену и пропускают письма, которые у обычного получателя на Яндексе или Gmail улетят в спам из-за не настроенных SPF и DKIM.

1С-Битрикс регулярно обновляется, поэтому перед внедрением я сверяюсь с актуальной документацией.

Частые вопросы

В чём разница между CEvent::Send и функцией mail() в PHP

mail() отправляет письмо сразу с тем текстом, что передан в аргументах, без шаблонов и без очереди. CEvent::Send работает через связку тип события плюс шаблон, привязанный к сайту: текст письма редактируется в админке без правок кода, а сама отправка может уйти в очередь и обрабатываться агентом по расписанию, что удобнее при массовых рассылках.

Можно ли отправить письмо без создания шаблона в админке

Нет. Если для переданных event_name и site_id не найден активный шаблон сообщения, CEvent::Send просто ничего не отправит и не покажет ошибку. Шаблон обязателен для каждой пары события и сайта.

Как отправить письмо с вложением через CEvent::Send

Файл сначала сохраняется в файловую систему Битрикса через CFile::SaveFile, полученный ID передаётся массивом в шестой параметр files, а пятый параметр message_id при этом остаётся пустой строкой. Сам файл в шаблоне никак не описывается, вложение крепится автоматически при отправке.

Почему CEvent::Send не сообщает об ошибке, а письмо не приходит

Чаще всего это несовпадение site_id с сайтом, для которого создан шаблон, отсутствие самого шаблона или проблема на стороне почтового сервера хостинга уже после того, как письмо поставлено в очередь. Метод подтверждает только то, что почтовое событие создано, а не факт доставки получателю.

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно