Не знаю, многим ли уже довелось столкнуться с ошибкой расчета начальных и конечных остатки по группировкам. Лично мне "посчастливилось", притом неоднократно. Причина, как мне удалось выяснить, кроется в неверных настройках полей данных СКД, важности которых многие начинающие (да и не очень) программисты пока до конца не осознают.
Когда набор данных СКД создается автоматически на основе запроса, проблемы обычно не возникает, т.к. платформа сама корректно заполняет настройки полей, исходя из текста запроса. Но бывают ситуации, когда настройки полей данных автоматически не заполняются (например, вы используете внешний источник данных), а исходные данные при этом содержат движения с остатками и оборотами.
Если вы ранее не сталкивались с этой проблемой, то для лучшего понимания ее сути предлагаю воспроизвести ее самим с помощью универсального отчета (по метаданным). Запускаем отчет, выбираем любой непустой регистр накопления с остатками и оборотами, включаем в настройках отчета (скрин1) флажок "Детальные записи", указываем какие-нибудь группировки и добавляем в состав выводимых полей Регистратор. Вуаля - начальные и конечные остатки суммируются по каждой группировке. Получается отчет с абсолютно некорректными цифрами, который показывать пользователям никак нельзя.
Для решения такой проблемы необходимо корректно заполнить настройки полей набора данных СКД - в частности, поле "Роль", которое имеет ключевое значение.
РЕШЕНИЕ интерактивное (не подходит для Универсального отчета):
Открываем схему компоновки данных вашего отчета и смотрим в настройки полей набора данных.
Для полей начальных и конечных остатков по каждому из ресурсов необходимо заполнить роль: выбрать группу ролей "Остаток" и в ней указать значение "Нач. остаток" или "Кон. остаток" соответственно. Так (скрин2) это делается в конструкторе СКД.
Аналогичным образом необходимо проставить роль "Измерение" для всех измерений вашего набора данных.
Но этого недостаточно для корректной работы отчетов. Для правильного расчета полей остатков необходимо знать период каждого движения, чтобы расставить их в правильном хронологическом порядке. Если в вашем исходном источнике данных поля периода нет, необходимо его туда добавить. Если же поле периода в наборе данных уже есть, ему необходимо указать роль "Период" и соответствующий номер периода (подробнее о нумерации периодов можно прочесть в справке).
Такие настройки полей данных СКД в большинстве случаев позволяют добиться корректного расчета остатков по группировкам, когда с настройками по умолчанию они рассчитываются некорректно .
РЕШЕНИЕ программное (на примере Универсального отчета по метаданным):
Теперь рассмотрим, как исправить эту же ошибку в Универсальном отчете по метаданным. Универсальный отчет отличается от большинства прочих отчетов тем, что схема компоновки данных там генерируется полностью программно, поэтому настраивать роли для полей данных СКД тоже приходится программно.
Для ролей начальных и конечных остатков по каждому из ресурсов проще всего не изобретать велосипед (все уже написано до нас) и воспользоваться типовой процедурой ЗаполнитьПолеНабораДанныхОстаток() из общего модуля ТиповыеОтчеты. Туда передаете в качестве параметров поле набора данных и имя ресурса, и в результате в наборе данных создается поле остатка с корректно заполненной ролью.
Аналогичным образом при создании полей набора данных для измерений необходимо проставить им роль "Измерение". Код будет примерно следующим:
НовоеИзмерение = ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Измерение.Имя, Измерение.Синоним);
НовоеИзмерение.Роль.Измерение = Истина;
Описанные выше манипуляции с полями ресурсов и измерений необходимы, но не достаточны для решения проблемы - основной бедой универсального отчета является отсутствие нумерации периодов. Поля периода присутствуют в наборе данных, но их роли не заполнены.
Поля периода в отчет добавляются процедурой общего модуля ТиповыеОтчеты.ДобавитьПоляПериодаВНаборДанных(), вызов которой происходит из процедуры модуля объекта ДобавитьПоляНабораДанных(). К сожалению, номера периодов эта процедура не проставляет.
Кроме того, в отчет нигде программно не добавляются поля "Номер строки" и "Регистратор". Мне показалось это странным, т.к. в итоговом наборе данных они присутствуют.
Как выяснилось, поля "Номер строки" и "Регистратор" (Recorder) добавляет сама платформа автоматически при инициализации компоновщика настроек. Причем платформа не заполняет роли для создаваемых ею полей, а программно их заполнить не получается, что создает проблемы при дальнейшей работе с ними. Но если эти поля создать "вручную" и программно прописать им правильные роли, то платформа уже не пытается создавать их заново.
Ниже я предлагаю рецепт, который помог мне почти полностью решить эту проблему платформы и Универсального отчета по метаданным:
Вот этот фрагмент кода модуля объекта:
/
// Добавляем поля периода
Если ИмяТаблицы = "ОстаткиИОбороты" ИЛИ ИмяТаблицы = "Обороты" Тогда
ТиповыеОтчеты.ДобавитьПоляПериодаВНаборДанных(СхемаКомпоновкиДанных.НаборыДанных[0]);
КонецЕсли;
нужно заменить на следующий:
// Добавляем поля периода
Если ИмяТаблицы = "ОстаткиИОбороты" ИЛИ ИмяТаблицы = "Обороты" Тогда
СписокПериодов = ТиповыеОтчеты.ДобавитьПоляПериодаВНаборДанных(СхемаКомпоновкиДанных.НаборыДанных[0]);
//Заполним служебные поля и проставим периоды вручную, т.к. платформа их не заполняет
Поле = ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], "НомерСтроки", "Номер строки");
Поле.Роль.НомерПериода = 1;
Поле = ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], "Регистратор", "Регистратор");
Поле.Роль.НомерПериода = 2;
сч = 3;
Для Каждого ПолеПериод Из СписокПериодов Цикл
ПолеПериод.Значение.Роль.НомерПериода = сч;
Если сч > 3 Тогда
ПолеПериод.Значение.Роль.ТипПериода = ТипПериодаКомпоновкиДанных.Дополнительный;
КонецЕсли;
сч = сч+1;
КонецЦикла;
КонецЕсли;
Мне удалось найти одно ограничение, связанное с этим решением. Для корректного расчета нач. и кон. остатков необходимо, чтобы при использовании в отчете каких-либо реквизитов документа-регистратора сам регистратор также был выбран. В остальном универсальный отчет после таких доработок больше не вызывает аллергии у пользователей.
UPDATE: мне подсказали в комментариях, что на диске ИТС в свое время вышла статья на эту тему. К сожалению, эта статья прошла мимо меня, но помочь мне в решении проблем с универсальным отчетом она бы смогла лишь отчасти. Увы, заморочки платформы со служебными полями СКД, такими как "Recorder", там также не описаны.
Тем не менее, я нашел на Мисте ссылку на нее и рекомендую ознакомиться, ибо полезно:
статья "Типичные проблемы при расчете остатков"
В любом случае, я надеюсь, что моя статья поможет всем тем, у кого возникли аналогичные проблемы. В свое время я потратил на поиски этого решения уйму времени...