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