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