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