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