Автоматическая рассылка отчетов и уведомлений — одна из частых задач при автоматизации бизнес-процессов. Однако разработчики нередко сталкиваются с ситуацией, когда адресатам приходят абсолютно одинаковые письма-дубликаты. Проанализируем типичную ситуацию, когда рассылка запускается через планировщик задач Windows и командный файл (батник), а отчеты уходят клиентам по два раза.
Рассмотрим случай из практики. Внешняя обработка рассылки вызывается скриптом из командной строки. При открытии формы обработки выполняется формирование отчета и отправка сформированных HTML- и XLS-файлов контрагентам. При этом журнал планировщика показывает одиночный запуск процесса, но контрагенты гарантированно получают по два письма в одно и то же время.
Проанализируем программный код отправки электронной почты. Причиной такого поведения очень часто оказывается банальная опечатка или внимательное копирование строк в процедуре отправки. Посмотрим на фрагмент кода, содержащий ошибку:
// Ошибочный вариант реализации
Почта = Новый ИнтернетПочта;
Попытка
Почта.Подключиться(Профиль);
Исключение
Сообщить("Ошибка подключения к почтовому серверу");
Возврат;
КонецПопытки;
Попытка
Почта.Послать(Сообщение, ОбработкаТекстаИнтернетПочтовогоСообщения.НеОбрабатывать, ПротоколИнтернетПочты.SMTP);
СообщенияОтправки = Почта.Послать(Сообщение, ОбработкаТекстаИнтернетПочтовогоСообщения.НеОбрабатывать, ПротоколИнтернетПочты.SMTP);
Исключение
Сообщить(Получатель.ЭлПочта);
Сообщить(ОписаниеОшибки());
КонецПопытки;
Почта.Отключиться();
Обратим внимание на блок отправки письма. Метод Послать у объекта ИнтернетПочта вызывается дважды подряд. Первая строка отправляет письмо, а следующая за ней строка присваивает результат повторного вызова метода переменной СообщенияОтправки. В результате почтовый сервер принимает два абсолютно идентичных сообщения и доставляет их получателю.
Для устранения дублирования необходимо оставить ровно один вызов метода Послать у объекта ИнтернетПочта. Исправим процедуру отправки писем следующим образом:
// Корректный вариант реализации
Почта = Новый ИнтернетПочта;
Попытка
Почта.Подключиться(Профиль);
Исключение
Сообщить("Ошибка подключения к почтовому серверу");
Сообщить(ОписаниеОшибки());
Возврат;
КонецПопытки;
Попытка
// Выполняем отправку сообщения строго один раз
СообщенияОтправки = Почта.Послать(Сообщение, ОбработкаТекстаИнтернетПочтовогоСообщения.НеОбрабатывать, ПротоколИнтернетПочты.SMTP);
Исключение
Сообщить("Не удалось отправить письмо адресату: " + Получатель.ЭлПочта);
Сообщить(ОписаниеОшибки());
КонецПопытки;
Почта.Отключиться();
Если в коде отправки метод Послать вызывается один раз, но письма все равно дублируются, проанализируем дополнительные технические причины:
250 OK. Если таймаут в объекте ИнтернетПочтовыйПрофиль слишком мал (например, 60 секунд), платформа 1С расценивает связь как разорванную и может совершить повторную попытку. Увеличьте параметр Таймаут до 120–180 секунд.ПриОткрытии и ПриСозданииНаСервере или нажатии кнопок.Чтобы полностью исключить повторные отправки при возможных сбоях связи или перезапусках системы, создадим механизм контроля на базе регистра сведений или файла-флага.
Рассмотрим вариант с проверкой перед отправкой:
// Проверка перед отправкой отчета
ДатаОтправки = НачалоДня(ТекущаяДата());
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ИсторияРассылки.Контрагент
|ИЗ
| РегистрСведений.ИсторияРассылки КАК ИсторияРассылки
|ГДЕ
| ИсторияРассылки.Контрагент = &Контрагент
| И ИсторияРассылки.Период = &ДатаОтправки";
Запрос.УстановитьПараметр("Контрагент", ВыбДилер);
Запрос.УстановитьПараметр("ДатаОтправки", ДатаОтправки);
Если Запрос.Выполнить().Пустой() Тогда
// Выполняем отправку и фиксируем факт отправки в базе
глОтправитьОтчетПоПочте(Таб, ВыбДилер, ТемаОтправки, 0, "file:G:\Bases2\Balance.htm", 2);
ЗафиксироватьФактОтправки(ВыбДилер, ДатаОтправки);
КонецЕсли;
Такой подход гарантирует, что даже при повторном запуске обработки или сбое скрипта письмо конкретному контрагенту будет отправлено строго один раз за выбранный период.