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

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

При разработке отчетов на платформе 1С программисты часто сталкиваются с задачей: необходимо вывести остатки и обороты по регистру накопления, но при этом наложить фильтр не по измерению, а по реквизиту документа, который сделал движение. Например, нам нужно увидеть движение товаров только по тем документам, где указан определенный "Проект" или "Менеджер", при этом данные поля не вынесены в измерения регистра.

Стандартная виртуальная таблица ОстаткиИОбороты в этом случае ведет себя коварно. Если мы просто добавим условие в секцию ГДЕ, платформа сначала рассчитает общие остатки по всем документам, а затем просто отфильтрует строки. В результате мы получим математическую ошибку: начальный остаток будет общим, оборот — отфильтрованным, а конечный остаток перестанет сходиться. Рассмотрим, как решить эту проблему правильно.

Почему стандартная виртуальная таблица не подходит?

Проанализируем ситуацию: виртуальная таблица 1С — это сложный SQL-запрос, который обращается к таблицам итогов. В таблицах итогов информация хранится только в разрезе измерений. Реквизитов регистратора там нет. Когда мы обращаемся к РегистрНакопления.ИмяРегистра.ОстаткиИОбороты и накладываем отбор Регистратор.Реквизит = &Значение, система вынуждена соединять таблицу итогов с таблицей движений.

Важный момент: начальный остаток на дату начала периода будет взят "как есть" из итогов. Если до этой даты были движения по другим менеджерам/проектам, они всё равно попадут в этот остаток. Обороты же внутри периода будут отсечены вашим фильтром. В итоге формула НачальныйОстаток + Приход - Расход = КонечныйОстаток перестанет работать. Чтобы получить "честный" результат, нам придется имитировать поведение системы, будто этот реквизит является полноценным измерением.

Способ 1. Алгоритм нарастающего итога в запросе

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

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

ВЫБРАТЬ
    Т1.Номенклатура,
    Т1.Период,
    Т1.Регистратор,
    Т1.КоличествоОборот,
    СУММА(Т2.КоличествоОборот) КАК НарастающийИтог
ИЗ
    ВТ_Обороты КАК Т1
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Обороты КАК Т2
    ПО Т1.Номенклатура = Т2.Номенклатура
    И Т1.МоментВремени >= Т2.МоментВремени
СГРУППИРОВАТЬ ПО
    Т1.Номенклатура,
    Т1.Период,
    Т1.Регистратор

В данном примере мы для каждой записи о движении (Т1) находим все предыдущие записи (Т2) и суммируем их. Это позволяет получить "виртуальный" остаток, который меняется с каждым документом.

  1. Финальное объединение: К полученным оборотам с нарастающим итогом мы прибавляем (математически) тот начальный остаток, который получили на шаге 1.

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

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

Рассмотрим подробнее настройку Ролей:

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

  1. Набор 1: Выбирает только обороты с фильтром по реквизиту регистратора.
  2. Набор 2: Выбирает остатки по измерениям (например, по Номенклатуре).

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

Способ 3. Метод предварительной фильтрации ключей аналитики

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

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

Разберем по шагам этот метод:

  1. Создаем временную таблицу ВТ_НужныеТовары: Выбираем РАЗЛИЧНЫЕ измерения (например, Номенклатура) из физической таблицы регистра, накладывая условие на Регистратор.ВашРеквизит.
  2. Запрос к ВТ ОстаткиИОбороты: Теперь мы обращаемся к стандартной виртуальной таблице, но в параметрах виртуальной таблицы (не в ГДЕ!) передаем условие: Номенклатура В (ВЫБРАТЬ Номенклатура ИЗ ВТ_НужныеТовары).

Этот способ позволяет использовать всю мощь индексов итогов 1С. Мы не ломаем математику регистра, а просто ограничиваем список аналитики теми позициями, которые нас интересуют в контексте выбранного реквизита. Конечный остаток в этом случае будет верным для каждой позиции номенклатуры (он будет учитывать все движения, а не только отфильтрованные), но сам список номенклатуры будет ограничен.

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

Проанализировав все подходы, можно вывести несколько правил эффективной работы:

  1. Избегайте отборов по Регистратору в ВТ Остатки: Помните, что в таблице итогов нет регистратора. Любой такой отбор заставляет систему сканировать движения, что медленно.
  2. Определитесь с бизнес-логикой: Что именно должен увидеть пользователь? Если он хочет видеть, как конкретный менеджер повлиял на склад — используйте нарастающий итог (Способ 1). Если он хочет видеть остатки товаров, по которым работал менеджер — используйте фильтрацию по аналитике (Способ 3).
  3. Производительность: Соединение таблицы самой с собой (Т1.МоментВремени >= Т2.МоментВремени) имеет квадратичную сложность. На регистре с миллионами записей такой запрос "положит" сервер. В таких случаях добавление нового измерения в регистр (несмотря на запрет в условии задачи) является единственным профессиональным решением.

Таким образом, расчет остатков по полям регистратора требует глубокого понимания того, как платформа 1С хранит данные. Используйте предложенные методы в зависимости от объема ваших данных и требуемой точности результата.

← На главную