Как исправить ошибку группировки полей составного типа и настроить итоги в отчете 1С?

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

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

Причина возникновения ошибки разыменования полей составного типа

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

На уровне СУБД такое обращение разворачивается во множество соединений (LEFT JOIN) со всеми возможными таблицами документов конфигурации и сложное условное выражение CASE WHEN ... THEN ... END. Если в секции ВЫБРАТЬ к данному полю применяется функция (например, НАЧАЛОПЕРИОДА(..., ДЕНЬ)), а в секции СГРУППИРОВАТЬ ПО это выражение указано не полностью, платформе не удается сопоставить структуры полей в блоке агрегации, из-за чего формируется исключение.

Посмотрим на пример запроса, вызвавшего ошибку:


ВЫБРАТЬ
    КурсыВалют.Курс,
    КурсыВалют.Валюта,
    ПартииТоваровНаСкладахОстатки.Номенклатура,
    ПартииТоваровНаСкладахОстатки.Склад,
    СУММА(ПартииТоваровНаСкладахОстатки.КоличествоОстаток) КАК КоличествоОстаток,
    СУММА(ПартииТоваровНаСкладахОстатки.СтоимостьОстаток) КАК СтоимостьОстаток,
    ПартииТоваровНаСкладахОстатки.ДокументОприходования.Дата,
    НАЧАЛОПЕРИОДА(ПартииТоваровНаСкладахОстатки.ДокументОприходования.Дата, ДЕНЬ) КАК Поле1,
    КурсыВалютСрезПоследних.Курс КАК Курс1,
    СУММА(ВЫРАЗИТЬ(ВЫБОР
            КОГДА ПартииТоваровНаСкладахОстатки.Номенклатура.Валютный = ИСТИНА
                ТОГДА ПартииТоваровНаСкладахОстатки.СтоимостьОстаток / КурсыВалют.Курс * КурсыВалютСрезПоследних.Курс
            ИНАЧЕ ПартииТоваровНаСкладахОстатки.СтоимостьОстаток
        КОНЕЦ КАК ЧИСЛО(15, 2))) КАК КорректнаяСтоимость,
    ПартииТоваровНаСкладахОстатки.ДокументОприходования
ИЗ
    РегистрНакопления.ПартииТоваровНаСкладах.Остатки(&Дата1, ) КАК ПартииТоваровНаСкладахОстатки
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
        ПО (НАЧАЛОПЕРИОДА(ПартииТоваровНаСкладахОстатки.ДокументОприходования.Дата, ДЕНЬ) = КурсыВалют.Период),
    РегистрСведений.КурсыВалют.СрезПоследних(&Дата1, ) КАК КурсыВалютСрезПоследних
ГДЕ
    КурсыВалют.Валюта.Код = "840"
    И КурсыВалютСрезПоследних.Валюта.Код = "840"
СГРУППИРОВАТЬ ПО
    КурсыВалют.Курс,
    КурсыВалют.Валюта,
    ПартииТоваровНаСкладахОстатки.Номенклатура,
    ПартииТоваровНаСкладахОстатки.Склад,
    ПартииТоваровНаСкладахОстатки.ДокументОприходования.Дата,
    КурсыВалютСрезПоследних.Курс,
    ПартииТоваровНаСкладахОстатки.ДокументОприходования

В данном предложении группировки отсутствует выражение НАЧАЛОПЕРИОДА(ПартииТоваровНаСкладахОстатки.ДокументОприходования.Дата, ДЕНЬ), что и вызвало синтаксическую ошибку платформы 1С.

Способы устранения ошибки группировки в обычном запросе

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

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

Правильная архитектура отчета в СКД: перенос агрегации на уровень компоновки

Когда отчет разрабатывается с использованием Системы компоновки данных (СКД), применение СГРУППИРОВАТЬ ПО и агрегатных функций (СУММА, КОЛИЧЕСТВО) в самом тексте запроса является ошибкой. Агрегация в запросе «схлопывает» детальные записи и лишает СКД возможности динамически рассчитывать промежуточные итоги, строить иерархию папок номенклатуры и вычислять общие суммы.

Разберем по шагам, как правильно организовать получение итогов.

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

Рекомендации по оптимизации запроса

Рассмотрим дополнительные моменты, влияющие на корректность и быстродействие запроса:

  1. Параметры виртуальных таблиц: условия по остаткам не следует помещать в секцию ГДЕ. Отборы по номенклатуре, складам или типам цен необходимо передавать напрямую во второй параметр виртуальной таблицы РегистрНакопления.ПартииТоваровНаСкладах.Остатки(&Дата1, [Условие]).
  2. Соединение с регистрами сведений курсов валют: строгое внутреннее соединение с таблицей РегистрСведений.КурсыВалют по точному равенству даты может привести к потере строк остатков, если на дату документа оприходования курс не вводился (например, в выходные или праздничные дни). Для получения курсов на исторические даты безопаснее использовать срез последних на каждую дату документа через временные таблицы либо вычисляемые поля.
  3. Учет кратности валюты: при расчете валютной суммы формула пересчета должна обязательно учитывать кратность валюты из регистра (Стоимость * Кратность / Курс), чтобы избежать искажения данных для валют с нестандартной кратностью.
← На главную