При работе с иерархическими справочниками в платформе 1С:Предприятие часто возникает задача получить полный список всех подчиненных элементов для определенного узла на всю глубину вложенности. Например, если узел А1 содержит узел А3, а тот в свою очередь содержит элемент А31, требуется получить как прямых потомков (А2, А3), так и вложенных (А31).
Проанализируем типовую ошибку, выясним, почему стандартный метод Выбрать() не возвращает элементы глубоких уровней, и подробно рассмотрим правильные способы решения задачи.
Рассмотрим стандартный вызов объектной выборки:
Под = Справочники.Номенклатура.НайтиПоНаименованию(Строка(Наимен));
Выборка = Справочники.Номенклатура.Выбрать(Под);
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Ссылка);
КонецЦикла;
В данном сценарии метод Выбрать(Родитель) возвращает только те элементы и группы, которые непосредственно подчинены переданному родителю. То есть выборка ограничивается ровно одним уровнем вложенности вниз. Вложенные папки и их содержимое при таком подходе автоматически не разворачиваются.
Кроме того, обратим внимание на поиск родителя: метод НайтиПоНаименованию() возвращает первый найденный элемент. Если в справочнике присутствуют дубли или элементы с одинаковыми наименованиями в разных ветках, поиск может вернуть не тот узел. Если на форме уже выбран ссылочный реквизит, передавать его строковое представление в поиск не нужно — следует сразу использовать готовую ссылку.
Наиболее эффективным, быстрым и надежным способом получения всех подчиненных элементов любой глубины является использование языка запросов 1С с оператором В ИЕРАРХИИ. Вся работа по рекурсивному обходу дерева в этом случае выполняется средствами СУБД.
Разберем пример запроса для получения плоского списка всех вложенных элементов:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Спр.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Спр
|ГДЕ
| Спр.Ссылка В ИЕРАРХИИ(&Родитель)
| И Спр.Ссылка <> &Родитель";
Запрос.УстановитьПараметр("Родитель", Под);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Ссылка);
КонецЦикла;
Обратим внимание на условие Спр.Ссылка <> &Родитель: оператор В ИЕРАРХИИ включает сам исходный узел-родитель в результат выборки. Если сам узел в результирующем списке не нужен, мы исключаем его дополнительным условием.
Если требуется не просто плоский список, а сохранение древовидной структуры справочника с сохранением уровней группировок, воспользуемся иерархическими итогами в запросе:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Спр.Ссылка КАК Ссылка,
| Спр.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Спр
|ГДЕ
| Спр.Ссылка В ИЕРАРХИИ(&Родитель)
|ИТОГИ ПО
| Ссылка ИЕРАРХИЯ";
Запрос.УстановитьПараметр("Родитель", Под);
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Наименование);
КонецЦикла;
Если по условиям задачи необходимо использовать объектную модель без обращения к запросам, применим метод менеджера справочника ВыбратьИерархически(). Главная особенность заключается в правильном методе обхода:
Выборка.Следующий() перемещается только по элементам текущего уровня.Выборка.ПолучитьСледующегоПоИерархии() осуществляет сквозной проход по всему дереву сверху вниз с заходом во все вложенные папки.Посмотрим на пример кода с правильным обходом:
Выборка = Справочники.Номенклатура.ВыбратьИерархически(Под);
Пока Выборка.ПолучитьСледующегоПоИерархии() Цикл
Сообщить(Выборка.Ссылка);
КонецЦикла;
Если код написан верно, но элементы всё равно не выбираются, проанализируем структуру метаданных конфигурации: