Как получить остатки номенклатуры на дату каждого документа в запросе 1С

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

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

Попытка решить задачу «в лоб» через обращение к виртуальной таблице РегистрНакопления.Запасы.Остатки с передачей даты документа в качестве параметра соединения не работает. Разберем, почему возникает эта ошибка и какими способами можно правильно рассчитать остатки на динамические даты документов.

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

Рассмотрим типовую ошибку, которую часто допускают при составлении запроса:


ВЫБРАТЬ 
    ЗаказПокупателяЗапасы.Ссылка КАК Заказ,
    ЗаказПокупателяЗапасы.Ссылка.Дата КАК Дата,
    ЗаказПокупателяЗапасы.Номенклатура КАК Номенклатура,
    ЗаказПокупателяЗапасы.Резерв КАК Резерв,
    ЗапасыОстатки.КоличествоОстаток КАК КоличествоОстаток
ИЗ 
    Документ.ЗаказПокупателя.Запасы КАК ЗаказПокупателяЗапасы
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Запасы.Остатки(&Дата, ) КАК ЗапасыОстатки
        ПО ЗаказПокупателяЗапасы.Номенклатура = ЗапасыОстатки.Номенклатура
ГДЕ 
    ЗаказПокупателяЗапасы.Резерв > 0
    И ЕСТЬNULL(ЗапасыОстатки.КоличествоОстаток, 0) = 0

Проанализируем ограничения платформы 1С:Предприятие:

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

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

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

Разберем по шагам настройку схемы:

  1. Создаем Набор данных — запрос 1 (список документов и строк):
    
    ВЫБРАТЬ
        ЗаказПокупателяЗапасы.Ссылка КАК Заказ,
        ЗаказПокупателяЗапасы.Ссылка.Дата КАК Дата,
        ЗаказПокупателяЗапасы.Номенклатура КАК Номенклатура,
        ЗаказПокупателяЗапасы.Резерв КАК Резерв
    ИЗ
        Документ.ЗаказПокупателя.Запасы КАК ЗаказПокупателяЗапасы
    ГДЕ
        ЗаказПокупателяЗапасы.Резерв > 0
    
  2. Создаем Набор данных — запрос 2 (остатки из регистра):
    
    ВЫБРАТЬ
        ЗапасыОстатки.Номенклатура КАК Номенклатура,
        ЗапасыОстатки.КоличествоОстаток КАК КоличествоОстаток
    ИЗ
        РегистрНакопления.Запасы.Остатки(&Период, ) КАК ЗапасыОстатки
    
  3. Переходим на вкладку Связи наборов данных:
    1. Источник связи: НаборДанных1, Приемник связи: НаборДанных2.
    2. Условие связи: Номенклатура = Номенклатура.
    3. В колонке Параметры указываем: имя параметра — Период, выражение источника — Дата.

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

Способ 2. Запрос через таблицу «ОстаткиИОбороты» и максимальный период

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

Рассмотрим корректную схему такого запроса:


ВЫБРАТЬ
    ЗаказПокупателяЗапасы.Ссылка КАК Ссылка,
    ЗаказПокупателяЗапасы.Ссылка.Дата КАК Дата,
    ЗаказПокупателяЗапасы.Номенклатура КАК Номенклатура,
    ЗаказПокупателяЗапасы.Резерв КАК Резерв
ПОМЕСТИТЬ ВтЗаказы
ИЗ
    Документ.ЗаказПокупателя.Запасы КАК ЗаказПокупателяЗапасы
ГДЕ
    ЗаказПокупателяЗапасы.Резерв > 0
;

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

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

ВЫБРАТЬ
    ВтСрезДат.Ссылка КАК Заказ,
    ВтСрезДат.Дата КАК ДатаЗаказа,
    ВтСрезДат.Номенклатура КАК Номенклатура,
    ВтСрезДат.Резерв КАК Резерв,
    ЕСТЬNULL(ВтОстаткиИОбороты.Остаток, 0) КАК ОстатокНаДату
ИЗ
    ВтСрезДат КАК ВтСрезДат
    ЛЕВОЕ СОЕДИНЕНИЕ ВтОстаткиИОбороты КАК ВтОстаткиИОбороты
        ПО ВтСрезДат.Номенклатура = ВтОстаткиИОбороты.Номенклатура
        И ВтСрезДат.ПериодСреза = ВтОстаткиИОбороты.Период
ГДЕ
    ЕСТЬNULL(ВтОстаткиИОбороты.Остаток, 0) = 0

Разберем логику работы алгоритма:

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

Способ 3. Расчет нарастающим итогом через оконные функции (для 1С 8.3.20+)

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

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


ВЫБРАТЬ
    Движения.Период КАК Период,
    Движения.Регистратор КАК Регистратор,
    Движения.Номенклатура КАК Номенклатура,
    ВЫБОР
        КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
            ТОГДА Движения.Количество
        ИНАЧЕ -Движения.Количество
    КОНЕЦ КАК КоличествоИзменение
ПОМЕСТИТЬ ВтДвижения
ИЗ
    РегистрНакопления.Запасы КАК Движения
ГДЕ
    Движения.Период МЕЖДУ &НачалоПериода И &КонецПериода
;

ВЫБРАТЬ
    ВтДвижения.Период КАК Период,
    ВтДвижения.Регистратор КАК Регистратор,
    ВтДвижения.Номенклатура КАК Номенклатура,
    СУММА(ВтДвижения.КоличествоИзменение) НАД (
        РАЗДЕЛИТЬ ПО ВтДвижения.Номенклатура 
        УПОРЯДОЧИТЬ ПО ВтДвижения.Период, ВтДвижения.Регистратор
    ) КАК ОстатокНаМомент
ПОМЕСТИТЬ ВтОстаткиПоВремени
ИЗ
    ВтДвижения КАК ВтДвижения

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

Важные практические нюансы

При контроле резервов и остатков следует учитывать прикладные особенности учетных систем (например, 1С:УНФ, 1С:ERP или 1С:КА):

  1. Учет движений самого документа: если проверяемый заказ уже проведен, он сам делает движения по расходу складского остатка или формированию резерва. При контроле остатка «до проведения» необходимо исключать движения проверяемого регистратора.
  2. Свободный остаток: физическое наличие товара на складе не всегда означает доступность для резервирования. Реальный свободный остаток рассчитывается как складской остаток за вычетом резервов, зафиксированных другими заказами.
← На главную