Как заполнить табличную часть одного документа данными другого на управляемых формах 1С?

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

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

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

Почему нельзя передать Таблицу Значений на клиент?

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

По этой причине попытка поместить таблицу значений во временное хранилище на сервере и затем извлечь ее на клиенте через ПолучитьИзВременногоХранилища завершается ошибкой типизации. Клиентский уровень платформы не может десериализовать данный тип.

Кроме того, реквизиты табличных частей формы имеют тип ДанныеФормыКоллекция, а не ТаблицаЗначений. Это специализированный платформенный прокси-объект, обеспечивающий эффективный обмен данными между клиентом и сервером.

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

Рассмотрим код, вызвавший затруднение:


&НаКлиенте
Процедура ЗаказНарядПриИзменении(Элемент)
    ПолучитьТовары(Объект.ЗаказНаряд, Объект.Товары);
КонецПроцедуры

&НаСервере
Процедура ПолучитьТовары(ЗаказНаряд, Товары)
    Т = ЗаказНаряд.Товары;
    Для каждого Строка Из Т Цикл
        НоваяСтрока = Товары.Добавить();
        // Заполнение колонок
    КонецЦикла; 
КонецПроцедуры

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

Следовательно, передавать коллекцию формы в процедуру формы параметром не нужно и недопустимо.

Способ 1. Заполнение в контексте формы через ЗаполнитьЗначенияСвойств

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


&НаКлиенте
Процедура ЗаказНарядПриИзменении(Элемент)
    ЗаполнитьТоварыНаСервере();
КонецПроцедуры

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

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

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

Способ 2. Оптимальное заполнение через запрос и метод Загрузить

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

Запрос позволяет на стороне СУБД отобрать только необходимые поля, при необходимости переименовать колонки через псевдонимы или применить фильтрацию:


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

Метод Загрузить полностью очищает текущую коллекцию формы и загружает в нее строки переданной таблицы значений с автоматическим сопоставлением колонок. Это работает в разы быстрее поэлементного перебора.

Способ 3. Преобразование данных формы через ТаблицаЗначенийВДанныеФормы

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


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

Данная функция выполняет прямое заполнение структуры ДанныеФормыКоллекция данными серверной таблицы.

Как передать табличные данные на клиент, если это действительно необходимо?

Если алгоритм требует обработки данных именно на клиенте (например, для интерактивного диалога с пользователем перед записью), обойти ограничение платформы на тип ТаблицаЗначений можно следующими путями:

  1. Массив структур: на сервере обходим таблицу и заполняем Массив, элементами которого являются объекты типа Структура. Массивы и структуры поддерживаются на тонком клиенте.
  2. Реквизит формы: если добавить реквизит формы с типом ТаблицаЗначений в редакторе формы, платформа автоматически трансформирует его в ДанныеФормыКоллекция, благодаря чему данные будут прозрачно и непрерывно доступны в коде формы как на клиенте, так и на сервере.

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

← На главную