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

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

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

Ограничение платформы: почему нельзя передать список дат в виртуальную таблицу остатков

Попытка передать массив, список значений или поле временной таблицы в параметр периода виртуальной таблицы РегистрНакопления.ИмяРегистра.Остатки(&Период, ...) приведет к синтаксической ошибке. Параметр периода в данной таблице принимает строго одно скалярное значение типа Дата, МоментВремени или Граница.

Выясним причину такого поведения. На уровне СУБД платформа 1С рассчитывает срез остатков, оптимизируя выборку из физической таблицы итогов регистра и компенсируя разницу записями из таблицы движений строго на одну заданную точку во времени. Механизм виртуальной таблицы не рассчитан на многократный расчет среза для разных строк в рамках одной инструкции выборки.

Тем не менее существует несколько способов получить нужные данные в зависимости от объема выборки и требований к производительности.

Способ 1. Выполнение запроса в цикле по списку дат

Если количество дат невелико (например, несколько контрольных точек) и данный участок кода не является критичным по производительности, наиболее простым и прозрачным решением будет выполнение запроса в цикле.

Посмотрим на пример реализации:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	УчетИмуществаОстатки.Номенклатура КАК Номенклатура,
    |	УчетИмуществаОстатки.КоличествоОстаток КАК Количество
    |ИЗ
    |	РегистрНакопления.УчетИмущества.Остатки(&Период, ) КАК УчетИмуществаОстатки";

Для Каждого ТекущаяДата Из МассивДат Цикл
    Запрос.УстановитьПараметр("Период", ТекущаяДата);
    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл
        // Обработка полученных остатков на конкретную дату
    КонецЦикла;
КонецЦикла;

Этот способ прост в отладке, однако при большом количестве дат создаст повышенную нагрузку на сервер из-за многократных обращений к СУБД.

Способ 2. Использование виртуальной таблицы ОстаткиИОбороты с нарастающим итогом

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

Разберем по шагам алгоритм решения:

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

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

Способ 3. Расчет по физической таблице движений

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

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

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

ВЫБРАТЬ
	ВТ_Даты.Дата КАК Дата,
	НачОстатки.Номенклатура КАК Номенклатура,
	ЕСТЬNULL(НачОстатки.КоличествоОстаток, 0) + 
	СУММА(ВЫБОР 
		КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход) ТОГДА Движения.Количество
		КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход) ТОГДА -Движения.Количество
		ИНАЧЕ 0 
	КОНЕЦ) КАК КоличествоОстаток
ИЗ
	ВТ_Даты КАК ВТ_Даты
	ЛЕВОЕ СОЕДИНЕНИЕ ВТ_НачальныеОстатки КАК НачОстатки
		ПО ИСТИНА
	ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.УчетИмущества КАК Движения
		ПО Движения.Номенклатура = НачОстатки.Номенклатура
		И Движения.Период > &МинДата
		И Движения.Период <= ВТ_Даты.Дата
СГРУППИРОВАТЬ ПО
	ВТ_Даты.Дата,
	НачОстатки.Номенклатура,
	НачОстатки.КоличествоОстаток

Важный нюанс: Обязательно отсекайте движения снизу параметром &МинДата. Если соединять таблицу движений без ограничения снизу только по условию <= ВТ_Даты.Дата, СУБД выполнит полное сканирование таблицы с самого начала ведения учета, что приведет к резкому падению производительности.

Способ 4. Использование связей наборов данных в СКД

Если задача решается при разработке отчета на Системе компоновки данных (СКД), можно воспользоваться встроенным механизмом связывания наборов данных:

  1. Создаем первый набор данных — запрос, выбирающий список дат или документов из временной таблицы.
  2. Создаем второй набор данных — запрос к виртуальной таблице РегистрНакопления.УчетИмущества.Остатки(&Период, ).
  3. На вкладке Связи наборов данных связываем первый набор со вторым, указав в качестве параметра передачу поля даты из источника в параметр Период приемника.

При таком подходе платформа 1С автоматически сформирует и рассчитает остатки для каждой строки набора данных.

← На главную