Разработка внешних печатных форм в формате Microsoft Word (или в современных альтернативах .docx) — одна из наиболее частых кадровых задач в среде 1С:Предприятие 8.3, особенно в конфигурации Зарплата и управление персоналом (ЗУП 3.1). Стандартные кадровые документы, такие как трудовые договоры, дополнительные соглашения и договоры ГПХ, традиционно верстаются и согласовываются юристами именно в текстовых процессорах. Переносить их структуру в стандартный табличный документ 1С (MXL) бывает крайне трудозатратно.
В этой статье мы подробно разберем, как с нуля создать подключаемую внешнюю печатную форму Word, правильно настроить процедуру ее регистрации через Библиотеку Стандартных Подсистем (БСП) и решить ключевую проблему групповой печати: как вывести несколько кадровых документов для списка сотрудников в один-единственный файл Word вместо открытия десятков отдельных окон.
Мы проанализируем два подхода: классический (через COM-соединение с MS Word) и современный кроссплатформенный (средствами БСП без использования COM, который работает на серверах Linux и не требует установленного MS Office на сервере).
---
Для того чтобы наша обработка корректно подключилась к типовой конфигурации ЗУП 3.1 (или любой другой конфигурации на базе БСП), в модуле объекта внешней обработки необходимо разместить экспортную функцию СведенияОВнешнейОбработке(). Она описывает, к каким документам прикрепляется печать и какие команды будут доступны пользователю.
Обратите внимание на важный нюанс: если мы хотим печатать договоры как из одиночного документа "Прием на работу", так и из списочного документа "Прием на работу списком", нам нужно зарегистрировать печатную форму для обоих объектов.
Рассмотрим пример реализации этой функции:
Функция СведенияОВнешнейОбработке() Экспорт
ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке();
ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиПечатнаяФорма();
// Привязываем обработку к кадровым документам приема на работу
ПараметрыРегистрации.Назначение.Добавить("Документ.ПриемНаРаботу");
ПараметрыРегистрации.Назначение.Добавить("Документ.ПриемНаРаботуСписком");
ПараметрыРегистрации.Наименование = "Трудовой договор (шаблон MS Word)";
ПараметрыРегистрации.Версия = "1.2";
ПараметрыРегистрации.БезопасныйРежим = Ложь; // Требуется Ложь, если используется COM-соединение
// Создаем команду для печати
Команда = ПараметрыРегистрации.Команды.Добавить();
Команда.Представление = "Печать трудового договора (Word)";
Команда.Идентификатор = "ПечатьТрудовогоДоговораWord";
// Если используем классический COM на клиенте:
Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыОткрытиеФормы();
// Если используем современный серверный метод БСП без COM:
// Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода();
Возврат ПараметрыРегистрации;
КонецФункции
Важное примечание: Если вы планируете использовать старый метод работы через COM-объект COMОбъект("Word.Application"), вам обязательно нужно установить свойство БезопасныйРежим = Ложь. В противном случае платформа 1С заблокирует создание внешних COM-объектов по соображениям безопасности.
---
Существует несколько вариантов хранения шаблона Word внутри внешней обработки. Разберем их преимущества и недостатки:
Выберем оптимальный вариант — загрузку файла .docx в макет с типом Двоичные данные. Назовем макет ТрудовойДоговорШаблон.
В самом шаблоне Word параметры для автозамены следует оформлять уникальными маркерами. Самый распространенный и читаемый стандарт БСП — использование фигурных скобок с префиксом: {v8 РаботникФИО}, {v8 НомерДоговора}, {v8 ДатаДоговора}.
---
Рассмотрим базовый алгоритм вывода данных в 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, система начнет сильно зависать, а пользователю придется вручную сохранять или отправлять на печать каждый файл. Это крайне неэффективно.
---
Для того чтобы объединить множество кадровых документов в один результирующий файл, у нас есть два пути: современный кроссплатформенный инструмент БСП и классический COM-метод.
Начиная с версии БСП 3.0, разработчикам доступен мощный функционал работы с файлами .docx непосредственно на сервере без создания COM-объектов. Метод основан на распаковке XML-структуры контейнера Office Open XML, поиске областей и их замене.
Для этого в шаблоне Word необходимо определить специальную область, например, обернув весь текст документа тегами {v8 Область.ТелоДоговора} в начале и {/v8 Область.ТелоДоговора} в конце.
Рассмотрим, как выглядит алгоритм объединения документов на сервере:
Функция СформироватьОбъединенныйДокументБСП(МассивДокументов) Экспорт
// Получаем двоичные данные макета из внешней обработки
ДвоичныеДанныеМакета = ЭтотОбъект.ПолучитьМакет("ТрудовойДоговорШаблон");
// Инициализируем макет офисного документа средствами БСП
Макет = УправлениеПечатью.ИнициализироватьМакетОфисногоДокумента(ДвоичныеДанныеМакета, Неопределено);
// Инициализируем результирующий документ в оперативной памяти сервера
ПечатнаяФорма = УправлениеПечатью.ИнициализироватьПечатнуюФорму(Неопределено, Макет.НастройкиСтраницыМакета, Макет);
// Выясним структуру данных для печати по всему списку документов
ТаблицаКадровыхДанных = ПолучитьТаблицуКадровыхДанных(МассивДокументов);
ПерваяСтраница = Истина;
Для Каждого СтрокаДанных Из ТаблицаКадровыхДанных Цикл
// Получаем область из шаблона
Область = УправлениеПечатью.ОбластьМакета(Макет, "ТелоДоговора");
// Формируем структуру параметров для заполнения
ДанныеЗаполнения = Новый Структура;
ДанныеЗаполнения.Вставить("НомерДоговора", СтрокаДанных.НомерДоговора);
ДанныеЗаполнения.Вставить("ДатаДоговора", Формат(СтрокаДанных.ДатаДоговора, "ДФ=dd.MM.yyyy"));
ДанныеЗаполнения.Вставить("РаботникФИО", СтрокаДанных.ФИОСотрудника);
ДанныеЗаполнения.Вставить("ОрганизацияПолноеНаименование", СтрокаДанных.ОрганизацияНаименование);
// Если это не первый сотрудник, нам необходимо вставить разрыв страницы перед выводом следующего договора
ВставитьРазрывСтраницы = Не ПерваяСтраница;
// Присоединяем область к итоговому документу и заполняем ее параметры
УправлениеПечатью.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, Область, ДанныеЗаполнения, ВставитьРазрывСтраницы);
ПерваяСтраница = Ложь;
КонецЦикла;
// Формируем готовый объединенный файл во временном хранилище
АдресХранилища = УправлениеПечатью.СформироватьДокумент(ПечатнаяФорма);
// Обязательно освобождаем память и очищаем ссылки
УправлениеПечатью.ОчиститьСсылки(ПечатнаяФорма);
УправлениеПечатью.ОчиститьСсылки(Макет);
Возврат АдресХранилища;
КонецФункции
Преимущества данного метода: Полная независимость от установленного ПО Microsoft Office. Скорость формирования документов возрастает в разы, а код стабильно выполняется на серверах под управлением Linux.
---
Если вы ограничены жесткими требованиями использовать только старый формат .doc и COM-соединение, то ни в коем случае не используйте копирование через буфер обмена операционной системы (методы Selection.Copy() и Selection.Paste()). Буфер обмена пользователя будет постоянно очищаться, что вызовет сильное раздражение, а при одновременной работе нескольких пользователей этот метод будет давать постоянные сбои.
Правильный архитектурный подход при работе с COM — использование встроенного метода MS Word под названием InsertFile. Этот метод позволяет бесшовно импортировать один документ Word в конец другого.
Рассмотрим детальный пошаговый алгоритм:
InsertBreak(2).InsertFile(), передавая путь к только что сохраненному временному файлу текущего сотрудника.Проанализируем программную реализацию данного подхода:
Процедура ПечатьСпискаЧерез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 в операционной системе.