Как программно установить сложный отбор (с условием ИЛИ) в динамическом списке 1С на управляемой форме?

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

При работе с платформой 1С:Предприятие 8.3 в режиме управляемых форм, разработчики часто сталкиваются с необходимостью настройки гибких отборов для динамических списков. Стандартные механизмы, предоставляемые объектом ДинамическийСписок, позволяют легко устанавливать отборы по полям с использованием логического оператора И (AND). Однако, когда возникает задача применить условия с оператором ИЛИ (OR) или более сложные комбинированные условия, стандартные методы, такие как ДинамическийСписок.Отбор.Элементы.Добавить(), оказываются недостаточными. Это связано с тем, что элементы отбора добавляются как отдельные условия, которые по умолчанию объединяются логическим И. В таких ситуациях нам потребуется более глубокое вмешательство в механизм работы динамического списка, а именно – программное изменение его текста запроса.

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

Ограничения стандартного механизма отбора в динамическом списке

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

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

Решение 1: Программное изменение текста запроса динамического списка с фиксированными параметрами

Самый прямой и эффективный способ реализовать сложное условие отбора, включая ИЛИ, — это напрямую задать текст запроса для динамического списка, используя свойство ДинамическийСписок.ТекстЗапроса. Этот метод дает нам полный контроль над формированием запроса, позволяя включить любые необходимые логические конструкции.

Рассмотрим сценарий, когда нам нужно отфильтровать записи регистра сведений по двум полям (например, Номенклатура или Контрагент) с фиксированными значениями, и дополнительно по Периоду. Предположим, значения для отбора мы получаем из реквизитов формы или другого объекта.

Шаги реализации:

  1. Определяем базовый запрос: Сначала нам нужно определить основной запрос, который будет извлекать данные для динамического списка.
  2. Добавляем условие ГДЕ с оператором ИЛИ: В секции ГДЕ запроса формируем нужное условие, используя скобки для группировки условий с ИЛИ.
  3. Используем параметры запроса: Для передачи значений в запрос применяем параметры (например, &Номенклатура1). Это критически важно для безопасности и производительности, предотвращая SQL-инъекции и позволяя СУБД кэшировать план выполнения запроса.
  4. Устанавливаем параметры: После присвоения нового текста запроса, используем метод ДинамическийСписок.УстановитьПараметр() для каждого параметра, определенного в запросе.

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


Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // Предполагаем, что у нас есть объект МойОбъект, из которого берутся значения для отбора
    // Или же реквизиты формы, которые уже заполнены.
    // Если объект формы не имеет ссылки (например, это новый элемент справочника),
    // или если значения для отбора еще не заданы, можем пропустить установку отбора.
    Если Объект.Ссылка.Пустая() Тогда
        // Для примера, если форма создается для нового элемента, не применяем отбор по МойОбъект.
        // Или же можно задать здесь другой запрос по умолчанию.
        Возврат;
    КонецЕсли;

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

    // Устанавливаем значения параметров для запроса
    // Предполагаем, что МойОбъект.Номенклатура и МойОбъект.Контрагент
    // содержат значения для отбора.
    // Также можно использовать реквизиты формы, например:
    // ДинамическийСписок.УстановитьПараметр("Номенклатура1", Элементы.ПолеНоменклатура.Значение);
    // ДинамическийСписок.УстановитьПараметр("Контрагент1", Элементы.ПолеКонтрагент.Значение);
    ДинамическийСписок.УстановитьПараметр("Номенклатура1", МойОбъект.Номенклатура);
    ДинамическийСписок.УстановитьПараметр("Контрагент1", МойОбъект.Контрагент);
    ДинамическийСписок.УстановитьПараметр("Период", НачалоДня(ТекущаяДата())); // Например, отбор по текущему дню

    // Важно: если нужно применить отбор сразу после установки,
    // иногда требуется вызвать Обновить() у динамического списка,
    // но в ПриСозданииНаСервере это обычно происходит автоматически.
КонецПроцедуры

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

Решение 2: Динамическое формирование текста запроса с изменяемыми параметрами

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

Основные принципы динамического формирования запроса:

  1. Базовый запрос: Определяем минимальный, всегда выполняющийся запрос без условий ГДЕ.
  2. Сбор условий: Создаем вспомогательную переменную (например, строку), в которую будем пошагово добавлять текстовые представления условий.
  3. Обработка пустого значения: Проверяем, заполнено ли поле отбора. Если оно пустое, то соответствующее условие не добавляем. Это позволяет пользователю выбирать, по каким полям фильтровать.
  4. Добавление логических операторов: Аккуратно размещаем операторы И/ИЛИ между условиями. Здесь важно следить за правильной расстановкой скобок.
  5. Установка параметров: Собираем все значения параметров в коллекцию (например, Соответствие) и затем устанавливаем их для динамического списка.
  6. Обновление списка: После изменения текста запроса и установки параметров, необходимо вызвать метод Обновить() динамического списка, чтобы перечитать данные.

Рассмотрим пример, где пользователь может ввести Номенклатуру и/или Контрагента, и список будет фильтроваться по этим значениям с условием ИЛИ, а также по фиксированному Периоду. Мы будем использовать отдельные поля на форме (например, ПолеНоменклатура, ПолеКонтрагент, ПолеПериод), связанные с реквизитами формы.


