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