Как в 1С 8.3 ЗУП 3.1 создать внешнюю печатную форму MS Word и объединить несколько документов в один файл при печати списком?

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

Разработка внешних печатных форм в формате Microsoft Word (или в современных альтернативах .docx) — одна из наиболее частых кадровых задач в среде 1С:Предприятие 8.3, особенно в конфигурации Зарплата и управление персоналом (ЗУП 3.1). Стандартные кадровые документы, такие как трудовые договоры, дополнительные соглашения и договоры ГПХ, традиционно верстаются и согласовываются юристами именно в текстовых процессорах. Переносить их структуру в стандартный табличный документ 1С (MXL) бывает крайне трудозатратно.

В этой статье мы подробно разберем, как с нуля создать подключаемую внешнюю печатную форму Word, правильно настроить процедуру ее регистрации через Библиотеку Стандартных Подсистем (БСП) и решить ключевую проблему групповой печати: как вывести несколько кадровых документов для списка сотрудников в один-единственный файл Word вместо открытия десятков отдельных окон.

Мы проанализируем два подхода: классический (через COM-соединение с MS Word) и современный кроссплатформенный (средствами БСП без использования COM, который работает на серверах Linux и не требует установленного MS Office на сервере).

---

Шаг 1. Регистрация внешней печатной формы в 1С (функция СведенияОВнешнейОбработке)

Для того чтобы наша обработка корректно подключилась к типовой конфигурации ЗУП 3.1 (или любой другой конфигурации на базе БСП), в модуле объекта внешней обработки необходимо разместить экспортную функцию СведенияОВнешнейОбработке(). Она описывает, к каким документам прикрепляется печать и какие команды будут доступны пользователю.

Обратите внимание на важный нюанс: если мы хотим печатать договоры как из одиночного документа "Прием на работу", так и из списочного документа "Прием на работу списком", нам нужно зарегистрировать печатную форму для обоих объектов.

Рассмотрим пример реализации этой функции:


Функция СведенияОВнешнейОбработке() Экспорт

    ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке();
    ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиПечатнаяФорма();
    
    // Привязываем обработку к кадровым документам приема на работу
    ПараметрыРегистрации.Назначение.Добавить("Документ.ПриемНаРаботу");
    ПараметрыРегистрации.Назначение.Добавить("Документ.ПриемНаРаботуСписком");
    
    ПараметрыРегистрации.Наименование = "Трудовой договор (шаблон MS Word)";
    ПараметрыРегистрации.Версия = "1.2";
    ПараметрыРегистрации.БезопасныйРежим = Ложь; // Требуется Ложь, если используется COM-соединение
    
    // Создаем команду для печати
    Команда = ПараметрыРегистрации.Команды.Добавить();
    Команда.Представление = "Печать трудового договора (Word)";
    Команда.Идентификатор = "ПечатьТрудовогоДоговораWord";
    
    // Если используем классический COM на клиенте:
    Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыОткрытиеФормы();
    
    // Если используем современный серверный метод БСП без COM:
    // Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода();
    
    Возврат ПараметрыРегистрации;

КонецФункции

Важное примечание: Если вы планируете использовать старый метод работы через COM-объект COMОбъект("Word.Application"), вам обязательно нужно установить свойство БезопасныйРежим = Ложь. В противном случае платформа 1С заблокирует создание внешних COM-объектов по соображениям безопасности.

---

Шаг 2. Как подготовить макет документа Word и передать параметры

Существует несколько вариантов хранения шаблона Word внутри внешней обработки. Разберем их преимущества и недостатки:

  1. Двоичные данные (.doc / .docx): Наиболее надежный и современный вариант. Файл шаблона загружается в макеты внешней обработки как двоичные данные. При печати 1С выгружает этот файл во временную директорию на диске и начинает заполнение.
  2. ActiveDocument: Устаревающий тип макета. Часто вызывает проблемы совместимости при обновлении версий платформы 1С и пакета Microsoft Office. Использовать его не рекомендуется.
  3. Присоединенные файлы: Интересный альтернативный вариант. Шаблон хранится не внутри кода обработки, а в справочнике присоединенных файлов в самой базе 1С. Это позволяет пользователям редактировать текст договора без привлечения программиста.

