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