Как программно настроить сложный отбор в динамическом списке 1С, чтобы всегда показывать определенный элемент?

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

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

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

Постановка задачи и анализ проблемы

Рассмотрим практическую ситуацию. У нас есть форма выбора или форма списка справочника. В форму передаются два параметра отбора: Программа и АБП. Нам нужно применить эти фильтры к динамическому списку. Но в справочнике также существует важный предопределенный элемент (например, с кодом «000»), который пользователи всегда должны иметь возможность выбрать. Если мы просто наложим стандартный отбор по параметрам, этот элемент с кодом «000» скроется, так как его реквизиты не соответствуют переданным параметрам программы и АБП.

Нам необходимо построить логическое условие следующего вида:

(Элемент.Программа = Параметр.Программа И Элемент.АБП = Параметр.АБП) ИЛИ (Элемент.Код = "000")

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

Разбор базовой ошибки при работе с группами отбора

Часто разработчики пытаются решить задачу «в лоб», просто добавляя все элементы отбора в одну плоскую группу с типом «ИЛИ». Давайте посмотрим на пример такого кода, который ошибочно считают рабочим:


// ПРИМЕР С ОШИБКОЙ! НЕ ИСПОЛЬЗОВАТЬ В РЕАЛЬНЫХ ЗАДАЧАХ
СтандартнаяОбработка = Ложь;
Список.Отбор.Элементы.Очистить();  

ГруппаОтбора = Список.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
ГруппаОтбора.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИЛИ;

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

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

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

Проанализируем ситуацию и поймем, почему этот код работает некорректно. Добавление всех элементов в одну группу ГруппаИЛИ приводит к тому, что платформа объединяет их по логическому закону «ИЛИ». В итоге система сформирует следующий запрос к базе данных:

Где (Программа = &Программа) ИЛИ (АБП = &АБП) ИЛИ (Код = "000")

Это критическая логическая ошибка! Если в форму переданы оба параметра (и программа, и АБП), то они должны фильтровать список одновременно (через логическое «И»), сужая выборку. В текущем же ошибочном варианте система покажет вообще все элементы с указанной программой (даже если у них другой АБП), а также вообще все элементы с указанным АБП (даже если у них другая программа). Фильтрация по пересечению параметров полностью ломается.

Правильное решение: построение древовидной структуры отборов

Чтобы реализовать математически верную логику, нам необходимо использовать вложенные группы отборов. Разберем по шагам правильный алгоритм построения дерева условий:

  1. Создаем корневую группу отбора с типом ГруппаИЛИ. Она будет отвечать за объединение основного фильтра и нашего исключения.
  2. В эту корневую группу добавляем элемент отбора для нашего исключения (условие по коду «000» или по ссылке на предопределенный элемент).
  3. В эту же корневую группу добавляем вложенную группу с типом ГруппаИ. Эта группа будет отвечать за строгое пересечение переданных параметров.
  4. Все входящие параметры фильтрации (Программа, АБП) добавляем уже непосредственно внутрь вложенной группы «И».

Рассмотрим подробнее, как выглядит реализация этой схемы на встроенном языке 1С:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    // Очищаем существующие отборы динамического списка
    Список.Отбор.Элементы.Очистить();  
    
    // Шаг 1. Создаем корневую группу ИЛИ
    КорневаяГруппаИЛИ = Список.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
    КорневаяГруппаИЛИ.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИЛИ;
    КорневаяГруппаИЛИ.Использование = Истина;
    
    // Шаг 2. Добавляем в группу ИЛИ условие-исключение (наш предопределенный элемент)
    ЭлементИсключение = КорневаяГруппаИЛИ.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    ЭлементИсключение.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Ссылка");
    ЭлементИсключение.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
    // Используем обращение к предопределенному элементу вместо жесткого кодирования строки
    ЭлементИсключение.ПравоеЗначение = ПредопределенноеЗначение("Справочник.ИмяВашегоСправочника.ПустойЭлемент");
    ЭлементИсключение.Использование = Истина;
    
    // Шаг 3. Создаем вложенную группу И для основных параметров фильтрации
    ВложеннаяГруппаИ = КорневаяГруппаИЛИ.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
    ВложеннаяГруппаИ.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИ;
    ВложеннаяГруппаИ.Использование = Истина;
    
    // Шаг 4. Наполняем вложенную группу И параметрами, если они переданы в форму
    Если Параметры.Свойство("Программа") И ЗначениеЗаполнено(Параметры.Программа) Тогда 
        ЭтотОбъект.вхПрограмма = Параметры.Программа; // Сохраняем в реквизит формы при необходимости
        
        ЭлементОтбора = ВложеннаяГруппаИ.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
        ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Программа");
        ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
        ЭлементОтбора.ПравоеЗначение = Параметры.Программа;
        ЭлементОтбора.Использование = Истина;
    КонецЕсли;
    
    Если Параметры.Свойство("АБП") И ЗначениеЗаполнено(Параметры.АБП) Тогда 
        ЭтотОбъект.вхАБП = Параметры.АБП;
        
        ЭлементОтбора = ВложеннаяГруппаИ.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
        ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("АБП");
        ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
        ЭлементОтбора.ПравоеЗначение = Параметры.АБП;
        ЭлементОтбора.Использование = Истина;
    КонецЕсли;
    
КонецПроцедуры

Данный подход гарантирует формирование абсолютно верного математического условия:

Где (Ссылка = &ПредопределенныйЭлемент) ИЛИ (Программа = &ПараметрПрограмма И АБП = &ПараметрАБП)

Список отобразит только те элементы, у которых совпадает и программа, и АБП, но при этом элемент-исключение всегда останется видимым в списке.

Важные правила хорошего тона при написании отборов

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

  1. Откажитесь от сравнения по коду (Hardcode). В кодовой базе обсуждения фигурировало сравнение по строковому коду: ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Код") и ПравоеЗначение = "000". Это крайне опасная практика. Если в пользовательском режиме кто-то случайно или намеренно изменит код этого элемента (например, переименует в «0001» или «00-000»), ваш программный отбор молча перестанет работать. Всегда используйте поле Ссылка и системную функцию ПредопределенноеЗначение(), обращающуюся к имени предопределенного элемента, заданному в конфигураторе. Поиск по ссылке также работает значительно быстрее, чем по строковым реквизитам.
  2. Контроль использования пустых параметров. Перед добавлением элементов отбора всегда проверяйте переданные параметры на заполненность с помощью функции ЗначениеЗаполнено(). Если параметр передан пустым, добавлять под под него неактивный или пустой элемент отбора бессмысленно и вредно для производительности СУБД.

Использование функций БСП для сокращения кода

Если вы ведете разработку в современной конфигурации на базе Библиотеки Стандартных Подсистем (БСП), вам не обязательно вручную конструировать объекты компоновки данных и писать громоздкий код. В БСП предусмотрены готовые инструменты в общем модуле ОбщегоНазначенияКлиентСервер.

Для создания групп отборов вы можете использовать функцию СоздатьГруппуЭлементовОтбора(). Рассмотрим пример того, как лаконично можно переписать наш алгоритм с использованием БСП:


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

Использование БСП значительно повышает читаемость кода, стандартизирует разработку и защищает от случайных опечаток при написании типов и свойств объектов компоновщика настроек динамического списка.

← На главную