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

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

При работе с регистрами накопления часто возникает задача — получить в одном отчете итоговые остатки на начало и конец периода, и одновременно с этим видеть обороты в разрезе каждого документа-регистратора. Стандартный подход с использованием виртуальной таблицы ОстаткиИОбороты и фильтром по регистратору часто приводит к неожиданным результатам: начальные и конечные остатки выглядят нелогичными, "космическими", и не сходятся с реальными данными. Разберемся, почему так происходит и как построить корректный запрос.

Анализ проблемы: почему остатки рассчитываются неправильно?

Корень проблемы лежит в непонимании того, как работает виртуальная таблица РегистрНакопления.ИмяРегистра.ОстаткиИОбороты при установке параметра Периодичность в значение "Регистратор".

Когда мы устанавливаем такую периодичность, платформа рассчитывает данные для каждой записи, соответствующей одному документу-регистратору. В этом контексте поля остатков означают следующее:

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

Главная ошибка, которую допустил автор темы, — это попытка отфильтровать саму виртуальную таблицу по виду документа или по конкретному регистратору. Когда вы добавляете условие вида ГДЕ Регистратор = &НужныйДокумент в параметры виртуальной таблицы, вы приказываете системе:

  1. Найти все движения, сделанные этим документом.
  2. А затем рассчитать начальный остаток, основываясь только на движениях, которые были до этого документа по тем же измерениям, что и в этом документе.

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

Правильный подход к построению запроса

Чтобы получить корректные данные, задачу нужно разделить на две части: получение итоговых остатков за весь период и получение детализированных оборотов по документам. Затем эти данные нужно соединить.

Шаг 1. Получаем корректные начальные и конечные остатки

Для этого нужно обратиться к той же виртуальной таблице ОстаткиИОбороты, но без указания периодичности или с периодичностью, охватывающей весь период (например, "Период"). В параметрах таблицы не должно быть никаких фильтров по регистраторам.

Посмотрим на пример. Этот подзапрос вернет нам правильные итоги по каждой номенклатуре и складу за указанный период.


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

Шаг 2. Получаем обороты в разрезе регистраторов

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


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

Шаг 3. Объединяем результаты в одном запросе

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

Этот запрос дает нам полную картину: для каждой номенклатуры мы видим итоговые остатки за период, а внутри — детализацию движений по каждому документу.


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

СГРУППИРОВАТЬ ПО
    Движения.Номенклатура,
    Движения.Склад,
    Движения.Регистратор

УПОРЯДОЧИТЬ ПО
    Движения.Номенклатура,
    Движения.Регистратор.МоментВремени

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

Выводы и рекомендации

  1. Не фильтруйте виртуальную таблицу ОстаткиИОбороты по полю Регистратор в ее параметрах, если ваша цель — получить итоговые остатки за период. Это исказит базу для расчета остатков.
  2. Разделяйте задачи: используйте один запрос (или подзапрос) для получения итоговых остатков без детализации по регистратору, и второй — для получения оборотов с детализацией.
  3. Правильно интерпретируйте поля: помните, что при периодичности "Регистратор" поля НачальныйОстаток и КонечныйОстаток являются промежуточными и не подлежат суммированию для получения общего итога.
  4. Используйте МоментВремени для сортировки: это обеспечит правильный хронологический порядок документов в отчете.

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

← На главную