Выберем оптимальный вариант — загрузку файла .docx в макет с типом Двоичные данные. Назовем макет ТрудовойДоговорШаблон.

В самом шаблоне Word параметры для автозамены следует оформлять уникальными маркерами. Самый распространенный и читаемый стандарт БСП — использование фигурных скобок с префиксом: {v8 РаботникФИО}, {v8 НомерДоговора}, {v8 ДатаДоговора}.

---

Шаг 3. Реализация печати одиночного документа через COM (Классический метод)

Рассмотрим базовый алгоритм вывода данных в Word через COM-соединение на стороне клиента. Метод заключается в сохранении двоичных данных макета во временный файл, открытии этого файла через приложение Word и замене маркеров на реальные кадровые данные.

Ниже представлен пример кода, который вызывается при выполнении команды печати:


Процедура ВыполнитьПечатьНаКлиенте(СсылкаНаОбъект) Экспорт
    
    // Получаем двоичные данные макета с сервера
    ДвоичныеДанныеМакета = ПолучитьМакетССервера();
    
    // Создаем временный файл на диске пользователя
    ИмяФайлаВременное = ПолучитьИмяВременногоФайла(".doc");
    ДвоичныеДанныеМакета.Записать(ИмяФайлаВременное);
    
    // Получаем данные кадрового документа с сервера
    СтруктураДанных = ПолучитьДанныеДляПечати(СсылкаНаОбъект);
    
    Попытка
        // Инициализируем COM-объект Microsoft Word
        ДокументWord = ПолучитьCOMОбъект(ИмяФайлаВременное);
        
        // Определяем константы для операции замены
        wdReplaceAll = 2; 
        
        // Получаем объект поиска и замены в тексте документа
        Замена = ДокументWord.Content.Find;
        
        // Выполняем замену параметров данными из нашей структуры
        Замена.Execute("{v8 НомерДоговора}", , , , , , , , , СтруктураДанных.НомерДоговора, wdReplaceAll);
        Замена.Execute("{v8 ДатаДоговора}", , , , , , , , , Формат(СтруктураДанных.ДатаДоговора, "ДФ=dd.MM.yyyy"), wdReplaceAll);
        Замена.Execute("{v8 РаботникФИО}", , , , , , , , , СтруктураДанных.ФИОСотрудника, wdReplaceAll);
        Замена.Execute("{v8 ОрганизацияПолноеНаименование}", , , , , , , , , СтруктураДанных.ОрганизацияНаименование, wdReplaceAll);
        
        // Делаем окно MS Word видимым для пользователя
        ДокументWord.Application.Visible = Истина;
        ДокументWord.Activate();
        
    Исключение
        // В случае отсутствия установленного Word или иных ошибок корректно информируем пользователя
        ВызватьИсключение "Ошибка при работе с MS Word: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
    КонецПопытки;

КонецПроцедуры

Этот код отлично решает задачу, когда нам нужно распечатать один документ. Но что делать, если бухгалтер или кадровик выделяет в списке 15 сотрудников и нажимает кнопку печати? На экране откроется 15 окон MS Word, система начнет сильно зависать, а пользователю придется вручную сохранять или отправлять на печать каждый файл. Это крайне неэффективно.

---

Шаг 4. Решение проблемы печати списка сотрудников в ОДИН файл

Для того чтобы объединить множество кадровых документов в один результирующий файл, у нас есть два пути: современный кроссплатформенный инструмент БСП и классический COM-метод.

Вариант А. Современный стандарт БСП без использования COM (Рекомендуется)

Начиная с версии БСП 3.0, разработчикам доступен мощный функционал работы с файлами .docx непосредственно на сервере без создания COM-объектов. Метод основан на распаковке XML-структуры контейнера Office Open XML, поиске областей и их замене.

Для этого в шаблоне Word необходимо определить специальную область, например, обернув весь текст документа тегами {v8 Область.ТелоДоговора} в начале и {/v8 Область.ТелоДоговора} в конце.

Рассмотрим, как выглядит алгоритм объединения документов на сервере:


