Почему итоги в СКД некорректно рассчитываются, если исключить из отчета определенные поля, и как это исправить?

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

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

Наш клиент столкнулся с тем, что в отчете по остаткам, построенном на базе регистра накопления ПартииТоваровНаСкладах.ОстаткиИОбороты, поля НачальныйОстаток и КонечныйОстаток отображались корректно, пока в отчет выводилось дополнительное поле ДнейВПериоде. Однако стоило убрать это поле, как остатки превращались в "бред сивой кобылы". Выясним, почему так происходит, и как этого избежать.

Анализ исходного запроса и его проблем

Рассмотрим исходный запрос, который использовался в СКД:


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    НАЧАЛОПЕРИОДА(ПартииТоваровНаСкладахОстаткиИОбороты.Период, МЕСЯЦ) КАК Период,
    ПартииТоваровНаСкладахОстаткиИОбороты.Номенклатура КАК Номенклатура,
    ПартииТоваровНаСкладахОстаткиИОбороты.Склад КАК Склад,
    СУММА(ПартииТоваровНаСкладахОстаткиИОбороты.КоличествоНачальныйОстаток) КАК НачальныйОстаток,
    СУММА(ПартииТоваровНаСкладахОстаткиИОбороты.КоличествоКонечныйОстаток) КАК КонечныйОстаток,
    РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&НачалоПериода, МЕСЯЦ), КОНЕЦПЕРИОДА(&КонецПериода, МЕСЯЦ), ДЕНЬ) + 1 КАК ДнейВПериоде
ИЗ
    РегистрНакопления.ПартииТоваровНаСкладах.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, Месяц, , {(Склад).* КАК Склад, (Номенклатура).* КАК Номенклатура}) КАК ПартииТоваровНаСкладахОстаткиИОбороты
СГРУППИРОВАТЬ ПО
    НАЧАЛОПЕРИОДА(ПартииТоваровНаСкладахОстаткиИОбороты.Период, МЕСЯЦ),
    ПартииТоваровНаСкладахОстаткиИОбороты.Номенклатура,
    ПартииТоваровНаСкладахОстаткиИОбороты.Склад

В этом запросе мы видим несколько особенностей:

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

Первое решение: Упрощение запроса для СКД

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

Посмотрим на пример упрощенного запроса:


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ПартииТоваровНаСкладахОстаткиИОбороты.Период КАК Период,
    ПартииТоваровНаСкладахОстаткиИОбороты.Номенклатура КАК Номенклатура,
    ПартииТоваровНаСкладахОстаткиИОбороты.Склад КАК Склад,
    ПартииТоваровНаСкладахОстаткиИОбороты.КоличествоНачальныйОстаток КАК НачальныйОстаток,
    ПартииТоваровНаСкладахОстаткиИОбороты.КоличествоКонечныйОстаток КАК КонечныйОстаток,
    РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&НачалоПериода, МЕСЯЦ), КОНЕЦПЕРИОДА(&КонецПериода, МЕСЯЦ), ДЕНЬ) + 1 КАК ДнейВПериоде
ИЗ
    РегистрНакопления.ПартииТоваровНаСкладах.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, Месяц, , {(Склад).* КАК Склад, (Номенклатура).* КАК Номенклатура}) КАК ПартииТоваровНаСкладахОстаткиИОбороты

Что изменилось?

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

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

Второе решение: Включение полей Приход и Расход

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

Посмотрим на пример запроса с добавлением этих полей:


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ПартииТоваровНаСкладахОстаткиИОбороты.Период КАК Период,
    ПартииТоваровНаСкладахОстаткиИОбороты.Номенклатура КАК Номенклатура,
    ПартииТоваровНаСкладахОстаткиИОбороты.Склад КАК Склад,
    ПартииТоваровНаСкладахОстаткиИОбороты.КоличествоНачальныйОстаток КАК НачальныйОстаток,
    ПартииТоваровНаСкладахОстаткиИОбороты.КоличествоПриход КАК Приход,
    ПартииТоваровНаСкладахОстаткиИОбороты.КоличествоРасход КАК Расход,
    ПартииТоваровНаСкладахОстаткиИОбороты.КоличествоКонечныйОстаток КАК КонечныйОстаток,
    &ДнейВПериоде КАК ДнейВПериоде
