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

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

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

В этой статье мы подробно разберем, как решить данную задачу с помощью механизмов СКД, рассмотрим вычисление «суммы максимумов» на разных уровнях иерархии, научимся скрывать ресурсы в детальных записях и разберем оптимальные способы построения запросов для вывода данных без оборотов.

Постановка проблемы и почему стандартный расчет ресурсов не работает

Представим структуру отчета со следующей иерархией группировок:

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

Если же мы укажем для ресурса функцию Максимум, то на уровне группировки «Договор» мы получим корректное значение. Однако на уровне группировки «Контрагент» и в «Общих итогах» СКД также выберет максимум среди всех договоров, вместо того чтобы сложить долги по разным договорам. Нам же требуется получить именно сумму максимумов.

Рассмотрим пошагово три профессиональных метода решения этой задачи.

Способ 1. Использование функции языка выражений СКД «ВычислитьВыражениеСГруппировкойМассив»

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

Разберем по шагам настройку этого механизма на вкладке «Ресурсы» в схеме компоновки данных:

Для того чтобы формула работала корректно и не вызывала ошибок компоновки, нам необходимо настроить расчет ресурса отдельно для каждого уровня группировки. В таблице ресурсов добавьте поле СуммаПоДоговоруОбщ несколько раз с указанием конкретных группировок в колонке «Рассчитывать по»:

  1. Для группировки «Договор»:

    В колонке «Выражение» укажите простую функцию максимума:

    Максимум(СуммаПоДоговоруОбщ)

    В колонке «Рассчитывать по» выберите группировку Договор.

  2. Для группировки «Контрагент»:

    В колонке «Выражение» пропишите следующую формулу:

    
    Сумма(ВычислитьВыражениеСГруппировкойМассив("Максимум(СуммаПоДоговоруОбщ)", "Договор"))
    

    В колонке «Рассчитывать по» выберите группировку Контрагент.

  3. Для общего итога (верхний уровень):

    Используйте ту же формулу, что и для контрагента:

    
    Сумма(ВычислитьВыражениеСГруппировкойМассив("Максимум(СуммаПоДоговоруОбщ)", "Договор"))
    

    В колонке «Рассчитывать по» укажите значение <Общие итоги>.

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

Способ 2. Как скрыть числовой показатель в детальных записях

Часто требуется, чтобы значение общего долга вообще не выводилось на уровне детальных записей (строк документов), а отображалось исключительно в строках группировок «Договор» и «Контрагент». Выясним, как это сделать тремя альтернативными путями.

Вариант А. Создание пустого вычисляемого поля (наиболее надежный метод)

Рассмотрим самый быстрый и изящный трюк, не требующий изменения настроек структуры отчета:

  1. Перейдем на вкладку «Вычисляемые поля».
  2. Добавим новое поле с именем ОбщийДолгИтог. Путь к данным укажем такой же: ОбщийДолгИтог.
  3. Колонку «Выражение» оставим абсолютно пустой. Благодаря этому на уровне детальных записей поле всегда будет возвращать значение NULL и визуально оставаться пустым.
  4. На вкладке «Ресурсы» добавим созданное поле ОбщийДолгИтог в список ресурсов. Настроим расчет этого ресурса по группировкам «Договор» и «Контрагент», используя формулы из Способа 1.

Вариант Б. Настройка выбранных полей для детальных записей

Если мы не хотим создавать новые поля, мы можем управлять составом колонок непосредственно в настройках структуры отчета:

  1. Перейдем на вкладку «Настройки», выделим в дереве структуры элемент «Детальные записи».
  2. Внизу перейдем на вкладку «Выбранные поля» и снимем флаг «Авто» (чтобы настройки детальных записей перестали наследоваться от родительских уровней).
  3. Вручную перенесем в выбранные поля детальных записей только те реквизиты, которые нам нужны (например, Период, Регистратор, СуммаОборот), исключив поле общего долга. При этом в настройках группировки «Договор» данное поле должно остаться выбранным.

Вариант В. Использование условного оформления

