Как игнорировать параметр в запросе динамического списка, пока не заполнен реквизит на форме

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

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

В этой статье мы подробно разберем несколько подходов к решению данной задачи: от простых манипуляций с текстом запроса до профессионального использования системы компоновки данных (СКД) и программного управления отборами.

Проблема использования условий ВЫБОР КОГДА в запросе

Рассмотрим ситуацию, с которой часто начинают новички. Попытка написать универсальный запрос прямо в тексте динамического списка выглядит примерно так:


ВЫБРАТЬ
    ДокументЗаказКлиента.Ссылка,
    ДокументЗаказКлиента.Дата,
    ДокументЗаказКлиента.Контрагент
ИЗ
    Документ.ЗаказКлиента КАК ДокументЗаказКлиента
ГДЕ
    ВЫБОР 
        КОГДА &Контрагент ССЫЛКА Справочник.Контрагенты 
        ТОГДА ДокументЗаказКлиента.Контрагент = &Контрагент 
        ИНАЧЕ ИСТИНА 
    КОНЕЦ

Проанализируем, почему этот подход работает некорректно. Когда форма только открывается, параметр &Контрагент чаще всего инициализируется как пустая ссылка соответствующего типа. В результате условие ВЫБОР отрабатывает первую ветку, и список пытается найти документы, где контрагент действительно не заполнен. Вместо того чтобы показать все записи, пользователь видит пустой список или только документы без контрагента.

Кроме того, использование логики ВЫБОР или сложных условий с ИЛИ в секции ГДЕ делает запрос «не-SARGable». Это означает, что система управления базами данных (SQL Server, PostgreSQL) не сможет эффективно использовать индексы по полю Контрагент. На больших объемах данных это приведет к полному сканированию таблицы (Table Scan) и существенным задержкам в работе интерфейса.

Решение №1: Использование фигурных скобок (Расширение языка запросов СКД)

Наиболее правильным и производительным способом для динамических списков является использование механизмов системы компоновки данных. Мы можем обернуть условия в фигурные скобки {}. Это подсказывает платформе, что данное условие является необязательным и должно добавляться в результирующий SQL-запрос только тогда, когда установлен соответствующий отбор или параметр.

Рассмотрим пример правильного написания запроса:


ВЫБРАТЬ
    Заказы.Ссылка,
    Заказы.Дата,
    Заказы.Контрагент
ИЗ
    Документ.ЗаказКлиента КАК Заказы
{ГДЕ
    Заказы.Контрагент.* КАК Контрагент,
    Заказы.Менеджер.* КАК Менеджер}

В данном случае, если пользователь не выбрал контрагента на форме, 1С сформирует максимально чистый SQL-запрос без лишних фильтров. Если же отбор установлен, платформа сама добавит условие WHERE. Это гарантирует использование индексов и высокую скорость работы.

Решение №2: Программное управление элементами отбора

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

Во-первых, создадим процедуру, которая будет очищать старые программные отборы и устанавливать новые. Важно идентифицировать наши отборы, чтобы не удалить те, которые пользователь поставил вручную через меню «Настроить список».


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

КонецПроцедуры

Эту процедуру необходимо вызывать в событиях ПриИзменении соответствующих реквизитов формы. Таким образом, список будет обновляться мгновенно и содержать только актуальные фильтры.

Решение №3: Оптимизация через Библиотеку стандартных подсистем (БСП)

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

Вместо громоздкого кода выше, мы можем использовать метод УстановитьЭлементОтбора. Рассмотрим пример:


&НаКлиенте
Процедура КонтрагентПриИзменении(Элемент)
    
    // Устанавливает отбор или удаляет его, если значение не заполнено
    ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбора(
        СписокЗаказов.Отбор, 
        "Контрагент", 
        Объект.Контрагент, 
        ЗначениеЗаполнено(Объект.Контрагент)
    );
    
КонецПроцедуры

Последний параметр функции (Булево) определяет состояние использования отбора. Если ЗначениеЗаполнено(Объект.Контрагент) вернет Ложь, система автоматически отключит этот элемент отбора, и список снова покажет все документы. Это самый чистый и современный способ реализации задачи.

Важные нюансы работы с параметрами

Выясним причину, почему иногда использование Список.ПараметрыДанных.УстановитьЗначениеПараметра может не срабатывать так, как ожидается. Если вы используете параметры именно в тексте запроса (а не в настройках компоновки), то параметр обязан иметь значение. У него нет свойства «Использование = Ложь» в том смысле, в каком оно есть у отборов.

Если же вы используете Параметры компоновки данных, то вы можете управлять их активностью:


// Пример на сервере
ПараметрСКД = Список.КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("Контрагент");
Если ПараметрСКД <> Неопределено Тогда
    ПараметрСКД.Значение = Объект.Контрагент;
    ПараметрСКД.Использование = ЗначениеЗаполнено(Объект.Контрагент);
КонецЕсли;

Подведем итог: для решения задачи игнорирования пустого фильтра в динамическом списке лучше всего отказаться от сложных условий ВЫБОР КОГДА внутри запроса. Вместо этого используйте программное управление отборами через КомпоновщикНастроек или механизмы БСП. Это обеспечит чистоту кода, удобство поддержки и максимальное быстродействие вашей информационной системы.

← На главную