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