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

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

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

Основная сложность заключается в том, что на клиенте и в контексте формы мы работаем не с самими прикладными объектами (такими как СправочникОбъект или ДокументОбъект), а с их специальными UI-проекциями — объектами типа ДанныеФормыСтруктура. В этой статье мы подробно разберем, как правильно конвертировать эти данные в полноценный прикладной объект, как избежать типичных ошибок платформы и какие альтернативные, более безопасные паттерны разработки существуют.

Способ 1. Использование функции ДанныеФормыВЗначение()

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

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

Рассмотрим пример правильного вызова этой функции. Допустим, мы получили данные из вспомогательной формы (переменная ДанныеФормыДругогоСправочника) и хотим создать на их основе реальный объект справочника «Контрагенты»:


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

// Теперь у нас в руках полноценный СправочникОбъект, который можно записать
НовыйОбъект.Записать();

Важный нюанс: В отличие от РеквизитФормыВЗначение(), где платформа способна самостоятельно определить тип по метаданным реквизита, для функции ДанныеФормыВЗначение() указание типа во втором параметре является строго обязательным. Если передать туда пустую ссылку или неверный тип, платформа выбросит ошибку «Несоответствие типов (параметр номер '2')».

Устранение ошибки: «Нельзя изменять поле, содержащее объект данных формы»

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

Выясним причину этого поведения платформы. По умолчанию параметры в процедуры и функции 1С передаются по ссылке (by reference). Объект типа ДанныеФормыСтруктура является специальным системным прокси-объектом, связывающим клиентскую часть с сервером. Платформа жестко контролирует его жизненный цикл. Если такой объект передается в процедуру по ссылке, система предполагает, что внутри вызываемого метода значение этой переменной может быть полностью перезаписано (например, ей будет присвоено значение Неопределено). Чтобы не допустить разрушения связи формы с ее данными, платформа превентивно блокирует такой вызов.

Существует два простых способа обойти это поведение:

  1. Использование промежуточной переменной. Перед передачей данных формы в процедуру или функцию, присвойте их локальной переменной. При этом физического копирования данных в памяти не происходит — создается лишь новая ссылка, изменение которой внутри процедуры не сможет повредить исходный реквизит формы:

// Помещаем прокси-объект в локальную переменную
ДанныеДляКонвертации = ДругаяФорма.Объект;

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

&НаСервереБезКонтекста
Процедура СоздатьОбъектИзДанныхФормы(Знач ДанныеИсточника)
    ПрикладнойОбъект = ДанныеФормыВЗначение(ДанныеИсточника, Тип("СправочникОбъект.Номенклатура"));
    ПрикладнойОбъект.Записать();
КонецПроцедуры

Подводные камни оптимизации: свойство «Использовать всегда»

Разберем важную техническую особенность платформы «1С:Предприятие 8», которая может приводить к неявной потере данных при обратной конвертации из формы в объект.

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

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

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

Способ 2. Альтернативный (безопасный) паттерн с заполнением через Структуру

Несмотря на удобство метода ДанныеФормыВЗначение(), «слепое» копирование данных формы напрямую в объект базы данных считается не самым лучшим стилем разработки. Оно обходит стандартные механизмы инициализации объектов, бизнес-логику и правила валидации, заложенные в модуле объекта.

Более надежным и архитектурно правильным подходом является создание объекта через менеджер с последующим заполнением по структуре параметров. Разберем этот паттерн по шагам:

  1. На клиенте сформируем легкую структуру, содержащую только те значения, которые действительно ввел пользователь в поля формы.
  2. Передадим эту структуру на сервер.
  3. На сервере создадим новый чистый объект при помощи менеджера справочника.
  4. Вызовем стандартный метод Заполнить(), передав туда нашу структуру.

Посмотрим, как выглядит программная реализация этого паттерна:


// Код в модуле основной управляемой формы
&НаКлиенте
Процедура СоздатьЭлементНаОсновеДанныхДругойФормы(ДанныеДругойФормы)
    
    // Шаг 1: Собираем только нужные данные в структуру
    ПараметрыЗаполнения = Новый Структура;
    ПараметрыЗаполнения.Вставить("Наименование", ДанныеДругойФормы.Наименование);
    ПараметрыЗаполнения.Вставить("Артикул", ДанныеДругойФормы.Артикул);
    ПараметрыЗаполнения.Вставить("СтавкаНДС", ДанныеДругойФормы.СтавкаНДС);
    
    // Шаг 2: Вызываем серверный метод для создания объекта
    СоздатьОбъектНаСервере(ПараметрыЗаполнения);
    
КонецПроцедуры

&НаСервереБезКонтекста
Процедура СоздатьОбъектНаСервере(ПараметрыЗаполнения)
    
    // Шаг 3: Создаем новый прикладной объект через менеджер
    НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
    
    // Шаг 4: Безопасно заполняем свойства объекта
    НовыйЭлемент.Заполнить(ПараметрыЗаполнения);
    
    // При необходимости дозаполняем технические реквизиты и записываем
    НовыйЭлемент.Записать();
    
КонецПроцедуры

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

← На главную