В процессе автоматизации бизнес-процессов часто возникает задача: в определенный момент, например, после согласования документа, необходимо автоматически создать прикрепленный к нему файл (например, Word-документ) на основе заранее подготовленного шаблона и заполнить его данными из этого документа. Рассмотрим пошагово, как реализовать этот механизм программно, проанализировав различные подходы и распространенные ошибки, с которыми сталкиваются разработчики. Если требуется настроить автоматическое добавление файлов на основе условий без изменения конфигурации, можно использовать расширение для настройки условий добавления шаблонов файлов в 1С:Документооборот 2.1.
Основная сложность заключается не просто в заполнении файла, а в его корректном создании. Простого вызова процедуры заполнения недостаточно, так как на момент вызова файла еще не существует. Его нужно сначала создать, привязать к документу-владельцу и только потом заполнять. Разберемся, как это сделать правильно, чтобы не повредить исходный шаблон.
Первый шаг — получить информацию о файлах, которые прикреплены к нашему шаблону документа. Для этого в системе существует специальная процедура общего модуля ШаблоныДокументов. Наша задача — подготовить таблицу значений нужной структуры и передать ее в эту процедуру.
Давайте посмотрим на пример. Предположим, у нас есть переменная ШаблонДокумента, содержащая ссылку на нужный шаблон (например, Справочники.ШаблоныВнутреннихДокументов), и ДокументСсылка — ссылка на документ, к которому мы хотим прикрепить файлы.
// Создаем таблицу для получения списка файлов из шаблона
ТаблицаФайлы = Новый ТаблицаЗначений;
ТаблицаФайлы.Колонки.Добавить("Наименование");
ТаблицаФайлы.Колонки.Добавить("Расширение");
ТаблицаФайлы.Колонки.Добавить("ПолныйПуть");
ТаблицаФайлы.Колонки.Добавить("ИндексКартинки");
ТаблицаФайлы.Колонки.Добавить("ШаблонОснованиеДляСоздания");
ТаблицаФайлы.Колонки.Добавить("ДобавленИзШаблона");
// Заполняем таблицу данными о файлах из шаблона документа
ШаблоныДокументов.ЗаполнитьФайлыДокументаПоШаблону(ШаблонДокумента, ТаблицаФайлы, Ложь);
После выполнения этого кода в переменной ТаблицаФайлы будет содержаться информация о каждом файле-шаблоне. Ключевая для нас колонка — ШаблонОснованиеДляСоздания, в которой находится ссылка на сам объект файла-шаблона (СправочникСсылка.Файлы).
Теперь, когда у нас есть список шаблонов, нужно создать на их основе новые файлы. Самый очевидный способ — скопировать объект файла-шаблона.
// Перебираем полученные из шаблона файлы
Для Каждого СтрокаФайл Из ТаблицаФайлы Цикл
// Копируем объект файла-шаблона
ФайлОбъект = СтрокаФайл.ШаблонОснованиеДляСоздания.Скопировать();
// Устанавливаем связь с исходным шаблоном
ФайлОбъект.ШаблонОснованиеДляСоздания = СтрокаФайл.ШаблонОснованиеДляСоздания;
// Назначаем владельцем наш новый документ
ФайлОбъект.ВладелецФайла = ДокументСсылка;
// Записываем новый файл в базу
ФайлОбъект.Записать();
// Теперь можно запустить автозаполнение (если оно настроено)
// ДанныеВыполнения = АвтозаполнениеШаблоновФайловКлиентСервер.ЗаполнитьПоляФайлаДаннымиВладельца(Ложь, ФайлОбъект.Ссылка, Истина);
КонецЦикла;
Этот код создаст прикрепленные файлы. Однако здесь кроется критически важная проблема, на которую наталкивались многие разработчики: при последующем автозаполнении такого файла изменяться будет не созданная копия, а исходный файл-шаблон! Выясним, почему это происходит и как это исправить.
Проблема заключается в том, что при вызове метода .Скопировать() для объекта файла копируется и его реквизит ТекущаяВерсия. Эта версия остается связанной с хранилищем исходного файла. Когда процедура автозаполнения обновляет двоичные данные, она обращается к этой версии и, как следствие, перезаписывает сам шаблон.
Чтобы избежать этого, необходимо выполнить более сложную последовательность действий: скопировать не только сам файл, но и его версию, а затем корректно перепривязать их друг к другу. Давайте разберем этот процесс по шагам.
Посмотрим, как это выглядит в коде. Этот вариант является надежным и проверенным решением, которое гарантирует сохранность исходных шаблонов.
// Предполагается, что ТаблицаФайлы уже заполнена, как в предыдущем примере
Для Каждого СтрокаФайл Из ТаблицаФайлы Цикл
// 1. Получаем ссылку на исходный шаблон файла
Шаблон = СтрокаФайл.ШаблонОснованиеДляСоздания;
// 2. Копируем объект файла-шаблона
ФайлОбъект = Шаблон.Скопировать();
// 3. Получаем объект текущей версии из скопированного файла
НоваяВерсия = ФайлОбъект.ТекущаяВерсия.ПолучитьОбъект();
// 4. Временно очищаем текущую версию у нового файла
ФайлОбъект.ТекущаяВерсия = Справочники.ВерсииФайлов.ПустаяСсылка;
ФайлОбъект.ШаблонОснованиеДляСоздания = Шаблон;
ФайлОбъект.ВладелецФайла = ДокументСсылка;
ФайлОбъект.Записать(); // Записываем файл без версии
// 5. У объекта версии меняем владельца на наш новый файл
НоваяВерсия.Владелец = ФайлОбъект.Ссылка;
НоваяВерсия.НомерВерсии = 1; // Сбрасываем номер версии на начальный
НоваяВерсия.Записать(); // Записываем обновленную версию
// 6. Снова получаем объект нового файла, чтобы обновить его данные
НФ = ФайлОбъект.Ссылка.ПолучитьОбъект();
// 7. Присваиваем файлу его новую, корректно привязанную версию
НФ.ТекущаяВерсия = НоваяВерсия.Ссылка;
НФ.Записать();
// 8. Запускаем автозаполнение. Теперь оно будет работать с новой копией, а не с шаблоном.
// Обратите внимание на параметры. Последний параметр "Истина" может быть важен
// для корректной работы в некоторых версиях.
ДанныеВыполнения = АвтозаполнениеШаблоновФайловКлиентСервер.ЗаполнитьПоляФайлаДаннымиВладельца(Ложь, НФ.Ссылка, Истина);
// Если нужно обновить файл из двоичных данных после заполнения
Если ДанныеВыполнения.Результат Тогда
Комментарий = "Автозаполнение полей файла";
АвтозаполнениеШаблоновФайловСервер.ОбновитьВерсиюИзДвоичныхДанных(ДанныеВыполнения.ДвоичныеДанныеЗаполненногоФайла, НФ.Ссылка, Комментарий);
КонецЕсли;
КонецЦикла;
Этот алгоритм полностью решает проблему порчи шаблона. Он хоть и выглядит громоздким, но детально управляет всеми связанными объектами, обеспечивая корректную работу механизма.
В современных конфигурациях, использующих Библиотеку стандартных подсистем (БСП), часто есть более простые и элегантные способы решения таких задач. Вместо ручного манипулирования объектами и их версиями можно использовать готовую функцию РаботаСФайламиВызовСервера.СкопироватьФайл.
Эта функция инкапсулирует всю сложную логику, которую мы разбирали выше, и делает код значительно чище и надежнее. Проанализируем пример использования этого подхода.
// Предполагается, что переменная ФайлыШаблона содержит результат,
// аналогичный ТаблицаФайлы из прошлых примеров.
// ДокЗаявление - ссылка на документ, куда копируем файл.
Для Каждого СтрокаФайл Из ФайлыШаблона Цикл
Шаблон = СтрокаФайл.ШаблонОснованиеДляСоздания;
// 1. Используем штатную функцию для копирования файла.
// Она сама корректно создаст новый файл и его версию.
СсылкаНаФайл = РаботаСФайламиВызовСервера.СкопироватьФайл(Шаблон, ДокЗаявление);
// 2. Получаем объект нового файла, чтобы установить связь с шаблоном
ФайлОбъект = СсылкаНаФайл.ПолучитьОбъект();
ФайлОбъект.ШаблонОснованиеДляСоздания = Шаблон;
ФайлОбъект.Записать();
// 3. Запускаем автозаполнение.
// В пятом параметре можно передать ссылку на владельца данных для заполнения.
ДанныеВыполнения = АвтозаполнениеШаблоновФайловКлиентСервер.ЗаполнитьПоляФайлаДаннымиВладельца(Ложь, СсылкаНаФайл, Истина, , ДокЗаявление);
КонецЦикла;
Этот подход является наиболее предпочтительным, если ваша конфигурация поддерживает данную функцию. Он не только сокращает объем кода, но и снижает вероятность ошибки, так как вся низкоуровневая работа с версиями выполняется внутри платформенного механизма.
В заключение, задача программного создания файла из шаблона имеет несколько решений. Ключевой момент — это понимание того, как устроена работа с файлами и их версиями в 1С. Неправильное копирование может привести к порче данных, поэтому всегда следует либо тщательно контролировать процесс перепривязки версий вручную, либо, что лучше, использовать для этого стандартные высокоуровневые функции БСП.