При разработке внешних печатных форм (ВПФ) программисты часто сталкиваются с архитектурным ограничением платформы «1С:Предприятие 8.3»: необходимо сформировать документ MS Word на стороне клиента (так как на сервере Word не установлен), но стандартный механизм печати инициируется в модуле объекта, который всегда выполняется на сервере. Попытка вызвать клиентский метод из серверного контекста или передать объект с сервера на клиент напрямую приводит к ошибкам передачи данных. Рассмотрим подробно, как правильно организовать этот процесс, используя возможности Библиотеки стандартных подсистем (БСП) и принципы разделения контекстов.
Разберем ситуацию, описанную в начале темы. Автор пытается использовать функцию РеквизитФормыВЗначение("Объект") на сервере, получает объект внешней обработки и пытается вернуть его на клиент. Проблема заключается в том, что прикладные объекты (СправочникОбъект, ДокументОбъект, ОбработкаОбъект) существуют только на сервере. Они не могут быть переданы на клиент в «чистом» виде. На клиенте мы работаем только с данными формы (ДанныеФормыСтруктура), которые являются лишь легкой проекцией объекта.
Кроме того, директива &НаКлиенте в модуле объекта не работает. Весь код модуля объекта априори считается серверным. Если вам нужно вызвать Word через COM-соединение, а Word установлен только на рабочих станциях пользователей, вам критически важно оказаться в контексте клиентской процедуры модуля формы или общего модуля с признаком «Клиент».
Если вы разрабатываете внешнюю печатную форму для конфигурации на базе БСП, самым правильным решением будет использование варианта запуска ВызовКлиентскогоМетода. Это позволяет системе при нажатии кнопки «Печать» обращаться не к модулю объекта, а сразу к модулю формы вашей обработки.
Рассмотрим по шагам, как это реализовать:
Шаг 1. Настройка функции СведенияОВнешнейОбработке
В модуле объекта необходимо правильно описать команду печати. Вместо стандартного вызова серверного метода мы указываем клиентский. Проанализируем пример заполнения таблицы команд:
Функция СведенияОВнешнейОбработке() Экспорт
ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("2.2.2.1");
НоваяКоманда = ПараметрыРегистрации.Команды.Добавить();
НоваяКоманда.Представление = "Печать в MS Word";
НоваяКоманда.Идентификатор = "ПечатьWordНаКлиенте";
// Важнейший момент: указываем тип запуска
НоваяКоманда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовКлиентскогоМетода();
НоваяКоманда.ПоказыватьОповещение = Истина;
Возврат ПараметрыРегистрации;
КонецФункции
Шаг 2. Создание процедуры в модуле формы
Так как мы указали ВызовКлиентскогоМетода, система будет искать в модуле формы вашей внешней обработки экспортную процедуру Печать. Важно соблюдать точную сигнатуру метода:
&НаКлиенте
Процедура Печать(ИдентификаторКоманды, ОбъектыНазначения) Экспорт
// 1. Собираем данные на сервере
ДанныеДляПечати = ПодготовитьДанныеНаСервере(ОбъектыНазначения);
// 2. Получаем макет (двоичные данные) с сервера
ДвоичныеДанныеМакета = ПолучитьМакетНаСервере();
// 3. Запускаем формирование Word на клиенте
СформироватьДокументWord(ДанныеДляПечати, ДвоичныеДанныеМакета);
КонецПроцедуры
Поскольку мы не можем передать объект, мы должны подготовить все необходимые данные для заполнения шаблона Word (реквизиты шапки, строки табличных частей) и упаковать их в универсальную структуру или массив структур. Выясним, как это выглядит на практике:
&НаСервере
Функция ПодготовитьДанныеНаСервере(ОбъектыНазначения)
Результат = Новый Массив;
Для Каждого СсылкаИзСписка Из ОбъектыНазначения Цикл
СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("Номер", СсылкаИзСписка.Номер);
СтруктураДанных.Вставить("Дата", Формат(СсылкаИзСписка.Дата, "ДЛФ=DD"));
СтруктураДанных.Вставить("Контрагент", СокрЛП(СсылкаИзСписка.Контрагент));
// Добавляем другие необходимые поля...
Результат.Добавить(СтруктураДанных);
КонецЦикла;
Возврат Результат;
КонецФункции
Когда данные и двоичные данные макета (файла .doc/.docx) получены на клиенте, мы можем приступить к генерации. Разберем пошагово алгоритм взаимодействия с MS Word:
COMОбъект("Word.Application").Find.Execute для поиска и замены параметров в тексте (например, заменяем "[НомерДокумента]" на реальное значение).Пример клиентского кода для работы с Word:
&НаКлиенте
Процедура СформироватьДокументWord(ДанныеДляПечати, ДвоичныеДанныеМакета)
Попытка
WordApp = Новый COMОбъект("Word.Application");
Исключение
Сообщить("MS Word не установлен на данном компьютере!");
Возврат;
КонецПопытки;
ИмяФайлаШаблона = ПолучитьИмяВременногоФайла("docx");
ДвоичныеДанныеМакета.Записать(ИмяФайлаШаблона);
Для Каждого ДанныеОбъекта Из ДанныеДляПечати Цикл
Документ = WordApp.Documents.Open(ИмяФайлаШаблона);
WordApp.Visible = Ложь;
// Пример замены текста
ReplaceText = Документ.Content.Find;
ReplaceText.Execute("[Номер]", , , , , , , , , ДанныеОбъекта.Номер, 2);
ReplaceText.Execute("[Дата]", , , , , , , , , ДанныеОбъекта.Дата, 2);
WordApp.Visible = Истина;
Документ.Activate();
КонецЦикла;
КонецПроцедуры
Проанализируем несколько критических моментов, которые могут помешать работе кода:
COMОбъект в этом режиме запрещено. При подключении обработки в справочнике «Дополнительные отчеты и обработки» необходимо настроить разрешения (профили безопасности) или убедиться, что обработка выполняется с необходимыми привилегиями.winword.exe может остаться «висеть» в памяти. Всегда используйте блоки Попытка...Исключение и не забывайте закрывать документы и выходить из приложения (WordApp.Quit()), если вы не планируете оставлять его открытым для пользователя.Таким образом, для запуска клиентской процедуры из внешней печатной формы мы отказываемся от прямого вызова из модуля объекта и переносим логику в модуль формы, используя штатный механизм БСП ВызовКлиентскогоМетода. Это позволяет соблюсти архитектурные требования платформы и успешно работать с локальными офисными приложениями.