Как в СКД определить текущий состав измерений группировки при расчете ресурсов?

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

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

Проблема "хаотичных" значений при прямом обращении

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

Способ 1: Использование функции ВычислитьВыражение

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

Для реализации этого метода в строке ресурса на закладке "Ресурсы" укажем следующее выражение:


ОбщийМодуль.ОстаткиПоРазрезам(
    ВычислитьВыражение("Организация", "Организация"), 
    ВычислитьВыражение("Склад", "Склад"), 
    ВычислитьВыражение("Номенклатура", "Номенклатура")
)

Как это работает:

  1. Когда СКД рассчитывает итог для группировки Организация, вызов ВычислитьВыражение("Склад", "Склад") вернет NULL, так как на уровне организации склад еще не определен как единица группировки.
  2. Внутри функции общего модуля мы просто проверяем входящие параметры на NULL или Неопределено.
  3. Если ПараметрСклад равен NULL, значит, мы находимся выше уровня склада, и расчет должен идти в целом по организации.

Этот подход хорош тем, что он устойчив к изменению порядка группировок пользователем. Если пользователь поменяет местами Склад и Номенклатуру, логика не сломается, так как мы проверяем наличие конкретного измерения в контексте, а не его порядковый номер.

Способ 2: Использование функции ВычисляетсяДляГруппировки

Начиная с современных версий платформы 8.3, в языке выражений СКД появилась еще более удобная функция — ВычисляетсяДляГруппировки. Она возвращает логическое значение (Истина/Ложь) в зависимости от того, по какой группировке в данный момент считается ресурс.

Рассмотрим пример использования этой функции непосредственно в выражении компоновки:


ВЫБОР 
    КОГДА ВычисляетсяДляГруппировки("Склад") ТОГДА
        ОбщийМодуль.РасчетДляСклада(Сумма, Склад)
    КОГДА ВычисляетсяДляГруппировки("Организация") ТОГДА
        ОбщийМодуль.РасчетДляОрганизации(Сумма, Организация)
    ИНАЧЕ
        Сумма(Сумма)
КОНЕЦ

Этот метод считается наиболее "чистым" с точки зрения платформы, так как он явно описывает намерения разработчика. Однако стоит помнить, что в параметре функции нужно указывать Имя группировки, которое задано в настройках отчета (на вкладке "Настройки" в конструкторе СКД). Если пользователь создаст новую группировку с другим именем, условие может не сработать.

Способ 3: Разделение выражений в таблице ресурсов

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

Разберем, как это настроить:

  1. Добавим одну и ту же колонку (например, Остаток) в список ресурсов несколько раз.
  2. В первой строке в колонке "Рассчитывать по..." выберем Организация. В выражении напишем ОбщийМодуль.ИтогПоОрганизации(Параметры).
  3. Во второй строке выберем Склад. В выражении напишем ОбщийМодуль.ИтогПоСкладу(Параметры).
  4. В третьей строке укажем "Общий итог", чтобы задать логику для самого верхнего уровня отчета.

Таким образом, СКД сама будет подставлять нужное выражение в зависимости от того, какую строку отчета она выводит в данный момент. Это исключает лишние проверки в коде 1С.

Способ 4: Определение через системное поле Уровень

Если структура вашего отчета жестко фиксирована (например, иерархия всегда: Организация -> Склад -> Номенклатура), мы можем использовать функцию Уровень(). Проанализируем, как это применить:

ОбщийМодуль.РасчетПоУровню(Сумма, Уровень())

В общем модуле код будет выглядеть примерно так:


Функция РасчетПоУровню(Сумма, ТекущийУровень) Экспорт
    Если ТекущийУровень = 1 Тогда
        // Мы в первой группировке (Организация)
    ИначеЕсли ТекущийУровень = 2 Тогда
        // Мы во второй группировке (Склад)
    КонецЕсли;
КонецФункции

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

Способ 5: Программный анализ через расшифровку

Если вам нужно не просто рассчитать значение, а выполнить какое-то действие при нажатии на ячейку (расшифровка), можно воспользоваться методом анализа полей расшифровки. Как было предложено в одном из сообщений форума, в обработчике расшифровки можно получить состав полей, которые сформировали данную ячейку.


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

Хотя этот метод относится больше к интерактивной части, он подтверждает общую идею: понимание того, "где мы находимся", строится на анализе доступных измерений в текущем срезе данных.

Подводя итог, для большинства задач оптимальным решением будет использование ВычислитьВыражение с проверкой на NULL или использование функции ВычисляетсяДляГруппировки, если вы работаете на актуальной платформе. Эти методы гарантируют, что ваш отчет будет работать корректно даже при активном изменении структуры пользователем.

← На главную