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