Как предотвратить закрытие формы и выполнить код при использовании команды «Провести и закрыть»?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

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

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

Анализ проблемы: почему закрывается форма-владелец?

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

Когда во вспомогательной форме мы пытаемся обратиться к ВладелецФормы.ПричинаВычерка, мы получаем ошибку. Это происходит потому, что ВладелецФормы уже равен Неопределено или указывает на объект, контекст которого уничтожен. Платформа не ждет завершения работы вашего кода в ПослеЗаписи, если была инициирована команда закрытия.

Решение №1: Использование параметра ПринудительноЗакрытьФорму

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

Рассмотрим пример кода в обработчике ПередЗаписью на клиенте:


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

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

Решение №2: Асинхронный вызов через ОписаниеОповещения

Современный стандарт разработки в 1С (особенно в режиме «Немодальности») требует отказа от метода ОткрытьМодально. Вместо этого мы должны использовать асинхронное открытие формы с обработкой результата. Это гарантирует, что контекст выполнения не будет «потерян».

Рассмотрим, как правильно реализовать выбор причины вычерка в УТ 11.4. Выясним алгоритм: сначала мы открываем форму выбора, передаем ей управление, а после закрытия этой формы срабатывает процедура-оповещение в основной форме.

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


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

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

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


&НаКлиенте
Процедура ВыбратьИЗакрыть(Команда)
    Закрыть(РеквизитВыбраннаяПричина);
КонецПроцедуры

Решение №3: Подмена стандартных команд

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

Проанализируем этот путь по шагам:

  1. В редакторе формы перейдите на закладку «Командный интерфейс» или в свойствах формы снимите флажок «Автозаполнение» у командной панели.
  2. Добавьте свою команду ПровестиИЗакрытьСПроверкой.
  3. В обработчике команды сначала вызовите диалог выбора причины.
  4. В оповещении о закрытии диалога вызовите метод Записать(Новый Структура("РежимЗаписи", РежимЗаписиДокумента.Проведение)).
  5. После успешной записи закройте форму программно.

Важные замечания по архитектуре УТ 11.4

Как упоминалось в обсуждении, если вы работаете с конфигурацией Управление торговлей 11.4, помните, что изменение реквизитов формы после записи объекта в базу данных часто не имеет смысла, если эти данные не сохраняются в самом документе или связанных регистрах. Если ваша задача — сделать «вычерк» в заказе на основании реализации, правильнее будет:

Резюмируем: Основная причина ошибки — попытка асинхронного взаимодействия с формой, которая уже помечена платформой на удаление из памяти. Используйте параметр ПринудительноЗакрытьФорму = Ложь для блокировки автоматического закрытия или переходите на событийную модель с ОписаниеОповещения.

← На главную