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