Отбор динамического списка из кода: компоновщик, параметры формы и БСП

· 9 мин

Содержание

Программно установить отбор в динамическом списке 1С 8.3 можно через компоновщик настроек: добавьте ЭлементОтбораКомпоновкиДанных в Список.КомпоновщикНастроек.ФиксированныеНастройки.Отбор в обработчике ПриСозданииНаСервере. Если конфигурация построена на БСП, то же самое делает одна строка: ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список, "Поле", Значение).

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

Как устроен динамический список и где у него живёт отбор

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

Отсюда главное отличие от обычного запроса. В запросе вы пишете ГДЕ Владелец = &Владелец один раз и получаете результат. В динамическом списке условие задаётся через настройки компоновки данных, и платформа сама подставляет его при каждом чтении порции, сортировке и поиске.

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

Где задать отборВидит пользовательМожет снятьКогда применять
КомпоновщикНастроек.ФиксированныеНастройки.Отборнетнетжёсткое ограничение по смыслу формы
КомпоновщикНастроек.Настройки.Отборда, в настройке спискадаотбор «по умолчанию», который можно поменять
Параметр формы Отборнетнетотбор задаёт вызывающий код при открытии
Параметр запроса &Имянетнетусловие внутри произвольного запроса

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

Отбор работает только по полям, которые есть в списке. Для списка на основной таблице это её реквизиты, для произвольного запроса — поля из секции ВЫБРАТЬ. Поле можно не выводить в колонки таблицы, но в запросе оно быть должно.

Отбор при открытии формы: ПриСозданииНаСервере и параметр «Отбор»

Способ 1. Код в модуле формы списка

Самый частый вариант. Пишем в форме, где лежит реквизит Список типа ДинамическийСписок:

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)

    Если Не ЗначениеЗаполнено(Параметры.Контрагент) Тогда
        Возврат;
    КонецЕсли;

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

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

Чтобы Параметры.Контрагент существовал, добавьте параметр формы с таким именем на вкладке «Параметры» редактора формы. Без этого обращение к нему вызовет ошибку при открытии.

Способ 2. Стандартный параметр «Отбор» при открытии

Форма списка понимает параметр Отбор без всякого кода внутри неё. Передаёте структуру «поле — значение», и платформа сама ставит фиксированный отбор на основной список формы:

ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("Отбор", Новый Структура("Владелец", Контрагент));
ОткрытьФорму("Справочник.ДоговорыКонтрагентов.ФормаВыбора", ПараметрыФормы, Элемент);

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

Через структуру удобно задавать простые условия на равенство. Для «больше», «в иерархии», групп «ИЛИ» и прочих сложных условий нужен способ 1 или функция БСП.

Способ 3. Функция БСП

В Бухгалтерии 3.0, ЗУП 3.1, УТ 11 и других конфигурациях на БСП есть готовая функция. Она сама ищет существующий элемент по полю и обновляет его, а не плодит дубли:

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

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

Сложные условия: группы «ИЛИ», «НЕ» и несколько полей

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

Пример: показать договоры, у которых срок действия не задан или ещё не истёк.

&НаСервере
Процедура УстановитьОтборПоСроку()

    Отбор = Список.КомпоновщикНастроек.ФиксированныеНастройки.Отбор;

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

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

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

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

Типы групп три: ГруппаИ, ГруппаИли и ГруппаНе. Группы вкладываются друг в друга, так что любое логическое выражение собирается как дерево.

🔹 Поле реквизита ссылки задаётся через точку: Новый ПолеКомпоновкиДанных("Владелец.ИНН"). Платформа сама добавит соединение. 🔹 Отбор по полю, которого нет в настройках списка, платформа не применит. Сначала проверьте, что поле доступно, например через Список.КомпоновщикНастроек.Настройки.ДоступныеПоляОтбора.НайтиПоле(...). 🔹 Если условие сложно выразить отбором, перейдите на произвольный запрос и поставьте условие с параметром в ГДЕ.

