При разработке сложных отчетов в системе компоновки данных (СКД) часто возникает задача: выполнить специфический расчет в общем модуле, который зависит от того, в какой именно группировке сейчас находится процессор компоновки. Например, если итог рассчитывается по Организации, нам нужно получить одни данные, а если по Складу внутри этой организации — совершенно другие. Проблема заключается в том, что обычная передача параметров в функцию ресурса не всегда дает понимание контекста. Давайте разберем, как правильно идентифицировать текущую группировку и передать в расчет только нужные измерения.
Рассмотрим типичную ситуацию: мы прописываем в выражении ресурса вызов функции ОбщийМодуль.РассчитатьДанные(Организация, Склад, Номенклатура). Логика подсказывает, что если мы находимся в группировке верхнего уровня (например, только по Организации), то параметры Склад и Номенклатура должны быть пустыми. Однако на практике СКД может передавать туда значения из детальных записей, которые попали в этот итог, причем зачастую это происходит произвольным образом (значение из первой или последней строки выборки). В итоге функция получает заполненный Склад, хотя расчет идет для всей Организации в целом. Чтобы этого избежать, необходимо использовать специализированные функции языка выражений СКД.
Проанализируем наиболее изящное решение, к которому пришел автор темы. Функция ВычислитьВыражение позволяет вычислить значение поля в контексте конкретной группировки. Если мы передаем имя поля вторым параметром, СКД возвращает значение только тогда, когда это поле доступно в текущем контексте.
Для реализации этого метода в строке ресурса на закладке "Ресурсы" укажем следующее выражение:
ОбщийМодуль.ОстаткиПоРазрезам(
ВычислитьВыражение("Организация", "Организация"),
ВычислитьВыражение("Склад", "Склад"),
ВычислитьВыражение("Номенклатура", "Номенклатура")
)
Как это работает:
ВычислитьВыражение("Склад", "Склад") вернет NULL, так как на уровне организации склад еще не определен как единица группировки.NULL или Неопределено.ПараметрСклад равен NULL, значит, мы находимся выше уровня склада, и расчет должен идти в целом по организации.Этот подход хорош тем, что он устойчив к изменению порядка группировок пользователем. Если пользователь поменяет местами Склад и Номенклатуру, логика не сломается, так как мы проверяем наличие конкретного измерения в контексте, а не его порядковый номер.
Начиная с современных версий платформы 8.3, в языке выражений СКД появилась еще более удобная функция — ВычисляетсяДляГруппировки. Она возвращает логическое значение (Истина/Ложь) в зависимости от того, по какой группировке в данный момент считается ресурс.
Рассмотрим пример использования этой функции непосредственно в выражении компоновки:
ВЫБОР
КОГДА ВычисляетсяДляГруппировки("Склад") ТОГДА
ОбщийМодуль.РасчетДляСклада(Сумма, Склад)
КОГДА ВычисляетсяДляГруппировки("Организация") ТОГДА
ОбщийМодуль.РасчетДляОрганизации(Сумма, Организация)
ИНАЧЕ
Сумма(Сумма)
КОНЕЦ
Этот метод считается наиболее "чистым" с точки зрения платформы, так как он явно описывает намерения разработчика. Однако стоит помнить, что в параметре функции нужно указывать Имя группировки, которое задано в настройках отчета (на вкладке "Настройки" в конструкторе СКД). Если пользователь создаст новую группировку с другим именем, условие может не сработать.
Мало кто обращает внимание на колонку "Рассчитывать по..." в таблице ресурсов СКД. Это мощный инструмент, позволяющий вообще не писать сложные условия внутри одной функции, а разнести логику на уровне настроек макета.
Разберем, как это настроить:
Остаток) в список ресурсов несколько раз.Организация. В выражении напишем ОбщийМодуль.ИтогПоОрганизации(Параметры).Склад. В выражении напишем ОбщийМодуль.ИтогПоСкладу(Параметры).Таким образом, СКД сама будет подставлять нужное выражение в зависимости от того, какую строку отчета она выводит в данный момент. Это исключает лишние проверки в коде 1С.
Если структура вашего отчета жестко фиксирована (например, иерархия всегда: Организация -> Склад -> Номенклатура), мы можем использовать функцию Уровень(). Проанализируем, как это применить:
ОбщийМодуль.РасчетПоУровню(Сумма, Уровень())
В общем модуле код будет выглядеть примерно так:
Функция РасчетПоУровню(Сумма, ТекущийУровень) Экспорт
Если ТекущийУровень = 1 Тогда
// Мы в первой группировке (Организация)
ИначеЕсли ТекущийУровень = 2 Тогда
// Мы во второй группировке (Склад)
КонецЕсли;
КонецФункции
Важное замечание: Этот способ крайне хрупкий. Если пользователь добавит новую группировку в начало или удалит промежуточную, уровни сместятся, и логика расчетов станет некорректной. Используйте его только в отчетах с защищенными настройками.
Если вам нужно не просто рассчитать значение, а выполнить какое-то действие при нажатии на ячейку (расшифровка), можно воспользоваться методом анализа полей расшифровки. Как было предложено в одном из сообщений форума, в обработчике расшифровки можно получить состав полей, которые сформировали данную ячейку.
Процедура РезультатОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
Данные = ПолучитьИзКэшаДанныеРасшифровки(Расшифровка); // Условный метод получения данных
Поля = Данные.ПолучитьПоля();
// Проверяем наличие полей в текущей строке
ЕстьСклад = Ложь;
Для Каждого Поле Из Поля Цикл
Если Поле.Поле = "Склад" Тогда
ЕстьСклад = Истина;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Хотя этот метод относится больше к интерактивной части, он подтверждает общую идею: понимание того, "где мы находимся", строится на анализе доступных измерений в текущем срезе данных.
Подводя итог, для большинства задач оптимальным решением будет использование ВычислитьВыражение с проверкой на NULL или использование функции ВычисляетсяДляГруппировки, если вы работаете на актуальной платформе. Эти методы гарантируют, что ваш отчет будет работать корректно даже при активном изменении структуры пользователем.