Группы иерархического справочника в 1С 8.3: создание, обход дерева, отбор в запросах и формах
Содержание
Обратиться к группе справочника 1С можно тремя путями: по ссылке через реквизит Родитель, поиском НайтиПоНаименованию() или НайтиПоКоду() с параметром родителя, и запросом с условием В ИЕРАРХИИ. Отличить группу от элемента помогает стандартный реквизит ЭтоГруппа. Всё это работает только в справочнике, у которого в свойствах метаданных включён флаг «Иерархический».
Приёмы ниже общие для платформы 1С:Предприятие 8.3 и не зависят от конфигурации: одинаково работают в БП 3.0, ЗУП 3.1, УТ 11 и в собственной разработке. Примеры даны на справочнике Номенклатура, но имя легко заменить на любое своё. Отдельный раздел — для аналитика, которому группа нужна в отборе отчёта или списка без строчки кода.
Как устроены группы: виды иерархии и свойства метаданных
Иерархия включается в конфигураторе, на закладке «Иерархия» свойств справочника. Там же выбирают вид иерархии. От него зависит, есть ли у справочника группы вообще.
| Вид иерархии | Что это значит | Есть ли ЭтоГруппа |
|---|---|---|
| Иерархия групп и элементов | Группы — папки, элементы лежат внутри. Элемент не может быть родителем | Да |
| Иерархия элементов | Любой элемент может быть родителем другого элемента, отдельных «папок» нет | Нет |
Остальные свойства на той же закладке:
- 📁 Ограничивать количество уровней иерархии и Количество уровней — сколько этажей дерева разрешено. При превышении платформа не даст записать объект.
- ⬆️ Размещение групп — показывать группы сверху списка или вместе с элементами по порядку сортировки.
- 🔒 Выбор групп у реквизитов-ссылок на справочник (свойство реквизита «Выбор групп и элементов») — можно ли выбрать в поле группу, элемент или и то, и другое.
Реквизиты справочника тоже делятся по назначению. У каждого есть свойство «Использование»: Для элемента, Для группы или Для группы и элемента. Реквизит «Ставка НДС», заданный только для элемента, в карточке группы не показывается и в ней не хранится. Код, который пишет такой реквизит в группу, результата не даст.
ЭтоГруппа задаётся один раз при создании. Превратить существующий элемент в группу нельзя: нужно создать новую группу и перенести в неё подчинённые элементы.
Создание, перенос и удаление групп справочника из кода
Новую группу создают менеджером справочника методом СоздатьГруппу(). Элемент — методом СоздатьЭлемент(). Дальше обычная запись объекта.
// Создаём группу «Материалы» в корне и подгруппу «Краски» внутри неё
ГруппаМатериалы = Справочники.Номенклатура.СоздатьГруппу();
ГруппаМатериалы.Наименование = "Материалы";
ГруппаМатериалы.Записать();
ГруппаКраски = Справочники.Номенклатура.СоздатьГруппу();
ГруппаКраски.Наименование = "Краски";
ГруппаКраски.Родитель = ГруппаМатериалы.Ссылка;
ГруппаКраски.Записать();
Корень дерева — это пустая ссылка. Если Родитель не заполнен, объект лежит на верхнем уровне. Проверка выглядит так: Ссылка.Родитель.Пустая() или Ссылка.Родитель = Справочники.Номенклатура.ПустаяСсылка().
Найти группу по наименованию или коду
Сигнатуры методов поиска у менеджера справочника:
НайтиПоНаименованию(Наименование, ТочноеСоответствие, Родитель, Владелец);НайтиПоКоду(Код, ПоискПоПолномуКоду, Родитель, Владелец).
Параметр Родитель ограничивает поиск одной группой. Это спасает, когда в разных ветках дерева лежат одноимённые группы «Прочее» или «Архив».
Группа = Справочники.Номенклатура.НайтиПоНаименованию("Краски", Истина, ГруппаМатериалы);
Если Группа.Пустая() Тогда
Сообщить("Группа не найдена");
ИначеЕсли Не Группа.ЭтоГруппа Тогда
Сообщить("Найден элемент, а не группа");
КонецЕсли;
Методы поиска возвращают первый подходящий объект — группу или элемент. Поэтому проверка ЭтоГруппа после поиска обязательна, если в одной ветке могут совпасть имена группы и товара.
Перенести объект в другую группу
Перенос — это смена реквизита Родитель и запись. Подчинённые элементы переезжают вместе с группой автоматически: они ссылаются на неё, а не на путь в дереве.
Объект = Ссылка.ПолучитьОбъект();
Объект.Родитель = НоваяГруппа;
Объект.Записать();
Переместить группу внутрь её же потомка платформа не позволит — при записи будет ошибка о зацикливании иерархии.
Пометить группу на удаление
У объекта справочника метод УстановитьПометкуУдаления(Пометка, ВключаяПодчиненные) по умолчанию помечает всё содержимое ветки. Если нужно пометить только саму группу, передайте Ложь вторым параметром.
Пометка группы со всеми подчинёнными в рабочей базе может зацепить сотни позиций. Перед массовой пометкой сделайте резервную копию: удаление помеченных объектов необратимо.
Обход дерева: родители, подчинённые элементы и уровень
Для навигации по дереву в объектной модели есть несколько методов ссылки и объекта справочника.
| Задача | Метод или свойство | Результат |
|---|---|---|
| Узнать прямого родителя | Ссылка.Родитель | Ссылка на группу или пустая ссылка |
| Узнать глубину | Ссылка.Уровень() | Число, верхний уровень = 0 |
| Проверить вхождение в ветку | Ссылка.ПринадлежитЭлементу(Группа) | Истина, если объект лежит в группе на любом уровне |
| Перебрать прямых детей | Справочники.X.Выбрать(Группа) | Выборка только первого уровня под группой |
| Перебрать всю ветку | Справочники.X.ВыбратьИерархически(Группа) | Выборка всех потомков |
Подняться до верхней группы
Типовая задача: по товару получить его корневую категорию. Идём вверх, пока родитель не пустой.
Функция КорневаяГруппа(Ссылка)
Текущая = Ссылка;
Пока Не Текущая.Родитель.Пустая() Цикл
Текущая = Текущая.Родитель;
КонецЦикла;
Возврат Текущая;
КонецФункции
Каждое обращение Текущая.Родитель — это чтение из базы. Для пяти уровней это пять маленьких запросов, для одного товара терпимо. Для тысячи товаров в цикле так делать не стоит — ниже есть вариант через запрос.
Спуститься по ветке
ВыбратьИерархически отдаёт всё содержимое группы: и подгруппы, и элементы. Если нужны только товары, отсекайте группы прямо в цикле.
Выборка = Справочники.Номенклатура.ВыбратьИерархически(ГруппаМатериалы);
Пока Выборка.Следующий() Цикл
Если Выборка.ЭтоГруппа Тогда
Продолжить;
КонецЕсли;
Сообщить(Выборка.Наименование);
КонецЦикла;
Выборка через менеджер удобна для коротких служебных обработок. Когда нужны отборы по реквизитам, связанным таблицам или сортировка, запрос работает быстрее и читается проще.
Группы справочника в языке запросов 1С
В запросе к группе обращаются через три конструкции. Их стоит знать на память.
Условие «В ИЕРАРХИИ»
Отбирает саму группу и всё её содержимое на любой глубине. Это главный инструмент.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка,
| Номенклатура.Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В ИЕРАРХИИ (&Группа)
| И НЕ Номенклатура.ЭтоГруппа
| И НЕ Номенклатура.ПометкаУдаления";
Запрос.УстановитьПараметр("Группа", ГруппаМатериалы);
Результат = Запрос.Выполнить().Выбрать();
В параметр можно передать и массив групп — тогда в результат попадёт содержимое всех веток сразу. Тот же В ИЕРАРХИИ работает в регистрах: Остатки.Номенклатура В ИЕРАРХИИ (&Группа) даст остатки по всем товарам категории без предварительного сбора их списка.
Отбор только по прямым потомкам
Если нужен один уровень, а не вся ветка, условие пишут через родителя:
"ГДЕ Номенклатура.Родитель = &Группа"
Для верхнего уровня передайте в параметр пустую ссылку справочника.
Итоги по иерархии
Чтобы получить дерево с промежуточными суммами по группам, используют ИТОГИ ... ПО ... ИЕРАРХИЯ. Выборку обходят с режимом ОбходРезультатаЗапроса.ПоГруппировкамСИерархией.
Запрос.Текст =
"ВЫБРАТЬ
| Остатки.Номенклатура КАК Номенклатура,
| Остатки.КоличествоОстаток КАК Количество
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
|ИТОГИ
| СУММА(Количество)
|ПО
| Номенклатура ИЕРАРХИЯ";
Дерево = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
Имя регистра в примере условное — подставьте свой. Результат удобно выгрузить в дерево значений и вывести в форму или макет: группы окажутся на своих местах с суммами по содержимому.
Ссылка В ИЕРАРХИИ (&Группа), при необходимости плюс НЕ ЭтоГруппа.Родитель = &Группа. Для корня — пустая ссылка в параметре.ИТОГИ СУММА(...) ПО Поле ИЕРАРХИЯ и выгрузка с обходом ПоГруппировкамСИерархией.Ссылка.ПринадлежитЭлементу(Группа) без запроса.Группы в управляемых формах, списках и отчётах
Форма списка
Форма списка иерархического справочника строится на динамическом списке. Вид отображения задаёт свойство таблицы формы Отображение:
- 🌳
Дерево— всё дерево с раскрытием веток; - 📂
ИерархическийСписок— показывается одна группа, вход в неё двойным щелчком; - 📋
Список— плоский список без групп-папок.
Пользователь переключает режим сам через командную панель списка («Ещё»), а начальный вид задаёт разработчик. Для дерева пригодится свойство НачальноеОтображениеДерева: раскрывать все уровни, только верхний или ничего.
Отбор списка по группе программно
Чтобы показать в динамическом списке только содержимое ветки, добавьте отбор компоновки с видом сравнения «В иерархии»:
ЭлементОтбора = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Ссылка");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВИерархии;
ЭлементОтбора.ПравоеЗначение = ГруппаМатериалы;
ЭлементОтбора.Использование = Истина;
При таком отборе удобнее переключить список в режим Список: в дереве пользователь всё равно будет видеть путь из групп-родителей.
Форма выбора
Когда реквизит документа ссылается на справочник, что можно выбрать — решает свойство реквизита «Выбор групп и элементов». Значение «Элементы» запрещает подставить группу: кнопка выбора на группе не сработает. Если нужно выбирать именно категорию (например, для правила скидки на всю группу товаров), поставьте «Группы» или «Группы и элементы».
Для аналитика: группа в отборе отчёта
Отчёты на СКД в 1С 8.3 умеют отбирать по группе без программиста. В настройках отчёта при отборе по полю-справочнику выберите вид сравнения:
- В группе — одна ветка со всем содержимым;
- В группе из списка — несколько веток сразу;
- Не в группе — исключить ветку.
Те же виды сравнения доступны в настройке отбора обычных списков — её открывают из меню «Ещё» над списком. Это аналог В ИЕРАРХИИ из языка запросов, только для пользователя. Если нужно видеть итоги по группам, в структуре отчёта у группировки по номенклатуре выберите тип «Иерархия».
Производительность и частые ошибки при работе с группами
Обращение через точку в цикле. Код Товар.Родитель.Родитель.Наименование выглядит безобидно, но каждый шаг читает объект из базы. В цикле по тысяче строк табличной части получается несколько тысяч обращений. Решение — один запрос, который заранее достаёт нужные поля, или В ИЕРАРХИИ вместо ручного обхода.
Рекурсия с запросом на каждом уровне. Собрать все подгруппы вручную, вызывая запрос для каждой группы, — медленно и сложно. Платформа уже умеет это делать: В ИЕРАРХИИ или ВыбратьИерархически решают задачу за один проход.
Забытое НЕ ЭтоГруппа. Условие В ИЕРАРХИИ возвращает и группы. В отчёте по товарам это пустые строки без цен и остатков, а в обработке — попытка записать в группу реквизит, которого у неё нет.
Группа там, где ждут элемент. Документ записан с группой в поле «Номенклатура», потому что реквизит разрешал выбирать группы. Потом отчёты и проводки ведут себя странно. Проверьте свойство «Выбор групп и элементов» у реквизита, а в коде перед записью проверяйте ЭтоГруппа.
Поиск без родителя. НайтиПоНаименованию("Прочее") без параметра Родитель вернёт первую попавшуюся группу из любой ветки. При загрузке данных это приводит к товарам, разложенным по чужим категориям.
Попытка сменить тип объекта. ЭтоГруппа недоступен для записи. Обработка, которая «переделывает» элемент в группу присвоением, упадёт с ошибкой.
Частые вопросы
Включает ли В ИЕРАРХИИ саму группу?
Ссылка <> &Группа или НЕ ЭтоГруппа.Что вернёт В ИЕРАРХИИ, если передать элемент, а не группу?
Можно ли сделать справочник иерархическим, когда в нём уже есть данные?
Как получить полный путь группы строкой, например «Материалы / Краски»?
Родитель до корня и соберите наименования в массив, затем соедините их функцией СтрСоединить() в обратном порядке.