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