Как при отборе в иерархическом справочнике 1С скрыть пустые группы?

Программист 1С v8.3 (Управляемые формы)
← На главную

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

Рассмотрим, почему так происходит и какие существуют способы, чтобы скрыть эти пустые группы и сделать отображение списка интуитивно понятным.

Анализ проблемы: почему группы остаются видимыми?

Проанализируем типичный код для установки отбора при открытии формы:


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

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

Ключевая причина — платформа применяет отбор непосредственно к элементам, а группы отображает для сохранения целостности иерархической структуры. Даже если у группы есть реквизит «Менеджер» и он не соответствует условию отбора, платформа все равно покажет эту группу, если она является родителем для каких-либо элементов (даже тех, которые скрыты отбором). Система считает, что пользователь должен видеть всю структуру папок, а уже внутри них — отфильтрованные данные. Наша задача — обойти это стандартное поведение.

Решение 1: Изменение режима просмотра списка

Самый простой и быстрый способ решить проблему — это изменить режим просмотра справочника. Динамические списки поддерживают несколько режимов:

В режиме «Список» отбор применяется ко всем строкам одинаково, включая группы. Если группа не удовлетворяет условию отбора по реквизиту «Менеджер», она будет скрыта вместе с элементами.

Как применить: Пользователь может вручную переключить режим через меню «Еще» -> «Режим просмотра». Программно можно установить этот режим по умолчанию для формы списка.

Недостатки: Этот метод не подходит, если для работы пользователя критически важна именно иерархическая структура данных. Он решает проблему отображения, но ценой потери функциональности иерархии.

Решение 2: Использование условного оформления

Более гибкий способ, который позволяет сохранить иерархию, — это использование условного оформления. Идея состоит в том, чтобы не отфильтровать пустые группы, а сделать их невидимыми. Однако напрямую задать условие «в группе нет видимых элементов» через стандартный конструктор условного оформления невозможно.

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

  1. Модификация запроса динамического списка. Откройте форму списка в конфигураторе, выберите динамический список и откройте его свойство «Основная таблица». Нажмите на кнопку «Настройка списка» и перейдите на вкладку «Произвольный запрос».

    Добавим в запрос левое соединение с вложенным запросом. Вложенный запрос будет для каждой родительской группы находить признак наличия «нужных» нам дочерних элементов.

    
    ВЫБРАТЬ
        СправочникКонтрагенты.Ссылка,
        СправочникКонтрагенты.ВерсияДанных,
        СправочникКонтрагенты.ПометкаУдаления,
        СправочникКонтрагенты.Родитель,
        СправочникКонтрагенты.ЭтоГруппа,
        СправочникКонтрагенты.Код,
        СправочникКонтрагенты.Наименование,
        СправочникКонтрагенты.Менеджер,
        // Наше вычисляемое поле
        ВЫБОР
            КОГДА СправочникКонтрагенты.ЭтоГруппа
                ТОГДА ЕСТЬNULL(НаличиеЭлементов.ЕстьЭлементы, ЛОЖЬ)
            ИНАЧЕ ИСТИНА
        КОНЕЦ КАК ЕстьВидимыеЭлементы
    ИЗ
        Справочник.Контрагенты КАК СправочникКонтрагенты
            ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
                ИСТИНА КАК ЕстьЭлементы,
                Т.Родитель КАК Родитель
            ИЗ
                Справочник.Контрагенты КАК Т
            ГДЕ
                Т.Менеджер = &МенеджерОтбора
            СГРУППИРОВАТЬ ПО
                Т.Родитель) КАК НаличиеЭлементов
            ПО СправочникКонтрагенты.Ссылка = НаличиеЭлементов.Родитель
    

    В этом запросе мы создали поле ЕстьВидимыеЭлементы. Для элементов оно всегда ИСТИНА, а для групп оно принимает значение ИСТИНА только если внутри есть хотя бы один контрагент с нужным менеджером. Обратите внимание на параметр &МенеджерОтбора.

  2. Передача параметра в запрос. Теперь нужно передавать значение текущего менеджера в этот параметр. В обработчике ПриСозданииНаСервере формы списка добавим код:

    
    Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
        Если РольДоступна("Менеджер") Тогда
            ТекущийМенеджер = Пользователи.ТекущийПользователь().ФизическоеЛицо;
            // Устанавливаем и основной отбор
            // ... ваш код отбора, как в начале ...
            
            // Устанавливаем параметр для запроса
            Список.Параметры.УстановитьЗначениеПараметра("МенеджерОтбора", ТекущийМенеджер);
        КонецЕсли;
    КонецПроцедуры
    
  3. Настройка условного оформления. Теперь, когда у нас есть поле ЕстьВидимыеЭлементы, настроим условное оформление:

    • В палитре свойств динамического списка найдите «Условное оформление» и откройте конструктор.
    • Добавьте новый элемент оформления.
    • На вкладке «Оформление» установите свойство «Видимость» в значение «Ложь» (снимите галочку).
    • На вкладке «Условие» добавьте отбор: ЕстьВидимыеЭлементы Равно Ложь.

    Теперь все группы, у которых нет подходящих дочерних элементов, станут невидимыми, при этом иерархия для видимых данных сохранится.

Решение 3: Программное формирование списка видимых групп и элементов

Это самый мощный, но и самый сложный метод. Он дает полный контроль над тем, что отображается в списке. Логика следующая: сначала мы получаем все элементы, которые соответствуют нашему условию, затем находим всех их родительских групп и формируем итоговый отбор, который разрешает показывать только найденные элементы и их родительские группы.

Шаг 1. Получаем список элементов и их родителей.

Для этого удобно использовать запрос с иерархическими итогами или временные таблицы. В обработчике ПриСозданииНаСервере напишем код, который выполнит эту логику.


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

        Запрос.УстановитьПараметр("Менеджер", ТекущийМенеджер);
        
        Результат = Запрос.Выполнить();
        СписокВидимых = Результат.Выгрузить().ВыгрузитьКолонку("Ссылка");
        
        // 2. Устанавливаем отбор по полученному списку
        ЭлементОтбора = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
        ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Ссылка");
        ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
        ЭлементОтбора.ПравоеЗначение = СписокВидимых;
        ЭлементОтбора.Использование = Истина;
        ЭлементОтбора.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный;
    КонецЕсли;
КонецПроцедуры

Как это работает:

  1. Первая часть пакетного запроса находит всех элементов (не группы) с нужным менеджером и помещает их во временную таблицу втЭлементыПоОтбору.
  2. Вторая часть запроса объединяет два набора данных:
    • Ссылки на сами найденные элементы.
    • Ссылки на всех их родителей, полученные с помощью виртуальной таблицы Иерархия. Эта таблица очень удобна, так как возвращает всю цепочку родителей для каждого элемента.
  3. В итоге мы получаем массив СписокВидимых, содержащий как сами целевые элементы, так и все группы, в которых они находятся.
  4. Устанавливаем на динамический список отбор вида Ссылка В (&СписокВидимых). Это заставит список показать только то, что нам нужно, сохранив при этом иерархию.

Преимущества: Этот метод наиболее точен и не требует изменения самого запроса динамического списка. Он отлично работает с большими объемами данных.

Выводы

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

Выбор конкретного способа зависит от ваших задач, но теперь вы знаете, как подойти к решению этой распространенной проблемы.

← На главную