При автоматизации рассылки документов из 1С разработчики часто сталкиваются с задачей пакетной отправки. Например, когда в исходной таблице содержится несколько строк по одному и тому же контрагенту (разные счета, акты или накладные), и их требуется объединить в одно электронное письмо, а не отправлять десяток писем подряд. Это убережет почтовый ящик клиента от спама и повысит лояльность к компании.
В этой статье мы подробно разберем, как правильно реализовать алгоритм группировки вложений по получателям, исправим типичные синтаксические ошибки при работе с таблицами значений и рассмотрим наиболее производительный способ формирования файлов в памяти без захламления жесткого диска сервера временными файлами.
Для того чтобы сгруппировать файлы и отправить их одному адресату, нам необходимо выполнить следующие шаги:
При реализации данного алгоритма разработчики часто допускают ошибку при поиске строк в таблице значений. Рассмотрим ошибочный код, с которым столкнулся автор темы на форуме:
ТЗ = ТЗФормы.Скопировать();
ТЗ.Свернуть("Контрагент");
Для Каждого СтрокаКонтрагент ИЗ ТЗ Цикл
// ОШИБКА! Метод НайтиСтроки ожидает структуру, а не ссылку на элемент
Ссылки = ТЗФормы.НайтиСтроки(СтрокаКонтрагент.Контрагент);
КонецЦикла;
При запуске такого кода система выдаст ошибку: Ошибка при вызове метода контекста (НайтиСтроки): Несоответствие типов (параметр номер '1'). Выясним причину этой ошибки. Согласно синтакс-помощнику, метод НайтиСтроки() в качестве параметра принимает исключительно Структуру. Ключами этой структуры должны быть имена колонок, по которым производится поиск, а значениями — искомые данные.
Правильный вариант вызова метода выглядит следующим образом:
ПараметрыОтбора = Новый Структура("Контрагент", СтрокаКонтрагент.Contragent);
Ссылки = ТЗФормы.НайтиСтроки(ПараметрыОтбора);
Ранее в старых конфигурациях разработчики часто сохраняли печатные формы во временные файлы на жестком диске с помощью метода ПолучитьИмяВременногоФайла(). Это приводило к фрагментации диска и захламлению временных каталогов операционной системы при сбоях отправки.
Современный стандарт разработки на платформе 1С:Предприятие 8.3 позволяет выполнять эти операции непосредственно в оперативной памяти с помощью объекта ПотокВПамяти. Рассмотрим готовое решение, которое работает быстро, безопасно и использует стандартные механизмы Библиотеки стандартных подсистем (БСП):
// Шаг 1. Получаем уникальный список контрагентов для отправки писем
ТЗКонтрагенты = ТЗФормы.Скопировать(, "Контрагент");
ТЗКонтрагенты.Свернуть("Контрагент");
// Шаг 2. Запускаем цикл по уникальным адресатам
Для Каждого СтрокаКонтрагент Из ТЗКонтрагенты Цикл
ТекущийКонтрагент = СтрокаКонтрагент.Контрагент;
// Проверим, заполнен ли контрагент в строке
Если Не ЗначениеЗаполнено(ТекущийКонтрагент) Тогда
Продолжить;
КонецЕсли;
// Массив вложений конкретно для ЭТОГО контрагента
ВложенияДляПисьма = Новый Массив;
// Шаг 3. Ищем все строки в исходной таблице по нашему контрагенту
Отбор = Новый Структура("Контрагент", ТекущийКонтрагент);
НайденныеСтроки = ТЗФормы.НайтиСтроки(Отбор);
// Шаг 4. Наполняем массив вложений для текущего письма
Для Каждого СтрокаПечати Из НайденныеСтроки Цикл
ТабДок = СтрокаПечати.ТабличныйДокумент; // Наш табличный документ (печатная форма)
ИмяВложения = СтрокаПечати.Представление; // Например, "Акт сверки №120"
// Оптимальная запись файла в память без использования диска
Поток = Новый ПотокВПамяти();
ТабДок.Записать(Поток, ТипФайлаТабличногоДокумента.PDF);
ДвоичныеДанныеФайла = Поток.ЗакрытьИПолучитьДвоичныеДанные();
// Помещаем во временное хранилище (требуется для БСП)
АдресХранилища = ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла, Новый УникальныйИдентификатор);
// Формируем структуру описания вложения для механизмов почты БСП
ОписаниеВложения = Новый Структура;
ОписаниеВложения.Вставить("Представление", ИмяВложения + ".pdf");
ОписаниеВложения.Вставить("АдресВоВременномХранилище", АдресХранилища);
ВложенияДляПисьма.Добавить(ОписаниеВложения);
КонецЦикла;
// Шаг 5. Формируем и отправляем письмо, если собраны вложения
Если ВложенияДляПисьма.Количество() > 0 Тогда
// Получаем адрес электронной почты контрагента с помощью функций БСП
АдресПолучателя = УправлениеКонтактнойИнформацией.КонтактнаяИнформацияОбъекта(
ТекущийКонтрагент,
Справочники.ВидыКонтактнойИнформации.EmailКонтрагента,
ТекущаяДата()
);
Если ПустаяСтрока(АдресПолучателя) Тогда
ЗаписьЖурналаРегистрации("Отправка писем", УровеньЖурналаРегистрации.Предупреждение, , ,
"Не найден адрес почты для контрагента: " + ТекущийКонтрагент);
Продолжить;
КонецЕсли;
// Настройки структуры параметров отправляемого письма
ПараметрыПисьма = Новый Структура;
ПараметрыПисьма.Вставить("Кому", АдресПолучателя);
ПараметрыПисьма.Вставить("Тема", "Пакет документов для компании " + ТекущийКонтрагент);
ПараметрыПисьма.Вставить("Тело", "Здравствуйте! Направляем вам пакет документов во вложении к этому письму.");
ПараметрыПисьма.Вставить("ТипТекста", "ПростойТекст");
ПараметрыПисьма.Вставить("Вложения", ВложенияДляПисьма);
// Отправка через системную учетную запись средствами общего модуля БСП
Попытка
УчетнаяЗапись = Справочники.УчетныеЗаписиЭлектроннойПочты.СистемнаяУчетнаяЗаписьЭлектроннойПочты;
РаботаСПочтовымиСообщениями.ОтправитьПочтовоеСообщение(УчетнаяЗапись, ПараметрыПисьма);
Исключение
ЗаписьЖурналаРегистрации("Отправка писем", УровеньЖурналаРегистрации.Ошибка, , ,
"Ошибка отправки почты контрагенту " + ТекущийКонтрагент + ": " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецЦикла;
Для того чтобы сделать ваше решение еще более надежным и масштабируемым, рекомендуем придерживаться следующих правил:
ЗаписьZipФайла перед отправкой.ИТОГИ ПО Контрагент. Это существенно снизит потребление оперативной памяти сервером 1С.Попытка ... Исключение и записывайте ошибки в Журнал регистрации. Это позволит вам оперативно выявлять проблемы с некорректными адресами почты клиентов или перебоями в работе SMTP-сервера.