Почему не открывается подчиненная форма при записи документа в 1С и как это исправить?

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

Разработка интерфейсных решений в обычных формах на платформе 1С:Предприятие часто преподносит сюрпризы, связанные с логикой взаимодействия окон. Типичный сценарий: при нажатии кнопки «ОК» (которая записывает, проводит и закрывает документ) необходимо открыть вспомогательную форму — например, форму логирования изменений или корректировки записей регистров. Однако на практике разработчики сталкиваются с тем, что подчиненное окно либо вообще не появляется на экране, либо закрывается мгновенно, а переданная ссылка нового документа оказывается пустой.

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

---

Почему ломается стандартная логика открытия форм?

Для начала выясним причину проблемы. В исходном коде первой публикации мы видим следующую конструкцию:


ФормаОбработкиУдаления = ОткрытьФорму("РегистрСведений.ЛогРедактированияДокументов.Форма.КорректировкаРеализации", , ЭтаФорма);

Давайте разберем по шагам, к чему приводит этот вызов, когда пользователь нажимает кнопку «ОК»:

  1. Кнопка «ОК» инициирует запись и проведение документа.
  2. В обработчике события ПослеЗаписи или ПриЗаписи срабатывает указанный выше код.
  3. Третьим параметром в функцию ОткрытьФорму передается ЭтаФорма (форма самого документа). Это означает, что открываемая форма регистра становится подчиненной по отношению к форме документа.
  4. Платформа успешно открывает вспомогательную форму.
  5. Сразу после этого завершается стандартный цикл обработки кнопки «ОК»: форма документа закрывается и уничтожается из оперативной памяти.
  6. Платформа 1С строго следует правилу: при закрытии формы-владельца автоматически закрываются все подчиненные ей формы. В итоге открытая форма регистра закрывается за доли миллисекунды. Пользователь даже не успевает заметить ее появление на экране, из-за чего создается иллюзия, что форма «не вызывается».

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

---

Проблема пустой ссылки при записи нового документа

Второй критический баг, с которым сталкиваются разработчики в данном сценарии, — пустая ссылка при попытке логирования данных нового (впервые создаваемого) документа.

Если мы пытаемся открыть подчиненное окно из события ПриЗаписи, транзакция записи в базу данных еще не завершена. Ссылка в памяти компьютера у объекта уже существует, но в СУБД физически отсутствуют данные табличной части по этой ссылке. Если в открываемой форме регистра выполняется запрос к табличным частям документа через СУБД:


// Данный запрос вернет пустой результат для нового документа,
// так как транзакция записи еще не зафиксирована (Commit)
Запрос.УстановитьПараметр("Ссылка", СсылкаНаДокумент);
Выборка = Запрос.Выполнить().Выгрузить();

Данные внутри транзакции еще «не видны» внешним сессиям и запросам, которые выполняются вне контекста этого объектного метода записи. Это классическая проблема грязного чтения в базах данных.

---

Решение №1: Независимый запуск формы и передача контекста «сверху вниз»

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

Мы откроем форму абсолютно независимо, предварительно создав в ней локальные реквизиты для хранения контекста документа. Таким образом, закрытие родительского документа никак не повлияет на работоспособность нашего лога.

Шаг 1. Подготовка формы регистра (приемника)

В форме регистра сведений (КорректировкаРеализации) добавьте в область переменных модуля или в свойства формы реквизиты:

Шаг 2. Код открытия в форме документа (источника)

В форме документа, в обработчике события, отвечающего за запись изменений (например, перед закрытием или в процессе интерактивного проведения), мы получаем форму программно, наполняем её локальные свойства актуальными данными из оперативной памяти и открываем независимо от родительского окна:


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

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

---

Решение №2: Отложенный вызов через обработчик ожидания

Попытка интерактивного открытия окон непосредственно в момент записи объекта СУБД (внутри процедур ПриЗаписи или ПослеЗаписи) считается плохой практикой программирования в 1С. Это может приводить к неявным блокировкам транзакций и некорректной отрисовке интерфейса.

Для реализации чистого разделения процессов записи и отображения интерфейса мы применим метод отложенного вызова с помощью однократного обработчика ожидания.

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

  1. В событии формы документа ПослеЗаписи мы регистрируем системный обработчик ожидания с минимальной задержкой.
  2. Процедура записи успешно завершается, транзакция фиксируется в СУБД, а блокировки снимаются.
  3. Через 0.1 секунды платформа автоматически вызывает нашу процедуру, в которой происходит безопасное и корректное открытие новой формы.

Пример реализации кода в модуле формы документа:


Процедура ПослеЗаписи()
    
    // Подключаем однократный обработчик ожидания.
    // Интервал 0.1 сек достаточен для завершения всех транзакционных процессов.
    ПодключитьОбработчикОжидания("ВыполнитьОтложенноеОткрытиеЛога", 0.1, Истина);
    
КонецПроцедуры

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

Использование метода ПодключитьОбработчикОжидания с третьим параметром Истина (однократный вызов) гарантирует, что процедура не зациклится и выполнится ровно один раз после успешного сохранения данных.

---

Подведем итоги

Для предотвращения ошибок при работе с жизненным циклом форм в 1С важно соблюдать следующие правила:

← На главную