ИЗ
    РегистрНакопления.ПартииТоваровНаСкладах.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, Месяц, , {(Склад).* КАК Склад, (Номенклатура).* КАК Номенклатура}) КАК ПартииТоваровНаСкладахОстаткиИОбороты

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

Ключевая причина: Изменения в платформе 8.3.27 и роль "Обязательное"

Главная причина, по которой отчет "сломался" после обновления платформы до версии 8.3.27, заключается в более строгой проверке отметки "Обязательное" в ролях полей СКД. Ранее, в версиях до 8.3.27 (например, 8.3.25), платформа могла быть более снисходительна к отсутствию этой отметки, если поле каким-либо образом участвовало в компоновке. В новых версиях СКД стала требовать явного указания для тех полей, которые критически важны для формирования структуры данных и расчета итогов.

Что такое роль "Обязательное"?

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

В нашем случае, поле ДнейВПериоде, хоть и является константой, могло неявно влиять на структуру данных или уникальность строк на каком-то уровне. При его исключении, если оно ранее было помечено как "Обязательное" (или платформа сама его так интерпретировала), СКД в новой версии перестает понимать, как правильно агрегировать данные, и выдает некорректные итоги.

Подробные рекомендации и лучшие практики по работе с СКД

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

  1. Проверка и настройка ролей полей в СКД.

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

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

    Для правильного расчета остатков из виртуальных таблиц, таких как ОстаткиИОбороты, крайне важно правильно настроить роли полей, относящихся к периодам и остаткам. Поле Период из ОстаткиИОбороты должно иметь номер периода, например, 3, Регистратор – 2, НомерСтроки – 1. Это позволяет платформе правильно рассчитывать итоги по полям остатка. При этом признак Дополнительный для поля-периода указывает, что это поле не является обязательным, если в отчете используются его дочерние периоды.

  3. Включение всех необходимых измерений в запрос.

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

  4. Использование параметров СКД для константных значений.

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

    1. Удалите вычисление поля ДнейВПериоде из запроса.
    2. Создайте параметр СКД, например, с именем ДнейВПериоде.
    3. В коде, формирующем отчет, перед выполнением компоновки, установите значение этого параметра:
    4. 
      МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
      ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
      ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
                  
      // Получаем и устанавливаем значение параметра
      НачалоПериода = Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("НачалоПериода")).Значение;
      КонецПериода = Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("КонецПериода")).Значение;
                  
      Если НачалоПериода <> Неопределено И КонецПериода <> Неопределено Тогда
          КоличествоДней = РазностьДат(НачалоПериода(НачалоПериода, Месяц), КонецПериода(КонецПериода, Месяц), День) + 1;
          ПроцессорКомпоновки.УстановитьЗначениеПараметра("ДнейВПериоде", КоличествоДней);
      КонецЕсли;
                  
      ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
      ПроцессорВывода.УстановитьДокумент(ТабДок);
      ПроцессорВывода.Вывести(ПроцессорКомпоновки);
      
  5. Повторное включение Прихода и Расхода.

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

  6. Проверка совместимости при обновлении платформы.

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

Итак, мы выяснили, что проблема с некорректными итогами в СКД при исключении полей часто связана с двумя основными факторами: неоптимальным запросом с избыточной агрегацией и, что более важно, со строгими требованиями платформы 1С 8.3.27+ к роли "Обязательное" для полей СКД. Применяя описанные подходы и лучшие практики, вы сможете обеспечить стабильную и точную работу ваших отчетов.

← К списку