При автозаполнении печатных форм в MS Word по списку сотрудников или документов часто возникает проблема: программа генерирует и открывает отдельное окно MS Word на каждого сотрудника. В результате пользователь получает десятки открытых окон вместо одного итогового документа, готового к печати.
Рассмотрим, почему так происходит, и разберем практические варианты объединения всех сформированных макетов в один общий документ средствами 1С и MS Word COM-объекта, а также с использованием Библиотеки стандартных подсистем (БСП).
Когда вы получаете макет из двоичных данных и вызываете метод Получить() для COM-объекта на каждой итерации цикла, 1С запускает новый экземпляр документа. Если выполнять поиск и замену текста (метод Content.Find.Execute) прямо в открытом шаблоне, исходные текстовые метки (плейсхолдеры вроде <ФИО> или <НомерДоговора>) заменяются на конкретные значения первого сотрудника. При попытке повторно использовать тот же документ для следующего сотрудника заменять становится нечего.
Выясним основные способы решения этой задачи.
Суть этого подхода заключается в следующем: создаем или берем исходный макет, копируем его содержимое в буфер обмена, вставляем разрыв страницы в итоговом документе и выполняем замену меток параметров для каждого сотрудника.
Разберем пример кода с обходом выборки сотрудников и заполнением меток:
// 1. Инициализируем COM-сервер Word и получаем макет
Макет = ПолучитьМакет("МакетРЕГ");
MSWord = Макет.Получить();
ДокументWord = MSWord.Application.Documents(1);
ДокументWord.Activate();
// Копируем исходный шаблон в буфер обмена
ДокументWord.Content.Copy();
Индекс = 0;
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
Индекс = Индекс + 1;
Если Индекс > 1 Тогда
// Переходим в конец итогового документа
ОбластьWord = ДокументWord.Range();
ОбластьWord.Collapse(0); // wdCollapseEnd = 0
// Вставляем разрыв страницы
ОбластьWord.InsertBreak(7); // wdPageBreak = 7
// Вставляем копию исходного шаблона из буфера обмена
ОбластьWord.Paste();
КонецЕсли;
// Заполнение параметров в текущем фрагменте или всем документе
ТекстПоиска = "<номер трудового договора>";
ТекстЗамены = ВыборкаДетальныеЗаписи.НомерДоговора;
Замена = ДокументWord.Content.Find;
Замена.Execute(ТекстПоиска, Ложь, Истина, Ложь, , , Истина, , Ложь, ТекстЗамены, 2); // wdReplaceAll = 2
ТекстПоиска = "<фамилия имя отчество сотрудника>";
ТекстЗамены = Строка(ВыборкаДетальныеЗаписи.Физлицо);
Замена = ДокументWord.Content.Find;
Замена.Execute(ТекстПоиска, Ложь, Истина, Ложь, , , Истина, , Ложь, ТекстЗамены, 2);
ТекстПоиска = "<адрес по прописке>";
ТекстЗамены = ВыборкаДетальныеЗаписи.ПредставлениеАдреса;
Замена = ДокументWord.Content.Find;
Замена.Execute(ТекстПоиска, Ложь, Истина, Ложь, , , Истина, , Ложь, ТекстЗамены, 2);
КонецЦикла;
// Делаем итоговый документ видимым пользователю
ДокументWord.Application.Visible = Истина;
ДокументWord.Activate();
Важное примечание: при замене текста через метод Find.Execute рекомендуется передавать значение 2 в качестве параметра типа замены (Replace), что соответствует константе wdReplaceAll. Это гарантирует, что все совпадающие плейсхолдеры в текущей секции будут заменены с первого раза.
Использование системного буфера обмена (методы Copy и Paste) в частых циклах может приводить к сбоям, если параллельно с работой 1С пользователь копирует другие данные или в системе работают сторонние утилиты. Более стабильным решением является сохранение заполненого документа по каждому сотруднику во временный файл и подгрузка файлов в общий документ через метод MS Word InsertFile.
Рассмотрим пошаговый алгоритм этого способа:
Find.Execute.SaveAs и закрываем его.Collapse), вставляем разрыв страницы (или разрыв раздела wdSectionBreakNextPage, если у документов разная ориентация страниц) и вызываем метод InsertFile(ПутьКФайлу).Если ваша конфигурация построена на базе БСП (например, «1С:Зарплата и Управление Персоналом», «1С:ERP» или «1С:Комплексная автоматизация»), рекомендуется использовать штатный модуль УправлениеПечатьюКлиент. Это избавляет от необходимости вручную писать вызовы COM-объектов и работать с низкоуровневыми командами MS Word.
Посмотрим на пример формирования печатной формы через БСП:
// Двоичные данные макета
ДвоичныеДанныеМакета = ПолучитьМакет("МакетРЕГ");
// Инициализация макета офисного документа
Макет = УправлениеПечатьюКлиент.ИнициализироватьМакетОфисногоДокумента(ДвоичныеДанныеМакета, "DOC", "ПФ_Договор");
// Подготовка структуры печатной формы
ПечатнаяФорма = УправлениеПечатьюКлиент.ИнициализироватьПечатнуюФормуОфисногоДокумента("DOC", Макет.Настройки, Макет);
// Получаем области
ОбластьШапка = УправлениеПечатьюКлиент.ОбластьМакета(Макет, Новый Структура("ИмяОбласти, ТипОбласти", "Документ", "Общая"));
ОбластьРазрыв = УправлениеПечатьюКлиент.ОбластьМакета(Макет, Новый Структура("ИмяОбласти, ТипОбласти", "Разрыв", "Общая"));
Индекс = 0;
Пока Выборка.Следующий() Цикл
Индекс = Индекс + 1;
Если Индекс > 1 Тогда
// Добавляем разрыв между документами сотрудников
УправлениеПечатьюКлиент.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, ОбластьРазрыв, Новый Структура, Ложь);
КонецЕсли;
// Подготовка структуры заполнения параметров
ДанныеЗаполнения = Новый Структура;
ДанныеЗаполнения.Вставить("НомерДоговора", Выборка.НомерДоговора);
ДанныеЗаполнения.Вставить("ФИО", Выборка.ФизлицоПредставление);
ДанныеЗаполнения.Вставить("ПаспортныеДанные", Выборка.Паспорт);
// Присоединение области с заполнением плейсхолдеров
УправлениеПечатьюКлиент.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, ОбластьШапка, ДанныеЗаполнения, Ложь);
КонецЦикла;
// Вывод итогового файла пользователю
УправлениеПечатьюКлиент.ПоказатьДокумент(ПечатнаяФорма);
УправлениеПечатьюКлиент.ОчиститьСсылки(ПечатнаяФорма);
Для автоматического получения всех параметров, заложенных в макете DOCX, в БСП можно воспользоваться функцией УправлениеПечатью.ПараметрыМакета() (или УправлениеПечатьюКлиент.ПараметрыМакета()). Она сканирует файл и возвращает массив найденных имен плейсхолдеров.