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

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

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

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

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

Почему функция ВычислитьВыражение выдает пустое значение?

В первом сообщении темы автор пытается рассчитать начальный остаток с помощью функции СКД ВычислитьВыражение:

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

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

Чтобы решить эту задачу, нам необходимо явно передать в систему информацию об остатках на момент старта периода отчета. Рассмотрим рабочие варианты реализации.

Способ 1. Использование механизма ОБЪЕДИНИТЬ ВСЕ в одном запросе

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

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

Проанализируем пример такого запроса в конструкторе схемы компоновки данных:


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

ОБЪЕДИНИТЬ ВСЕ

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

После объединения результатов СКД получит полную картину: на начало периода у нас будет строка с начальным остатком, а далее пойдут детальные записи оборотов с точными датами. Чтобы конечный остаток рассчитывался правильно, на вкладке Ресурсы мы задаем следующие формулы:

Способ 2. Настройка ролей полей компоновки данных

Для того чтобы система компоновки данных автоматически понимала природу наших остатков и правильно рассчитывала итоги по группировкам (не дублируя начальный остаток при сложении строк), необходимо правильно настроить Роли полей на вкладке «Набор данных».

Выполним настройку по шагам:

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

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

Способ 3. Использование двух независимых наборов данных и связи между ними

В сложных архитектурных решениях (как упоминалось в сообщении 27) объединение запросов в один может значительно снизить общую производительность, особенно если в запросе используются тяжелые левые соединения с таблицами цен или аналитики. В этом случае правильным подходом будет разделение логики на два набора данных-запроса:

Мы связываем эти наборы данных на вкладке Связи наборов данных:

  1. Источником связи выступает основной набор с оборотами, приемником — набор с остатками.
  2. Связь устанавливается по ключевым полям: Номенклатура = Номенклатура, Характеристика = Характеристика.
  3. В параметрах связи мы передаем дату начала периода в качестве условия для получения остатка.

Этот метод позволяет СУБД построить оптимальный план выполнения запросов, минимизируя нагрузку на сервер при расчете цен на каждую конкретную дату документа.

Оптимизация производительности: проблема «Среза последних»

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

Чтобы избежать этого, рекомендуется использовать временные таблицы (ВТ):

  1. Сначала выберите все уникальные ключи записей (Номенклатура, Склад, Характеристика, ДатаДокумента) во временную таблицу ВТ_Движения.
  2. Для этой таблицы получите актуальные цены на каждую дату с помощью среза последних, либо выбрав максимальные даты цен, которые меньше или равны дате документа, и поместите результат во вторую таблицу ВТ_Цены.
  3. И только на финальном шаге выполните соединение таблиц оборотов с таблицей ВТ_Цены по ключам аналитики и даты.

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

← На главную