Как скрыть пустые папки в списке номенклатуры 1С?

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

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

Анализ проблемы иерархических списков

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

Основная сложность заключается в том, что «пустота» папки — понятие относительное. Папка считается непустой, если:

  1. В ней непосредственно содержатся элементы-товары, удовлетворяющие условию.
  2. В ней содержатся другие папки, которые, в свою очередь, не являются пустыми.

Разберем по шагам, как реализовать фильтрацию, которая учитывает эти нюансы.

Решение через запрос и временные таблицы

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

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


// 1. Получаем список товаров с остатками
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |	ТоварыВРозницеОстатки.Номенклатура КАК Ссылка
    |ПОМЕСТИТЬ ВТ_ТоварыСГруппами
    |ИЗ
    |	РегистрНакопления.ТоварыВРознице.Остатки(&Период, Склад = &Склад) КАК ТоварыВРозницеОстатки
    |ГДЕ
    |	ТоварыВРозницеОстатки.КоличествоОстаток > 0
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |	Номенклатура.Ссылка В (ВЫБРАТЬ ВТ.Ссылка ИЗ ВТ_ТоварыСГруппами КАК ВТ)";

Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("Склад", ВыбранныйСклад);

СписокНоменклатуры = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");

Однако простого списка товаров недостаточно. Если мы передадим этот массив в отбор ВСпискеПоИерархии, пустые папки могут все равно отобразиться. Выясним причину: штатный механизм 1С при таком виде сравнения показывает всех родителей элементов, входящих в список, но он не всегда умеет скрывать «соседние» пустые папки в том же уровне иерархии, если включен режим просмотра «Дерево».

Корректный алгоритм скрытия пустых групп

Чтобы добиться идеального результата, мы должны получить список всех узлов дерева (и товаров, и их родителей), которые должны присутствовать на экране, и использовать вид сравнения ВСписке (без иерархии), но при этом программно управлять отображением.

Проанализируем более продвинутый подход:

  1. Выбираем все товары с остатками во временную таблицу.
  2. С помощью метода В ИЕРАРХИИ в запросе или итеративного обхода в коде получаем массив всех вышестоящих родителей для найденных товаров.
  3. Объединяем массив товаров и массив их родителей.
  4. Устанавливаем отбор ВидСравнения.ВСписке для ТабличногоПоля.

Пример реализации сбора родителей (использование временных таблиц для производительности):


// В запросе выбираем не только номенклатуру, но и всю иерархию
Запрос.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |	Номенклатура.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |	Номенклатура.Ссылка В ИЕРАРХИИ (
    |		ВЫБРАТЬ РАЗЛИЧНЫЕ
    |			Остатки.Номенклатура
    |		ИЗ
    |			РегистрНакопления.ТоварыВРознице.Остатки КАК Остатки
    |		ГДЕ
    |			Остатки.КоличествоОстаток > 0
    |	)";

СписокДляВывода = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");

// Устанавливаем отбор в списке
Отбор = СписокНоменклатуры.Отбор.Ссылка;
Отбор.ВидСравнения = ВидСравнения.ВСписке;
Отбор.Значение.ЗагрузитьЗначения(СписокДляВывода);
Отбор.Использование = Истина;

Важный момент: использование ВидСравнения.ВСписке заставит 1С показать плоский список. Если вам жизненно необходимо сохранить структуру дерева в ТабличномПоле с типом СправочникСписок, то задача становится сложнее. В обычных формах СправочникСписок не очень гибко работает с динамическим скрытием групп. В этом случае лучшим решением будет использование объекта ДеревоЗначений.

Использование Дерева Значений для полной свободы отображения

Если стандартный отбор не дает нужного визуального эффекта (например, нужно скрыть группы, но оставить возможность разворачивать узлы), рассмотрим вариант замены СправочникСписок на ДеревоЗначений. Это потребует больше кода, но обеспечит полный контроль.

Алгоритм работы с деревом:

  1. Запросом получаем иерархическую выборку (используя ИТОГИ ПО... ИЕРАРХИЯ).
  2. В условии запроса ГДЕ ограничиваем выборку только товарами с остатками.
  3. Результат запроса выгружаем в ДеревоЗначений.
  4. Привязываем это дерево к ТабличномуПолю.

Посмотрим на пример такого запроса:


Запрос.Текст = 
    "ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК Ссылка,
    |	Номенклатура.Артикул КАК Артикул,
    |	ЕСТЬNULL(ТоварыВРозницеОстатки.КоличествоОстаток, 0) КАК Остаток
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура
    |		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыВРознице.Остатки КАК ТоварыВРозницеОстатки
    |		ПО Номенклатура.Ссылка = ТоварыВРозницеОстатки.Номенклатура
    |ГДЕ
    |	ТоварыВРозницеОстатки.КоличествоОстаток > 0
    |ИТОГИ ПО
    |	Ссылка ИЕРАРХИЯ";

ДеревоРезультат = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
ЭлементыФормы.ТабличноеПолеНоменклатура.Значение = ДеревоРезультат;
ЭлементыФормы.ТабличноеПолеНоменклатура.СоздатьКолонки();

Этот метод гарантирует, что в дереве не будет ни одной «ветки», которая не заканчивается «листом» (товаром) с остатком. Пустые папки просто не попадут в результат запроса благодаря ВНУТРЕННЕМУ СОЕДИНЕНИЮ и особенностям работы ИТОГИ ПО... ИЕРАРХИЯ.

Оптимизация производительности

Проанализируем ситуацию с большими базами данных. Если справочник номенклатуры содержит 100 000 элементов, а остатки разбросаны по всей иерархии, запрос с В ИЕРАРХИИ или сложными итогами может выполняться долго. Для оптимизации рекомендуется:

Важное замечание по рекурсии: Как было отмечено в обсуждении, если папка содержит только другие папки, которые в свою очередь пусты, стандартные методы соединения могут дать сбой. Использование ИТОГИ ПО Ссылка ИЕРАРХИЯ в 1С является наиболее надежным встроенным способом автоматически «подтянуть» всю цепочку родителей для каждой найденной строки товара, исключая при этом те ветви дерева, где целевых строк не найдено.

Таким образом, мы рассмотрели несколько способов решения задачи. Выбор конкретного метода зависит от типа формы (Обычная или Управляемая) и требований к интерфейсу. Для максимальной гибкости в Обычных формах мы рекомендуем использовать ДеревоЗначений, а для простых списков — программное формирование массива ссылок всех значимых узлов с последующей установкой отбора ВСписке.

← На главную