Как задать отбор отчёта 1С из кода: Отбор, параметры и пользовательские настройки

· 9 мин

Содержание

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

Типичная ситуация: бухгалтер просит кнопку «Продажи по этому контрагенту» прямо в карточке, а руководитель — чтобы менеджер видел в отчёте только свои сделки и не мог снять отбор. Обе задачи решаются кодом, но в разных местах. Всё ниже относится к платформе 8.3 и управляемым формам и не зависит от конфигурации.

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

Отбор живёт внутри настроек компоновки. У отчёта есть КомпоновщикНастроек, а у него три слоя:

  • 📐 Настройки — вариант отчёта, который задал разработчик;
  • 👤 ПользовательскиеНастройки — то, что пользователь поменял в форме (быстрые отборы, период);
  • 🔒 ФиксированныеНастройки — то, что пользователь изменить не может.

Итоговые настройки компоновщик собирает методом ПолучитьНастройки(). Если пользователь заполнил быстрый отбор, его значение перекроет то, что вы поставили в Настройки.Отбор для того же элемента.

Коллекция Отбор.Элементы хранит элементы двух типов:

ТипЧто этоКлючевые свойства
ЭлементОтбораКомпоновкиДанныхОдно условиеЛевоеЗначение, ВидСравнения, ПравоеЗначение, Использование
ГруппаЭлементовОтбораКомпоновкиДанныхГруппа условийТипГруппы (И / ИЛИ / НЕ), Элементы

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

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

Добавление условия в отбор отчёта: пошаговый код

Самый надёжный вариант — модуль объекта отчёта и обработчик ПриКомпоновкеРезультата. Он выполняется на сервере при каждом нажатии «Сформировать».

Шаг 1. Простой элемент отбора

Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)

    СтандартнаяОбработка = Ложь;

    Настройки = КомпоновщикНастроек.ПолучитьНастройки();

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

    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    Макет = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);

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

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

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

Мы работаем с копией настроек из ПолучитьНастройки(). Поэтому отбор не накапливается при повторных нажатиях и не сохраняется в вариант отчёта.

Использование = Истина обязательно. Новый элемент создаётся выключенным, и без этой строки отбор молча не сработает.

Шаг 2. Не плодить дубли

Если вы меняете не копию, а КомпоновщикНастроек.Настройки (например, из формы), сначала ищите существующий элемент по полю:

Функция ЭлементОтбораПоПолю(Отбор, ИмяПоля) Экспорт

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

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

КонецФункции

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

Иногда нужно обратное: снять все отборы варианта и поставить свои. Удаляйте элементы обратным циклом, чтобы индексы не съезжали:

Количество = Настройки.Отбор.Элементы.Количество();
Для Сч = 1 По Количество Цикл
    Настройки.Отбор.Элементы.Удалить(Настройки.Отбор.Элементы[Количество - Сч]);
КонецЦикла;

Для копии из ПолучитьНастройки() это безопасно: вариант отчёта и сохранённые настройки пользователя не пострадают. В КомпоновщикНастроек.Настройки так делать не стоит — пропадут быстрые отборы, которые пользователь видит в шапке формы.

Шаг 3. Список значений и группа «ИЛИ»

Отбор по нескольким складам — вид сравнения ВСписке, правое значение — СписокЗначений:

Склады = Новый СписокЗначений;
Склады.Добавить(СкладОсновной);
Склады.Добавить(СкладРозница);

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

Условие «покупатель из Москвы ИЛИ сумма больше 100 000» собирают через группу:

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

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

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

Для иерархических справочников есть ВИерархии и ВСпискеПоИерархии: отбор по группе номенклатуры захватит все вложенные позиции. Для пустых значений — Заполнено и НеЗаполнено, правое значение не нужно.

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

Период и даты: параметр или отбор

С датами частая ошибка — ставить отбор по полю Период вместо параметров. Отбор по дате работает, но для регистров с виртуальными таблицами это медленно и иногда неверно: остатки на дату через отбор не получить.

Правильно — параметры данных:

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

Метод УстановитьЗначениеПараметра сам включает использование параметра. Если параметра с таким именем в схеме нет, ничего не произойдёт и ошибки не будет. Проверить можно так:

Параметр = Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("НачалоПериода"));
Если Параметр = Неопределено Тогда
    ВызватьИсключение "В схеме нет параметра НачалоПериода";
КонецЕсли;

Три приёма, которые экономят время:

  1. 📅 Один параметр-период. Заведите в схеме параметр Период типа СтандартныйПериод, а у НачалоПериода и КонецПериода укажите выражения &Период.ДатаНачала и &Период.ДатаОкончания. Пользователь выбирает период одним полем, а из кода вы передаёте Новый СтандартныйПериод(ВариантСтандартногоПериода.ПрошлыйМесяц).
  2. 🕛 Конец дня. Дата без времени означает 00:00:00. Передавайте КонецДня(Дата), иначе документы последнего дня выпадут.
  3. 📌 Остатки «включая» момент. Для параметра виртуальной таблицы остатков используйте Новый Граница(КонецДня(Дата), ВидГраницы.Включая).

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

