При разработке отчетов на базе Системы компоновки данных (СКД) одной из самых трудозатратных задач является получение корректных начальных и конечных остатков, особенно когда требуется детализация до конкретного документа движения — Регистратора. Часто программисты сталкиваются с ситуацией, когда общие итоги по отчету или итоги по группировкам начинают «задваиваться», «затраиваться» или принимать совершенно непредсказуемые значения. Особенно остро эта проблема проявляется при попытке вывести в отчет поля, полученные через разыменование (через точку) от измерений регистра.
В этой статье мы подробно разберем, почему возникают «кривые» итоги, проанализируем механизм работы ролей СКД и выясним, как использование настройки Родительское измерение помогает решить проблему раз и навсегда.
Рассмотрим типичный запрос к виртуальной таблице ОстаткиИОбороты регистра накопления (например, РасчетыСКлиентами). Проблема часто начинается с того, что в запрос включаются не только измерения регистра, но и их реквизиты. Проанализируем пример такого запроса:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
oren_Взаиморасчеты.АналитикаУчетаПоПартнерам.Организация КАК Организация,
oren_Взаиморасчеты.АналитикаУчетаПоПартнерам.Контрагент КАК Партнер,
oren_Взаиморасчеты.АналитикаУчетаПоПартнерам.Партнер КАК ТорговаяТочка,
oren_Взаиморасчеты.ОбъектРасчетов.Менеджер КАК Агент,
oren_Взаиморасчеты.АналитикаУчетаПоПартнерам.Договор КАК Договор,
oren_Взаиморасчеты.АналитикаУчетаПоПартнерам КАК АналитикаУчетаПоПартнерам,
oren_Взаиморасчеты.ПериодСекунда КАК ПериодСекунда,
ВЫБОР
КОГДА oren_Взаиморасчеты.Регистратор = НЕОПРЕДЕЛЕНО
ТОГДА NULL
ИНАЧЕ oren_Взаиморасчеты.Регистратор
КОНЕЦ КАК Регистратор,
oren_Взаиморасчеты.СуммаНачальныйОстаток КАК СуммаНачальныйОстаток,
oren_Взаиморасчеты.СуммаПриход КАК СуммаПриход,
oren_Взаиморасчеты.СуммаРасход КАК СуммаРасход,
oren_Взаиморасчеты.СуммаКонечныйОстаток КАК СуммаКонечныйОстаток
ИЗ
РегистрНакопления.РасчетыСКлиентами.ОстаткиИОбороты(, , Авто, ДвиженияИГраницыПериода, ) КАК oren_Взаиморасчеты
На первый взгляд запрос выглядит корректно. Мы используем периодичность Авто и метод дополнения ДвиженияИГраницыПериода, что является стандартом для получения остатков по регистратору. Однако в этом запросе кроется «мина замедленного действия» — поле oren_Взаиморасчеты.ОбъектРасчетов.Менеджер.
Выясним причину некорректного поведения СКД. Когда мы выбираем поле через точку, например ОбъектРасчетов.Менеджер, для системы компоновки данных это поле перестает быть частью измерения ОбъектРасчетов. СКД воспринимает его как отдельную сущность, по которой нужно агрегировать данные.
Если это поле используется в группировках отчета, СКД пытается рассчитать остатки именно в разрезе этого «нового» поля. Но так как в самом регистре накопления нет измерения Менеджер, система не понимает, как правильно соотнести остатки регистра с этим значением. В результате при расчете итогов по вышестоящим группировкам суммы начинают суммироваться некорректно, так как СКД теряет связь между реквизитом и его владельцем-измерением.
Прежде чем исправлять проблему с менеджером, убедимся, что базовые настройки периодов выполнены правильно. Для корректного расчета остатков по регистратору необходимо соблюдать строгую иерархию ролей:
Период. В поле «Тип периода» указываем Дополнительный (хотя в некоторых случаях СКД справляется и без этого флага, его наличие помогает системе при автоматическом построении временных таблиц). Порядковый номер (индекс) должен быть 1.Период. Тип периода — Основной. Порядковый номер (индекс) — 2. Это критически важно: регистратор всегда должен следовать после периода-даты.Проанализируем ситуацию: если мы поменяем индексы местами, СКД может попытаться рассчитать остаток сначала по регистратору, а потом по времени, что приведет к математическому абсурду в итогах.
Теперь перейдем к главному решению. Чтобы поле Агент (которое является менеджером из объекта расчетов) не ломало итоги, нам нужно явно указать СКД, к какому измерению регистра оно относится. Это делается в конструкторе схемы компоновки данных на вкладке Наборы данных в колонке Роли.
Рассмотрим алгоритм действий:
Агент).ОбъектРасчетов.АналитикаУчетаПоПартнерам, то для них родительским измерением указываем АналитикаУчетаПоПартнерам.Что это дает? Указав родительское измерение, мы сообщаем СКД: «Это поле — просто дополнительный атрибут измерения ОбъектРасчетов. Не пытайся считать по нему самостоятельные остатки, используй данные того измерения, к которому оно привязано». После этого система группировки начинает работать корректно, и итоги по менеджеру перестают дублироваться.
Иногда даже после настройки родительских измерений остатки «внутри» одной секунды могут отображаться неверно, если в эту секунду было несколько движений по одним и тем же аналитикам. Для решения этой проблемы проанализируем ситуацию с полем НомерСтроки.
В запрос необходимо добавить поле НомерСтроки и назначить ему соответствующую роль в СКД. Это поле будет третьим в иерархии периодов (индекс 3). Оно служит «арбитром» для системы: если два документа имеют одинаковое время, СКД определит их последовательность по номеру строки в таблице движений, что позволит вычислить правильный входящий остаток для второго документа.
Также обратим внимание на обработку пустых значений. При использовании метода ДвиженияИГраницыПериода могут возникать записи с пустым регистратором (например, для начального остатка, если в периоде не было движений). В роли поля Регистратор рекомендуется установить флаг Игнорировать значения NULL, чтобы расчетные механизмы СКД не спотыкались о пустые ссылки при агрегации ресурсов.
Подведем итог всему вышесказанному. Чтобы получить идеальный отчет с остатками по регистратору, выполним следующие шаги:
ОстаткиИОбороты всегда выбираем измерения регистра (ключи аналитики) и отдельно — реквизиты этих измерений, если они нужны в группировках.ПериодСекунда ставим Тип: Дополнительный, Индекс: 1.Регистратор ставим Тип: Основной, Индекс: 2 и устанавливаем флаг Обязательное.Менеджер, Подразделение), в колонке Родительское измерение выбираем соответствующее измерение из набора данных.Сумма(СуммаНачальныйОстаток) и Сумма(СуммаКонечныйОстаток) должны корректно рассчитываться по всем группировкам, включая регистратор.Следуя этим правилам, мы добьемся того, что отчет будет показывать верные цифры независимо от сложности структуры группировок и количества разыменованных полей в запросе.