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