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