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