Как исправить ошибку «Отсутствует отображение для типа ValueTable» при вызове сервера из тонкого клиента в 1С

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

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

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

Выясняем причину ошибки: понятие мутабельности и ограничения тонкого клиента

Ошибка, с которой столкнулся автор темы, выглядит следующим образом:

«Ошибка передачи данных между клиентом и сервером. Значение недопустимого типа. по причине: Ошибка преобразования данных XDTO: НачалоСвойства: ret ... Отсутствует отображение для типа ValueTable».

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

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

На клиенте разрешено оперировать только примитивными типами данных (числа, строки, даты, булево), ссылками на объекты базы данных, а также специализированными коллекциями (такими как Массив или Структура), при условии, что они сами не содержат мутабельных значений.


Вариант 1. Использование реквизитов управляемой формы (ДанныеФормыКоллекция)

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

Разберем этот паттерн проектирования по шагам:

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

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

Посмотрим на пример правильной реализации серверной процедуры:


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

&НаКлиенте
Процедура Сформировать(Команда)
    // Вызываем контекстный серверный метод выполнения запроса
    СформироватьНаСервере();
КонецПроцедуры

Преимущества: Это самый простой и естественный способ работы с табличными данными в управляемом интерфейсе. Платформа 1С берет на себя все заботы по минимизации трафика, передавая на клиент только видимые пользователю строки.


Вариант 2. Конвертация таблицы значений в массив структур

Бывают ситуации, когда данные таблицы нужны на клиенте не для визуализации в табличном поле, а для каких-то промежуточных расчетов, проверок или интерактивного выбора в коде. В этом случае мы можем преобразовать таблицу значений в массив структур. И массив, и структура являются сериализуемыми типами данных и отлично передаются с сервера на клиент.

Рассмотрим подробнее программную конвертацию:


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

&НаКлиенте
Процедура Сформировать(Команда)
    
    // Серверная функция возвращает массив структур, ошибки XDTO не возникает
    МассивДанных = ПолучитьПродажиМассивом();
    
    // Обрабатываем данные непосредственно на клиенте
    Для Каждого СтрокаДанных Из МассивДанных Цикл
        Сообщить("Продавец: " + СтрокаДанных.Продавец + " - Сумма: " + СтрокаДанных.СтоимостьОборот);
    КонецЦикла;
    
КонецПроцедуры

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


// Вместо ручного обхода используем БСП:
МассивРезультатов = ОбщегоНазначения.ТаблицаЗначенийВМассив(ТЗ);
Возврат МассивРезультатов;

Важное предупреждение: Метод конвертации в массив структур не рекомендуется применять для таблиц с большим количеством строк (более 1000-5000 записей). При передаче массивных коллекций структур по сети возрастает нагрузка на сериализатор, что может замедлить работу интерфейса пользователя.


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

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

Проанализируем этот сценарий по шагам:


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

&НаКлиенте
Процедура ВыполнитьОбработкуДанных(Команда)
    
    // Получаем строку-адрес
    АдресДанных = ПоместитьТаблицуВоВременноеХранилище();
    
    // Передаем этот адрес в другую форму или в другой серверный метод обработки
    ОбработатьДанныеНаСервере(АдресДанных);
    
КонецПроцедуры

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

Преимущества: Данные физически остаются в оперативной памяти сервера (или в СУБД) и не гоняются по сети на клиентский компьютер. Клиент оперирует лишь легкой строкой-ссылкой вида "e1cib/tempstorage/...".


Вариант 4. Сериализация в формат JSON (для интеграций и веб-клиентов)

Для веб-клиента, работающего через веб-браузеры, или при построении интеграционных интерфейсов высокой производительности, отличным способом передачи табличных данных является их сериализация в строку JSON. Строка является базовым примитивным типом и мгновенно передается на клиент без каких-либо накладных расходов XML-конвертации XDTO.

Давайте посмотрим на пример реализации:


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

&НаКлиенте
Процедура ПолучитьИРазобратьJSON(Команда)
    
    СтрокаДанных = ПолучитьПродажиВФорматеJSON();
    
    // Десериализуем строку обратно в массив на клиенте
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(СтрокаДанных);
    
    МассивОбъектов = ПрочитатьJSON(ЧтениеJSON);
    ЧтениеJSON.Закрыть();
    
    // Работаем с коллекцией на клиенте
    Для Каждого Элемент Из МассивОбъектов Цикл
        // Элемент представляет собой Соответствие или Структуру
    КонецЦикла;
    
КонецПроцедуры

Резюме

Мы подробно проанализировали ошибку передачи данных XDTO и изучили особенности работы с табличными данными в 1С. Подведем итог, какой метод выбрать:

← На главную