Как получить остатки и обороты по регистру накопления с детализацией до регистратора и реквизита?

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

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

В чем заключается проблема задвоения данных

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


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

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

  1. Множественность записей одного документа: Документ-регистратор обычно формирует в регистре не одну, а несколько строк движений. При соединении по условию ПартииОстаткиИОбороты.Регистратор = Партии.Регистратор каждая строка виртуальной таблицы соединяется со всеми строками этого же документа из таблицы движений. Возникает эффект размножения записей (декартово произведение), и числовые поля многократно увеличиваются.
  2. Природа реквизитов регистра накопления: Реквизиты хранятся только в таблице движений и полностью отсутствуют в таблицах итогов. Рассчитать остатки в разрезе реквизита встроенными средствами СУБД невозможно — итоги формируются исключительно в разрезе измерений. Если требуется полноценный учет остатков по контрагенту, поле Контрагент должно быть измерением регистра.
  3. Служебные строки границ периода: При периодичности Регистратор виртуальная таблица формирует начальные и конечные строки периода, в которых поле Регистратор равно NULL. При соединении такие строки не находят соответствия в таблице движений.
  4. Суммирование остатков в итогах: Использование агрегатной функции СУММА(СуммаНачальныйОстаток) по группировкам с регистратором складывает моментальные остатки на разные точки времени, что дает некорректную величину.

Способ 1. Устранение дублирования через вложенный запрос с РАЗЛИЧНЫЕ

Если в рамках одного документа-регистратора значение реквизита всегда одинаково (или нас интересует уникальный список контрагентов по документу), можно соединить виртуальную таблицу с подзапросом, содержащим ключевое слово РАЗЛИЧНЫЕ:


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

Важное замечание: Обратим внимание на перенос фильтра ПриказМСП = &ПриказМСП из секции ГДЕ в параметры виртуальной таблицы. Фильтрация в параметрах позволяет СУБД отсечь лишние записи на этапе чтения итогов, что существенно ускоряет выполнение запроса.

Способ 2. Точное соединение по регистратору и номеру строки

Если один документ может содержать разные значения реквизита в разных строках, использование РАЗЛИЧНЫЕ приведет к дублированию строк в случае наличия двух контрагентов в одном документе. Чтобы однозначно связать запись регистра с таблицей остатков и оборотов, установим периодичность Запись или Авто и выполним соединение по двум полям — Регистратор и НомерСтроки:


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

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

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

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

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

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

← На главную