Почему в 1С при массовой рассылке отчетов контрагентам прикрепляются чужие файлы и как это исправить?

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

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

Анализ проблемы и основные причины сбоя

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

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

Шаг 1. Гарантированное удаление временных файлов

Самое быстрое и действенное решение для предотвращения отправки старых файлов — явное удаление сохраненного документа сразу после отправки письма получателю с помощью процедуры УдалитьФайлы(). Рассмотрим пример правильной организации кода:


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

Шаг 2. Генерация гарантированно уникальных имен файлов

Чтобы избежать совпадения путей к файлам при высокой скорости обработки цикла, откажемся от конструирования имени через ТекущаяДата(). Используем встроенную функцию платформы ПолучитьИмяВременногоФайла() или добавим уникальный идентификатор:


// Вариант 1: Использование платформенного механизма
ИмяФайла = ПолучитьИмяВременногоФайла("xls");

// Вариант 2: Добавление УИД и кода контрагента
ИмяФайла = КаталогВременныхФайлов() + "Отчет_" + СокрЛП(ВыбДилер.Код) + "_" + Строка(Новый УникальныйИдентификатор) + ".xls";

Шаг 3. Корректное добавление вложений и исправление ошибок обхода

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


// Ошибочный вариант:
Для каждого МЗнач из ИмяФайла Цикл
    Сообщение.Вложения.Добавить(ИмяФайла.ПолноеИмя, ИмяФайла.Имя); // Ошибка: у массива нет свойств ПолноеИмя и Имя
КонецЦикла;

// Исправленный вариант:
Для каждого МЗнач из ИмяФайла Цикл
    Сообщение.Вложения.Добавить(МЗнач.ПолноеИмя, МЗнач.Имя);
КонецЦикла;

Шаг 4. Оптимизация работы с интернет-почтой

Постоянное открытие и закрытие соединения (Подключиться() и Отключиться()) на каждой итерации цикла сильно замедляет работу процедуры и может привести к блокировке со стороны почтового сервера за превышение частоты подключений. Вынесем установку соединения за пределы цикла:


Профиль = ПолучитьПочтовыйПрофиль();
Почта = Новый ИнтернетПочта;

Попытка
    Почта.Подключиться(Профиль);
Исключение
    Сообщить("Не удалось подключиться к серверу: " + ОписаниеОшибки());
    Возврат;
КонецПопытки;

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

// Отключаемся после завершения всей рассылки
Почта.Отключиться();

Резюме: для стабильной рассылки отчетов без риска отправки чужих данных всегда создавайте новый объект ТабличныйДокумент внутри цикла, генерируйте пути через ПолучитьИмяВременногоФайла() и обязательно удаляйте сформированные файлы через УдалитьФайлы() сразу после их отправки.

← На главную