Функция СформироватьОбъединенныйДокументБСП(МассивДокументов) Экспорт

    // Получаем двоичные данные макета из внешней обработки
    ДвоичныеДанныеМакета = ЭтотОбъект.ПолучитьМакет("ТрудовойДоговорШаблон");
    
    // Инициализируем макет офисного документа средствами БСП
    Макет = УправлениеПечатью.ИнициализироватьМакетОфисногоДокумента(ДвоичныеДанныеМакета, Неопределено);
    
    // Инициализируем результирующий документ в оперативной памяти сервера
    ПечатнаяФорма = УправлениеПечатью.ИнициализироватьПечатнуюФорму(Неопределено, Макет.НастройкиСтраницыМакета, Макет);
    
    // Выясним структуру данных для печати по всему списку документов
    ТаблицаКадровыхДанных = ПолучитьТаблицуКадровыхДанных(МассивДокументов);
    
    ПерваяСтраница = Истина;
    
    Для Каждого СтрокаДанных Из ТаблицаКадровыхДанных Цикл
        
        // Получаем область из шаблона
        Область = УправлениеПечатью.ОбластьМакета(Макет, "ТелоДоговора");
        
        // Формируем структуру параметров для заполнения
        ДанныеЗаполнения = Новый Структура;
        ДанныеЗаполнения.Вставить("НомерДоговора", СтрокаДанных.НомерДоговора);
        ДанныеЗаполнения.Вставить("ДатаДоговора", Формат(СтрокаДанных.ДатаДоговора, "ДФ=dd.MM.yyyy"));
        ДанныеЗаполнения.Вставить("РаботникФИО", СтрокаДанных.ФИОСотрудника);
        ДанныеЗаполнения.Вставить("ОрганизацияПолноеНаименование", СтрокаДанных.ОрганизацияНаименование);
        
        // Если это не первый сотрудник, нам необходимо вставить разрыв страницы перед выводом следующего договора
        ВставитьРазрывСтраницы = Не ПерваяСтраница;
        
        // Присоединяем область к итоговому документу и заполняем ее параметры
        УправлениеПечатью.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, Область, ДанныеЗаполнения, ВставитьРазрывСтраницы);
        
        ПерваяСтраница = Ложь;
        
    КонецЦикла;
    
    // Формируем готовый объединенный файл во временном хранилище
    АдресХранилища = УправлениеПечатью.СформироватьДокумент(ПечатнаяФорма);
    
    // Обязательно освобождаем память и очищаем ссылки
    УправлениеПечатью.ОчиститьСсылки(ПечатнаяФорма);
    УправлениеПечатью.ОчиститьСсылки(Макет);
    
    Возврат АдресХранилища;

КонецФункции

Преимущества данного метода: Полная независимость от установленного ПО Microsoft Office. Скорость формирования документов возрастает в разы, а код стабильно выполняется на серверах под управлением Linux.

---

Вариант Б. Объединение документов через COM-метод InsertFile (без буфера обмена)

Если вы ограничены жесткими требованиями использовать только старый формат .doc и COM-соединение, то ни в коем случае не используйте копирование через буфер обмена операционной системы (методы Selection.Copy() и Selection.Paste()). Буфер обмена пользователя будет постоянно очищаться, что вызовет сильное раздражение, а при одновременной работе нескольких пользователей этот метод будет давать постоянные сбои.

Правильный архитектурный подход при работе с COM — использование встроенного метода MS Word под названием InsertFile. Этот метод позволяет бесшовно импортировать один документ Word в конец другого.

Рассмотрим детальный пошаговый алгоритм:

  1. Создаем один главный (базовый) невидимый документ Word, в котором будут последовательно собираться все договоры.
  2. В цикле по сотрудникам выгружаем чистый шаблон во временный файл на диске, открываем его вторым COM-объектом, производим заполнение параметров.
  3. Сохраняем заполненный временный документ на диск и закрываем его процесс.
  4. В главном документе перемещаем курсор в самый конец файла.
  5. Если это не первый документ в списке, добавляем в главный документ разрыв страницы (Page Break) программным вызовом InsertBreak(2).
  6. Вызываем метод InsertFile(), передавая путь к только что сохраненному временному файлу текущего сотрудника.
  7. Удаляем заполненный временный файл с диска. Повторяем цикл.

