Как построить произвольную многоуровневую иерархию в СКД без дублей

Программист 1С v8.3 (Управляемые формы) Управленческий учет
← На главную

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

Анализ проблемы: почему стандартная иерархия дает дубли

Когда мы пытаемся построить иерархию по ссылочным полям (например, Номенклатура и Родитель в табличной части состава), СКД ориентируется на уникальность ссылок. Если один и тот же комплект (например, «Секция левая») входит в состав нескольких разных изделий, система при построении дерева может вывести его несколько раз. Это происходит потому, что в классической древовидной структуре справочника 1С один элемент не может иметь двух родителей. Пытаясь обойти это ограничение в СКД через связи наборов данных по ссылкам, мы получаем ситуацию, когда итоги начинают двоиться, а визуальная структура становится непредсказуемой.

Стратегия решения: метод составных ключей (Материализованный путь)

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

Например, если у нас есть путь: Шкаф (001) -> Секция (005) -> Полка (009), то:

При таком подходе каждый узел в дереве отчета становится уникальным объектом, даже если ссылочно это одна и та же номенклатура. Это полностью исключает проблему дублирования при суммировании ресурсов.

Почему иерархия по ключам превращается в плоскую таблицу

Выясним причину распространенной ошибки: вы сформировали составные ключи, настроили связь набора данных «сам на себя», но вместо дерева получили обычный список. Рассмотрим два критических условия, без которых СКД не построит дерево:

  1. Отсутствие родительских узлов в выборке. Это самая частая причина. Чтобы СКД построила связь между «Полкой» (ключ 001/005/009) и её родителем «Секцией» (ключ 001/005), в итоговой таблице данных обязательно должна присутствовать строка, где КлючЭлемента равен 001/005. Если ваш запрос выбирает только «листья» (конечные товары), дерево не соберется. Вам нужно программно или запросом добавить в таблицу все промежуточные уровни комплектации.
  2. Начальное значение связи. СКД должна знать, с какого элемента начинать «раскручивать» дерево. В настройках связи наборов данных необходимо четко определить корень. Если корень — это пустая строка или NULL, укажите это в колонке «Начальное значение связи».

Разбор технической реализации в СКД

Проанализируем процесс настройки наборов данных. Предположим, вы подготовили таблицу значений ТЗ_Данные с полями КлючЭлемента, КлючРодителя, Номенклатура, Количество.

Перейдем на вкладку «Связи наборов данных» и добавим связь:

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


// Пример формирования структуры в ТЗ
Для Каждого СтрокаСостава Из ТаблицаКомплектации Цикл
    НоваяСтрока = ТЗ_Данные.Добавить();
    // Формируем уникальный ключ как путь: "КодРодителя/КодЭлемента"
    НоваяСтрока.КлючЭлемента = СтрокаСостава.ПутьКРодителю + "/" + СтрокаСостава.КодЭлемента;
    НоваяСтрока.КлючРодителя = СтрокаСостава.ПутьКРодителю;
    НоваяСтрока.Номенклатура = СтрокаСостава.Номенклатура;
    НоваяСтрока.Количество   = СтрокаСостава.Остаток;
КонецЦикла;

Использование рекурсивных запросов

Если версия платформы позволяет (8.3.20+), эффективнее использовать рекурсивные временные таблицы. Это позволит переложить логику построения «материализованного пути» на СУБД. Посмотрим на логику построения такого запроса:


ВЫБРАТЬ
    ИсходныеДанные.Ссылка КАК Ссылка,
    ИсходныеДанные.Родитель КАК Родитель,
    ВЫРАЗИТЬ(ИсходныеДанные.Ссылка.Код КАК СТРОКА(100)) КАК Путь
ПОМЕСТИТЬ ВТ_Дерево
ИЗ
    Справочник.Номенклатура КАК ИсходныеДанные
ГДЕ
    ИсходныеДанные.Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Номенклатура.Ссылка,
    Номенклатура.Родитель,
    ВТ_Дерево.Путь + "/" + Номенклатура.Код
ИЗ
    Справочник.Номенклатура КАК Номенклатура
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Дерево КАК ВТ_Дерево
    ПО Номенклатура.Родитель = ВТ_Дерево.Ссылка

Особенности расчета ресурсов

При построении произвольной иерархии важно правильно настроить ресурсы. Поскольку один и тот же физический товар может отображаться в разных ветках (как часть разных комплектов), стандартное суммирование «по всем уровням» может привести к логическим ошибкам в понимании общего остатка.

Рекомендуется:

  1. В поле «Рассчитывать по...» для ресурса Количество указывать только детальные записи или конкретную группировку по вашему составному ключу.
  2. Использовать разные поля для «личного» остатка позиции и «вложенного» остатка комплекта, чтобы пользователь видел: остаток самого комплекта (как единицы) и сумму остатков его составляющих.

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

← На главную