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

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

В практике разработки на платформе 1С:Предприятие 8 часто возникают ситуации, когда стандартных механизмов виртуальных таблиц остатков недостаточно. Типичный пример: необходимо построить отчет по бюджету или движению денежных средств, используя оборотный регистр накопления (например, ДвиженияДенСредств или Затраты), при этом отчет должен выглядеть как полноценная ведомость с начальным сальдо, оборотами за период и конечным сальдо. Ситуация осложняется, если данные нужно фильтровать по реквизитам документов-регистраторов, которых нет в измерениях регистра.

Рассмотрим подробнее, какими способами можно решить эту задачу в Системе компоновки данных (СКД), проанализируем архитектурные подходы и разберем конкретные программные приемы.

Метод 1. Использование функций СКД для расчета нарастающего итога

Самый современный и эффективный способ получения остатков по оборотному регистру в СКД — это использование встроенной функции ВычислитьВыражение в поле ресурсов. Этот метод позволяет не перегружать запрос сложными соединениями и вычислять остатки «на лету» при формировании результата.

Для реализации этого метода выполним следующие шаги:

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

ВычислитьВыражение("Сумма(СуммаОборот)", , , "Первая", "Текущая")

Разберем, как это работает: функция суммирует ресурс СуммаОборот, начиная с самой первой записи («Первая») и заканчивая текущей строкой группировки («Текущая»). Таким образом, для каждой строки периода мы получаем накопленный итог, который и является конечным остатком.

Для начального остатка выражение будет выглядеть так:

ВычислитьВыражение("Сумма(СуммаОборот)", , , "Первая", "Предыдущая")

Здесь суммирование идет до записи, предшествующей текущей, что в точности соответствует логике начального сальдо.

Метод 2. Использование ролей полей (Начальный и Конечный остаток)

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

Проанализируем алгоритм настройки:

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

Метод 3. Расчет остатков через соединение таблиц в запросе

Иногда требуется получить остаток непосредственно в тексте запроса (например, для последующих сложных расчетов в ВТ). В этом случае мы можем применить классический метод «треугольного соединения». Суть его заключается в том, что для каждой записи оборотов мы присоединяем все предыдущие записи и суммируем их.

Посмотрим на пример кода запроса:


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

Важный нюанс: Обратите внимание, что во второй таблице (Т2) мы не ограничиваем начало периода. Это необходимо, чтобы собрать обороты от «начала времен» до текущей точки, формируя правильный остаток. Однако будьте осторожны: на больших объемах данных такой запрос может работать медленно.

Проблема фильтрации по реквизитам регистратора

Как отмечал автор темы, часто нужно учитывать не все движения регистра, а только сделанные определенным документом (например, ОтчетОРозничныхПродажах) с конкретными реквизитами. Виртуальные таблицы .Обороты позволяют накладывать условия только на измерения.

В такой ситуации мы рекомендуем:

  1. Использовать физическую таблицу регистра накопления вместо виртуальной.
  2. В секции ГДЕ накладывать условия на Регистратор.Ссылка и его реквизиты.
  3. Обязательно использовать индексированные поля для ускорения выборки.

Рассмотрим пример условия в запросе:


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

Оптимизация: когда данных слишком много

Если расчет «от начала времен» занимает слишком много времени, проанализируем возможность создания вспомогательного регистра сведений для хранения «контрольных точек». Например, раз в месяц фоновое задание может рассчитывать остатки по нужным нам правилам и записывать их в непериодический регистр. В СКД мы будем объединять (ОБЪЕДИНИТЬ ВСЕ) актуальный остаток из этого регистра с текущими оборотами из оборотного регистра. Это кардинально ускорит отчет.

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

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

← На главную