При организации автоматической рассылки индивидуальных отчетов, расчетных листов или актов сверки взаиморасчетов разработчики нередко сталкиваются с неприятной ситуацией: адресату уходит письмо с файлом вложения, предназначенным для другого контрагента. Разберем по шагам, почему возникает эта ошибка и как выстроить надежный алгоритм отправки сообщений.
Проанализируем типовой сценарий: в цикле по выборке контрагентов формируется печатная форма, сохраняется во временный файл на диске, прикрепляется к объекту ИнтернетПочтовоеСообщение и отправляется через ИнтернетПочта. В такой схеме существует несколько уязвимых мест:
ТекущаяДата() с форматом до секунд (например, ддммгггг_ЧЧммсс), а итерации цикла выполняются быстрее одной секунды, несколько контрагентов подряд получат один и тот же путь к файлу. Программа либо не успеет перезаписать файл, либо подхватит старую версию из-за дискового кэширования.Очистить() в новый отчет могут попасть данные предыдущего контрагента.Самое быстрое и действенное решение для предотвращения отправки старых файлов — явное удаление сохраненного документа сразу после отправки письма получателю с помощью процедуры УдалитьФайлы(). Рассмотрим пример правильной организации кода:
Пока Рез.Следующий() Цикл
ВыбДилер = Рез.Контрагент;
ТемаОтправки = "Взаиморасчеты по дилеру " + СокрЛП(ВыбДилер.Наименование);
// Формируем отчет
Таб = Новый ТабличныйДокумент;
СформироватьОтчетПоДилеру(Таб, ВыбДилер);
// Сохраняем и отправляем
ИмяФайлаОтчета = КаталогВременныхФайлов() + "Отчет_" + СокрЛП(ВыбДилер.Код) + ".xls";
Таб.Записать(ИмяФайлаОтчета, ТипФайлаТабличногоДокумента.XLS);
Попытка
глОтправитьОтчетПоПочте(ИмяФайлаОтчета, ВыбДилер, ТемаОтправки);
Исключение
ЗаписьЖурналаРегистрации("РассылкаОтчетов", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
КонецПопытки;
// Обязательно удаляем временный файл после отправки
УдалитьФайлы(ИмяФайлаОтчета);
КонецЦикла;
Чтобы избежать совпадения путей к файлам при высокой скорости обработки цикла, откажемся от конструирования имени через ТекущаяДата(). Используем встроенную функцию платформы ПолучитьИмяВременногоФайла() или добавим уникальный идентификатор:
// Вариант 1: Использование платформенного механизма
ИмяФайла = ПолучитьИмяВременногоФайла("xls");
// Вариант 2: Добавление УИД и кода контрагента
ИмяФайла = КаталогВременныхФайлов() + "Отчет_" + СокрЛП(ВыбДилер.Код) + "_" + Строка(Новый УникальныйИдентификатор) + ".xls";
Посмотрим на частую синтаксическую ошибку при отправке массива вложений. В коде автора производилось обращение к переменной массива вместо текущего элемента:
// Ошибочный вариант:
Для каждого МЗнач из ИмяФайла Цикл
Сообщение.Вложения.Добавить(ИмяФайла.ПолноеИмя, ИмяФайла.Имя); // Ошибка: у массива нет свойств ПолноеИмя и Имя
КонецЦикла;
// Исправленный вариант:
Для каждого МЗнач из ИмяФайла Цикл
Сообщение.Вложения.Добавить(МЗнач.ПолноеИмя, МЗнач.Имя);
КонецЦикла;
Постоянное открытие и закрытие соединения (Подключиться() и Отключиться()) на каждой итерации цикла сильно замедляет работу процедуры и может привести к блокировке со стороны почтового сервера за превышение частоты подключений. Вынесем установку соединения за пределы цикла:
Профиль = ПолучитьПочтовыйПрофиль();
Почта = Новый ИнтернетПочта;
Попытка
Почта.Подключиться(Профиль);
Исключение
Сообщить("Не удалось подключиться к серверу: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
Пока Рез.Следующий() Цикл
ИмяФайла = ПолучитьИмяВременногоФайла("xls");
// Формирование и сохранение отчета
Таб = Новый ТабличныйДокумент;
Сформировать(Таб, Рез.Контрагент);
Таб.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.XLS);
Сообщение = Новый ИнтернетПочтовоеСообщение;
Сообщение.Получатели.Добавить(СокрЛП(Рез.Контрагент.ЭлПочта));
Сообщение.Тема = "Отчет по взаиморасчетам";
Сообщение.Вложения.Добавить(ИмяФайла, "Отчет.xls");
Попытка
Почта.Послать(Сообщение);
Исключение
ЗаписьЖурналаРегистрации("РассылкаОтчетов", УровеньЖурналаРегистрации.Предупреждение,,,
"Ошибка отправки для " + Рез.Контрагент + ": " + ОписаниеОшибки());
КонецПопытки;
// Удаляем временный файл
УдалитьФайлы(ИмяФайла);
КонецЦикла;
// Отключаемся после завершения всей рассылки
Почта.Отключиться();
Резюме: для стабильной рассылки отчетов без риска отправки чужих данных всегда создавайте новый объект ТабличныйДокумент внутри цикла, генерируйте пути через ПолучитьИмяВременногоФайла() и обязательно удаляйте сформированные файлы через УдалитьФайлы() сразу после их отправки.