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