Как правильно рассчитать общие итоги в запросе 1С при выводе остатков и детальных оборотов

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

При разработке отчетов по регистрам бухгалтерии или накопления разработчики часто сталкиваются с ситуацией, когда добавление новой группировки (например, корреспондирующего счета или регистратора) приводит к искажению сумм в секции ИТОГИ ПО ОБЩИЕ. При этом суммы оборотов рассчитываются верно, а начальные и конечные остатки многократно завышаются. Разберем детально, почему это происходит и как правильно организовать структуру запроса.

Причина некорректного расчета итогов

Проанализируем механику работы языка запросов 1С. Когда в одном запросе выполняется ЛЕВОЕ СОЕДИНЕНИЕ таблицы остатков (свернутой до уровня субконто) с таблицей оборотов (детализированной по регистраторам или корр. счетам), для одной строки остатка находится сразу несколько строк движений.

В плоской результирующей таблице строка с начальным остатком дублируется столько раз, сколько движений по данному субконто присутствует в выборке. Конструкция ИТОГИ СУММА(СуммаНачальныйОстаток) ПО ОБЩИЕ честно суммирует значения поля по всем строкам выборки, включая все дубли. В языке запросов 1С нет возможности написать агрегатную функцию с исключением дублей (аналог DISTINCT внутри СУММА()) или отключить суммирование конкретного поля в секции ОБЩИЕ.

Решение 1. Архитектура выборки через ОБЪЕДИНИТЬ ВСЕ

Рассмотрим основной способ решения задачи в классическом запросе без использования соединений. Чтобы избежать размножения строк с остатками, разделим получение остатков и оборотов на две независимые ветки и объединим их с помощью конструкции ОБЪЕДИНИТЬ ВСЕ.

Разберем по шагам логику построения такого запроса:

  1. Ветка остатков: обращаемся к виртуальной таблице остатков или остатков и оборотов без детализации по регистраторам. Выбираем организацию, субконто, начальный и конечный остатки. Поля регистратора и корреспондирующего счета заполняем пустыми значениями (NULL или пустые ссылки), а поля оборотов — нулями.
  2. Ветка оборотов: обращаемся к виртуальной таблице оборотов с нужной периодичностью (Регистратор) и разрезом корр. счетов. Выбираем обороты Дт и Кт, а поля начального и конечного остатков заполняем нулями.
  3. Расчет итогов: применяем секцию ИТОГИ ко всему объединенному результату.

Посмотрим на пример реализации запроса:


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

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Обороты.Фирма,
    Обороты.Субконто1,
    Обороты.КорСчет,
    Обороты.Регистратор,
    0,
    Обороты.СуммаОборотДт,
    Обороты.СуммаОборотКт,
    0
ИЗ
    РегистрБухгалтерии.Основной.Обороты(&ДатаНач, &ДатаКон, Регистратор, Счет В (&СписокСчетов), , Фирма В (&Фирма), КорСчет В (&СписокКорСчетов)) КАК Обороты

УПОРЯДОЧИТЬ ПО
    Фирма,
    Субконто1,
    Регистратор
ИТОГИ
    СУММА(СуммаНачальныйОстаток),
    СУММА(Приход),
    СУММА(Расход),
    СУММА(СуммаКонечныйОстаток)
ПО
    ОБЩИЕ,
    Фирма,
    Субконто1,
    КорСчет,
    Регистратор

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

Решение 2. Использование Системы компоновки данных (СКД)

Если отчет строится на базе СКД, платформа берет расчет остатков на себя. Для этого достаточно настроить роли полей и правила расчета ресурсов:

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

Механизм СКД автоматически предотвращает мультипликацию начальных и конечных остатков при детализации строк оборотами.

Решение 3. Расчет нарастающего остатка в коде при выводе

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


ВыборкаСубконто = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаСубконто.Следующий() Цикл
    ТекущийОстаток = ВыборкаСубконто.СуммаНачальныйОстаток;
    
    ВыборкаДетали = ВыборкаСубконто.Выбрать();
    Пока ВыборкаДетали.Следующий() Цикл
        ТекущийОстаток = ТекущийОстаток + ВыборкаДетали.Приход - ВыборкаДетали.Расход;
        // Вывод строки с текущим рассчитанным остатком
    КонецЦикла;
КонецЦикла;

Рекомендация по условиям в виртуальных таблицах

Обратим внимание на условия внутри параметров виртуальных таблиц. Конструкции вида ВЫБОР КОГДА &БезФирм = 0 ТОГДА ИСТИНА ИНАЧЕ Фирма В (&Фирма) КОНЕЦ снижают производительность запроса, так как СУБД не всегда может эффективно применить индексы таблиц итогов.

Рекомендуется передавать фильтры в текст запроса динамически на этапе формирования текста либо при отсутствии необходимости отбора передавать в параметр полный массив всех доступных организаций.

← На главную