Как выстроить элементы Таблицы Значений по иерархии справочника в 1С 7.7?

Программист 1С v7.7 Управленческий учет
← На главную

В платформе 1С:Предприятие 7.7 работа с Таблицей Значений (ТЗ) зачастую требует ручных манипуляций, особенно когда необходимо вывести плоский список данных в виде иерархического дерева (например, с группировками товаров). Стандартная ТЗ не умеет автоматически «подтягивать» родителей, если они не были добавлены изначально. Рассмотрим несколько эффективных способов решения этой задачи, от оптимизации запросов до рекурсивных алгоритмов.

Способ 1. Использование встроенного языка запросов (оптимальный)

Самый правильный и производительный метод — получить иерархию сразу на этапе выборки данных. Если вы заполняете ТЗ из регистров, использование объекта Запрос позволяет задействовать встроенную возможность обхода группировок с учетом иерархии справочника.

Проанализируем ситуацию: часто разработчики пытаются сначала выбрать движения регистра через ВыбратьДвижения, а затем отдельно получить остатки. Это порождает «плоскую» таблицу. Вместо этого мы можем использовать конструкцию Когда в функциях запроса. Это позволит разделить приходы по разным видам документов прямо внутри одного запроса, сохраняя при этом связь с иерархией справочника.

Рассмотрим пример кода, который объединяет получение данных по разным видам документов и иерархию:


ТекстЗапроса = "
|Период с Дата1 по Дата2;
|МестоХранения = Регистр.Остатки.МестоХранения;
|Номенклатура = Регистр.Остатки.ТМЦ;
|Кол = Регистр.Остатки.ОстатокТовара;
|ТекДок = Регистр.Остатки.ТекущийДокумент;
|Функция НачКол = НачОст(Кол);
|Функция ПрихВсего = Приход(Кол);
|Функция ПрихИзлишек = Приход(Кол) Когда (ТекДок.Вид = ""ПриходИзлишков"");
|Функция РасхВсего = Расход(Кол);
|Функция КонКол = КонОст(Кол);
|Группировка Номенклатура С Группами;
|";

Обратим внимание на ключевой момент: использование параметра С Группами в описании группировки Номенклатура. При обходе такого запроса методом Запрос.Группировка(1), система будет последовательно возвращать сначала папки, а затем вложенные в них элементы. Это позволяет выводить данные в печатную форму в правильном иерархическом порядке без дополнительных манипуляций с ТЗ.

Способ 2. Рекурсивное добавление родителей в готовую ТЗ

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

Разберем, как это реализовать с помощью рекурсии. Нам понадобится основная ТЗ (сделаем её глобальной или передадим по ссылке) и функция, которая «поднимается» вверх по дереву справочника.


Процедура ДобавитьРодителей(ТекНоменклатура, ТЗ)
    Если ТекНоменклатура.Родитель.Выбран() = 1 Тогда
        // Рекурсивно вызываем для родителя родителя
        ДобавитьРодителей(ТекНоменклатура.Родитель, ТЗ);
        
        // Проверяем, нет ли уже этой группы в нашей таблице
        НомерСтроки = 0;
        Если ТЗ.НайтиЗначение(ТекНоменклатура.Родитель, НомерСтроки, "Товар") = 0 Тогда
            ТЗ.НоваяСтрока();
            ТЗ.Товар = ТекНоменклатура.Родитель;
            // Здесь можно пометить, что это группа, чтобы в печати выделить жирным
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

После наполнения ТЗ всеми родителями, её необходимо свернуть и отсортировать. Однако помните, что обычная сортировка ТЗ.Сортировать("Товар") отсортирует всё по алфавиту, что может нарушить вложенность. Для корректной визуализации лучше использовать специализированную сортировку.

Способ 3. Сортировка ТЗ по иерархии справочника

Существует интересный алгоритм, позволяющий выстроить строки ТЗ ровно в том порядке, в котором они идут в самом справочнике 1С. Суть метода заключается в создании вспомогательной таблицы, которая нумерует все элементы справочника при полном обходе.

Выясним, как работает этот алгоритм по шагам:

  1. Создаем временную таблицу ТЗПор, где будут две колонки: Элемент и Позиция.
  2. Перебираем весь справочник методом ВыбратьЭлементы(). В 1С 7.7 этот метод по умолчанию идет по иерархии.
  3. Каждому найденному элементу присваиваем порядковый номер.
  4. В основной ТЗ создаем служебную колонку и заполняем её этими номерами, используя НайтиЗначение.
  5. Сортируем основную ТЗ по этой служебной колонке.

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


Процедура СортироватьПоИерархии(ТЗ, ИмяКолонкиТовар, ТипСправочника)
    ТЗПор = СоздатьОбъект("ТаблицаЗначений");
    ТЗПор.НоваяКолонка("Элемент");
    ТЗПор.НоваяКолонка("Позиция");
    
    Спр = СоздатьОбъект("Справочник." + ТипСправочника);
    к = 0;
    Спр.ВыбратьЭлементы(); // Выборка в иерархическом порядке
    Пока Спр.ПолучитьЭлемент() = 1 Цикл
        ТЗПор.НоваяСтрока();
        ТЗПор.Элемент = Спр.ТекущийЭлемент();
        ТЗПор.Позиция = к;
        к = к + 1;
    КонецЦикла;
    
    ТЗ.НоваяКолонка("СлужебнаяСортировка");
    ТЗ.ВыбратьСтроки();
    Пока ТЗ.ПолучитьСтроку() = 1 Цикл
        Стр = 0;
        Если ТЗПор.НайтиЗначение(ТЗ.ПолучитьЗначение(ТЗ.НомерСтроки, ИмяКолонкиТовар), Стр, "Элемент") = 1 Тогда
            ТЗ.УстановитьЗначение(ТЗ.НомерСтроки, "СлужебнаяСортировка", ТЗПор.ПолучитьЗначение(Стр, "Позиция"));
        КонецЕсли;
    КонецЦикла;
    
    ТЗ.Сортировать("СлужебнаяСортировка");
    ТЗ.УдалитьКолонку("СлужебнаяСортировка");
КонецПроцедуры

Визуализация иерархии в печатной форме

Просто отсортировать таблицу недостаточно для красивого отчета. Чтобы пользователь видел дерево, нам нужно добавить отступы. Для этого в шаблоне печатной формы в ячейке наименования товара можно использовать метод Уровень().

Проанализируем ситуацию: если мы выводим элемент, его уровень может быть 1, 2, 3 и т.д. Мы можем программно добавлять пробелы перед названием:


// В коде вывода секции
Отступ = "";
Для н = 1 по ТЗ.Товар.Уровень() - 1 Цикл
    Отступ = Отступ + "    "; // 4 пробела для каждого уровня
КонецЦикла;
ПечТовар = Отступ + СокрЛП(ТЗ.Товар.Наименование);

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

Использование внешних компонент (1С++)

Если ваша конфигурация использует расширение 1С++, задача решается значительно элегантнее с помощью объекта ИндексированнаяТаблица. Этот объект обладает методом Группировать() и ВставитьРодителей(). Эти методы работают на уровне оптимизированных алгоритмов C++ и позволяют мгновенно превратить плоскую таблицу в иерархическую структуру с промежуточными итогами по группам, что является стандартом для качественных отчетов в среде 7.7.

← На главную