Отбор в системе компоновки данных из кода: от одного условия до групп ИЛИ

· 9 мин

Содержание

Чтобы программно задать отбор в СКД, добавьте элемент в коллекцию Настройки.Отбор.Элементы с типом ЭлементОтбораКомпоновкиДанных и заполните у него четыре свойства: ЛевоеЗначение (поле), ВидСравнения, ПравоеЗначение и Использование. Для отчёта настройки берут из КомпоновщикНастроек, для динамического списка — из Список.КомпоновщикНастроек или свойства Список.Отбор.

Типичная ситуация: бухгалтер просит открыть оборотно-сальдовую ведомость сразу по одному контрагенту, а пользователь каждый раз выставляет отбор руками. Программная установка отборов в системе компоновки данных решает это одной процедурой. Механизм одинаков для всех конфигураций на платформе 8.3: он принадлежит платформе, а не БП, ЗУП или УТ.

Как устроен объект Отбор в настройках компоновки

Отбор — это не одно условие, а дерево. Корень — объект ОтборКомпоновкиДанных, у него есть коллекция Элементы. В ней лежат элементы двух типов:

  • 🔹 ЭлементОтбораКомпоновкиДанных — одно условие вида «поле — сравнение — значение»;
  • 🔸 ГруппаЭлементовОтбораКомпоновкиДанных — контейнер со своей коллекцией Элементы и типом группы (И, ИЛИ, НЕ).

Группы можно вкладывать друг в друга на любую глубину. Элементы корня связаны между собой по «И».

Отбор есть не только у настроек целиком. Своя коллекция Отбор есть у каждой группировки и таблицы в структуре отчёта. Например, Настройки.Структура[0].Отбор фильтрует только первую группировку, а не весь набор данных.

Основные свойства элемента отбора:

СвойствоЧто хранитПример значения
ЛевоеЗначениеПоле, по которому фильтруемНовый ПолеКомпоновкиДанных("Контрагент")
ВидСравненияУсловие сравненияВидСравненияКомпоновкиДанных.Равно
ПравоеЗначениеЗначение для сравненияссылка, число, дата, СписокЗначений
ИспользованиеВключён ли элементИстина
ПредставлениеТекст, который видит пользователь«Только поставщики»
РежимОтображенияВиден ли элемент в формеРежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный
ИдентификаторПользовательскойНастройкиСвязь с пользовательскими настройкамистрока, обычно Новый УникальныйИдентификатор

Поле пишут строкой через точку, как в запросе: "Контрагент.ИНН", "Номенклатура.Родитель". Имя должно совпадать с полем набора данных или его псевдонимом в схеме, а не с именем реквизита в метаданных.

Базовый код: как программно задать отбор по одному полю

Минимальный пример — отбор отчёта по контрагенту. Код подходит для модуля формы отчёта, сервер или клиент.

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

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

У этого кода есть проблема: при каждом вызове он добавляет новый элемент. Через пять открытий отчёта в настройках окажется пять одинаковых отборов. Поэтому в рабочем коде сначала ищут существующий элемент и только потом создают новый.

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

Проверка типа в цикле обязательна. У группы нет свойства ЛевоеЗначение, и обращение к нему на группе вызовет ошибку выполнения.

Какой вид сравнения выбрать

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

Для ВСписке передавайте СписокЗначений, даже если под рукой массив. Перегнать массив можно одной строкой: Список = Новый СписокЗначений; Список.ЗагрузитьЗначения(Массив);.

Где писать код: отчёт, динамический список, обработка

Место установки отбора определяет, какой объект настроек вы трогаете. Это главная развилка темы.

ГдеОткуда брать отборКогда ставить
Форма отчётаОтчет.КомпоновщикНастроек.Настройки.Отборв ПриСозданииНаСервере или перед формированием
Модуль объекта отчётаКомпоновщикНастроек.ПолучитьНастройки()в ПриКомпоновкеРезультата
Динамический список формыСписок.Отбор или Список.КомпоновщикНастроек.ФиксированныеНастройки.Отборв ПриСозданииНаСервере
Обработка, регламентное заданиенастройки, загруженные из схемыперед вызовом компоновщика макета

