При разработке печатных форм, особенно внешних, программисты часто сталкиваются с ограничением: метод Показать() объекта ТабличныйДокумент работает только на клиенте. Если логика формирования документов находится в модуле объекта (который исполняется на сервере), прямой вызов окна просмотра невозможен. Ситуация усложняется, когда нужно вывести не один, а сразу несколько разных документов (например, комплекты документов по разным товарам). Рассмотрим подробнее, как решить эту задачу, сохранив структуру кода и обеспечив удобство пользователя.
Прежде чем переходить к решениям, проанализируем причину проблемы. Модуль объекта внешней обработки или документа выполняется на сервере. Серверная часть 1С ничего не знает об интерфейсе пользователя, окнах и экранных формах. Метод Показать() предназначен для визуализации данных в клиентском приложении. Следовательно, наша задача сводится к тому, чтобы сформировать данные на сервере, передать их на клиент и уже там инициировать открытие окон.
Это наиболее гибкий способ. Вместо того чтобы пытаться вызвать показ документа сразу, мы соберем все сформированные табличные документы в массив и вернем этот массив в вызывающую форму. Разберем этот процесс по шагам.
Шаг 1. Модификация серверной функции в модуле объекта
В модуле объекта создадим функцию, которая будет возвращать массив. Проанализируем пример кода:
Функция СформироватьПакетДокументов(МассивОбъектов) Экспорт
МассивРезультатов = Новый Массив;
Для каждого Ссылка Из МассивОбъектов Цикл
ТабДок = Новый ТабличныйДокумент;
// Здесь логика заполнения ТабДок (получение макета, вывод областей)
// ...
Область = Макет.ПолучитьОбласть("Заголовок");
ТабДок.Вывести(Область);
МассивРезультатов.Добавить(ТабДок);
КонецЦикла;
Возврат МассивРезультатов;
КонецФункции
Шаг 2. Вызов на стороне клиента
В модуле формы, откуда инициируется печать, мы принимаем этот массив и обходим его в цикле. Важно помнить, что ТабличныйДокумент является мутабельным типом, но его передача с сервера на клиент в пределах одного контекста вызова обычно разрешена.
&НаКлиенте
Процедура ВыполнитьПечать(Команда)
РезультатПечати = ПолучитьДокументыНаСервере();
Для каждого ТекущийДок Из РезультатПечати Цикл
ТекущийДок.Показать("Печать: " + ТекущийДок.ИмяПараметровПечати);
КонецЦикла;
КонецПроцедуры
&НаСервере
Функция ПолучитьДокументыНаСервере()
ОбъектОбработка = РеквизитФормыВЗначение("Объект");
Возврат ОбъектОбработка.СформироватьПакетДокументов(ОбъектыНазначения);
КонецФункции
Если ваша обработка подключается через БСП (Библиотека стандартных подсистем), стандартный механизм «Вызов серверного метода» ожидает заполнения коллекции печатных форм. Если мы хотим полностью контролировать процесс вывода, проанализируем вариант с изменением типа команды в функции СведенияОВнешнейОбработке.
Установим использование команды в значение ОткрытиеФормы. Это позволит системе при нажатии кнопки «Печать» не сразу бежать в модуль объекта, а открывать форму обработки. В этой форме мы можем автоматически запустить процесс формирования документов в процедуре ПриСозданииНаСервере или через обработчик ожидания на клиенте.
Для того чтобы форма знала, какие объекты печатать, БСП передает параметр ОбъектыНазначения. Посмотрим, как его извлечь:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если Параметры.Свойство("ОбъектыНазначения") Тогда
МассивСсылок = Параметры.ОбъектыНазначения;
// Теперь у нас есть ссылки на документы, для которых вызвана печать
КонецЕсли;
КонецПроцедуры
После получения ссылок мы вызываем серверный код формирования, получаем массив табличных документов и выводим их методом Показать() в цикле на клиенте, как в первом решении.
Если документов очень много (например, 50), открытие 50 отдельных окон загромождает интерфейс и мешает пользователю. Выясним, как сделать это элегантнее с помощью объекта ПакетОтображаемыхДокументов. Этот объект позволяет показать один интерфейс, в котором слева будет список документов (оглавление), а справа — выбранный документ.
Рассмотрим пример формирования пакета:
&НаСервере
Функция СформироватьПакетДляПоказа()
Пакет = Новый ПакетОтображаемыхДокументов;
Для Каждого Стр Из ДанныеДляПечати Цикл
ТабДок = СформироватьОдинДокумент(Стр);
ЭлементПакета = Пакет.Состав.Добавить();
ЭлементПакета.Данные = ПоместитьВоВременноеХранилище(ТабДок, УникальныйИдентификатор);
ЭлементПакета.Наименование = "Документ №" + Стр.Номер;
КонецЦикла;
Возврат Пакет;
КонецФункции
На клиенте достаточно вызвать Пакет.Показать(). Это значительно повышает удобство работы пользователя, объединяя разрозненные документы в одну логическую сущность.
При передаче больших объемов данных (десятки тяжелых табличных документов с картинками и QR-кодами) рекомендуется использовать Временное хранилище. Вместо передачи массива объектов напрямую, поместим массив во временное хранилище на сервере, передадим на клиент адрес (строку), а на клиенте извлечем данные. Это снижает риск возникновения ошибок сериализации мутабельных значений и оптимизирует объем передаваемых данных между сервером и клиентом.
Резюме: для вывода нескольких документов из модуля объекта необходимо вынести стадию визуализации (метод Показать()) на сторону клиентского модуля формы, используя массив документов или специальный объект ПакетОтображаемыхДокументов.