Проанализируем программную реализацию данного подхода:


Процедура ПечатьСпискаЧерезCOM(МассивДокументов) Экспорт

    // Инициализируем основное приложение MS Word
    Попытка
        MSWord = Новый COMОбъект("Word.Application");
        MSWord.Visible = Ложь;
    Исключение
        ВызватьИсключение "Не удалось запустить Microsoft Word. Убедитесь, что приложение установлено.";
    КонецПопытки;
    
    // Создаем один главный пустой документ
    ГлавныйДокумент = MSWord.Documents.Add();
    
    ТаблицаКадровыхДанных = ПолучитьТаблицуКадровыхДанных(МассивДокументов);
    ДвоичныеДанныеМакета = ЭтотОбъект.ПолучитьМакет("ТрудовойДоговорШаблон");
    
    ПервыйДокумент = Истина;
    
    Для Каждого СтрокаДанных Из ТаблицаКадровыхДанных Цикл
        
        // Создаем уникальный временный файл для заполнения данных текущего сотрудника
        ИмяВременногоШаблона = ПолучитьИмяВременногоФайла(".doc");
        ДвоичныеДанныеМакета.Записать(ИмяВременногоШаблона);
        
        // Открываем временный файл шаблона как отдельный документ
        ТекущийДокумент = MSWord.Documents.Open(ИмяВременногоШаблона);
        
        // Выполняем автозамену полей
        wdReplaceAll = 2;
        Замена = ТекущийДокумент.Content.Find;
        Замена.Execute("{v8 НомерДоговора}", , , , , , , , , СтрокаДанных.НомерДоговора, wdReplaceAll);
        Замена.Execute("{v8 РаботникФИО}", , , , , , , , , СтрокаДанных.ФИОСотрудника, wdReplaceAll);
        
        // Сохраняем и закрываем временный заполненный документ
        ТекущийДокумент.Save();
        ТекущийДокумент.Close();
        
        // Позиционируем курсор в конец главного документа
        MSWord.Selection.EndKey(6); // 6 соответствует wdStory - переход в самый конец файла
        
        Если Не ПервыйДокумент Тогда
            // Вставляем разрыв страницы (2 соответствует wdPageBreak)
            MSWord.Selection.InsertBreak(2);
        КонецЕсли;
        
        // Импортируем заполненный временный файл в главный документ
        MSWord.Selection.InsertFile(ИмяВременногоШаблона);
        
        // Удаляем временный файл с диска за ненадобностью
        Попытка
            УдалитьФайлы(ИмяВременногоШаблона);
        Исключение
            // Ошибка удаления не должна прерывать процесс печати
        КонецПопытки;
        
        ПервыйДокумент = Ложь;
        
    КонецЦикла;
    
    // Делаем итоговый объединенный файл видимым пользователю
    MSWord.Visible = Истина;
    MSWord.Application.WindowState = 1; // wdWindowStateMaximize - разворачиваем на весь экран
    MSWord.Activate();

КонецПроцедуры

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

---

Сравнительный анализ подходов

Подведем итоги и выберем оптимальный метод разработки в зависимости от инфраструктуры вашей компании:

Критерий сравнения Использование БСП (Вариант А) Использование COM-объекта (Вариант Б)
Зависимость от ОС Linux Полностью работает на серверах Linux без графической оболочки. Не работает. Требуется только Windows-сервер или запуск исключительно на клиенте.
Лицензирование MS Office Лицензия на MS Office на серверах не требуется. На каждом рабочем месте (или сервере) должен быть установлен и лицензирован MS Word.
Скорость работы Очень высокая (обработка XML-контейнера в оперативной памяти). Низкая (требуется время на физический запуск процессов Word и сохранение файлов на диск).
Сложность программирования Средняя (требуется разметка областей БСП в шаблоне Word). Низкая (простая объектная модель замен, знакомая большинству разработчиков).

Таким образом, для современных и производительных баз данных 1С:ЗУП 3.1 крайне рекомендуется переходить на использование механизмов Библиотеки Стандартных Подсистем (БСП). Это гарантирует надежность системы, высокую скорость формирования отчетов и избавляет от проблем зависания процессов winword.exe в операционной системе.

← На главную