// В обработчике модуля формы

// Процедуры-обработчики событий "ПриИзменении" для полей ввода на форме
Процедура ПолеНоменклатураПриИзменении(Элемент)
    ОбновитьДинамическийСписок();
КонецПроцедуры

Процедура ПолеКонтрагентПриИзменении(Элемент)
    ОбновитьДинамическийСписок();
КонецПроцедуры

Процедура ПолеПериодПриИзменении(Элемент)
    ОбновитьДинамическийСписок();
КонецПроцедуры

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

    // Строка для сбора условий и коллекция для параметров запроса
    Перем Условия;
    Условия = "";
    Перем ПараметрыЗапроса;
    ПараметрыЗапроса = Новый Соответствие;

    // --- Обработка условий для Номенклатуры ИЛИ Контрагента ---
    Перем ЕстьУсловиеПоНоменклатуре;
    ЕстьУсловиеПоНоменклатуре = Не ЗначениеЗаполнено(ПолеНоменклатура); // ПолеНоменклатура - реквизит формы
    Перем ЕстьУсловиеПоКонтрагенту;
    ЕстьУсловиеПоКонтрагенту = Не ЗначениеЗаполнено(ПолеКонтрагент); // ПолеКонтрагент - реквизит формы

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

    // --- Добавляем условие по Периоду (если заполнено) ---
    Перем ЕстьУсловиеПоПериоду;
    ЕстьУсловиеПоПериоду = Не ПустаяДата(ПолеПериод); // ПолеПериод - реквизит формы

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

    // --- Применяем сформированный запрос к динамическому списку ---
    Если Условия <> "" Тогда
        ДинамическийСписок.ТекстЗапроса = ТекстЗапросаБазовый + " ГДЕ " + Условия;
        // Очищаем старые параметры и устанавливаем новые
        ДинамическийСписок.Параметры.Очистить();
        Для Каждого КлючЗначение Из ПараметрыЗапроса Цикл
            ДинамическийСписок.УстановитьПараметр(КлючЗначение.Ключ, КлючЗначение.Значение);
        КонецЦикла;
    Иначе
        // Если никаких условий не задано, показываем весь список (базовый запрос)
        ДинамическийСписок.ТекстЗапроса = ТекстЗапросаБазовый;
        ДинамическийСписок.Параметры.Очистить(); // Удаляем все параметры, если они были
    КонецЕсли;

    // Обновляем динамический список, чтобы применились новые настройки
    ДинамическийСписок.Обновить();
КонецПроцедуры

// Также убедитесь, что процедура ОбновитьДинамическийСписок вызывается
// при открытии формы, чтобы применить отборы по умолчанию или первоначальные значения.
// Например, в ПриСозданииНаСервере:
// Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
//     // Инициализация полей отбора, если нужно
//     // ПолеПериод = ТекущаяДата();
//     ОбновитьДинамическийСписок();
// КонецПроцедуры

Данный пример демонстрирует гибкость подхода. Мы можем адаптировать его для любых комбинаций условий, в том числе и более сложных конструкций с несколькими уровнями вложенности операторов И и ИЛИ.

Дополнительные соображения и альтернативные подходы

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

Использование свойства ДинамическийСписок.ПроизвольныйЗапрос

Свойство ПроизвольныйЗапрос динамического списка также позволяет задать пользовательский текст запроса, но оно, по сути, является частью более широкого механизма СКД. Если вы используете схему компоновки данных, привязанную к динамическому списку, то ПроизвольныйЗапрос позволяет более гибко работать с запросами внутри СКД, не затрагивая при этом структуру самого динамического списка. Он может быть полезен, когда вам нужно добавить нестандартные таблицы, объединения или сложные конструкции, которые выходят за рамки простого изменения секции ГДЕ. Однако для чистой установки отбора с ИЛИ, прямое изменение ТекстЗапроса часто бывает проще и понятнее.

Обработчик события ПриПолученииДанных

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

  1. Производительность: Фильтрация происходит на клиенте после того, как все данные уже были выбраны из базы данных и переданы на клиент. Это может привести к значительным задержкам и потреблению памяти при работе с большими списками.
  2. Нагрузка на сеть: Независимо от того, сколько строк останется после фильтрации, весь объем данных сначала передается из СУБД на сервер 1С, а затем на клиент.

Метод ПриПолученииДанных следует использовать только для специфических задач, когда фильтрация не может быть выполнена на уровне запроса СУБД (например, при расчете каких-либо показателей, которые не хранятся в базе, но нужны для фильтрации), и объем данных невелик.

Работа со схемой компоновки данных (СКД)

Для очень сложных и многоуровневых отборов, а также для создания пользовательских настроек, 1С предоставляет мощный механизм Системы Компоновки Данных (СКД). Динамический список сам по себе является упрощенным представлением СКД. Если задача требует не только отбора, но и группировок, расчетов и других элементов компоновки, то можно создавать и настраивать схему компоновки данных программно. В СКД вы можете задавать любые условия, в том числе с ИЛИ, прямо в тексте запроса или через настройки отборов компоновки данных. Этот подход более сложен, но предоставляет максимальную гибкость. Для простых случаев, описанных в этой статье, прямое изменение ТекстЗапроса динамического списка является более оптимальным решением.

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

Заключение

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

← На главную