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

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

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

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

Почему возникает сложность при расчете по иерархии

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

Если же связать данные через Номенклатура.Родитель, мы получим значение только непосредственного родителя на один уровень выше, что не сработает при произвольной вложенности групп в каталоге.

Для корректного вычисления показателя на уровне групп разберем основные рабочие способы.

Способ 1. Вызов функции общего модуля в выражении ресурса

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

Разберем реализацию по шагам:

  1. Создадим в конфигурации общий модуль (например, ОтчетыСервер) с установленными флажками Сервер и Вызов сервера.
  2. Добавим экспортную функцию, которая по переданной ссылке на номенклатуру возвращает значение площади из регистра сведений:
    
    Функция ПолучитьПлощадьГруппы(НоменклатураСсылка) Экспорт
        
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ
            |   РС.Площадь КАК Площадь
            |ИЗ
            |   РегистрСведений.Площадь.СрезПоследних(&Период, Номенклатура = &Номенклатура) КАК РС";
        
        Запрос.УстановитьПараметр("Период", ТекущаяДатаСеанса());
        Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
        
        Выборка = Запрос.Выполнить().Выбрать();
        Если Выборка.Следующий() Тогда
            Возврат Выборка.Площадь;
        КонецЕсли;
        
        Возврат 0;
        
    КонецФункции
    
  3. В схеме компоновки данных перейдем на вкладку «Ресурсы» и настроим расчет ресурса отдельно для детальных записей и для иерархии:

    Для поля Сумма добавим две строки настройки:

    В первой строке для детальных записей и группировки Номенклатура указываем стандартную агрегацию:

    Сумма(Сумма)

    Во второй строке в колонке «Рассчитать по...» выбираем Номенклатура Иерархия и прописываем расчетное выражение:

    
    ВЫБОР 
        КОГДА ЕСТЬNULL(ОтчетыСервер.ПолучитьПлощадьГруппы(Номенклатура), 0) > 0 
        ТОГДА Сумма(Сумма) / ОтчетыСервер.ПолучитьПлощадьГруппы(Номенклатура)
        ИНАЧЕ Сумма(Сумма) 
    КОНЕЦ
    

Способ 2. Связывание двух наборов данных в СКД

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

  1. Создадим НаборДанных1 (Запрос), выбирающий базовые данные по элементам:
    
    ВЫБРАТЬ
        Продажи.Номенклатура КАК Номенклатура,
        Продажи.Сумма КАК Сумма
    ИЗ
        РегистрНакопления.Продажи КАК Продажи
    
  2. Создадим НаборДанных2 (Запрос), выбирающий данные из регистра сведений по группам:
    
    ВЫБРАТЬ
        ПлощадьНоменклатуры.Номенклатура КАК ГруппаНоменклатуры,
        ПлощадьНоменклатуры.Площадь КАК Площадь
    ИЗ
        РегистрСведений.Площадь КАК ПлощадьНоменклатуры
    
  3. Перейдем на вкладку «Связи наборов данных»:

    Укажем источник — НаборДанных1, приемник — НаборДанных2. В качестве выражения источника укажем Номенклатура, а в выражении приемника — ГруппаНоменклатуры. При необходимости включим условие связи по иерархии.

  4. На вкладке «Ресурсы» настроим формулу расчета с проверкой деления на ноль:
    
    Сумма(Сумма) / ВЫБОР КОГДА ЕСТЬNULL(Максимум(Площадь), 0) = 0 ТОГДА 1 ИНАЧЕ Максимум(Площадь) КОНЕЦ
    

    В поле «Рассчитать по...» укажем Номенклатура Иерархия.

Способ 3. Соединение в запросе по условию «В ИЕРАРХИИ»

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


ВЫБРАТЬ
    Продажи.Номенклатура КАК Номенклатура,
    Продажи.Сумма КАК Сумма,
    РС_Площадь.Площадь КАК Площадь
ИЗ
    РегистрНакопления.Продажи КАК Продажи
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Площадь КАК РС_Площадь
        ПО Продажи.Номенклатура В ИЕРАРХИИ (РС_Площадь.Номенклатура)

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

← На главную