Как открыть форму документа и получить ссылку на него при закрытии в управляемом приложении 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С:Предприятие 8.3 часто возникает ситуация, когда при работе с одним документом (назовем его Документ1) необходимо создать и заполнить связанный с ним Документ2. При этом Документ1 должен «дождаться», пока пользователь запишет Документ2, получить ссылку на него и подставить её в соответствующий реквизит. В обычном приложении эта задача решалась модальным открытием формы, но в управляемых формах подход кардинально изменился из-за асинхронности клиентского кода.

В этой статье мы подробно разберем, как правильно организовать этот процесс, рассмотрим механизмы оповещений и научимся корректно прерывать и возобновлять запись объекта.

Суть проблемы: почему код не останавливается?

Разберем типичную ошибку начинающего разработчика. Мы пытаемся открыть форму в процедуре ПередЗаписью и ожидаем, что выполнение кода замрет на строке открытия формы. Однако в управляемом приложении метод ОткрытьФорму() выполняется мгновенно: форма открывается, а код основной процедуры продолжает бежать дальше, не дожидаясь действий пользователя. В итоге документ записывается с пустым реквизитом, так как Документ2 еще даже не успели заполнить.

Для решения этой задачи нам потребуется реализовать механизм «прерывания-возобновления» процесса записи с использованием Описания Оповещения.

Способ 1: Использование ОписанияОповещенияОЗакрытии (Рекомендуемый)

Это самый современный и правильный способ с точки зрения архитектуры платформы 8.3. Мы разделим нашу логику на две части: инициирующую процедуру и процедуру-обработчик завершения.

Рассмотрим пошагово, как это реализовать. Сначала проанализируем код в модуле формы Документ1:


&НаКлиенте
Переменная ВыполняетсяДополнительнаяЗапись; // Флаг для предотвращения зацикливания

&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
    
    // Если мы уже получили ссылку и инициировали повторную запись, просто выходим
    Если ВыполняетсяДополнительнаяЗапись = Истина Тогда
        ВыполняетсяДополнительнаяЗапись = Ложь;
        Возврат;
    КонецЕсли;

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

Теперь выясним, что происходит, когда пользователь закрывает форму Документ2. В процедуру ОбработкаЗавершенияСоздания придет управление. Нам нужно проанализировать результат и, если документ был записан, подставить ссылку и вызвать запись заново.


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

Важный момент: Чтобы в переменную Результат попала ссылка, в форме Документ2 (которую мы открываем) желательно переопределить команду закрытия или использовать событие ПослеЗаписи, вызывая Закрыть(Объект.Ссылка).

Способ 2: Использование режима выбора и события ОбработкаВыбора

Если Документ2 является вспомогательным, мы можем открыть его форму в режиме выбора. Это позволит использовать стандартное событие ОбработкаВыбора.

  1. В процедуре ПередЗаписью устанавливаем Отказ = Истина.
  2. Открываем форму Документ2, передавая параметр РежимВыбора = Истина.
  3. В форме Документ2 после записи вызываем ОповеститьОВыборе(Объект.Ссылка).
  4. В основной форме Документ1 ловим это событие:

&НаКлиенте
Процедура ОбработкаВыбора(ВыбранноеЗначение, ИсточникВыбора)
    Если ТипЗнч(ВыбранноеЗначение) = Тип("ДокументСсылка.Документ2") Тогда
        Объект.Документ2 = ВыбранноеЗначение;
        // Повторяем попытку записи
        ВыполняетсяДополнительнаяЗапись = Истина;
        ЭтаФорма.Записать();
    КонецЕсли;
КонецПроцедуры

Этот метод хорош тем, что он более «родной» для платформы, но он требует, чтобы форма второго документа умела работать в режиме выбора и возвращать ссылку. Рассмотрим подробнее, как это реализовать во второй форме:


&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
    // Если форма открыта для выбора, возвращаем ссылку сразу после записи
    Если Параметры.РежимВыбора Тогда
        ОповеститьОВыборе(Объект.Ссылка);
    КонецЕсли;
КонецПроцедуры

Способ 3: Механизм глобальных оповещений (Оповестить)

Иногда документы логически слабо связаны, и мы не хотим жестко привязывать формы друг к другу через ОписаниеОповещения. В этом случае поможет метод Оповестить().

Проанализируем ситуацию: Документ1 открывает форму Документ2 и «слушает» эфир. Как только Документ2 сообщает: «Я записан, вот моя ссылка», Документ1 подхватывает эти данные.

В форме Документ1:


&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
    Если ИмяСобытия = "СозданДокумент2" Тогда
        Объект.Документ2 = Параметр;
        // Далее логика перезаписи
    КонецЕсли;
КонецПроцедуры

В форме Документ2 после успешной записи:


&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
    Оповестить("СозданДокумент2", Объект.Ссылка, ЭтаФорма);
КонецПроцедуры

Сравнение методов и рекомендации

Разберем, какой метод выбрать в зависимости от ситуации:

Проанализируем важный нюанс: при программном вызове ЭтаФорма.Записать() в обработчике оповещения, вы снова попадете в процедуру ПередЗаписью. Если не использовать флаг (переменную ВыполняетсяДополнительнаяЗапись), программа уйдет в бесконечный цикл, бесконечно открывая новые формы Документа2. Всегда следите за состоянием этого флага!

Также стоит помнить о пользовательском опыте. Блокировка интерфейса (РежимОткрытияОкнаФормы.БлокироватьВесьИнтерфейс) при открытии второй формы гарантирует, что пользователь не изменит ничего в первом документе, пока не закончит работу со вторым. Это предотвращает рассинхронизацию данных на клиенте и сервере.

Таким образом, мы выяснили, что отсутствие «остановки кода» в управляемом приложении — это не ограничение, а особенность архитектуры, которая требует событийного подхода. Используя ОписаниеОповещения и грамотное управление флагами записи, вы сможете реализовать любые сложные цепочки создания документов.

← На главную