При работе с платформой 1С:Предприятие 8.3, особенно при разработке внешних отчетов или обработок, которые используются в режиме тонкого клиента, мы можем столкнуться с ошибкой "Отсутствует отображение для типа '{http://v8.1c.ru/8.1/data/core}ValueTable'". Эта ошибка возникает, когда мы пытаемся передать программно созданную таблицу значений (объект типа ТаблицаЗначений) в качестве параметра запроса, который затем выполняется в клиент-серверном взаимодействии тонкого клиента.
Давайте подробно разберем, почему возникает эта ошибка и как ее эффективно исправить, используя рекомендуемые механизмы платформы.
Для начала, давайте выясним, в чем корень проблемы. Ошибка "Отсутствует отображение для типа ValueTable" является прямым следствием архитектуры платформы 1С:Предприятие и ее клиент-серверной модели работы. Рассмотрим подробнее:
ТаблицаЗначений – это объект серверного контекста: Объект ТаблицаЗначений в 1С предназначен для создания произвольных табличных данных непосредственно в памяти компьютера. Однако, он относится к так называемым "мутабельным" (изменяемым) типам данных, которые по своей природе тесно связаны с серверным контекстом выполнения.ТаблицаЗначений, не поддерживаются для прямой работы или передачи между клиентом и сервером без специальной обработки. Платформа не может корректно "отобразить" или сериализовать/десериализовать такой объект в контексте тонкого клиента для дальнейшей передачи.ТаблицуЗначений на клиенте и пытаемся передать ее в серверный запрос как параметр, платформа сталкивается с необходимостью "перебросить" этот объект через границу клиент-сервер. Именно в этот момент возникает ошибка, так как прямая передача ТаблицыЗначений в таком виде не поддерживается для тонкого клиента. Как верно было подмечено в сообщении (7) форума, если бы клиент был толстым, этой ошибки, скорее всего, не возникло бы, поскольку в толстом клиенте многие операции могут выполняться непосредственно, и объект ТаблицаЗначений не требует такой сложной сериализации.Таким образом, суть проблемы заключается в попытке использовать серверный объект ТаблицаЗначений напрямую в контексте, где платформа ожидает более "дружественные" для клиент-серверного взаимодействия типы данных или специальные механизмы для их передачи.
Наиболее часто используемым и рекомендуемым способом обхода данной проблемы, который и был найден автором вопроса на форуме (сообщение 10), является применение объекта МенеджерВременныхТаблиц. Этот механизм позволяет нам эффективно работать с табличными данными в запросах, перенося всю логику создания и обработки временных таблиц на сторону сервера.
Давайте разберем, как это работает и как применять МенеджерВременныхТаблиц по шагам.
Суть работы МенеджераВременныхТаблиц:
МенеджерВременныхТаблиц – это специальный объект платформы 1С, который позволяет создавать временные таблицы непосредственно в базе данных на стороне сервера. Эти временные таблицы существуют только в рамках сеанса выполнения запроса или до тех пор, пока существует сам объект МенеджерВременныхТаблиц, после чего они автоматически уничтожаются. Это идеально подходит для хранения промежуточных данных, которые нам нужны для выполнения сложных многошаговых запросов или для подготовки данных для системы компоновки данных (СКД).
Как использовать МенеджерВременныхТаблиц в запросе:
Для использования этого механизма нам потребуется изменить подход к формированию запроса. Вместо того чтобы напрямую пытаться "подсунуть" ТаблицуЗначений в запрос в виде параметра, мы сначала "помещаем" содержимое этой таблицы во временную таблицу базы данных. После этого мы можем работать с этой временной таблицей как с любой другой таблицей в базе данных.
Создаем экземпляр МенеджераВременныхТаблиц:
Если мы программно формируем запрос, то нам необходимо создать экземпляр МенеджераВременныхТаблиц и присвоить его свойству МенеджерВременныхТаблиц объекта Запрос. В случае использования СКД во внешнем отчете, если в тексте запроса используются инструкции ПОМЕСТИТЬ, платформа 1С зачастую автоматически управляет МенеджеромВременныхТаблиц для запроса СКД.
// Пример программного создания запроса с МенеджеромВременныхТаблиц
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Определяем параметр-источник ТаблицаЗначений:
Мы продолжаем передавать нашу ТаблицуЗначений в запрос как параметр. Однако теперь запрос будет использовать этот параметр не напрямую как виртуальную таблицу, а как источник данных для создания временной таблицы.
КРИТИЧЕСКИ ВАЖНО: При передаче ТаблицыЗначений в качестве параметра для создания временной таблицы, мы всегда должны явно указывать типы колонок этой таблицы значений. Без этого платформа не сможет корректно создать временную таблицу в базе данных, и мы можем столкнуться с ошибками.
// Пример программного создания ТаблицыЗначений с описанием колонок
ТаблицаОстатков = Новый ТаблицаЗначений;
ТаблицаОстатков.Колонки.Добавить("КодНоменклатурыБП", Новый ОписаниеТипов("Строка", , , Новый КвалификаторыСтроки(10)));
ТаблицаОстатков.Колонки.Добавить("НоменклатураБП", Новый ОписаниеТипов("Строка", , , Новый КвалификаторыСтроки(200)));
ТаблицаОстатков.Колонки.Добавить("ОстатокБП", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
// Заполняем ТаблицуОстатков данными...
// ...
// Установка параметра запроса
Запрос.УстановитьПараметр("ТаблицаИсходныхДанных", ТаблицаОстатков);
Используем инструкцию ПОМЕСТИТЬ в тексте запроса:
В тексте запроса мы сначала используем оператор ПОМЕСТИТЬ, чтобы сохранить данные из нашего параметра-ТаблицыЗначений во временную таблицу базы данных. Затем мы можем выбрать данные из этой временной таблицы.
// Пример текста запроса с использованием ПОМЕСТИТЬ
Запрос.Текст =
"ВЫБРАТЬ
| ИсходныеДанные.КодНоменклатурыБП,
| ИсходныеДанные.НоменклатураБП,
| ИсходныеДанные.ОстатокБП
|ПОМЕСТИТЬ ВременнаяТаблицаОстатков
|ИЗ
| &ТаблицаИсходныхДанных КАК ИсходныеДанные // Наш параметр типа ТаблицаЗначений
|;
|
|////////////////////////////////////////////////////////////////////////////////
|// Теперь, в следующем блоке запроса, мы можем работать
|// с ВременнаяТаблицаОстатков как с обычной таблицей базы данных.
|ВЫБРАТЬ
| ВременнаяТаблицаОстатков.КодНоменклатурыБП,
| ВременнаяТаблицаОстатков.НоменклатураБП,
| ВременнаяТаблицаОстатков.ОстатокБП
|ИЗ
| ВременнаяТаблицаОстатков КАК ВременнаяТаблицаОстатков
|";
Результат = Запрос.Выполнить().Выгрузить(); // Выполняем запрос
В этом примере, первый блок запроса (до символов ////////////////////////////////////////////////////////////////////////////////) создает временную таблицу ВременнаяТаблицаОстатков, наполняя ее данными из параметра &ТаблицаИсходныхДанных. Второй блок запроса уже выбирает данные из этой созданной временной таблицы. Вся эта операция выполняется на сервере, обходя ограничения тонкого клиента.
Преимущества МенеджераВременныхТаблиц:
МенеджерВременныхТаблиц является краеугольным камнем для построения сложных отчетов на Системе Компоновки Данных, позволяя СКД формировать промежуточные выборки данных.Хотя МенеджерВременныхТаблиц является основным решением для запросов, существуют и другие подходы к работе с табличными данными в тонком клиенте, каждый из которых имеет свою область применения.
Временное хранилище:
Механизм временного хранилища предназначен для передачи больших объемов данных между клиентом и сервером. Суть заключается в том, что данные (например, большая ТаблицаЗначений) помещаются во временное хранилище на сервере, а на клиент передается только адрес этого хранилища. Клиент затем может получить данные по этому адресу (с помощью метода ПолучитьИзВременногоХранилища). Этот способ хорош для общей передачи данных, но не для непосредственного использования в запросах как виртуальная таблица. Применяется, когда нам нужно передать данные с сервера на клиент для отображения или дальнейшей обработки на клиенте, но не для построения запросов.
// На сервере
Функция ПередатьТаблицуНаКлиент()
ТаблицаДанных = Новый ТаблицаЗначений;
// Заполняем ТаблицаДанных
...
АдресХранилища = ПоместитьВоВременноеХранилище(ТаблицаДанных, УникальныйИдентификатор);
Возврат АдресХранилища;
КонецФункции
// На клиенте
Процедура ПолучитьДанныеССервера()
Адрес = ПередатьТаблицуНаКлиент();
ТаблицаДанных = ПолучитьИзВременногоХранилища(Адрес);
// Работаем с ТаблицаДанных на клиенте
КонецПроцедуры
Реквизиты формы типа "ТаблицаЗначений":
Если мы создаем реквизит формы типа ТаблицаЗначений, платформа 1С автоматически преобразует его в объект типа ДанныеФормыКоллекция. С таким реквизитом мы можем работать как на клиенте, так и на сервере. Однако, его основное назначение — это отображение и манипуляция данными непосредственно на форме, а не использование в запросах как виртуальная таблица. Хотя данные из ДанныеФормыКоллекция можно выгрузить в ТаблицуЗначений и затем использовать с МенеджеромВременныхТаблиц, сам реквизит формы не является прямой заменой параметра запроса.
Массив из структур:
ТаблицаЗначений может быть преобразована в массив структур (или массив произвольных объектов) для передачи между клиентом и сервером. Этот способ позволяет обойти ограничения на передачу мутабельных типов. Каждая строка ТаблицыЗначений преобразуется в структуру, а набор этих структур — в массив. Массив структур можно передавать как параметр между клиентом и сервером. На сервере его можно обратно преобразовать в ТаблицуЗначений и затем использовать с МенеджеромВременныхТаблиц. Этот метод более универсален для передачи любых табличных данных, но требует дополнительного кода для преобразования.
// Пример преобразования ТаблицыЗначений в МассивСтруктур
// и обратно (для передачи через клиент-сервер)
// На сервере (или клиенте, если нужно передать на другую сторону)
Функция ТаблицаВМассивСтруктур(Таблица)
МассивСтруктур = Новый Массив;
Для Каждого СтрокаТЗ Из Таблица Цикл
СтруктураСтроки = Новый Структура;
Для Каждого КолонкаТЗ Из Таблица.Колонки Цикл
СтруктураСтроки.Вставить(КолонкаТЗ.Имя, СтрокаТЗ[КолонкаТЗ.Имя]);
КонецЦикла;
МассивСтруктур.Добавить(СтруктураСтроки);
КонецЦикла;
Возврат МассивСтруктур;
КонецФункции
// На сервере (после получения массива с клиента)
Функция МассивСтруктурВТаблицу(МассивСтруктур)
Таблица = Новый ТаблицаЗначений;
Если МассивСтруктур.Количество() > 0 Тогда
ПерваяСтруктура = МассивСтруктур[0];
Для Каждого ЭлементСтруктуры Из ПерваяСтруктура Цикл
Таблица.Колонки.Добавить(ЭлементСтруктуры.Ключ);
КонецЦикла;
Для Каждого СтруктураСтроки Из МассивСтруктур Цикл
НоваяСтрока = Таблица.Добавить();
Для Каждого ЭлементСтруктуры Из СтруктураСтроки Цикл
НоваяСтрока[ЭлементСтруктуры.Ключ] = ЭлементСтруктуры.Значение;
КонецЦикла;
КонецЦикла;
КонецЕсли;
Возврат Таблица;
КонецФункции
Чтобы избежать подобных ошибок и строить эффективные и надежные решения в 1С, особенно при работе с тонкими клиентами, мы рекомендуем придерживаться следующих принципов:
Максимально переносить ресурсоемкие операции на сервер:
Любая сложная обработка данных, выполнение объемных запросов, расчеты и формирование больших таблиц должны происходить на сервере 1С:Предприятия. Тонкий клиент должен использоваться в основном для отображения данных, ввода информации и вызова серверных команд.
Четкое разделение клиентского и серверного контекстов:
Всегда помните, какие объекты и операции доступны в клиентском, а какие в серверном контексте. Используйте директивы компиляции (#Если НаКлиенте Тогда, #Если НаСервере Тогда) для явного разделения кода.
Для сложных запросов и многошаговой обработки использовать МенеджерВременныхТаблиц:
Этот механизм является мощным инструментом для решения задач, связанных с подготовкой данных для отчетов и сложной выборкой из базы данных.
При передаче ТаблицыЗначений как параметра запроса для временной таблицы, всегда явно указывать типы колонок:
Это гарантирует, что платформа корректно создаст временную таблицу в базе данных с правильными типами полей, избегая ошибок и проблем с совместимостью данных.
Избегать передачи "мутабельных" объектов напрямую через границу клиент-сервер:
Там, где это невозможно, используйте механизмы сериализации (например, массив структур) или временные хранилища.
Следуя этим рекомендациям, мы сможем эффективно справляться с особенностями тонкого клиента 1С и успешно реализовывать функционал, требующий работы с табличными данными в запросах.