При разработке отчетов по регистрам бухгалтерии или накопления разработчики часто сталкиваются с ситуацией, когда добавление новой группировки (например, корреспондирующего счета или регистратора) приводит к искажению сумм в секции ИТОГИ ПО ОБЩИЕ. При этом суммы оборотов рассчитываются верно, а начальные и конечные остатки многократно завышаются. Разберем детально, почему это происходит и как правильно организовать структуру запроса.
Проанализируем механику работы языка запросов 1С. Когда в одном запросе выполняется ЛЕВОЕ СОЕДИНЕНИЕ таблицы остатков (свернутой до уровня субконто) с таблицей оборотов (детализированной по регистраторам или корр. счетам), для одной строки остатка находится сразу несколько строк движений.
В плоской результирующей таблице строка с начальным остатком дублируется столько раз, сколько движений по данному субконто присутствует в выборке. Конструкция ИТОГИ СУММА(СуммаНачальныйОстаток) ПО ОБЩИЕ честно суммирует значения поля по всем строкам выборки, включая все дубли. В языке запросов 1С нет возможности написать агрегатную функцию с исключением дублей (аналог DISTINCT внутри СУММА()) или отключить суммирование конкретного поля в секции ОБЩИЕ.
Рассмотрим основной способ решения задачи в классическом запросе без использования соединений. Чтобы избежать размножения строк с остатками, разделим получение остатков и оборотов на две независимые ветки и объединим их с помощью конструкции ОБЪЕДИНИТЬ ВСЕ.
Разберем по шагам логику построения такого запроса:
NULL или пустые ссылки), а поля оборотов — нулями.Регистратор) и разрезом корр. счетов. Выбираем обороты Дт и Кт, а поля начального и конечного остатков заполняем нулями.ИТОГИ ко всему объединенному результату.Посмотрим на пример реализации запроса:
ВЫБРАТЬ
Остатки.Фирма КАК Фирма,
Остатки.Субконто1 КАК Субконто1,
ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ПустаяСсылка) КАК КорСчет,
НЕОПРЕДЕЛЕНО КАК Регистратор,
Остатки.СуммаНачальныйОстаток КАК СуммаНачальныйОстаток,
0 КАК Приход,
0 КАК Расход,
Остатки.СуммаКонечныйОстаток КАК СуммаКонечныйОстаток
ИЗ
РегистрБухгалтерии.Основной.ОстаткиИОбороты(&ДатаНач, &ДатаКон, , , Счет В (&СписокСчетов), , Фирма В (&Фирма)) КАК Остатки
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Обороты.Фирма,
Обороты.Субконто1,
Обороты.КорСчет,
Обороты.Регистратор,
0,
Обороты.СуммаОборотДт,
Обороты.СуммаОборотКт,
0
ИЗ
РегистрБухгалтерии.Основной.Обороты(&ДатаНач, &ДатаКон, Регистратор, Счет В (&СписокСчетов), , Фирма В (&Фирма), КорСчет В (&СписокКорСчетов)) КАК Обороты
УПОРЯДОЧИТЬ ПО
Фирма,
Субконто1,
Регистратор
ИТОГИ
СУММА(СуммаНачальныйОстаток),
СУММА(Приход),
СУММА(Расход),
СУММА(СуммаКонечныйОстаток)
ПО
ОБЩИЕ,
Фирма,
Субконто1,
КорСчет,
Регистратор
При таком подходе каждая сумма остатка попадает в результат строго один раз для каждой комбинации организации и субконто, благодаря чему общий итог рассчитывается корректно.
Если отчет строится на базе СКД, платформа берет расчет остатков на себя. Для этого достаточно настроить роли полей и правила расчета ресурсов:
Сумма), а также выставим тип остатка — Начальный остаток и Конечный остаток.Механизм СКД автоматически предотвращает мультипликацию начальных и конечных остатков при детализации строк оборотами.
Если отчет формируется программно в табличный документ без использования СКД, начальный остаток можно получать только на уровне группировки субконто, а остатки по строкам регистраторов рассчитывать непосредственно в цикле обхода выборки:
ВыборкаСубконто = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаСубконто.Следующий() Цикл
ТекущийОстаток = ВыборкаСубконто.СуммаНачальныйОстаток;
ВыборкаДетали = ВыборкаСубконто.Выбрать();
Пока ВыборкаДетали.Следующий() Цикл
ТекущийОстаток = ТекущийОстаток + ВыборкаДетали.Приход - ВыборкаДетали.Расход;
// Вывод строки с текущим рассчитанным остатком
КонецЦикла;
КонецЦикла;
Обратим внимание на условия внутри параметров виртуальных таблиц. Конструкции вида ВЫБОР КОГДА &БезФирм = 0 ТОГДА ИСТИНА ИНАЧЕ Фирма В (&Фирма) КОНЕЦ снижают производительность запроса, так как СУБД не всегда может эффективно применить индексы таблиц итогов.
Рекомендуется передавать фильтры в текст запроса динамически на этапе формирования текста либо при отсутствии необходимости отбора передавать в параметр полный массив всех доступных организаций.