Условие через параметр произвольного запроса

Включите у реквизита Список флажок «Произвольный запрос», напишите в тексте ГДЕ Договоры.Организация = &Организация и задайте значение:

Список.Параметры.УстановитьЗначениеПараметра("Организация", Параметры.Организация);

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

Виды сравнения: какой выбрать и что класть в ПравоеЗначение

Ошибка в виде сравнения — самая частая причина «пустого» списка. Свод основных вариантов ВидСравненияКомпоновкиДанных:

Вид сравненияЧто делаетПравоеЗначение
Равно / НеРавноточное совпадениеодно значение
Больше, БольшеИлиРавно, Меньше, МеньшеИлиРавночисла и датыодно значение
ВСписке / НеВСпискеодно из несколькихСписокЗначений
ВИерархии / НеВИерархиигруппа и всё, что в нейссылка на группу
ВСпискеПоИерархиинесколько групп с подчинённымиСписокЗначений групп
Содержит / НеСодержитподстрока в строковом полестрока
НачинаетсяС / НеНачинаетсяСначало строкистрока
Подобно / НеПодобношаблон с % и _строка-шаблон
Заполнено / НеЗаполненозначение задано или пустоене нужно

Пример с несколькими значениями:

Статусы = Новый СписокЗначений;
Статусы.Добавить(Перечисления.СтатусыДоговоров.Действует);
Статусы.Добавить(Перечисления.СтатусыДоговоров.НаСогласовании);

Элемент.ВидСравнения   = ВидСравненияКомпоновкиДанных.ВСписке;
Элемент.ПравоеЗначение = Статусы;

Для ВСписке передавайте именно СписокЗначений. Это стандартный тип правого значения в компоновке: с ним отбор корректно показывается и в пользовательской настройке списка.

Содержит на большой таблице работает медленно: индекс по строке тут не помогает. Для поиска по наименованию в списке с десятками тысяч строк лучше НачинаетсяС или штатная строка поиска.

Отборы в иерархических справочниках

В справочнике с группами (номенклатура, контрагенты, статьи затрат) отбор ведёт себя иначе, чем в плоском списке. Режим вывода задаётся у элемента формы: Элементы.Список.Отображение принимает ОтображениеТаблицы.Список, ИерархическийСписок или Дерево.

Что учесть:

  • 📁 Отбор по группе делайте через ВИерархии по полю Ссылка или Родитель. Условие Родитель Равно Группа покажет только непосредственных потомков, без вложенных подгрупп.
  • 🔍 Отбор по реквизиту элемента (например, ВидНоменклатуры) в иерархическом режиме оставляет на экране группы, которые по реквизиту не подходят, но содержат подходящие элементы. Пользователь видит «пустые» на вид папки. Если это мешает, переключите Отображение на Список на время отбора.
  • 🗂 Открыть список сразу внутри группы можно через свойство таблицы Элементы.Список.ТекущийРодитель. Это не отбор: пользователь может подняться выше. Если выше подниматься нельзя, ставьте фиксированный отбор ВИерархии.
  • 🧩 Отбор по полю ЭтоГруппа со значением Ложь убирает группы совсем. Удобно для форм подбора, где выбирать можно только элементы.

Как менять отбор на лету и обновлять список

Отбор не обязательно ставить один раз. Частый сценарий: в шапке формы есть поле «Организация», пользователь меняет его, и список тут же перестраивается.

Компоновщик настроек динамического списка доступен и на клиенте, поэтому серверный вызов не нужен:

&НаКлиенте
Процедура ОрганизацияОтборПриИзменении(Элемент)

    ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
        Список,
        "Организация",
        ОрганизацияОтбор,
        ВидСравненияКомпоновкиДанных.Равно,
        ,
        ЗначениеЗаполнено(ОрганизацияОтбор));

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

Шестой параметр (Использование) решает главную проблему такого поля: когда пользователь очищает «Организацию», отбор выключается, а не начинает искать записи с пустой организацией.

