Как программно получить и настроить отборы по итоговым полям в группировках СКД?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с Системой компоновки данных (СКД) в 1С разработчики часто сталкиваются с необходимостью управлять настройками отчета из кода. Если с общими отборами отчета все достаточно просто (они лежат на поверхности в свойствах компоновщика), то работа с отборами, наложенными на конкретные группировки (особенно по итоговым полям или ресурсам), вызывает массу вопросов. В отладчике такие отборы не видны в привычных коллекциях, а при неправильной инициализации компоновщика настроек они и вовсе могут «исчезнуть» из структуры объектов.

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


Разница между общим отбором и отбором группировки

Прежде чем перейти к написанию кода, давайте проанализируем разницу в поведении этих двух механизмов на уровне СУБД. Это критически важно для понимания того, почему отборы по итоговым полям настраиваются именно внутри группировок, а не в общем списке отборов отчета.

  1. Общий отбор (фильтр набора данных): Работает аналогично оператору WHERE в языке SQL. Этот отбор накладывается на детальные записи еще до того, как система начнет рассчитывать агрегатные функции (такие как сумма, количество, среднее). Если мы попытаемся наложить общий отбор на поле-ресурс (например, «СуммаОборота > 100000»), система выдаст ошибку или вернет пустой результат, так как детальные записи СУБД еще не сгруппированы и итоговое поле для них не рассчитано.
  2. Отбор в группировке (фильтр по итогам): Работает аналогично оператору HAVING в языке SQL. Этот фильтр применяется уже после того, как СУБД сгруппировала записи и рассчитала все итоги (ресурсы) по этой группировке. Именно этот механизм позволяет отсечь, например, только тех контрагентов, у которых сумма продаж превысила заданный порог.

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


Где именно искать отборы группировок в структуре СКД

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

Все группировки, таблицы и диаграммы отчета хранятся в коллекции КомпоновщикНастроек.Настройки.Структура. Каждый элемент этой коллекции может содержать свои собственные настройки, включая отборы, сортировки, выбранные поля и параметры.

Рассмотрим структуру хранения:


Рекурсивный поиск отборов в сложных структурах настроек

Разберем практический пример функции, которая осуществляет поиск настроек конкретной группировки по ее имени или по имени поля группировки. Нам нужно обойти все вложенные элементы структуры настроек СКД.


// Функция выполняет поиск элемента структуры настроек по имени поля группировки
Функция НайтиГруппировкуПоПолю(КоллекцияЭлементовСтруктуры, ИмяПоля) Экспорт

    Для Каждого ЭлементСтруктуры Из КоллекцияЭлементовСтруктуры Цикл
        
        // Проверяем, является ли элемент группировкой компоновки данных
        Если ТипЗнч(ЭлементСтруктуры) = Тип("ГруппировкаКомпоновкиДанных") Тогда
            
            // Проверяем поля группировки
            Для Каждого ПолеГруппировки Из ЭлементСтруктуры.ПоляГруппировки.Элементы Цикл
                Если Строка(ПолеГруппировки.Поле) = ИмяПоля Тогда
                    Возврат ЭлементСтруктуры;
                КонецЕсли;
            КонецЦикла;
            
            // Если у группировки есть вложенные элементы, ищем в них рекурсивно
            Если ЭлементСтруктуры.Структура.Количество() > 0 Тогда
                НайденныйЭлемент = НайтиГруппировкуПоПолю(ЭлементСтруктуры.Структура, ИмяПоля);
                Если НайденныйЭлемент <> Неопределено Тогда
                    Возврат НайденныйЭлемент;
                КонецЕсли;
            КонецЕсли;
            
        // Если элемент является таблицей компоновки данных (встречается в отчетах-таблицах)
        ИначеЕсли ТипЗнч(ЭлементСтруктуры) = Тип("ТаблицаКомпоновкиДанных") Тогда
            
            // Ищем в строках таблицы
            НайденныйЭлемент = НайтиГруппировкуПоПолю(ЭлементСтруктуры.Строки, ИмяПоля);
            Если НайденныйЭлемент <> Неопределено Тогда
                Возврат НайденныйЭлемент;
            КонецЕсли;
            
            // Ищем в колонках таблицы
            НайденныйЭлемент = НайтиГруппировкуПоПолю(ЭлементСтруктуры.Колонки, ИмяПоля);
            Если НайденныйЭлемент <> Неопределено Тогда
                Возврат НайденныйЭлемент;
            КонецЕсли;
            
        КонецЕсли;
        
    КонецЦикла;
    
    Возврат Неопределено;

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

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


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

