При разработке отчетов в системе компоновки данных (СКД) разработчики часто сталкиваются с задачей вывода начальных и конечных остатков. Если базой для отчета служит классический регистр накопления с видом Остатки, задача решается тривиально — через использование виртуальной таблицы ОстаткиИОбороты. Однако в реальной практике, особенно в таких крупных конфигурациях, как 1С:Комплексная автоматизация или 1С:ERP, многие подсистемы (например, продажи или закупки) построены на регистрах накопления вида Обороты.
В таких регистрах физически отсутствуют таблицы остатков. Термин «начальный остаток» для оборотного регистра означает лишь одно: сумму всех оборотов от начала ведения учета в базе данных до даты начала выбранного периода отчета. Если мы просто наложим на запрос ограничение по периоду (например, «Этот месяц»), СКД физически не сможет рассчитать входящее сальдо, так как данные за предыдущие периоды не попадут в выборку запроса.
В этой статье мы подробно разберем, как обойти это ограничение, и рассмотрим три профессиональных метода расчета начальных и конечных остатков для оборотных регистров в СКД.
В первом сообщении темы автор пытается рассчитать начальный остаток с помощью функции СКД ВычислитьВыражение:
ВычислитьВыражение("Сумма(СуммаПриход)-Сумма(СуммаРасход)", , , "Первая", "Предыдущая")
Этот подход работает корректно только внутри выбранного интервала дат. Проблема заключается в том, что функция ВычислитьВыражение работает исключительно с теми строками данных, которые уже получены из СУБД и находятся в результирующей выборке запроса. Если отчет строится за текущий месяц, СУБД возвращает записи только за этот месяц. Данные за прошлый год или прошлый месяц в СКД не передаются, поэтому расчет «от начала времен» выдает пустое значение на первую секунду отчета.
Чтобы решить эту задачу, нам необходимо явно передать в систему информацию об остатках на момент старта периода отчета. Рассмотрим рабочие варианты реализации.
Это самый популярный и производительный способ решения задачи в рамках одного набора данных. Мы разбиваем наш запрос на два логических блока:
&НачалоПериода. Полученное значение мы записываем в поле НачальныйОстаток, а поля прихода и расхода зануляем. В качестве периода мы устанавливаем пустую дату или саму дату начала периода.&НачалоПериода и &КонецПериода). Здесь поле НачальныйОстаток заполняется нулем, а реальные движения распределяются по полям прихода и расхода.Проанализируем пример такого запроса в конструкторе схемы компоновки данных:
ВЫБРАТЬ
Продажи.АналитикаУчетаНоменклатуры.Номенклатура КАК Номенклатура,
Продажи.АналитикаУчетаНоменклатуры.Характеристика КАК Характеристика,
&НачалоПериода КАК Период,
Продажи.КоличествоОборот КАК НачальныйОстатокКоличество,
Продажи.СуммаВыручкиОборот КАК НачальныйОстатокСумма,
0 КАК КоличествоПриход,
0 КАК КоличествоРасход,
0 КАК СуммаПриход,
0 КАК СуммаРасход
ИЗ
РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(, &НачалоПериода, , ) КАК Продажи
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Продажи.АналитикаУчетаНоменклатуры.Номенклатура,
Продажи.АналитикаУчетаНоменклатуры.Характеристика,
Продажи.ПериодСекунда,
0,
0,
Продажи.КоличествоОборот,
0,
Продажи.СуммаВыручкиОборот,
0
ИЗ
РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&НачалоПериода, &КонецПериода, Секунда, ) КАК Продажи
После объединения результатов СКД получит полную картину: на начало периода у нас будет строка с начальным остатком, а далее пойдут детальные записи оборотов с точными датами. Чтобы конечный остаток рассчитывался правильно, на вкладке Ресурсы мы задаем следующие формулы:
Сумма(НачальныйОстатокСумма)Сумма(НачальныйОстатокСумма) + Сумма(СуммаПриход) - Сумма(СуммаРасход)Для того чтобы система компоновки данных автоматически понимала природу наших остатков и правильно рассчитывала итоги по группировкам (не дублируя начальный остаток при сложении строк), необходимо правильно настроить Роли полей на вкладке «Набор данных».
Выполним настройку по шагам:
НачальныйОстатокСумма. Нажмем на поле Роль.СтоимостьТовара.КонечныйОстатокСумма.КонечныйОстатокСумма проделаем аналогичные действия, но укажем вид остатка Конечный и ту же самую группу СтоимостьТовара.Период укажем роль Период с типом Дополнительный (или порядковым номером периода).Настройка ролей гарантирует, что при группировке по иерархии номенклатуры или складам остатки будут рассчитываться корректно математически, без простого суммирования начальных остатков по подчиненным строкам.
В сложных архитектурных решениях (как упоминалось в сообщении 27) объединение запросов в один может значительно снизить общую производительность, особенно если в запросе используются тяжелые левые соединения с таблицами цен или аналитики. В этом случае правильным подходом будет разделение логики на два набора данных-запроса:
&НачалоПериода и &КонецПериода).&НачалоПериода.Мы связываем эти наборы данных на вкладке Связи наборов данных:
Номенклатура = Номенклатура, Характеристика = Характеристика.Этот метод позволяет СУБД построить оптимальный план выполнения запросов, минимизируя нагрузку на сервер при расчете цен на каждую конкретную дату документа.
Как упоминается в обсуждении на форуме, автор отчета делает левое соединение с регистром сведений ЦеныНоменклатуры на каждую секунду документа. При больших объемах данных такое соединение гарантированно приведет к падению производительности сервера 1С.
Чтобы избежать этого, рекомендуется использовать временные таблицы (ВТ):
ВТ_Движения.ВТ_Цены.ВТ_Цены по ключам аналитики и даты.Применение этих методик позволит вам разрабатывать быстрые, надежные и масштабируемые отчёты в СКД даже для нетипичных оборотных регистров.