Отбор в системе компоновки данных из кода: от одного условия до групп ИЛИ
Содержание
Чтобы программно задать отбор в СКД, добавьте элемент в коллекцию Настройки.Отбор.Элементы с типом ЭлементОтбораКомпоновкиДанных и заполните у него четыре свойства: ЛевоеЗначение (поле), ВидСравнения, ПравоеЗначение и Использование. Для отчёта настройки берут из КомпоновщикНастроек, для динамического списка — из Список.КомпоновщикНастроек или свойства Список.Отбор.
Типичная ситуация: бухгалтер просит открыть оборотно-сальдовую ведомость сразу по одному контрагенту, а пользователь каждый раз выставляет отбор руками. Программная установка отборов в системе компоновки данных решает это одной процедурой. Механизм одинаков для всех конфигураций на платформе 8.3: он принадлежит платформе, а не БП, ЗУП или УТ.
Как устроен объект Отбор в настройках компоновки
Отбор — это не одно условие, а дерево. Корень — объект ОтборКомпоновкиДанных, у него есть коллекция Элементы. В ней лежат элементы двух типов:
- 🔹
ЭлементОтбораКомпоновкиДанных— одно условие вида «поле — сравнение — значение»; - 🔸
ГруппаЭлементовОтбораКомпоновкиДанных— контейнер со своей коллекциейЭлементыи типом группы (И, ИЛИ, НЕ).
Группы можно вкладывать друг в друга на любую глубину. Элементы корня связаны между собой по «И».
Отбор есть не только у настроек целиком. Своя коллекция Отбор есть у каждой группировки и таблицы в структуре отчёта. Например, Настройки.Структура[0].Отбор фильтрует только первую группировку, а не весь набор данных.
Основные свойства элемента отбора:
| Свойство | Что хранит | Пример значения |
|---|---|---|
ЛевоеЗначение | Поле, по которому фильтруем | Новый ПолеКомпоновкиДанных("Контрагент") |
ВидСравнения | Условие сравнения | ВидСравненияКомпоновкиДанных.Равно |
ПравоеЗначение | Значение для сравнения | ссылка, число, дата, СписокЗначений |
Использование | Включён ли элемент | Истина |
Представление | Текст, который видит пользователь | «Только поставщики» |
РежимОтображения | Виден ли элемент в форме | РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный |
ИдентификаторПользовательскойНастройки | Связь с пользовательскими настройками | строка, обычно Новый УникальныйИдентификатор |
Поле пишут строкой через точку, как в запросе: "Контрагент.ИНН", "Номенклатура.Родитель". Имя должно совпадать с полем набора данных или его псевдонимом в схеме, а не с именем реквизита в метаданных.
Базовый код: как программно задать отбор по одному полю
Минимальный пример — отбор отчёта по контрагенту. Код подходит для модуля формы отчёта, сервер или клиент.
Отбор = Отчет.КомпоновщикНастроек.Настройки.Отбор;
ЭлементОтбора = Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Контрагент");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.ПравоеЗначение = Контрагент;
ЭлементОтбора.Использование = Истина;
У этого кода есть проблема: при каждом вызове он добавляет новый элемент. Через пять открытий отчёта в настройках окажется пять одинаковых отборов. Поэтому в рабочем коде сначала ищут существующий элемент и только потом создают новый.
Функция УстановитьОтборСКД(КоллекцияЭлементов, ИмяПоля, Значение,
Вид = Неопределено, Использование = Истина) Экспорт
Если Вид = Неопределено Тогда
Вид = ВидСравненияКомпоновкиДанных.Равно;
КонецЕсли;
Найденный = Неопределено;
Для Каждого Элемент Из КоллекцияЭлементов Цикл
Если ТипЗнч(Элемент) = Тип("ЭлементОтбораКомпоновкиДанных")
И Строка(Элемент.ЛевоеЗначение) = ИмяПоля Тогда
Найденный = Элемент;
Прервать;
КонецЕсли;
КонецЦикла;
Если Найденный = Неопределено Тогда
Найденный = КоллекцияЭлементов.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Найденный.ЛевоеЗначение = Новый ПолеКомпоновкиДанных(ИмяПоля);
КонецЕсли;
Найденный.ВидСравнения = Вид;
Найденный.ПравоеЗначение = Значение;
Найденный.Использование = Использование;
Возврат Найденный;
КонецФункции
Проверка типа в цикле обязательна. У группы нет свойства ЛевоеЗначение, и обращение к нему на группе вызовет ошибку выполнения.
Какой вид сравнения выбрать
| Задача | Вид сравнения | Что передать в ПравоеЗначение |
|---|---|---|
| Одно значение | Равно, НеРавно | ссылка, число, строка |
| Несколько значений | ВСписке, НеВСписке | СписокЗначений |
| Группа справочника с подчинёнными | ВИерархии | ссылка на группу |
| Список групп с подчинёнными | ВСпискеПоИерархии | СписокЗначений |
| Подстрока | Содержит, НеСодержит | строка |
| Пустое или заполненное поле | Заполнено, НеЗаполнено | не нужно |
| Граница диапазона | Больше, БольшеИлиРавно, Меньше, МеньшеИлиРавно | число или дата |
Для ВСписке передавайте СписокЗначений, даже если под рукой массив. Перегнать массив можно одной строкой: Список = Новый СписокЗначений; Список.ЗагрузитьЗначения(Массив);.
Где писать код: отчёт, динамический список, обработка
Место установки отбора определяет, какой объект настроек вы трогаете. Это главная развилка темы.
| Где | Откуда брать отбор | Когда ставить |
|---|---|---|
| Форма отчёта | Отчет.КомпоновщикНастроек.Настройки.Отбор | в ПриСозданииНаСервере или перед формированием |
| Модуль объекта отчёта | КомпоновщикНастроек.ПолучитьНастройки() | в ПриКомпоновкеРезультата |
| Динамический список формы | Список.Отбор или Список.КомпоновщикНастроек.ФиксированныеНастройки.Отбор | в ПриСозданииНаСервере |
| Обработка, регламентное задание | настройки, загруженные из схемы | перед вызовом компоновщика макета |
Отчёт: отбор в обработчике ПриКомпоновкеРезультата
Если отбор должен действовать всегда, независимо от того, что выбрал пользователь, перехватите формирование в модуле объекта отчёта:
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
УстановитьОтборСКД(Настройки.Отбор.Элементы, "Организация", Организация);
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки, Истина);
ДокументРезультат.Очистить();
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедуры
ПолучитьНастройки() возвращает копию, в которой уже объединены вариант и пользовательские настройки. Правка копии не попадает в сохранённый вариант отчёта, а это обычно и нужно.
Динамический список: отбор, который пользователь не снимет
В форме списка отбор чаще всего ставят при открытии. Обычный отбор через Список.Отбор пользователь видит и может отключить. Если отбор должен быть жёстким, используйте фиксированные настройки:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Элементы = Список.КомпоновщикНастроек.ФиксированныеНастройки.Отбор.Элементы;
УстановитьОтборСКД(Элементы, "Владелец", Параметры.Владелец);
КонецПроцедуры
Элементу обычного отбора, который нужно спрятать от пользователя, задайте РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный.
Обработка или фоновое задание без формы
Когда отчёт строится в коде, например для рассылки, настройки загружают из схемы, ставят отбор и выводят результат в таблицу значений:
Схема = Отчеты.ВедомостьПоТоварам.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
Настройки = Схема.НастройкиПоУмолчанию;
УстановитьОтборСКД(Настройки.Отбор.Элементы, "Склад", Склад);
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
Макет = КомпоновщикМакета.Выполнить(Схема, Настройки, , ,
Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
Процессор = Новый ПроцессорКомпоновкиДанных;
Процессор.Инициализировать(Макет);
Результат = Новый ТаблицаЗначений;
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(Результат);
ПроцессорВывода.Вывести(Процессор);
Имя отчёта и макета здесь условные: подставьте свои. В типовых конфигурациях основная схема обычно называется ОсновнаяСхемаКомпоновкиДанных.
Группы отбора и логические операторы И, ИЛИ, НЕ
Корневые элементы всегда соединены через «И». Чтобы получить условие «Контрагент = А ИЛИ Сумма > 100 000», нужна группа с типом ИЛИ:
Отбор = Настройки.Отбор;
ГруппаИли = Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
ГруппаИли.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИли;
ГруппаИли.Использование = Истина;
УстановитьОтборСКД(ГруппаИли.Элементы, "Контрагент", Контрагент);
УстановитьОтборСКД(ГруппаИли.Элементы, "Сумма", 100000,
ВидСравненияКомпоновкиДанных.Больше);
Функция поиска из прошлого раздела работает и здесь: ей всё равно, корневая это коллекция или коллекция группы.
Три типа групп:
- ✅
ГруппаИ— все условия внутри должны выполниться. Нужна, когда «И» вкладывается внутрь «ИЛИ». - 🔀
ГруппаИли— достаточно одного условия. - 🚫
ГруппаНе— инвертирует содержимое. Внутри «НЕ» несколько элементов связаны по «И», то есть группа даёт «НЕ (А И Б)».
Пример сложного условия: «(Склад = Основной И Количество > 0) ИЛИ Номенклатура в группе Акции». Строится так: корень → группа ИЛИ → внутри неё группа И с двумя элементами и отдельный элемент с видом ВИерархии.
Пустая группа с Использование = Истина не ломает отчёт, но захламляет настройки. Если условие не нужно, удаляйте группу целиком: Отбор.Элементы.Удалить(ГруппаИли).
Чтобы найти свою группу при повторном вызове, задайте ей уникальное Представление и ищите по нему. Иначе при каждом открытии формы будет плодиться новая группа.
Отбор только для одной группировки отчёта
Иногда фильтровать нужно не весь набор, а одну ветку структуры. Например, в отчёте две таблицы: в первой все продажи, во второй только крупные. Тогда элемент добавляют в отбор конкретной группировки:
Группировка = Настройки.Структура[1];
УстановитьОтборСКД(Группировка.Отбор.Элементы, "Сумма", 500000,
ВидСравненияКомпоновкиДанных.БольшеИлиРавно);
Индекс в Структура зависит от порядка элементов в варианте отчёта. Если пользователь может менять структуру, надёжнее перебрать Настройки.Структура и найти нужную группировку по её полям группировки, а не по номеру.
Отбор по периоду и диапазону дат в СКД
С датами чаще всего ошибаются. Есть два принципиально разных способа ограничить период, и они дают разный результат.
| Способ | Как задаётся | Когда подходит |
|---|---|---|
| Параметр данных | Настройки.ПараметрыДанных.УстановитьЗначениеПараметра("НачалоПериода", Дата) | виртуальные таблицы остатков и оборотов, параметры запроса |
| Отбор по полю | два элемента БольшеИлиРавно и МеньшеИлиРавно по полю даты | реальные таблицы: документы, записи регистров |
Отбор по полю Период у виртуальной таблицы остатков не превращается в дату остатков. Он отфильтрует уже полученные строки. Если запрос в схеме использует &НачалоПериода и &КонецПериода, устанавливайте именно параметры:
Параметры = Настройки.ПараметрыДанных;
Параметры.УстановитьЗначениеПараметра("НачалоПериода", НачалоМесяца(ДатаОтчета));
Параметры.УстановитьЗначениеПараметра("КонецПериода", КонецМесяца(ДатаОтчета));
Если параметр в схеме имеет тип СтандартныйПериод, передайте объект этого типа:
Период = Новый СтандартныйПериод;
Период.Вариант = ВариантСтандартногоПериода.ПрошлыйМесяц;
Настройки.ПараметрыДанных.УстановитьЗначениеПараметра("Период", Период);
Для диапазона по полю даты нужны два элемента, по одному на границу:
УстановитьОтборСКД(Отбор.Элементы, "Дата", НачалоДня(ДатаС),
ВидСравненияКомпоновкиДанных.БольшеИлиРавно);
Второй элемент через ту же функцию не добавить: она найдёт первый по имени поля и перезапишет его. Для верхней границы создайте элемент напрямую через Добавить или положите обе границы в отдельную группу И.
Верхнюю границу всегда приводите к КонецДня(). Дата 31.03 без времени означает 31.03 00:00:00, и все документы последнего дня выпадут из отчёта.
Отбор через функции БСП
В конфигурациях на Библиотеке стандартных подсистем (БП 3.0, ЗУП 3.1, УТ 11 и другие) поиск и замену элементов уже написали за вас. Основные функции общего модуля ОбщегоНазначенияКлиентСервер:
- 🔧
УстановитьЭлементОтбораДинамическогоСписка(Список, ИмяПоля, ПравоеЗначение, ВидСравнения, ...)— для динамических списков; - 🔧
УстановитьЭлементОтбора(ОбластьПоиска, ИмяПоля, ПравоеЗначение, ВидСравнения, ...)— найти и изменить элемент или создать его; - 🔧
ДобавитьЭлементКомпоновки(ОбластьДобавления, ИмяПоля, ВидСравнения, ПравоеЗначение, ...)— всегда добавить новый; - 🔧
СоздатьГруппуЭлементовОтбора(КоллекцияЭлементов, Представление, ТипГруппы)— найти группу по представлению или создать; - 🔧
УдалитьЭлементыГруппыОтбора(ОбластьУдаления, ИмяПоля, Представление)— убрать элементы по полю.
Пример для формы списка:
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
Список, "Организация", Организация, ВидСравненияКомпоновкиДанных.Равно, , Истина);
Порядок параметров у функций разный: у ДобавитьЭлементКомпоновки вид сравнения стоит перед значением, у УстановитьЭлементОтбора — после. Перед использованием откройте модуль в конфигураторе и сверьтесь с описанием: сигнатура зависит от версии БСП.
Почему отбор в СКД не срабатывает: отладка
Код выполнился без ошибок, а в отчёте те же строки. Причин немного, и все проверяются за пару минут.
Проверьте, видит ли компоновка поле. Поле, которого нет в доступных полях отбора, не отфильтрует ничего:
Поле = Отбор.ДоступныеПоляОтбора.НайтиПоле(Новый ПолеКомпоновкиДанных("Контрагент"));
Если Поле = Неопределено Тогда
Сообщить("Поле Контрагент недоступно для отбора");
КонецЕсли;
Посмотрите итоговый запрос. Поставьте точку останова после КомпоновщикМакета.Выполнить и вычислите в отладчике МакетКомпоновки.НаборыДанных[0].Запрос. Если ваш отбор попал в запрос, вы увидите его в секции ГДЕ или в параметрах виртуальной таблицы. Нет условия — значит, отбор не дошёл до макета.
Проверьте, те ли настройки вы правите. Отчет.КомпоновщикНастроек.Настройки в форме и настройки внутри ПриКомпоновкеРезультата — разные стадии. Пользовательские настройки накладываются поверх варианта и могут перекрыть ваш элемент, если у него задан ИдентификаторПользовательскойНастройки.
Частые вопросы по программной установке отборов
Можно ли установить отбор СКД на клиенте?
Как удалить все отборы перед установкой своих?
Настройки.Отбор.Элементы.Очистить(). Учтите, что так пропадут и отборы, которые выставил пользователь.Как сделать отбор по реквизиту реквизита?
Новый ПолеКомпоновкиДанных("Контрагент.ГоловнойКонтрагент"). Поле должно быть доступно в схеме: проверьте его через ДоступныеПоляОтбора.НайтиПоле.Чем отбор в настройках отличается от условия в запросе схемы?
Как временно отключить отбор, не удаляя его?
Использование = Ложь. Элемент останется в настройках, но в макет не попадёт.