При работе с Системой компоновки данных (СКД) программисты часто сталкиваются с ситуацией, когда программно установленный отбор по ресурсу (например, «Долг меньше 0») приводит к неожиданным результатам: строки в отчете фильтруются верно, но общие итоги остаются прежними или рассчитываются некорректно. Разберем по шагам, почему это происходит и как правильно настроить программный отбор, чтобы данные и итоги соответствовали друг другу.
Рассмотрим ситуацию, представленную в исходном коде. Разработчик пытается программно добавить элемент отбора в КомпоновщикНастроек:
НовыйОтбор = КомпоновщикНастроек.Настройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
НовыйОтбор.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ДолгКонечныйОстаток");
НовыйОтбор.ВидСравнения = ВидСравненияКомпоновкиДанных.Меньше;
НовыйОтбор.ПравоеЗначение = 0;
НовыйОтбор.Использование = Истина;
Проблема заключается в том, что поле ДолгКонечныйОстаток является ресурсом. Когда мы накладываем отбор на ресурс в общих настройках компоновщика, СКД интерпретирует это как условие, которое должно применяться к уже рассчитанным агрегатным значениям (аналог HAVING в языке SQL). Однако механизмы расчета итогов (особенно для остаточных регистров) могут продолжать учитывать все записи, если компоновка не понимает, что отсекаемые данные не должны участвовать в расчете итоговых сумм.
Самый надежный способ гарантировать правильность итогов — это ограничить выборку данных еще на уровне выполнения запроса в наборе данных СКД. Если задача стоит «выбрать партнеров, у которых итоговый долг меньше нуля», то оптимальным решением будет использование параметров виртуальной таблицы или секции ГДЕ.
Проанализируем пример запроса:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
СУММА(РасчетыСКлиентами.СуммаКонечныйОстаток) КАК ДолгКонечныйОстаток,
РегистрАналитикаУчетаПоПартнерам.Партнер КАК Партнер
ИЗ
РегистрНакопления.РасчетыСКлиентами.ОстаткиИОбороты(...) КАК РасчетыСКлиентами
ГДЕ
...
Чтобы итоги считались только по отрицательным остаткам, мы можем обернуть основной запрос во вложенный и наложить условие там, либо использовать параметры. Если отбор должен быть динамическим (то включаться, то выключаться), рекомендуется добавить в запрос параметр:
ВЫБРАТЬ ...
ИЗ (...) КАК ВТ
ГДЕ (НЕ &ТолькоОтрицательные ИЛИ ВТ.ДолгКонечныйОстаток < 0)
В этом случае при установке параметра ТолькоОтрицательные в значение Истина, база данных вернет только нужные строки, и СКД рассчитает итоги исключительно на основе этой выборки.
Часто «неправильные итоги» в отчетах по регистрам накопления связаны с тем, что в наборе данных СКД неверно заполнены роли полей. Выясним причину: для полей типа «Остаток» СКД применяет особые алгоритмы агрегации. Если поле ДолгКонечныйОстаток не помечено как остаток, СКД может просто суммировать его значения при группировке, что приведет к завышенным результатам.
Рассмотрим, как правильно настроить роли:
ДолгКонечныйОстаток.Правильная расстановка ролей позволяет СКД корректно обрабатывать отборы даже в тех случаях, когда они накладываются программно поверх результата запроса.
Если мы устанавливаем отбор в КомпоновщикНастроек.Настройки.Отбор, он считается глобальным. Если же нам нужно отфильтровать только детальные записи (например, список партнеров), сохранив при этом общие итоги или корректно их пересчитав только для этой части отчета, можно наложить отбор на конкретную группировку.
Проанализируем ситуацию программно. Вместо того чтобы добавлять отбор в корень, найдем нужную группировку в структуре настроек:
// Поиск группировки по Партнеру
Для Каждого ЭлементСтруктуры Из КомпоновщикНастроек.Настройки.Структура Цикл
Если ТипЗнч(ЭлементСтруктуры) = Тип("ГруппировкаКомпоновкиДанных") Тогда
// Проверяем, что это группировка по нужному полю
Если ЭлементСтруктуры.ПоляГруппировки.Элементы[0].Поле = Новый ПолеКомпоновкиДанных("Партнер") Тогда
НовыйОтбор = ЭлементСтруктуры.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
// ... настройка полей отбора ...
КонецЕсли;
КонецЕсли;
КонецЦикла;
Такой подход заставляет СКД применять фильтр только к конкретному узлу дерева отчета, что часто исправляет логические ошибки в отображении итогов.
В объекте ЭлементОтбораКомпоновкиДанных существует важное свойство — ПрименениеОтбора. Оно определяет, в какой момент будет выполнена фильтрация: до расчета итогов или после. Рассмотрим доступные варианты:
Программно это настраивается так:
НовыйОтбор.Применение = ТипПримененияОтбораКомпоновкиДанных.Элементы;
При генерации макета программно (через КомпоновщикМакетаКомпоновкиДанных), крайне важно передавать в метод Выполнить актуальные настройки. Посмотрим на пример кода:
// Получаем полные настройки, включая пользовательские и программные изменения
НастройкиДляВыполнения = КомпоновщикНастроек.ПолучитьНастройки();
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, НастройкиДляВыполнения, ДанныеРасшифровки);
Если использовать напрямую КомпоновщикНастроек.Настройки, некоторые программно добавленные элементы могут быть проигнорированы, если они перекрываются пользовательскими настройками. Метод ПолучитьНастройки() собирает результирующий «образ» настроек, который гарантирует применение всех фильтров.
Подводя итог: для корректного расчета итогов при программном отборе по ресурсу лучше всего использовать фильтрацию на уровне параметров виртуальных таблиц в запросе либо тщательно настраивать роли полей остатков в схеме СКД.