Мы можем скрыть вывод данных визуально, используя механизм условного оформления:

  1. В настройках отчета перейдем на вкладку «Условное оформление».
  2. Добавим новый элемент оформления. В свойстве «Оформление» найдем параметр «Текст» и установим для него пустое значение (не пробел, а именно отсутствие символов).
  3. В «Условии» добавим проверку: системное поле СистемныеПоля.УровеньВГруппировке (или СистемныеПоля.Уровень) больше определенного значения. Либо зададим условие, при котором детальные записи определяются по отсутствию аналитики договора в конкретной строке.
  4. В «Оформляемых полях» выберем наше поле общего долга. На уровне детальных записей СКД применит оформление и скроет числовые значения, оставив их только в итогах.

Способ 3. Вывод договоров без движений (Связь наборов данных vs Единый пакетный запрос)

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

Подход 1. Связь наборов данных в СКД

Мы можем создать два независимых набора данных - запроса:

На вкладке «Связи наборов данных» свяжем НаборДанных1 (источник) с НаборомДанных2 (приемник) по полям Контрагент и Договор.

Преимущества: Любая связь наборов данных в СКД по умолчанию работает как левое внешнее соединение. Мы гарантированно получим все договоры из первого набора. Более того, СКД автоматически оптимизирует расчет ресурсов и предотвратит задвоение числовых показателей из первого набора при выводе детальных записей второго.

Недостатки: Если таблицы для получения детальных записей довольно «тяжелые» (как в случае автора, где приходится обращаться напрямую к таблицам документов без использования регистров), выполнение двух отдельных запросов к СУБД может негативно сказаться на производительности.

Подход 2. Оптимизация в рамках одного запроса (пакетный запрос с ВТ)

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


// Шаг 1: Получаем остатки долга по всем договорам во временную таблицу
ВЫБРАТЬ
    ВзаиморасчетыОстатки.Контрагент КАК Контрагент,
    ВзаиморасчетыОстатки.Договор КАК Договор,
    ВзаиморасчетыОстатки.СуммаОстаток КАК СуммаПоДоговоруОбщ
ПОМЕСТИТЬ ВТ_Остатки
ИЗ
    РегистрНакопления.Взаиморасчеты.Остатки(&КонецПериода, ) КАК ВзаиморасчетыОстатки
;

////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Получаем детальные движения документов за выбранный период
ВЫБРАТЬ
    Движения.Контрагент КАК Контрагент,
    Движения.Договор КАК Договор,
    Движения.Ссылка КАК ДокументДвижения,
    Движения.Сумма КАК СуммаОборот
ПОМЕСТИТЬ ВТ_Обороты
ИЗ
    Документ.ЗаявкаНаОплату КАК Движения
ГДЕ
    Движения.Дата МЕЖДУ &НачалоПериода И &КонецПериода
;

////////////////////////////////////////////////////////////////////////////////
// Шаг 3: Объединяем данные левым соединением, чтобы вывести договоры без движений
ВЫБРАТЬ
    ВТ_Остатки.Контрагент КАК Контрагент,
    ВТ_Остатки.Договор КАК Договор,
    ВТ_Остатки.СуммаПоДоговоруОбщ КАК СуммаПоДоговоруОбщ,
    ВТ_Обороты.ДокументДвижения КАК ДокументДвижения,
    ВТ_Обороты.СуммаОборот КАК СуммаОборот
ИЗ
    ВТ_Остатки КАК ВТ_Остатки
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Обороты КАК ВТ_Обороты
        ПО ВТ_Остатки.Контрагент = ВТ_Обороты.Контрагент
        И ВТ_Остатки.Договор = ВТ_Обороты.Договор

При таком подходе СУБД выполнит один оптимальный пакет запросов. Все договоры с остатками долга попадут в выборку. Если по какому-то договору движений за период не было, поля ДокументДвижения и СуммаОборот примут значение NULL, но сам договор и его общий долг успешно отобразятся в отчете.

Резюме

Мы проанализировали особенности проектирования сложных отчетов по взаиморасчетам в СКД. Для реализации «суммы максимумов» всегда используйте функцию ВычислитьВыражениеСГруппировкойМассив с разделением расчета ресурсов по конкретным группировкам. Для скрытия детальных записей используйте вычисляемые поля без выражений. А при работе с договорами без оборотов отдавайте предпочтение пакетным запросам с временными таблицами, что гарантирует стабильно высокую скорость работы системы.

← На главную