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