При разработке внешних обработок часто возникает задача добавить возможность вывода данных в печатную форму формата Microsoft Word (docx). Если в типовых печатных формах (ВПФ) всё жестко регламентировано БСП (Библиотекой стандартных подсистем), то во внешней обработке, которая не привязана к конкретному объекту метаданных, разработчики часто сталкиваются с трудностями. Рассмотрим подробнее, как правильно связать форму обработки, её модуль объекта и механизмы БСП для формирования офисных документов.
Представим ситуацию: у нас есть внешняя обработка, в которой заполняется список сотрудников. Нам нужно нажать кнопку на форме и получить заполненный шаблон Word. Проанализируем ситуацию: в стандартных ВПФ код печати обычно находится в модуле объекта в процедуре Печать. Во внешней обработке нам нужно воспроизвести этот механизм, сохранив разделение на клиентскую и серверную части.
Основная проблема, с которой сталкиваются программисты (как показано в обсуждении), — это ошибка Метод объекта не обнаружен (Найти) при вызове функций БСП. Это происходит из-за того, что структура коллекции печатных форм не инициализирована должным образом или идентификаторы макетов не совпадают. Выясним причину: функции БСП ожидают строго определенные типы данных (ТаблицаЗначений) и наличие определенных колонок.
На форме обработки создадим команду «Печать». Нам потребуется вызвать серверный метод, который сформирует коллекцию данных, а затем передать её на клиент для открытия печатной формы. Разберем по шагам реализацию в модуле формы:
&НаКлиенте
Процедура Печать(Команда)
// Вызываем серверную функцию для подготовки данных
КоллекцияПечатныхФормМассив = ПечатьНаСервере();
// Используем стандартный клиентский метод БСП для вывода окна печати
УправлениеПечатьюКлиент.ПечатьДокументов(КоллекцияПечатныхФормМассив);
КонецПроцедуры
&НаСервере
Функция ПечатьНаСервере()
// 1. Инициализируем коллекцию печатных форм
// Имя макета должно совпадать с тем, что мы будем использовать далее
КоллекцияПечатныхФорм = УправлениеПечатью.ПодготовитьКоллекциюПечатныхФорм("Извещение");
// 2. Получаем объект обработки для вызова методов модуля объекта
ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
// 3. Вызываем процедуру формирования данных (передаем коллекцию и наши данные, например, ТЗ сотрудников)
// Параметр "Сотрудники" — это реквизит формы или данные на форме
ОбъектыПечати = Новый СписокЗначений; // Для внешних обработок можно оставить пустым или заполнить фиктивно
ОбработкаОбъект.СформироватьПечатныеФормы(КоллекцияПечатныхФорм, ОбъектыПечати, Объект.Сотрудники);
// 4. Преобразуем ТЗ в массив для передачи на клиент (требование БСП)
Возврат ОбщегоНазначения.ТаблицаЗначенийВМассив(КоллекцияПечатныхФорм);
КонецФункции
Теперь посмотрим на пример реализации логики в модуле объекта. Здесь важно правильно использовать методы УправлениеПечатью.СведенияОПечатнойФорме и правильно работать с макетом Word, который должен храниться в обработке как Двоичные данные.
Процедура СформироватьПечатныеФормы(КоллекцияПечатныхФорм, ОбъектыПечати, ТаблицаСотрудников) Экспорт
// Получаем макет из состава обработки
ДвоичныеДанныеМакета = ПолучитьМакет("ПФ_DOC_Извещение");
Если ДвоичныеДанныеМакета <> Неопределено Тогда
// Получаем структуру сведений о печатной форме из коллекции
// Идентификатор "Извещение" должен СТРОГО совпадать с тем, что передали в ПодготовитьКоллекциюПечатныхФорм
ПечатнаяФорма = УправлениеПечатью.СведенияОПечатнойФорме(КоллекцияПечатныхФорм, "Извещение");
// Для Word-документов НЕЛЬЗЯ использовать ПечатнаяФорма.ТабличныйДокумент
// Вместо этого используется соответствие ОфисныеДокументы
// Инициализируем макет офисного документа
Макет = УправлениеПечатью.ИнициализироватьМакетОфисногоДокумента(ДвоичныеДанныеМакета, Неопределено);
ПечатнаяФормаДокумента = УправлениеПечатью.ИнициализироватьПечатнуюФорму(Неопределено, Макет.НастройкиСтраницыМакета, Макет);
// Описываем области (если в Word-файле есть Begin_Область / End_Область)
// Если печать простая (замена параметров), можно пропустить создание областей
// Пример заполнения параметров для каждого сотрудника
Для Каждого СтрокаСотрудник Из ТаблицаСотрудников Цикл
ДанныеЗаполнения = Новый Структура;
ДанныеЗаполнения.Вставить("ФИО", СтрокаСотрудник.ФИО);
ДанныеЗаполнения.Вставить("Должность", СтрокаСотрудник.Должность);
// Если документ многостраничный (на каждого сотрудника своя страница)
// УправлениеПечатью.ПрисоединитьОбласть(ПечатнаяФормаДокумента, Макет.Область("Основная"), ДанныеЗаполнения);
КонецЦикла;
// Формируем итоговый документ во временное хранилище
АдресХранилища = УправлениеПечатью.СформироватьДокумент(ПечатнаяФормаДокумента);
// Очищаем ресурсы
УправлениеПечатью.ОчиститьСсылки(ПечатнаяФормаДокумента, Ложь);
УправлениеПечатью.ОчиститьСсылки(Макет);
// Помещаем результат в структуру печатной формы
ОфисныеДокументы = Новый Соответствие;
ОфисныеДокументы.Вставить(АдресХранилища, "ИзвещениеСотрудников.docx");
ПечатнаяФорма.ОфисныеДокументы = ОфисныеДокументы;
КонецЕсли;
КонецПроцедуры
Проанализируем ситуацию с самим файлом Word. Чтобы БСП корректно заменила параметры в документе, они должны быть оформлены особым образом:
{v8 ИмяПараметра}.v8 Begin_ИмяОбласти и v8 End_ИмяОбласти.В ходе обсуждения автор столкнулся с ошибкой: Значение не является значением объектного типа (ТабличныйДокумент). Выясним причину: это классическая ошибка при попытке применить логику MXL-печати (табличных документов) к офисным документам.
Запомним правила:
ПечатнаяФорма.ТабличныйДокумент.ПечатнаяФорма.ОфисныеДокументы (тип Соответствие).УправлениеПечатью.СведенияОПечатнойФорме возвращает Неопределено, значит Идентификатор, переданный в неё, не найден в таблице, созданной функцией ПодготовитьКоллекциюПечатныхФорм.Если ваша база работает только на Windows и вам не нужно использовать механизмы БСП, можно воспользоваться прямым обращением к MS Word через COM. Рассмотрим этот пример для понимания разницы:
&НаКлиенте
Процедура ПечатьЧерезCOM(МакетБинарный)
ПутьКФайлу = ПолучитьИмяВременногоФайла("docx");
МакетБинарный.Записать(ПутьКФайлу);
Попытка
Word = Новый COMОбъект("Word.Application");
Документ = Word.Documents.Open(ПутьКФайлу);
Word.Visible = Истина;
// Поиск и замена текста
Замена = Документ.Content.Find;
Замена.Execute("[ФИО]", Ложь, Истина, Ложь, , , Истина, 1, , "Иванов Иван Иванович", 2);
Word.Activate();
Исключение
Сообщить("Ошибка при работе с Word: " + ОписаниеОшибки());
Если Word <> Неопределено Тогда
Word.Quit();
КонецЕсли;
КонецПопытки;
КонецПроцедуры
Однако такой метод считается устаревшим. Рекомендуем использовать стандартные механизмы УправлениеПечатью, так как они обеспечивают стабильность, корректную очистку временных файлов и независимость от установленного ПО на сервере.
Таким образом, для реализации печати из внешней обработки достаточно правильно инициализировать коллекции БСП в модуле формы и перенести логику заполнения параметров в модуль объекта, используя структуру ОфисныеДокументы.