Отбор из формы и при открытии отчёта

Открыть отчёт с готовым отбором

Для кнопки «Продажи по контрагенту» в карточке модуль отчёта не нужен. У формы отчёта есть стандартные параметры Отбор и СформироватьПриОткрытии:

&НаКлиенте
Процедура ПродажиПоКонтрагенту(Команда)

    ПараметрыФормы = Новый Структура;
    ПараметрыФормы.Вставить("Отбор", Новый Структура("Контрагент", Объект.Ссылка));
    ПараметрыФормы.Вставить("СформироватьПриОткрытии", Истина);

    ОткрытьФорму("Отчет.ПродажиПоКонтрагентам.Форма", ПараметрыФормы);

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

Ключи структуры Отбор — имена полей схемы. Платформа сама создаст элементы отбора с видом сравнения «Равно». Если значение — СписокЗначений, получится «В списке».

Поменять отбор в открытой форме

В модуле формы отчёта настройки доступны через реквизит Отчет:

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

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

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

РежимОтображения = Недоступный прячет элемент от пользователя. Строка Пользователи.ТекущийПользователь() — функция БСП; в самописной конфигурации подставьте свой способ получить пользователя.

Быстрые отборы: пользовательские настройки

Если элемент отбора вынесен в шапку формы, у него есть ИдентификаторПользовательскойНастройки. Значение, которое видит пользователь, лежит не в Настройки.Отбор, а в ПользовательскиеНастройки. Менять нужно там:

&НаСервере
Процедура УстановитьБыстрыйОтбор(ИмяПоля, Значение)

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

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

Отбор, который нельзя снять

Для ограничения «менеджер видит только своё» добавляйте элемент в ФиксированныеНастройки.Отбор. Пользователь не сможет его изменить ни через форму, ни через настройки варианта.

Фиксированный отбор — это удобство интерфейса, а не защита данных. Пользователь с правом на конфигуратор или на свой отчёт обойдёт его. Настоящее ограничение доступа делают через RLS в ролях.

Почему программный отбор не срабатывает

Отбор добавлен, а отчёт показывает всё. Проверяйте по порядку.

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

Удобный приём отладки — посмотреть, какой запрос ушёл в базу. Поставьте точку останова после КомпоновщикМакета.Выполнить, раскройте в отладчике Макет → НаборыДанных → <набор> → Запрос. Если ваше условие появилось в тексте запроса, отбор дошёл до СУБД. Если нет — СКД фильтрует уже готовые строки.

Ещё один способ — сохранить итоговые настройки в XML и сравнить с ожидаемыми:

Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку();
СериализаторXDTO.ЗаписатьXML(Запись, Настройки);
ТекстНастроек = Запись.Закрыть();

Отбор и скорость отчёта

СКД старается переносить отбор в запрос. Получается это не всегда, и разница в скорости бывает в десятки раз на больших регистрах.

  • ✅ Поле из запроса набора — условие встаёт в ГДЕ запроса, фильтрует СУБД.
  • ✅ Поле виртуальной таблицы при автозаполнении — СКД передаёт условие в параметры виртуальной таблицы, если поле есть среди её измерений.
  • ⚠️ Вычисляемое поле и поле из набора-объекта — фильтрация после получения всех данных.
  • ⚠️ Ресурс (Сумма, Количество) — условие применяется к итогам, как ИМЕЮЩИЕ: сначала считаются все суммы.
  • ⚠️ Реквизит через точку (Контрагент.Город) — в запросе появится соединение с таблицей справочника.

Если вы пишете запрос набора вручную и отключили автозаполнение, СКД переносит в запрос только те условия, для которых вы явно разрешили это место. Откройте конструктор запроса набора, перейдите на вкладку «Компоновка данных» и отметьте поля, по которым допустим отбор, для каждой таблицы. Конструктор сам допишет в текст запроса фрагменты в фигурных скобках: в параметрах виртуальной таблицы и в блоке условий.

Фигурные скобки в запросе СКД означают «сюда можно подставить отбор пользователя». Для виртуальной таблицы это особенно важно: если отбор по измерению не попал в её параметры, обороты сначала рассчитаются по всей базе и лишь потом будут отфильтрованы.

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

Правило для больших отчётов простое: фильтр по измерениям регистра — в параметры виртуальной таблицы, фильтр по документам — в ГДЕ запроса, отбор по ресурсам и вычисляемым полям — только когда без него никак.

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

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