Правильная инициализация компоновщика настроек

Частая проблема, с которой сталкиваются разработчики (и которая обсуждалась в сообщениях форума), заключается в том, что при программном создании или инициализации объектов СКД коллекция настроек оказывается пустой. Кажется, что отборы настроены в макете, но программно обратиться к ним не получается.

Выясним причину этого поведения. Она кроется в нарушении последовательности создания и инициализации объектов компоновщика. Если просто создать новый КомпоновщикНастроекКомпоновкиДанных и инициализировать его источником доступных настроек, он будет содержать лишь метаданные (доступные поля), но сами настройки (группировки, отборы, выбранные поля) будут абсолютно пустыми.

Рассмотрим правильный алгоритм инициализации компоновщика настроек:

  1. Получаем схему компоновки данных из макета.
  2. Инициализируем источник доступных настроек компоновки данных на основе этой схемы.
  3. Создаем экземпляр компоновщика настроек и инициализируем его этим источником.
  4. Критически важный шаг: Загружаем в компоновщик настройки по умолчанию из нашей схемы компоновки данных (или настройки конкретного варианта отчета).
  5. Вызываем метод Восстановить() для синхронизации пользовательских настроек с основными.

Посмотрим на пример правильного программного кода:


// 1. Получаем макет схемы компоновки
СхемаКомпоновки = ПолучитьМакет("Макет");

// 2. Создаем источник доступных настроек
ИсточникНастроек = Новый ИсточникДоступныхНастроекКомпоновкиДанных(СхемаКомпоновки);

// 3. Создаем и инициализируем компоновщик настроек
КомпоновщикНастроек = Новый КомпоновщикНастроекКомпоновкиДанных;
КомпоновщикНастроек.Инициализировать(ИсточникНастроек);

// 4. Загружаем настройки по умолчанию из схемы в компоновщик
КомпоновщикНастроек.ЗагрузитьНастройки(СхемаКомпоновки.НастройкиПоУмолчанию);

// 5. Восстанавливаем связь с пользовательскими настройками
КомпоновщикНастроек.Восстановить(СпособВосстановленияНастроекКомпоновкиДанных.Полное);

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


Программный вывод отборов группировки на форму отчета

Для того чтобы пользователь мог управлять отборами по группировкам (включая итоговые поля) непосредственно с формы отчета, эти отборы необходимо связать с механизмом пользовательских настроек.

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

Разберем по шагам, как программно создать отбор в группировке по ресурсу и сделать его доступным пользователю на форме:


// Ищем нужную группировку по полю "Контрагент"
ГруппировкаКонтрагенты = НайтиГруппировкуПоПолю(КомпоновщикНастроек.Настройки.Stricture, "Контрагент");

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

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


Важные свойства и особенности работы с ресурсами

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

  1. Обязательное условие для ресурсов: Поле, по которому вы накладываете отбор в группировке в качестве "итога", обязательно должно быть определено как Ресурс на соответствующей вкладке схемы компоновки данных. Если поле не объявлено ресурсом (то есть для него не задана агрегатная функция вроде СУММА или КОЛИЧЕСТВО), СКД не позволит рассчитать по нему промежуточные итоги и применить логику фильтрации HAVING. Поле будет доступно только как обычное детальное поле отчета.
  2. Свойство «Применение» (Application): У элементов отбора внутри группировок есть специфическое свойство Применение. Оно определяет, на каком шаге построения отчета сработает фильтр:
    • После расчета иерархии — отбор применится к уже построенному дереву группировок с учетом иерархических связей;
    • Только для иерархических записей — позволяет отфильтровать только родительские узлы (например, папки контрагентов), не затрагивая при этом детальные записи.

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

← На главную