Как вернуть результат работы и реквизиты после закрытия формы в 1С 8.3

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

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

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

Решение 1. Использование ОписанияОповещения и метода Закрыть()

Это классический способ, рекомендуемый платформой для асинхронной работы. Суть метода заключается в том, что при открытии подчиненной формы мы передаем в нее специальный объект ОписаниеОповещения. При закрытии формы мы передаем параметры в метод Закрыть(), и платформа автоматически передает их в процедуру-обработчик родительской формы.

Разберем по шагам, как это реализовать.

Шаг 1. Вызов формы из родительской формы (обработки)

В модуле формы-владельца мы создаем описание оповещения, указывая имя экспортной процедуры, которая примет результат, и открываем форму:


&НаКлиенте
Процедура СоздатьНовыйДокумент(Команда)

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

КонецПроцедуры

&НаКлиенте
Процедура СоздатьЗаказЗавершение(Результат, ДополнительныеПараметры) Экспорт

    // Проверяем, что результат был передан и содержит нужные нам ключи
    Если ТипЗнч(Результат) = Тип("Структура") Тогда
        СсылкаНаДокумент = Результат.Ссылка;
        Комментарий = Результат.Комментарий;
        
        // Выполняем дальнейшую логику в форме обработки
        ПоказатьПредупреждение(, "Документ успешно создан: " + Строка(СсылкаНаДокумент));
    КонецЕсли;

КонецПроцедуры

Шаг 2. Передача данных из закрываемой формы

Чтобы родительская форма получила эти данные, в открываемой форме документа нам необходимо передать их в метод Закрыть(). Если мы используем собственную кнопку (например, «Готово» или «Оформить»), то код будет выглядеть следующим образом:


&НаКлиенте
Процедура ОформитьИЗакрыть(Команда)

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

КонецПроцедуры

Внимание: Опасность бесконечной рекурсии в событии ПередЗакрытием

Очень часто разработчики пытаются перехватить стандартное закрытие формы (например, по кнопке «Провести и закрыть» или по системному «крестику» окна) через обработчик события ПередЗакрытием и вызывают в нем метод Закрыть(ПараметрыЗакрытия).

Внимание! Это грубейшая ошибка, которая приводит к бесконечному циклу и аварийному завершению работы программы (Stack Overflow / переполнение стека).

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


&НаКлиенте
Перем РазрешитьЗакрытие; // Переменная модуля формы (сохраняет значение между вызовами процедур)

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

Решение 2. Использование механизма «Оповестить»

Если вам не хочется связывать формы жесткой зависимостью через ОписаниеОповещения, либо форма документа может быть закрыта самыми разными стандартными способами, можно использовать глобальный механизм оповещений платформы. Этот метод особенно удобен, когда документ проводится («Провести и закрыть») — при проведении он сначала записывается в базу, и мы можем передать его ссылку родителю еще до физического закрытия формы.

Проанализируем этот подход на примере события ПослеЗаписиНаКлиенте формы документа:


&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
    
    // Оповещаем всю систему (и форму-владельца в частности) о том, что документ создан
    ПараметрыОповещения = Новый Структура;
    ПараметрыОповещения.Вставить("Ссылка", Объект.Ссылка);
    ПараметрыОповещения.Вставить("Сумма", Объект.СуммаДокумента);
    
    Оповестить("ЗаказУспешноСоздан", ПараметрыОповещения, ЭтотОбъект);

КонецПроцедуры

В вызывающей форме (обработке) мы должны перехватить это событие в стандартной процедуре ОбработкаОповещения:


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

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

Решение 3. Стандартный механизм выбора (без написания лишнего кода)

Выясним, можно ли решить задачу стандартными средствами платформы 1С. Если ваша единственная цель — открыть форму нового документа, дождаться его создания и получить ссылку обратно, вы можете использовать встроенный механизм выбора. При этом вам не нужно вручную создавать ОписаниеОповещения.

Для этого выполняем следующие действия:

  1. При открытии формы документа передаем в качестве параметра Владелец вызывающую форму (обработку).
  2. В открываемой форме документа при записи или по кнопке завершения вызываем метод ОповеститьОВыборе(Объект.Ссылка).
  3. В вызывающей форме обрабатываем результат в стандартном событии ОбработкаВыбора.

Рассмотрим пример кода в форме обработки:


&НаКлиенте
Процедура СоздатьСВыбором(Команда)

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

КонецПроцедуры

&НаКлиенте
Процедура ОбработкаВыбора(ВыбранноеЗначение, ИсточникВыбора)

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

КонецПроцедуры

В форме документа достаточно написать:


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

Бонус: Почему переданные параметры не заполняют реквизиты нового документа?

Часто разработчики сталкиваются с проблемой: при вызове ОткрытьФорму() передается структура параметров (например, тПараметры.Вставить("Комментарий", "Тест")), однако при открытии формы нового документа поле «Комментарий» остается пустым.

Причина: Платформа не сопоставляет переданные на верхнем уровне параметры со свойствами заполняемого объекта по умолчанию. Они просто помещаются в коллекцию параметров формы Параметры.

Как сделать правильно: Чтобы платформа автоматически заполнила реквизиты нового документа, необходимо использовать предопределенный параметр ЗначенияЗаполнения. Платформа распознает его, передает в обработчик модуля объекта ОбработкаЗаполнения и сама сопоставляет данные с реквизитами.

Рассмотрим правильный пример передачи параметров из формы-родителя:


&НаКлиенте
Процедура СоздатьСЗаполнением(Команда)

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

КонецПроцедуры

При таком подходе вам не придется писать код заполнения в самой форме документа — платформа сделает все автоматически при инициализации объекта.

← На главную