После изменения отбора список перечитывается сам. Явный вызов Элементы.Список.Обновить() нужен, когда изменились данные в базе, а условия остались прежними: например, после записи документа из другой формы фоновым заданием.

Для экранов-мониторов, где данные меняют другие пользователи, у таблицы формы есть автообновление (свойства Автообновление и ПериодАвтообновления). Не ставьте период в несколько секунд на тяжёлом списке: каждое обновление — это новый запрос к базе от каждого открытого окна.

Пользовательский отбор, который не затирается

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

Решения:

  1. Отбор, который пользователь менять не должен, — только в ФиксированныеНастройки.
  2. Отбор по умолчанию, который пользователь может поменять, ставьте в обработчике ПриЗагрузкеДанныхИзНастроекНаСервере либо задавайте у элемента идентификатор пользовательской настройки, чтобы он попал в быстрые настройки над списком.
  3. Если сохранённые настройки не нужны вовсе, снимите у таблицы флажок сохранения пользовательских настроек.
Диагностика: выберите симптом

Ошибки и ограничения, о которых узнают слишком поздно

  • ⛔ Отбор — не защита данных. Пользователь с правом чтения может открыть таблицу через другую форму, отчёт или «Все функции». Если доступ надо реально ограничить, нужны роли и RLS, а не отбор в форме.
  • 🐢 Медленный отбор по неиндексированному полю. На таблице в сотни тысяч строк условие по реквизиту без индекса даёт полное сканирование при каждой прокрутке. Включите у реквизита индексирование в конфигураторе, если по нему фильтруют постоянно. Это изменение структуры, оно требует обновления конфигурации базы данных.
  • 📏 Огромные списки значений. ВСписке с тысячами элементов превращается в тяжёлое условие в запросе. В таком случае лучше перенести логику в произвольный запрос с соединением.
  • 🧮 Отбор по виртуальной таблице. Для остатков и оборотов условие внутри параметров виртуальной таблицы через отбор не задаётся. Его ставят параметром в тексте произвольного запроса.
  • 🔀 Дата со временем. Равно по полю Дата документа почти никогда не срабатывает, ведь время у документа обычно не полночь. Для отбора за день ставьте два условия: БольшеИлиРавно НачалоДня и МеньшеИлиРавно КонецДня.
  • 🧱 Доработка типовой формы. Не правьте модуль формы типовой конфигурации напрямую: это мешает обновлениям. Отбор добавляется в расширении конфигурации через обработчик ПриСозданииНаСервере с директивой &После.

Частые вопросы

Как снять программный отбор с динамического списка?
Найдите элемент в коллекции Отбор.Элементы и удалите его методом Удалить, либо выставьте Использование = Ложь. В конфигурациях на БСП удобнее процедура УдалитьЭлементыГруппыОтбораДинамическогоСписка.
Чем отбор на пустое значение отличается от «НеЗаполнено»?
Равно с пустой ссылкой ловит только пустую ссылку нужного типа. НеЗаполнено срабатывает на любое пустое значение, включая Null после соединения и составные типы, поэтому надёжнее.
Можно ли установить отбор динамического списка на клиенте?
Да. Компоновщик настроек списка доступен в клиентском контексте, отбор меняют прямо в обработчике ПриИзменении поля формы, без серверного вызова.
Почему отбор, переданный через параметр «Отбор» в ОткрытьФорму, не работает?
Чаще всего ключ структуры не совпадает с именем реквизита списка либо форма открыта не та: у справочника могут быть разные формы списка и выбора. Ещё одна причина — у списка в форме не стоит признак основного реквизита.
Как вывести программный отбор в быстрые настройки над списком?
Ставьте элемент в Настройки.Отбор, задайте ему ИдентификаторПользовательскойНастройки и режим отображения БыстрыйДоступ. Тогда отбор появится полем над таблицей и пользователь сможет его менять.