Как поместить ДокументОбъект в реквизит формы произвольного типа и исправить ошибку метода ЗначениеВРеквизитФормы?

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

При разработке универсальных управляемых форм часто возникает задача отобразить или обработать данные объектов различных типов — например, документов разных видов. Однако при попытке программно связать объект с реквизитом формы разработчики нередко сталкиваются с ошибкой времени выполнения.

Проанализируем типовую ситуацию: в форме создан реквизит с именем Объект, для которого в конфигураторе указан произвольный тип (или тип не задан вовсе). В серверном обработчике формы выполняется следующий вызов:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    ЗначениеВРеквизитФормы(Параметры.Ключ.Объект.ПолучитьОбъект(), "Объект");
КонецПроцедуры

В результате платформа 1С прерывает выполнение и возвращает исключение:

Ошибка при вызове метода контекста (ЗначениеВРеквизитФормы): Недопустимое значение параметра (параметр номер '2')

Выясним причину ошибки

Для понимания корня проблемы обратимся к описанию метода ЗначениеВРеквизитФормы. Этот метод предназначен для преобразования прикладного серверного значения (такого как СправочникОбъект или ДокументОбъект) во внутреннюю структуру данных управляемой формы — ДанныеФормыСтруктура.

Чтобы платформа смогла выполнить сериализацию и заполнение реквизитов формы, второй параметр (имя целевого реквизита) должен указывать на реквизит, для которого строго определено преобразование в данные формы. Если в палитре свойств реквизита задан тип Произвольный, реквизит инициализируется со значением Неопределено. Платформа не располагает метаданными о составе полей, свойствах и табличных частях произвольного типа, поэтому преобразование прикладного объекта оказывается невозможным.

Также важно учитывать два ключевых ограничения платформы 1С:

  1. Запрет составных типов для объектов: в 1С нельзя создать реквизит с составным типом из нескольких вариантов ДокументОбъект. Составными могут быть исключительно ссылочные типы (например, ДокументСсылка).
  2. Ограничения клиент-серверного взаимодействия: типы группы *Объект существуют исключительно в контексте сервера. Их невозможно напрямую отправить на клиент или положить в реквизит формы произвольного типа для сохранения между вызовами.

Разберем основные практические способы обхода данного ограничения в зависимости от решаемой задачи.

Способ 1. Программное создание типизированного реквизита формы

Если для универсальной формы принципиально важно работать с интерфейсными элементами и табличными частями документа через механизм данных формы, мы можем типизировать форму динамически при её открытии. Для этого реквизит создается программно в момент, когда точный тип объекта уже известен.

Рассмотрим пошаговую реализацию в обработчике ПриСозданииНаСервере:

  1. Удаляем статический реквизит «Объект» произвольного типа из дерева реквизитов формы в конфигураторе.
  2. Определяем имя метаданных открываемого документа из переданных параметров.
  3. Формируем массив добавляемых реквизитов через РеквизитФормы с точным указанием типа ДокументОбъект.<ИмяДокумента>.
  4. Применяем изменения методом формы ИзменитьРеквизиты.
  5. Заполняем созданный реквизит с помощью ЗначениеВРеквизитФормы.

Посмотрим на пример программного кода:


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

Способ 2. Работа через ссылку документа

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

Рассмотрим данный подход:

  1. Создаем в форме реквизит ДокументСсылка с составным ссылочным типом (или типом ЛюбаяСсылка).
  2. При открытии формы помещаем переданную ссылку в этот реквизит обычным присваиванием, не используя метод ЗначениеВРеквизитФормы.
  3. При подтверждении операции в серверной процедуре получаем объект по ссылке, применяем необходимые изменения и записываем его.

Посмотрим, как выглядит работа со ссылкой:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // Простое присваивание ссылки реквизиту формы
    РеквизитСсылка = Параметры.ЗначениеСсылки;
КонецПроцедуры

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

Способ 3. Использование временного хранилища

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

Разберем этот сценарий по шагам:

  1. На форме создаем строковый реквизит АдресОбъектаВХранилище.
  2. В ПриСозданииНаСервере помещаем полученный объект во временное хранилище с уникальным идентификатором текущей формы.
  3. В последующих серверных вызовах извлекаем объект из хранилища, обновляем его свойства и при необходимости перезаписываем обратно в хранилище либо в базу данных.

Пример реализации механизма:


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

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

Подведем итог: попытка вызвать ЗначениеВРеквизитФормы для реквизита произвольного типа всегда приводит к исключению. Для построения универсальных решений следует либо динамически типизировать реквизиты формы через ИзменитьРеквизиты, либо работать с составными ссылочными типами данных и получать прикладной объект только в момент выполнения серверных алгоритмов.

← На главную