Как перенести печать через COM-объект Word из формы в модуль объекта внешней печатной формы

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

Перенос логики формирования печатных форм из модуля формы в модуль объекта — стандартная задача при разработке внешних печатных форм (ВПФ) для конфигураций на базе БСП (Библиотеки стандартных подсистем). Основная сложность заключается в том, что COMОбъект("Word.Application") требует наличия установленного Microsoft Word на той стороне, где выполняется код. Если код переносится в модуль объекта без учета контекста, он по умолчанию пытается выполниться на сервере, где Word зачастую не установлен.

Рассмотрим подробно, как правильно организовать этот процесс, сохранив возможность печати на стороне клиента, и разберем типичные ошибки, возникающие при переходе на новую архитектуру.

Разбор архитектурной проблемы: Клиент vs Сервер

Проанализируем ситуацию, описанную в теме. Изначально код работал в модуле формы с директивой &НаКлиенте. При попытке вызвать метод модуля объекта через РеквизитФормыВЗначение("Объект"), система принудительно переходит на сервер. Тип COM-объект не является сериализуемым, его нельзя просто передать между сервером и клиентом.

Если на сервере Microsoft Word не установлен или не настроен для работы под учетной записью службы 1С, вызов Новый COMОбъект("Word.Application") вернет ошибку. Выясним причину ошибки "Значение не является значением объектного типа (Documents)": она возникает из-за того, что блок Попытка...Исключение подавляет ошибку создания объекта, но выполнение кода продолжается, и попытка обращения к свойству Documents у неопределенной переменной вызывает критический сбой.

Решение через механизмы БСП: Вызов клиентского метода

Если ваша конфигурация использует БСП, самым правильным способом будет регистрация команды с типом ВызовКлиентскогоМетода. Это позволит коду, написанному в модуле объекта, выполняться в контексте клиентского приложения, имея доступ к локально установленному Microsoft Word.

Разберем по шагам настройку функции СведенияОВнешнейОбработке в модуле объекта:

  1. В таблице команд для поля Использование укажите значение ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовКлиентскогоМетода().
  2. Это заставит систему искать экспортную процедуру Печать именно на клиенте.

Пример заполнения регистрации команды:


Функция СведенияОВнешнейОбработке() Экспорт
    ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("2.2.2.1");
    
    НоваяКоманда = ПараметрыРегистрации.Команды.Добавить();
    НоваяКоманда.Представление = НСтр("ru = 'Уведомление о задолженности (Word)'");
    НоваяКоманда.Идентификатор = "УведомлениеОЗадолженности";
    НоваяКоманда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовКлиентскогоМетода();
    НоваяКоманда.ПоказыватьОповещение = Истина;
    
    Возврат ПараметрыРегистрации;
КонецФункции

Реализация процедуры Печать в модуле объекта

При использовании типа команды ВызовКлиентскогоМетода, в модуле объекта должна присутствовать экспортная процедура Печать. Несмотря на то, что она находится в модуле объекта, при таком вызове она будет исполняться на стороне клиента. Проанализируем правильную структуру этой процедуры:


&НаКлиенте
Процедура Печать(ИдентификаторКоманды, ОбъектыНазначения) Экспорт
    
    // Получаем данные для заполнения с сервера
    ДанныеДляПечати = ПолучитьДанныеДляПечатиНаСервере(ОбъектыНазначения);
    
    Попытка
        ОбъектВорд = Новый COMОбъект("Word.Application");
    Исключение
        Сообщить("Не удалось запустить Microsoft Word: " + ОписаниеОшибки());
        Возврат; // Обязательно выходим, чтобы не получить ошибку 'объектного типа'
    КонецПопытки;

    Для Каждого СсылкаИзСписка Из ОбъектыНазначения Цикл
        // Логика открытия шаблона и заполнения
        ШаблонПуть = "\\serv8\ftp\Шаблоны\Уведомление.docx";
        ОбъектВорд.Documents.Add(ШаблонПуть);
        Док = ОбъектВорд.ActiveDocument;
        
        // Пример заполнения закладок
        Попытка
            Док.Bookmarks("ФИО").Select();
            Док.Application.Selection.TypeText(ДанныеДляПечати[СсылкаИзСписка].ФИО);
        Исключение
            // Обработка отсутствия закладки
        КонецПопытки;
        
        ОбъектВорд.Visible = Истина;
    КонецЦикла;

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

Обратите внимание: данные для печати (запросы к базе) лучше собирать одним серверным вызовом перед циклом, чтобы избежать многократных обращений Клиент-Сервер внутри цикла по объектам.

Почему не стоит запускать Word на сервере

Рассмотрим ситуацию, когда разработчик все же решает оставить код на сервере. Это сопряжено с рядом проблем:

Если печать на сервере неизбежна (например, для формирования файлов в регламентном задании), рекомендуется отказаться от COM в пользу работы с XML структурой файла .docx через встроенные средства 1С (распаковка ZIP-архива документа) или использовать библиотеку БСП УправлениеПечатью.СформироватьПечатныеФормы().

Организация массовой печати

Посмотрим на пример из сообщения №23 форума. Автор жалуется, что выводится либо первая, либо последняя строка. Это происходит из-за того, что создание объекта Word и открытие документа вынесены за пределы цикла или внутри цикла не происходит сохранение/отображение каждого нового документа.

Проанализируем правильный алгоритм массовой печати:

  1. Один раз создаем COMОбъект("Word.Application").
  2. Начинаем цикл Для Каждого Ссылка Из ОбъектыНазначения.
  3. Внутри цикла вызываем ОбъектВорд.Documents.Add().
  4. Заполняем параметры.
  5. Либо оставляем документ открытым (ОбъектВорд.Visible = Истина), либо сохраняем его в файл и закрываем Док.Close(), чтобы не перегружать память компьютера пользователя.

Важные замечания по безопасности и путям файлов

При использовании сетевых путей типа \\serv8\ftp\, помните, что при выполнении НаКлиенте доступ к папке проверяется для текущего пользователя Windows. При выполнении НаСервере — для пользователя, под которым запущен сервер 1С. Чтобы избежать проблем с правами, рекомендуется загружать шаблоны Word в Макеты внешней обработки. В коде извлекайте их во временный файл с помощью ПолучитьМакет() и Макет.Записать() перед открытием в Word.

Таким образом, использование механизма ВызовКлиентскогоМетода в сочетании с грамотным сбором данных на сервере позволяет эффективно перенести печать из форм в модуль объекта, сохраняя стабильность работы приложения и удобство пользователя.

← На главную