Как правильно организовать отправку Email-уведомления при создании нового документа в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Автоматизация уведомлений — одна из самых частых задач в 1С. Казалось бы, что может быть проще: при записи нового документа вызвать процедуру отправки письма. Однако на практике разработчики сталкиваются с множеством проблем: от «зависания» интерфейса пользователя до отправки писем по документам, которые в итоге не были записаны из-за ошибок. В этой статье мы подробно разберем, как реализовать этот механизм профессионально, используя лучшие практики разработки на платформе 1С:Предприятие 8.3.

Почему нельзя просто отправить письмо в процедуре ПриЗаписи?

Начинающие программисты часто пытаются вставить код отправки письма непосредственно в обработчик ПриЗаписи модуля объекта или ПередЗаписьюНаСервере модуля формы. Разберем, почему это является грубой ошибкой:

  1. Блокировка транзакции. Любое действие в процедуре ПриЗаписи выполняется внутри транзакции базы данных. Отправка электронного письма — это сетевая операция, которая может длиться от нескольких секунд до минуты (в зависимости от отклика почтового сервера). Все это время база данных будет удерживать блокировки, а пользователь будет видеть «песочные часы».
  2. Риск отмены записи. Если после кода отправки письма в этой же транзакции произойдет ошибка (например, сработает проверка в другой подписке или возникнет исключение при записи в связанные регистры), транзакция будет откачена. Документ в базе не появится, а письмо пользователю уже уйдет. Это дезинформирует сотрудников.
  3. Отсутствие номера. В процедуре ПередЗаписью у нового документа еще может не быть номера, если используется стандартная автонумерация. А пользователю в письме обычно нужно сообщить именно номер и дату.

Рассмотрим по шагам, как решить эту задачу правильно.

Шаг 1. Определение «нового» документа и использование ДополнительныхСвойств

Главная проблема в том, что в обработчиках, срабатывающих после фиксации транзакции, метод ЭтоНовый() уже всегда возвращает Ложь. Чтобы передать информацию о том, что документ был новым, мы проанализируем ситуацию и используем структуру ДополнительныеСвойства.

В модуле объекта документа пропишем следующий код:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    // Проверяем, является ли объект новым до начала записи
    Если ЭтоНовый() Тогда
        ДополнительныеСвойства.Вставить("ЭтоНовыйОбъектДляУведомления", Истина);
    КонецЕсли;
КонецПроцедуры

Здесь мы создаем временный признак, который будет жить в памяти объекта на протяжении всего цикла записи. Мы также добавили проверку ОбменДанными.Загрузка, чтобы уведомления не спамили при синхронизации данных.

Шаг 2. Использование подписки на события

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

  1. Создадим новую подписку на событие, например, ОтправитьУведомлениеПриЗаписи.
  2. В качестве источника выберем типы документов, которые должны отправлять письма.
  3. Событие выберем ПриЗаписи (оно срабатывает в самом конце транзакции).

В обработчике подписки (в общем модуле) напишем логику:


Процедура ОтправитьУведомлениеПриЗаписиПриЗаписи(Источник, Отказ) Экспорт
    
    // Проверяем наличие нашего флага
    ФлагНового = Ложь;
    Если Источник.ДополнительныеСвойства.Свойство("ЭтоНовыйОбъектДляУведомления", ФлагНового) И ФлагНового Тогда
        
        // Подготавливаем данные для письма
        ПараметрыПисьма = Новый Структура;
        ПараметрыПисьма.Вставить("Номер", Источник.Номер);
        ПараметрыПисьма.Вставить("Дата", Источник.Дата);
        ПараметрыПисьма.Вставить("Автор", Источник.Автор); // Если есть такой реквизит
        
        // ВНИМАНИЕ: Не отправляем письмо здесь напрямую!
        // Передаем задачу во внешний механизм.
        ЗарегистрироватьЗаданиеНаОтправку(Источник.Ссылка, ПараметрыПисьма);
        
    КонецЕсли;

КонецПроцедуры

Шаг 3. Реализация очереди уведомлений (самый надежный способ)

Проанализируем стратегию создания очереди. Чтобы не тормозить работу пользователя, мы не будем обращаться к почтовому серверу немедленно. Мы создадим «задание» в специальном регистре сведений.

