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

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

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

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

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

В чем причина некорректного расчета остатков по регистраторам?

Основная причина кроется в математической логике группировки данных. Представим простую последовательность документов движения по регистру накопления:

Если система компоновки данных не понимает, что поля НачальныйОстаток и КонечныйОстаток являются специальными полями остатков, привязанными ко времени, она обрабатывает их как обычные числовые ресурсы (например, как обороты). В результате простого арифметического суммирования по группировке «Итого» мы получим:

Чтобы избежать этого арифметического коллапса, СКД должна четко понимать временную шкалу и роли каждого поля. Для этого используется механизм ролей компоновки данных. Если роли настроены неверно или «слетели» из-за особенностей написания запроса, расчет итогов ломается.


Причина 1: Сброс автозаполнения ролей при использовании конструкции ВЫБОР

Очень часто разработчики пытаются внедрить в запрос логические условия для выбора разных ресурсов (например, в зависимости от флага регламентированного или управленческого учета) или для обработки пустых значений. Рассмотрим типичный пример такого запроса:


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

Важный нюанс: Механизм Автозаполнение в СКД автоматически распознает роли остатков (поле компоновки данных Роль.Остатки) и роли периодов только тогда, когда эти поля выбираются напрямую из виртуальной таблицы БЕЗ каких-либо преобразований.

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

То же самое происходит и с полем Регистратор: обернув его в условие, мы лишаем его автоматической роли «Период».


Причина 2: Специфика виртуальной таблицы и значение НЕОПРЕДЕЛЕНО

Если мы формируем отчет за период, в котором не было движений по регистру, но на начало периода существовал остаток, виртуальная таблица ОстаткиИОбороты все равно вернет одну строку. В этой строке будут заполнены поля остатков, а системное поле Регистратор примет значение НЕОПРЕДЕЛЕНО.

В СКД у роли поля «Период» есть флаг «Игнорировать значение NULL». Однако этот флаг по умолчанию игнорирует именно значение NULL, но никак не реагирует на системный тип НЕОПРЕДЕЛЕНО. Из-за этого в отчете может выводиться пустая строка детальной записи, которая «двоит» остатки на уровне общих итогов.


Пошаговое руководство по исправлению проблемы

Давайте по шагам разберем, как правильно настроить отчет в СКД, чтобы остатки по регистраторам рассчитывались корректно.

Шаг 1. Преобразование НЕОПРЕДЕЛЕНО в NULL для Регистратора в запросе

Чтобы СКД могла отработать игнорирование пустых значений регистратора при выводе остатков без движений, нам необходимо явно привести значение НЕОПРЕДЕЛЕНО к значению NULL еще на уровне запроса. Перепишем выборку регистратора следующим образом:


ВЫБОР
    КОГДА УчетВзаиморасчетовОстаткиИОбороты.Регистратор = НЕОПРЕДЕЛЕНО
        ТОГДА NULL
    ИНАЧЕ УчетВзаиморасчетовОстаткиИОбороты.Регистратор
КОНЕЦ КАК Регистратор

Этот прием обязателен при построении отчетов по остаткам с детализацией до регистратора.

Шаг 2. Настройка иерархии периодов на вкладке «Наборы данных»

Для корректного расчета остатков по временной оси СКД требует строгой настройки иерархии периодов. Нам необходимо вручную назначить роли для полей периодов. Перейдем на вкладку «Наборы данных», найдем наши поля и укажем для них роли:

  1. Полю Регистратор задаем роль:
    • Тип роли: Период
    • Порядковый номер: 1 (это самый точный, младший период)
    • Флаг: Игнорировать значение NULL (обязательно!)
  2. Полю ПериодСекунда задаем роль:
    • Тип роли: Период
    • Порядковый номер: 2 (более старший период)

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

Шаг 3. Ручная настройка ролей полей остатков

Так как мы использовали конструкцию ВЫБОР для определения выводимой суммы (в зависимости от параметра &РеглУчет), нам необходимо вручную вернуть роли остатков для полей компоновки.

На вкладке «Наборы данных» для поля СуммаНачальныйОстаток настроим роль Остатки:

Для поля СуммаКонечныйОстаток настроим аналогичную роль Остатки:

После этого СКД свяжет эти два поля в единую пару «Начальный-Конечный остаток» и будет рассчитывать их по правильным алгоритмам временных интервалов, а не простым суммированием.

Шаг 4. Альтернативный (более надежный) способ обхода условия &РеглУчет

Чтобы избежать ручной настройки сложных ролей остатков (которые легко забыть настроить при изменении запроса), мы можем применить другой архитектурный подход. Мы не будем использовать конструкцию ВЫБОР в самом тексте запроса. Вместо этого выберем все поля напрямую из виртуальной таблицы:


ВЫБРАТЬ
    РасчетыОстаткиИОбороты.Организация КАК Организация,
    РасчетыОстаткиИОбороты.ФизическоеЛицо КАК ФизическоеЛицо,
    ВЫБОР
        КОГДА РасчетыОстаткиИОбороты.Регистратор = НЕОПРЕДЕЛЕНО
            ТОГДА NULL
        ИНАЧЕ РасчетыОстаткиИОбороты.Регистратор
    КОНЕЦ КАК Регистратор,
    РасчетыОстаткиИОбороты.СуммаНачальныйОстаток КАК СуммаНачальныйОстаток,
    РасчетыОстаткиИОбороты.СуммаКонечныйОстаток КАК СуммаКонечныйОстаток,
    РасчетыОстаткиИОбороты.СуммаРеглНачальныйОстаток КАК СуммаРеглНачальныйОстаток,
    РасчетыОстаткиИОбороты.СуммаРеглКонечныйОстаток КАК СуммаРеглКонечныйОстаток,
    РасчетыОстаткиИОбороты.ПериодСекунда КАК ПериодСекунда
ИЗ
    РегистрНакопления.РасчетыСПодотчетниками.ОстаткиИОбороты(, , Авто, , ) КАК РасчетыОстаткиИОбороты

При таком написании запроса СКД автоматически назначит правильные роли для всех полей остатков. А выбор нужной суммы (управленческой или регламентированной) мы перенесем на вкладку «Ресурсы» СКД.

Для ресурса СуммаНачальныйОстаток пропишем формулу расчета:


Выбор 
    Когда &РеглУчет 
        Тогда Сумма(СуммаРеглНачальныйОстаток) 
    Иначе Сумма(СуммаНачальныйОстаток) 
Конец

Для ресурса СуммаКонечныйОстаток пропишем аналогичную формулу:


Выбор 
    Когда &РеглУчет 
        Тогда Сумма(СуммаРеглКонечныйОстаток) 
    Иначе Сумма(СуммаКонечныйОстаток) 
Конец

Этот способ является более предпочтительным, так как минимизирует ручную работу в настройках схемы компоновки данных и снижает вероятность возникновения ошибок при модификации отчета в будущем.

Таким образом, соблюдение этих простых правил — преобразование НЕОПРЕДЕЛЕНО в NULL, правильное назначение ролей периодов с нумерацией и аккуратная работа с полями остатков — гарантирует получение точных и корректных результатов в любых отчетах СКД с детализацией до регистратора.

← На главную