При разработке отчетов на базе Системы Компоновки Данных (СКД) программисты часто сталкиваются с ситуацией, когда итоговые значения остатков «разваливаются», как только в структуру отчета добавляется детальная группировка по регистратору. Типичный симптом: сумма начальных остатков по строкам документов оказывается значительно выше, чем итог по группировке склада или номенклатуры. Рассмотрим подробнее, почему это происходит и какими способами можно добиться корректных данных.
Для начала проанализируем ситуацию. Когда мы используем виртуальную таблицу ОстаткиИОбороты, СКД пытается автоматически вычислить остатки на каждую точку времени (каждый регистратор). Если настройки системы выполнены неверно, СКД воспринимает поле НачальныйОстаток не как расчетный показатель на момент времени, а как обычное числовое поле, которое нужно просуммировать. В результате начальный остаток, который был актуален для десяти документов в течение дня, суммируется десять раз вместо того, чтобы отобразиться один раз.
Выясним причину такого поведения. В 90% случаев проблема кроется в неправильной настройке Ролей полей на закладке «Наборы данных». Чтобы СКД понимала иерархию времени, необходимо соблюдать следующие правила:
ПериодСекунда и поле Регистратор, для них должны быть заданы порядковые номера в роли «Период». Например, для ПериодСекунда устанавливается номер 1, а для Регистратор — номер 2. Это указывает системе, что регистратор является уточнением внутри секунды.Посмотрим на настройки на закладке «Ресурсы». Это второй критический узел, где возникают ошибки. Часто разработчики просто добавляют поля остатков с выражением Сумма(НачальныйОстаток). Для корректной работы этого может быть недостаточно.
Проанализируем поле «Рассчитывать по...» в ресурсах. Если мы хотим, чтобы остатки считались правильно в разрезе регистратора, но не суммировались «в лоб» в вышестоящих группировках, в этой колонке должны быть перечислены все измерения (Склад, Номенклатура) и все поля периодов. Если оставить это поле пустым, СКД будет применять агрегатную функцию ко всем уровням иерархии одинаково, что и приводит к завышенным цифрам. При необходимости можно использовать методику вывода ресурса СКД в выбранной колонке.
Рассмотрим еще один важный технический нюанс — параметр периодичности виртуальной таблицы ОстаткиИОбороты. Если в параметрах запроса указана периодичность Месяц, а в СКД мы пытаемся выводить данные в разрезе Регистратор, расчет может быть непредсказуемым.
Для детальных отчетов с регистратором рекомендуется устанавливать периодичность в значение Авто или Запись. Это заставляет виртуальную таблицу возвращать данные с максимальной детализацией, которую СКД потом сможет корректно «схлопнуть» до нужных группировок пользователя.
Если стандартная настройка ролей не помогает (что иногда случается в сложных отчетах с объединениями нескольких регистров), применим более надежный, хоть и более трудоемкий метод. Разберем по шагам, как подготовить данные вручную, чтобы СКД не пришлось «гадать» при расчете итогов.
Суть метода заключается в разделении получения данных на три части: остатки на начало периода, обороты за период и остатки на конец. Рассмотрим пример построения такого запроса:
// Часть 1: Начальные остатки
ВЫБРАТЬ
ТМЦ.Номенклатура,
ТМЦ.Склад,
ТМЦ.КоличествоОстаток КАК НачальныйОстаток,
0 КАК Приход,
0 КАК Расход,
0 КАК КонечныйОстаток,
ДАТАВРЕМЯ(1, 1, 1) КАК Период,
НЕОПРЕДЕЛЕНО КАК Регистратор
ИЗ
РегистрНакопления.ТМЦВЭксплуатации.Остатки(&НачалоПериода, ) КАК ТМЦ
ОБЪЕДИНИТЬ ВСЕ
// Часть 2: Обороты
ВЫБРАТЬ
ТМЦ.Номенклатура,
ТМЦ.Склад,
0,
ТМЦ.КоличествоПриход,
ТМЦ.КоличествоРасход,
0,
ТМЦ.Период,
ТМЦ.Регистратор
ИЗ
РегистрНакопления.ТМЦВЭксплуатации.Обороты(&НачалоПериода, &КонецПериода, Регистратор, ) КАК ТМЦ
ОБЪЕДИНИТЬ ВСЕ
// Часть 3: Конечные остатки
ВЫБРАТЬ
ТМЦ.Номенклатура,
ТМЦ.Склад,
0,
0,
0,
ТМЦ.КоличествоОстаток,
ДАТАВРЕМЯ(3999, 12, 31) КАК Период,
НЕОПРЕДЕЛЕНО КАК Регистратор
ИЗ
РегистрНакопления.ТМЦВЭксплуатации.Остатки(&КонецПериода, ) КАК ТМЦ
При таком подходе мы фактически отключаем встроенный механизм расчета остатков СКД по периодам. Чтобы отчет вывел верные данные, нам нужно правильно настроить ресурсы. В данном случае выражение для ресурсов остатков будет выглядеть иначе:
Для поля НачальныйОстаток выражение: ВычислитьВыражение("Сумма(НачальныйОстаток)", "ОбщийИтог", , "Первая", "Первая").
Для поля КонечныйОстаток выражение: ВычислитьВыражение("Сумма(КонечныйОстаток)", "ОбщийИтог", , "Последняя", "Последняя").
Это гарантирует, что система возьмет только те данные, которые мы явно пометили как начальные (на минимальную дату) или конечные (на максимальную дату), и не будет суммировать их по строкам регистраторов.
Проанализируем ситуацию, когда в одну секунду происходит несколько движений по одному и тому же регистратору или разными документами. Для СКД порядок этих документов может стать неочевидным. Чтобы избежать путаницы в остатках «внутри» секунды, крайне важно включать поле НомерСтроки в набор данных и назначать ему роль периода с самым младшим приоритетом (например, номер 3 после Секунды и Регистратора).
Важный момент: Если вы используете регистр накопления, который имеет специфические измерения (например, «Физлицо» или «Инвентарный номер»), убедитесь, что все они выбраны в запросе. Если измерение есть в регистре, но отсутствует в наборе данных СКД, виртуальная таблица ОстаткиИОбороты может некорректно «схлопывать» итоги на границах периодов, что также приведет к искажению сумм при разворачивании до регистратора.
Подводя итог, можно сказать: если типовые настройки ролей периодов в СКД не дают правильного результата при группировке по регистратору, наиболее надежным способом будет либо переход к ручному формированию остатков через объединение запросов, либо использование функций ВычислитьВыражение для жесткого контроля агрегации данных.