Отчёт: отбор в обработчике ПриКомпоновкеРезультата

Если отбор должен действовать всегда, независимо от того, что выбрал пользователь, перехватите формирование в модуле объекта отчёта:

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

ПолучитьНастройки() возвращает копию, в которой уже объединены вариант и пользовательские настройки. Правка копии не попадает в сохранённый вариант отчёта, а это обычно и нужно.

Динамический список: отбор, который пользователь не снимет

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

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

Элементу обычного отбора, который нужно спрятать от пользователя, задайте РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный.

Обработка или фоновое задание без формы

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

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

КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
Макет = КомпоновщикМакета.Выполнить(Схема, Настройки, , ,
	Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));

Процессор = Новый ПроцессорКомпоновкиДанных;
Процессор.Инициализировать(Макет);

Результат = Новый ТаблицаЗначений;
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(Результат);
ПроцессорВывода.Вывести(Процессор);

Имя отчёта и макета здесь условные: подставьте свои. В типовых конфигурациях основная схема обычно называется ОсновнаяСхемаКомпоновкиДанных.

Группы отбора и логические операторы И, ИЛИ, НЕ

Корневые элементы всегда соединены через «И». Чтобы получить условие «Контрагент = А ИЛИ Сумма > 100 000», нужна группа с типом ИЛИ:

Отбор = Настройки.Отбор;

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

УстановитьОтборСКД(ГруппаИли.Элементы, "Контрагент", Контрагент);
УстановитьОтборСКД(ГруппаИли.Элементы, "Сумма", 100000,
	ВидСравненияКомпоновкиДанных.Больше);

Функция поиска из прошлого раздела работает и здесь: ей всё равно, корневая это коллекция или коллекция группы.

Три типа групп:

  • ✅ ГруппаИ — все условия внутри должны выполниться. Нужна, когда «И» вкладывается внутрь «ИЛИ».
  • 🔀 ГруппаИли — достаточно одного условия.
  • 🚫 ГруппаНе — инвертирует содержимое. Внутри «НЕ» несколько элементов связаны по «И», то есть группа даёт «НЕ (А И Б)».

Пример сложного условия: «(Склад = Основной И Количество > 0) ИЛИ Номенклатура в группе Акции». Строится так: корень → группа ИЛИ → внутри неё группа И с двумя элементами и отдельный элемент с видом ВИерархии.

Пустая группа с Использование = Истина не ломает отчёт, но захламляет настройки. Если условие не нужно, удаляйте группу целиком: Отбор.Элементы.Удалить(ГруппаИли).

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

Отбор только для одной группировки отчёта

Иногда фильтровать нужно не весь набор, а одну ветку структуры. Например, в отчёте две таблицы: в первой все продажи, во второй только крупные. Тогда элемент добавляют в отбор конкретной группировки:

Группировка = Настройки.Структура[1];
УстановитьОтборСКД(Группировка.Отбор.Элементы, "Сумма", 500000,
	ВидСравненияКомпоновкиДанных.БольшеИлиРавно);

Индекс в Структура зависит от порядка элементов в варианте отчёта. Если пользователь может менять структуру, надёжнее перебрать Настройки.Структура и найти нужную группировку по её полям группировки, а не по номеру.

Отбор по периоду и диапазону дат в СКД

С датами чаще всего ошибаются. Есть два принципиально разных способа ограничить период, и они дают разный результат.

СпособКак задаётсяКогда подходит
Параметр данныхНастройки.ПараметрыДанных.УстановитьЗначениеПараметра("НачалоПериода", Дата)виртуальные таблицы остатков и оборотов, параметры запроса
Отбор по полюдва элемента БольшеИлиРавно и МеньшеИлиРавно по полю датыреальные таблицы: документы, записи регистров

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

Параметры = Настройки.ПараметрыДанных;
Параметры.УстановитьЗначениеПараметра("НачалоПериода", НачалоМесяца(ДатаОтчета));
Параметры.УстановитьЗначениеПараметра("КонецПериода", КонецМесяца(ДатаОтчета));

Если параметр в схеме имеет тип СтандартныйПериод, передайте объект этого типа:

Период = Новый СтандартныйПериод;
Период.Вариант = ВариантСтандартногоПериода.ПрошлыйМесяц;
Настройки.ПараметрыДанных.УстановитьЗначениеПараметра("Период", Период);

Для диапазона по полю даты нужны два элемента, по одному на границу:

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

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

Верхнюю границу всегда приводите к КонецДня(). Дата 31.03 без времени означает 31.03 00:00:00, и все документы последнего дня выпадут из отчёта.

Отбор через функции БСП

В конфигурациях на Библиотеке стандартных подсистем (БП 3.0, ЗУП 3.1, УТ 11 и другие) поиск и замену элементов уже написали за вас. Основные функции общего модуля ОбщегоНазначенияКлиентСервер:

  • 🔧 УстановитьЭлементОтбораДинамическогоСписка(Список, ИмяПоля, ПравоеЗначение, ВидСравнения, ...) — для динамических списков;
  • 🔧 УстановитьЭлементОтбора(ОбластьПоиска, ИмяПоля, ПравоеЗначение, ВидСравнения, ...) — найти и изменить элемент или создать его;
  • 🔧 ДобавитьЭлементКомпоновки(ОбластьДобавления, ИмяПоля, ВидСравнения, ПравоеЗначение, ...) — всегда добавить новый;
  • 🔧 СоздатьГруппуЭлементовОтбора(КоллекцияЭлементов, Представление, ТипГруппы) — найти группу по представлению или создать;
  • 🔧 УдалитьЭлементыГруппыОтбора(ОбластьУдаления, ИмяПоля, Представление) — убрать элементы по полю.

Пример для формы списка:

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

Порядок параметров у функций разный: у ДобавитьЭлементКомпоновки вид сравнения стоит перед значением, у УстановитьЭлементОтбора — после. Перед использованием откройте модуль в конфигураторе и сверьтесь с описанием: сигнатура зависит от версии БСП.

Почему отбор в СКД не срабатывает: отладка

Код выполнился без ошибок, а в отчёте те же строки. Причин немного, и все проверяются за пару минут.

Проверьте, видит ли компоновка поле. Поле, которого нет в доступных полях отбора, не отфильтрует ничего:

Поле = Отбор.ДоступныеПоляОтбора.НайтиПоле(Новый ПолеКомпоновкиДанных("Контрагент"));
Если Поле = Неопределено Тогда
	Сообщить("Поле Контрагент недоступно для отбора");
КонецЕсли;

Посмотрите итоговый запрос. Поставьте точку останова после КомпоновщикМакета.Выполнить и вычислите в отладчике МакетКомпоновки.НаборыДанных[0].Запрос. Если ваш отбор попал в запрос, вы увидите его в секции ГДЕ или в параметрах виртуальной таблицы. Нет условия — значит, отбор не дошёл до макета.

Проверьте, те ли настройки вы правите. Отчет.КомпоновщикНастроек.Настройки в форме и настройки внутри ПриКомпоновкеРезультата — разные стадии. Пользовательские настройки накладываются поверх варианта и могут перекрыть ваш элемент, если у него задан ИдентификаторПользовательскойНастройки.

Диагностика: выберите симптом

Частые вопросы по программной установке отборов

Можно ли установить отбор СКД на клиенте?
Да. Объекты настроек компоновки доступны на клиенте, поэтому отбор в форме можно менять в клиентской процедуре, например по кнопке. Получение макета схемы и формирование отчёта при этом остаются на сервере.
Как удалить все отборы перед установкой своих?
Вызовите Настройки.Отбор.Элементы.Очистить(). Учтите, что так пропадут и отборы, которые выставил пользователь.
Как сделать отбор по реквизиту реквизита?
Укажите поле через точку: Новый ПолеКомпоновкиДанных("Контрагент.ГоловнойКонтрагент"). Поле должно быть доступно в схеме: проверьте его через ДоступныеПоляОтбора.НайтиПоле.
Чем отбор в настройках отличается от условия в запросе схемы?
Условие в тексте запроса действует всегда, и пользователь его не видит. Отбор настроек гибче: его можно включить, выключить, показать в форме и сохранить в варианте отчёта.
Как временно отключить отбор, не удаляя его?
Поставьте элементу Использование = Ложь. Элемент останется в настройках, но в макет не попадёт.