Родитель, вся цепочка групп и корень иерархии в 1С 8.3: объектная модель, язык запросов, план счетов и ПВХ
Содержание
Родителя в 1С получают через стандартный реквизит Родитель: в коде это Ссылка.Родитель, в запросе — поле Таблица.Родитель. Частая ошибка — ждать, что Родитель сразу вернёт весь путь «Товары / Бытовая техника / Холодильники». Он возвращает только ближайшую группу. Всех родителей до корня нужно собрать циклом или запросом с несколькими уровнями.
Механизм общий для платформы 8.3 и не зависит от конфигурации. Одинаково работает в БП 3.0, ЗУП 3.1, УТ 11 и самописных базах: в справочниках, планах счетов и иерархических планах видов характеристик (ПВХ). Ниже — рабочий код для каждого случая и то, что ломается на больших данных.
Как устроена иерархия и где хранится родитель
Родитель есть только у объектов, которые поддерживают иерархию. В конфигураторе это флажок Иерархический на вкладке «Иерархия» свойств справочника или ПВХ. У плана счетов иерархия встроена: субсчёт всегда подчинён счёту.
У справочника бывает два вида иерархии:
| Вид иерархии | Кто может быть родителем | Признак ЭтоГруппа |
|---|---|---|
| Иерархия групп и элементов | только группа (папка) | у групп Истина, у элементов Ложь |
| Иерархия элементов | любой элемент | всегда Ложь |
📌 Что держать в голове:
- 🔹
Родитель— ссылка того же типа, что и сам объект. УСправочник.Номенклатурародитель тожеСправочникСсылка.Номенклатура. - 🔹 У элемента верхнего уровня
Родитель— пустая ссылка, а неНеопределено. - 🔹 Флажок «Ограничение количества уровней» и число уровней в свойствах справочника задают максимальную глубину. Если ограничение снято, глубина не ограничена.
- 🔹
ВладелециРодитель— разные вещи.Владелец— это объект из другого справочника, которому подчинён элемент (договор подчинён контрагенту).Родитель— группа внутри того же справочника.
Родитель в коде модуля
На сервере всё просто:
&НаСервере
Процедура ПоказатьРодителяНаСервере()
Родитель = Объект.Ссылка.Родитель;
Если ЗначениеЗаполнено(Родитель) Тогда
Сообщить("Группа: " + Строка(Родитель));
Иначе
Сообщить("Элемент лежит в корне справочника");
КонецЕсли;
КонецПроцедуры
В форме элемента родитель уже есть в данных формы: Объект.Родитель. Обращаться к базе не нужно.
На тонком клиенте нельзя получить реквизит ссылки через точку. Код Ссылка.Родитель в процедуре &НаКлиенте выдаст ошибку. Вынесите получение в серверную функцию или общий модуль с флажком «Сервер». В конфигурациях на БСП есть готовая функция ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Родитель").
Проверка «входит ли элемент в группу на любой глубине» делается без цикла, методом ПринадлежитЭлементу:
Если Товар.ПринадлежитЭлементу(ГруппаБытоваяТехника) Тогда
// товар лежит в этой группе или в любой её подгруппе
КонецЕсли;
Родитель в запросе: поле, разыменование и В ИЕРАРХИИ
В языке запросов родитель — обычное поле таблицы. Его можно выбрать, отфильтровать по нему и разыменовать через точку.
ВЫБРАТЬ
Номенклатура.Ссылка КАК Товар,
Номенклатура.Родитель КАК Группа,
Номенклатура.Родитель.Наименование КАК ИмяГруппы
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
НЕ Номенклатура.ЭтоГруппа
Три приёма, которые нужны почти в каждой задаче с иерархией:
- Элементы верхнего уровня. Отбор по пустому родителю:
ГДЕ Номенклатура.Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка). - Прямые потомки группы — только первый уровень вложенности:
ГДЕ Номенклатура.Родитель = &Группа. - Вся ветка на любой глубине — оператор
В ИЕРАРХИИ:ГДЕ Номенклатура.Ссылка В ИЕРАРХИИ (&Группа).
💡 В ИЕРАРХИИ включает в результат и саму группу из параметра. Если она не нужна, добавьте И Номенклатура.Ссылка <> &Группа или И НЕ Номенклатура.ЭтоГруппа.
Тот же оператор работает с измерениями регистров. Остатки по всем субсчетам 60-го счёта берут так: ГДЕ Счет В ИЕРАРХИИ (&Счет60). Параметр может быть и списком значений: В ИЕРАРХИИ (&СписокГрупп).
Вся цепочка родителей до корня программно
Задача «получить всех родителей» — это подъём от элемента вверх, пока родитель не станет пустым. Функция ниже возвращает массив групп от корня к ближайшей. Её удобно держать в серверном общем модуле.
// Возвращает массив родителей: [0] — корневая группа, последний — ближайший родитель.
Функция ЦепочкаРодителей(Ссылка, МаксГлубина = 100) Экспорт
Результат = Новый Массив;
Пройденные = Новый Соответствие;
Текущий = Ссылка.Родитель;
Пока ЗначениеЗаполнено(Текущий) Цикл
Если Пройденные[Текущий] <> Неопределено
Или Результат.Количество() >= МаксГлубина Тогда
ВызватьИсключение "Ошибка иерархии: зацикливание у " + Строка(Ссылка);
КонецЕсли;
Пройденные.Вставить(Текущий, Истина);
Результат.Вставить(0, Текущий);
Текущий = Текущий.Родитель;
КонецЦикла;
Возврат Результат;
КонецФункции
Из массива легко собрать полный путь строкой:
Функция ПолныйПуть(Ссылка, Разделитель = " / ") Экспорт
Части = Новый Массив;
Для Каждого Группа Из ЦепочкаРодителей(Ссылка) Цикл
Части.Добавить(Строка(Группа));
КонецЦикла;
Части.Добавить(Строка(Ссылка));
Возврат СтрСоединить(Части, Разделитель);
КонецФункции
Для холодильника получится, например, Товары / Бытовая техника / Холодильники / Холодильник Х-200. Метод СтрСоединить есть с платформы 8.3.6.
Корень и уровень элемента
- 🔝 Верхний родитель (корень) — первый элемент массива:
Цепочка[0], если массив не пустой. Пустой массив значит, что элемент сам лежит в корне. - 📏 Уровень вложенности —
Цепочка.Количество(). У элемента в корне уровень 0. У объекта справочника и у выборки есть методУровень()с тем же смыслом. - 🔁 Нужен ли «Пройденные» в Соответствии? Платформа не даёт перенести группу в её же подгруппу при интерактивной записи. Но при загрузке через обмен с
ОбменДанными.Загрузка = Истинапроверки отключены, и кольцо из двух групп возможно. Без защиты цикл станет вечным и повесит сеанс.
Группа заданного уровня
В отчётах часто нужен не ближайший родитель, а группа конкретного уровня: например, «направление» на втором уровне, даже если товар лежит на пятом. Берите её из той же цепочки по индексу:
// Уровень считается от корня: 1 — верхняя группа, 2 — её подгруппа и так далее.
Функция ГруппаУровня(Ссылка, НомерУровня) Экспорт
Цепочка = ЦепочкаРодителей(Ссылка);
Если Цепочка.Количество() >= НомерУровня Тогда
Возврат Цепочка[НомерУровня - 1];
КонецЕсли;
Возврат Неопределено;
КонецФункции
Если товар лежит выше запрошенного уровня, функция вернёт Неопределено. Решите заранее, что показывать в отчёте в этом случае: пустую строку или ближайшую группу.
Все родители одним запросом
Рекурсивных запросов в языке 1С нет. Подняться по дереву одним запросом можно двумя способами.
Способ 1. Фиксированная глубина через разыменование
Если глубина справочника известна (например, ограничена четырьмя уровнями), выбирайте родителей через цепочку точек:
ВЫБРАТЬ
Т.Ссылка КАК Ссылка,
Т.Родитель КАК Р1,
Т.Родитель.Родитель КАК Р2,
Т.Родитель.Родитель.Родитель КАК Р3,
Т.Родитель.Родитель.Родитель.Родитель КАК Р4
ИЗ
Справочник.Номенклатура КАК Т
ГДЕ
Т.Ссылка В (&СписокТоваров)
Каждая точка — это соединение таблицы самой с собой. Четыре уровня — четыре соединения, и это быстро. Если глубина не ограничена, проверьте поле Р4: заполнено — запустите тот же запрос ещё раз, передав в параметр значения Р4. Так обрабатывается сколько угодно элементов за несколько обращений к базе, а не по запросу на каждый уровень каждого товара.
Способ 2. ИТОГИ ПО … ИЕРАРХИЯ
Ключевое слово ИЕРАРХИЯ в итогах заставляет платформу добавить в результат строки всех вышестоящих групп:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Ссылка КАК Номенклатура
|ИЗ
| Справочник.Номенклатура КАК Т
|ГДЕ
| Т.Ссылка = &Товар
|ИТОГИ ПО
| Номенклатура ИЕРАРХИЯ";
Запрос.УстановитьПараметр("Товар", Товар);
Выборка = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.Прямой);
Пока Выборка.Следующий() Цикл
Если Выборка.ТипЗаписи() = ТипЗаписиЗапроса.ИтогПоИерархии Тогда
Сообщить("Группа: " + Строка(Выборка.Номенклатура));
Иначе
Сообщить("Элемент: " + Строка(Выборка.Номенклатура));
КонецЕсли;
КонецЦикла;
Строки групп идут сверху вниз, от корня к ближайшему родителю. Способ удобен для отчётов и выгрузок, где нужно дерево с подытогами. Для одного элемента он избыточен: проще функция из предыдущего раздела.
План счетов, ПВХ и другие иерархические объекты
Принцип везде одинаковый, различаются детали:
| Объект | Есть ли Родитель | Особенности |
|---|---|---|
| Справочник | если включена иерархия | бывают группы (ЭтоГруппа) или иерархия элементов |
| План счетов | всегда | групп нет, родитель субсчёта — счёт: у 60.01 родитель 60 |
| План видов характеристик | если включена иерархия | есть группы и ЭтоГруппа, как у справочника |
| Документ, регистр | нет | иерархия только через реквизиты или измерения ссылочного типа |
План счетов
Родитель субсчёта второго уровня получают так же: Счет.Родитель. В запросе к плану счетов Хозрасчетный (БП 3.0) работает и В ИЕРАРХИИ, и разыменование:
ВЫБРАТЬ
Х.Ссылка КАК Счет,
Х.Код КАК Код,
Х.Родитель.Код КАК КодРодителя
ИЗ
ПланСчетов.Хозрасчетный КАК Х
ГДЕ
Х.Ссылка В ИЕРАРХИИ (ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.РасчетыСПоставщиками))
💡 В регистре бухгалтерии отбор по счёту почти всегда пишут через В ИЕРАРХИИ. Проводки ложатся на субсчета, и отбор Счет = &Счет60 по самому счёту 60 вернёт пустоту.
ПВХ
Иерархический план видов характеристик ведёт себя как справочник с группами. Типичный пример — дополнительные реквизиты и свойства, сгруппированные по наборам. Поля Родитель и ЭтоГруппа доступны в коде и в запросе. Функция ЦепочкаРодителей работает без изменений: тип ссылки она не проверяет.
Подчинённые справочники
Если справочник подчинён другому, у элемента есть и Владелец, и (при иерархии) Родитель. Путь «контрагент → группа договоров → договор» собирают в два шага: сначала цепочка родителей, затем Владелец любого элемента цепочки. Владелец у всех элементов одной ветки совпадает.
Пустой родитель, битые ссылки и зацикливание
Иерархия в рабочей базе редко бывает идеальной. Три ситуации, которые обязательно встретятся.
Запрос для поиска элементов с битым родителем:
ВЫБРАТЬ
Т.Ссылка КАК Элемент,
Т.Родитель КАК Родитель
ИЗ
Справочник.Номенклатура КАК Т
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Р
ПО Т.Родитель = Р.Ссылка
ГДЕ
Т.Родитель <> ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
И Р.Ссылка ЕСТЬ NULL
Перед массовым переносом элементов между группами сделайте копию базы — изменение Родитель у тысяч элементов отменить одной кнопкой нельзя.
Скорость: что тормозит на больших справочниках
Сама иерархия не медленная: по полю Родитель у иерархического справочника платформа держит индекс. Тормозит то, как к ней обращаются.
| Способ | Обращений к базе | Когда подходит |
|---|---|---|
Ссылка.Родитель в цикле по одному элементу | по одному на уровень | единичный элемент, форма, проверка |
ЦепочкаРодителей для 10 000 товаров | десятки тысяч | не подходит, переписать на запрос |
Запрос с Р1…Р4 порциями | 1–3 запроса на весь список | выгрузки, обработки, отчёты |
ИТОГИ … ИЕРАРХИЯ | один запрос | отчёты с деревом и подытогами |
В ИЕРАРХИИ | один запрос | отбор всей ветки по группе |
Что ещё помогает:
- ⚡ Не разыменовывайте в цикле по выборке.
Выборка.Товар.Родитель.Наименованиев цикле — это отдельное чтение объекта на каждой строке. Добавьте полеТ.Родитель.Наименованиепрямо в текст запроса. - ⚡ Кешируйте результат. Если путь группы нужен много раз за один вызов, храните его в
Соответствиис ключом-ссылкой группы. У тысячи товаров обычно несколько десятков групп. - ⚡ Ограничивайте глубину разыменования. Цепочка из 8–10 точек
Родитель.Родитель…даёт столько же соединений и тяжёлый план запроса. Лучше 3–4 уровня и повтор порцией.
Частые вопросы
Можно ли вручную поменять родителя элемента из кода?
Ссылка.ПолучитьОбъект(), присвойте Объект.Родитель = НоваяГруппа и вызовите Объект.Записать(). При иерархии групп и элементов родителем может быть только группа этого же справочника.Как получить всех потомков группы, а не родителей?
Ссылка В ИЕРАРХИИ (&Группа). Только прямых потомков — условием Родитель = &Группа.Как получить родителя у элемента табличной части документа?
Строка.Номенклатура.Родитель на сервере или поле Товары.Номенклатура.Родитель в запросе к табличной части Документ.Имя.Товары. У самой строки табличной части родителя нет.Как вывести иерархию в отчёте на СКД?
Чем родитель отличается от владельца в запросе?
Родитель — группа в том же справочнике, Владелец — элемент другого справочника. Для отбора договоров одного контрагента пишут Владелец = &Контрагент, а не В ИЕРАРХИИ.