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