Создадим регистр сведений ОчередьУведомлений со следующей структурой:

Теперь процедура ЗарегистрироватьЗаданиеНаОтправку будет просто создавать запись в этом регистре. Это происходит мгновенно в рамках той же транзакции записи документа. Если транзакция документа откатится — запись в регистре тоже не появится. Это гарантирует целостность данных.

Шаг 4. Фоновая отправка через регламентное задание

Теперь выясним, как доставить письмо адресату. Настроим регламентное задание, которое будет раз в минуту проверять наш регистр ОчередьУведомлений.

Пример кода регламентного задания:


Процедура ВыполнитьРассылкуУведомлений() Экспорт
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Очередь.Объект,
        |   Очередь.Идентификатор
        |ИЗ
        |   РегистрСведений.ОчередьУведомлений КАК Очередь
        |ГДЕ
        |   Очередь.Состояние = ЗНАЧЕНИЕ(Перечисление.СостоянияУведомлений.ВОчереди)";
    
    Результат = Запрос.Выполнить().Выбрать();
    
    Пока Результат.Следующий() Цикл
        
        Попытка
            // Вызываем процедуру реальной отправки почты
            ОтправитьПисьмоПоОбъекту(Результат.Объект);
            
            // Помечаем как успешно отправленное
            ЗаписатьСтатусУведомления(Результат.Идентификатор, "Отправлено");
        Исключение
            // Фиксируем ошибку, чтобы администратор мог разобраться
            ЗаписатьСтатусУведомления(Результат.Идентификатор, "Ошибка", ОписаниеОшибки());
        КонецПопытки;
        
    КонецЦикла;
    
КонецПроцедуры

Альтернатива: Мгновенная отправка через ФоновыеЗадания

Если бизнес-процесс требует, чтобы письмо уходило мгновенно (в течение 1-2 секунд), а не через минуту, можно использовать метод ФоновыеЗадания.Выполнить(). Мы запустим процедуру отправки асинхронно сразу после того, как управление вернется в форму документа (событие ПослеЗаписи или ПослеЗаписиНаСервере).

Рассмотрим пример в модуле формы:


&НаСервере
Процедура ПослеЗаписиНаСервере(ПараметрыЗаписи)
    // Если это новый документ, запускаем фоновый поток
    Если ПараметрыЗаписи.Свойство("ЭтоНовыйОбъект") Тогда
        ФоновыеЗадания.Выполнить("МойОбщийМодуль.ОтправитьУведомлениеФоном", 
                                 Новый Массив(Объект.Ссылка));
    КонецЕсли;
КонецПроцедуры

В этом случае пользователь уже может продолжать работу, а система в отдельном фоновом сеансе будет пытаться соединиться с SMTP-сервером и отправить данные.

Использование Библиотеки Стандартных Подсистем (БСП)

Если вы работаете в современной типовой конфигурации (УТ 11, БП 3.0, ERP), мы настоятельно рекомендуем использовать готовую подсистему «Работа с почтовыми сообщениями». Рассмотрим её преимущества:

Проанализируем код с использованием БСП:


// Пример формирования письма через БСП
ПочтовоеСообщение = Справочники.ШаблоныСообщений.СформироватьСообщение(Шаблон, ДокументСсылка);
РаботаСПочтой.ОтправитьПочтовоеСообщение(СистемнаяУчетнаяЗапись, ПочтовоеСообщение);

Подведем итоги

Для качественной реализации отправки уведомлений следуйте этим правилам:

  1. Никогда не отправляйте почту внутри транзакции записи. Это замедляет работу и создает риск неконсистентности данных.
  2. Используйте ДополнительныеСвойства, чтобы передать признак новизны объекта из обработчика ПередЗаписью в последующие этапы.
  3. Используйте Подписки на события. Это сделает ваш код чище и позволит легко масштабировать решение на другие типы документов.
  4. Организуйте очередь. Даже если вы используете фоновые задания, логирование факта отправки в регистр сведений поможет вам отлаживать систему и понимать, почему то или иное письмо не дошло до адресата.
  5. Проверяйте заполнение реквизитов. Перед отправкой убедитесь, что в документе заполнены ключевые поля (например, Сумма или Контрагент), иначе письмо с пустыми данными будет бесполезным.

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

← На главную