Как настроить отправку email-уведомления при проведении документа в 1С:ERP?

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

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

Основная проблема заключается в том, что отправка письма — это синхронная сетевая операция. Когда ваш код обращается к почтовому серверу, система ждет от него ответа. Если сервер недоступен, медленно отвечает или есть проблемы с сетью, вся транзакция проведения документа "зависнет" в ожидании. Пользователь в это время не сможет работать, а таблицы базы данных будут заблокированы, мешая работе других сотрудников.

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

Поэтому ключевое правило: любое взаимодействие с внешними системами (почта, HTTP-сервисы и т.д.) должно выполняться асинхронно, то есть вне основной транзакции записи данных. Для решения подобных задач в высоконагруженных системах часто используют специальные конвейеры обработки задач, позволяющие избежать зависаний интерфейса.

Рассмотрим два основных правильных подхода к реализации этой задачи.

Решение 1: Надежный метод через очередь и регламентное задание (Рекомендуемый)

Это классический и самый надежный способ, который гарантирует, что ваше уведомление не потеряется, даже если почтовый сервер временно недоступен или произошел сбой в системе. Суть метода в том, чтобы разделить процесс на два этапа: быструю постановку задачи в очередь и последующую неспешную обработку этой очереди фоновым процессом. Вы можете добавить возможность отправки почтовых сообщений в вашу конфигурацию, используя расширения, чтобы не менять типовые механизмы — для этого подойдёт расширение для триггерных рассылок Email, Telegram и SMS в 1С.

Шаг 1. Создаем очередь сообщений

В качестве очереди будем использовать обычный Регистр сведений. Создадим его в конфигураторе, назовем, например, ОчередьОтправкиУведомлений. Структура может быть следующей:

Шаг 2. Добавляем задачу в очередь при проведении документа

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


Процедура ОбработкаПроведения(Отказ, РежимПроведения)

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

Теперь, как только документ успешно проведется, в нашем регистре появится новая запись со статусом "КОтправке". Транзакция завершится, и пользователь сможет продолжить работу без задержек.

Шаг 3. Создаем обработчик очереди

Для обработки нашей очереди создадим Регламентное задание. Оно будет с заданной периодичностью (например, раз в 5 минут) запускать процедуру, которая проверит очередь и отправит все накопившиеся письма.

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

Шаг 4. Пишем код для отправки писем

В общем модуле (например, УправлениеПочтой) создадим экспортную процедуру, которую будет вызывать наше регламентное задание. Если вы хотите сделать уведомления более информативными, можно отправить HTML таблицу на почту с данными из документа, а также настроить подпись в сообщениях, чтобы письма выглядели официально.


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

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

Решение 2: Быстрый метод через фоновые задания

Этот метод позволяет запустить отправку почти мгновенно после проведения документа, но делает это в отдельном, неинтерактивном сеансе, не "подвешивая" интерфейс пользователя. Однако он менее надежен, чем метод с очередью.

Ключевое отличие: код запуска фонового задания нужно размещать не в модуле объекта, а в модуле формы документа. Это защитит систему от массовых рассылок при групповом перепроведении, так как в этом случае формы документов не открываются.

Шаг 1. Размещаем код запуска в форме документа

В форме документа найдем обработчик события, который выполняется после успешной записи, например, ПослеЗаписиНаСервере.


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

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

Шаг 2. Пишем код для выполнения в фоновом задании

В общем модуле УправлениеПочтой создадим еще одну экспортную процедуру, которую мы указали при вызове.


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

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

Важные дополнительные рекомендации

Независимо от выбранного способа, обязательно учтите следующие моменты:

1. Защита от отправки писем из тестовых баз

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


Если НЕ Константы.РазрешитьОтправкуЭлектроннойПочты.Получить() Тогда
    Возврат; // Не отправляем письма из этой базы
КонецЕсли;

// ... ваш код отправки ...

2. Использование штатного документа ЭлектронноеПисьмоИсходящее

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

3. Шаблонизация текста писем

Не "зашивайте" текст писем прямо в код. Используйте для этого Макеты. Создайте макет с текстом письма и специальными плейсхолдерами (например, [НомерДокумента], [ДатаДокумента]). В коде получайте текст из макета и заменяйте плейсхолдеры реальными данными. Это позволит изменять шаблоны уведомлений без привлечения программиста.

← На главную