Как передать и заполнить реквизит объектного типа в управляемой форме обработки 1С?

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

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

Причины возникновения ошибки

Выясним причину такого поведения платформы. В управляемом приложении клиентская и серверная части строго разделены, а работа с данными формы кардинально отличается от обычных форм:

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

Разберем основные варианты правильной реализации этой логики в зависимости от решаемой бизнес-задачи.

Способ 1. Передача через параметры и методы конвертации формы (для несохраненных объектов)

Если нам необходимо передать в форму обработки еще не записанный в базу документ со всеми внесенными изменениями, используем временное хранилище и контекстные методы управляемой формы.

Рассмотрим, как выглядит этот процесс по шагам. На стороне вызывающего серверного кода помещаем объект во временное хранилище и передаем полученный адрес в форму:


// Серверный контекст вызывающего модуля
АдресВоВременномХранилище = ПоместитьВоВременноеХранилище(НашДокументОбъект, Новый УникальныйИдентификатор);

ПараметрыОткрытия = Новый Структура;
ПараметрыОткрытия.Вставить("АдресОбъектаВХранилище", АдресВоВременномХранилище);

// Открываем форму обработки с передачей параметров
ОткрытьФорму("Обработка.МояОбработка.Форма.Форма", ПараметрыОткрытия);

В модуле самой управляемой формы обработки в обработчике ПриСозданииНаСервере получаем объект из хранилища и загружаем его в реквизит формы с помощью специального метода ЗначениеВРеквизитФормы:


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

Важно помнить: метод ЗначениеВРеквизитФормы является методом контекста самой формы и не может быть вызван через точку из внешнего общего модуля.

Способ 2. Использование метода КопироватьДанныеФормы

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

Посмотрим на пример в обработчике инициализации формы обработки:


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

Способ 3. Передача ссылки вместо объекта (рекомендуемый паттерн)

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

Разберем по шагам правильную организацию работы:

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

Способ 4. Перенос реквизита в метаданные объекта обработки

Если логика обработки предполагает плотную работу с реквизитом на сервере через модуль объекта, эффективнее объявить реквизит не в форме, а в самом объекте обработки в дереве метаданных конфигуратора (например, реквизит ДокументОбъект или ДокументСсылка у объекта Обработка.МояОбработка).

В таком случае в форме мы работаем с основным реквизитом Объект:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Обращение идет к реквизиту объекта обработки через реквизит формы "Объект"
    Если Параметры.Свойство("ДокументСсылка") Тогда
        Объект.ДокументСсылка = Параметры.ДокументСсылка;
    КонецЕсли;
    
КонецПроцедуры

Для выполнения сложных серверных алгоритмов в модуле формы можно преобразовать основной реквизит формы в экземпляр объекта обработки через метод РеквизитФормыВЗначение("Объект"), выполнить нужные процедуры и при необходимости вернуть результат обратно в форму через ЗначениеВРеквизитФормы(ОбработкаОбъект, "Объект").

← На главную