При разработке сложных управленческих отчетов, таких как "Рентабельность продаж", программисты 1С часто сталкиваются с ситуацией, когда стандартный механизм отборов Системы компоновки данных (СКД) перестает функционировать. Особенно остро это проявляется, когда ресурс рассчитывается не просто как сумма по полю, а с использованием сложных условий ВЫБОР КОГДА, параметров и функций контекстного вычисления, таких как ВычислитьВыражениеСГруппировкойМассив.
Разберем детально, почему так происходит и какие существуют методики решения этой задачи, чтобы ваш отчет не только правильно считал данные, но и позволял пользователю фильтровать результат, например, по условию "Рентабельность <= 3%".
Прежде всего, нам необходимо понять внутреннюю логику работы процессора компоновки. Когда мы накладываем отбор на обычное поле, СКД старается включить это условие непосредственно в SQL-запрос (в секцию ГДЕ или ИМЕЮЩИЕ). Однако, когда формула ресурса содержит функции семейства ВычислитьВыражение, расчет происходит уже в оперативной памяти после того, как основные данные получены из базы.
Проанализируем ситуацию: если в формуле используется массив (результат функции ВычислитьВыражениеСГруппировкойМассив), система не может транслировать это в понятный базе данных запрос. В итоге отбор либо применяется к «пустым» значениям, либо полностью игнорируется, выдавая неадекватный результат (например, выводятся только строки с нулевой рентабельностью).
Один из наиболее эффективных способов — это разделение этапа вычисления и этапа агрегации. Рассмотрим, как это можно реализовать шаг за шагом:
Сумма(ИмяВычисляемогоПоля)).Важный нюанс: если вам нужно рассчитывать агрегат от агрегата, используйте вложенные конструкции в ресурсах. Пример корректной записи для суммы по массиву вычислений:
Сумма(ВычислитьВыражениеСГруппировкойМассив("Сумма(ВаловаяПрибыль)", "Соглашение"))
Такой подход помогает СКД корректно определить тип возвращаемого значения и подготовить структуру данных для наложения фильтров.
Если математическая сложность ресурса слишком высока и стандартный числовой отбор не срабатывает, мы можем применить хитрость с логическим флагом. Рассмотрим алгоритм:
СоответствуетКритериюРентабельности.
Выбор
Когда (Сумма(Выручка) <> 0 И (Сумма(ВаловаяПрибыль) / Сумма(Выручка) * 100) <= &ПорогРентабельности)
Тогда Истина
Иначе Ложь
Конец
СоответствуетКритериюРентабельности Равно Истина.Процессору компоновки гораздо проще обработать булево значение на этапе формирования итогов, чем пытаться сравнить результат динамического вычисления массива с числом.
Если отчет работает слишком медленно или формулы настолько «навороченные», что СКД не справляется, наиболее надежным (хотя и более трудозатратным) методом будет использование Набора данных - объект. Разберем этот метод по шагам:
1. В модуле отчета программно сформируйте запрос, который получает все необходимые базовые данные.
2. Выгрузите результат запроса в ТаблицаЗначений.
3. Пройдите циклом по таблице или воспользуйтесь методом ЗаполнитьЗначения() для расчета рентабельности на уровне строк и итогов именно так, как требует бизнес-логика. На этом этапе вы можете использовать любые сложные алгоритмы 1С, которые недоступны в языке запросов или формулах СКД.
4. Передайте готовую таблицу в СКД:
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Получаем данные
ТаблицаДанных = ПолучитьТаблицуДляОтчета(); // Ваша функция расчета
ВнешниеНаборыДанных = Новый Структура;
ВнешниеНаборыДанных.Вставить("ТаблицаРентабельности", ТаблицаДанных);
// Инициализируем компоновку
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедуры
Поскольку в СКД данные попадут уже в виде готовых чисел в колонке Рентабельность, стандартный отбор будет работать мгновенно и безошибочно, так как для системы это станет простым полем с типом Число.
Проанализируем еще несколько важных моментов, которые могут влиять на работу отборов:
ЕСТЬNULL(..., 0). В СКД наличие NULL в любой части сложной формулы ресурса может привести к тому, что весь результат станет NULL, и отбор <= 3 просто «не увидит» эту строку.Число(15, 2)). Это подскажет системе, как правильно подготавливать данные для сравнения в фильтрах.Выяснив причину неработоспособности отборов, мы видим, что проблема чаще всего кроется в попытке заставить СКД делать невозможное в рамках одного SQL-запроса. Использование вычисляемых полей или объектных наборов данных позволяет обойти эти ограничения, сохраняя гибкость и мощь Системы компоновки данных.