При разработке отчетов и контрольных проверок в 1С часто возникает задача: сопоставить строки табличной части документов с актуальными остатками регистра накопления именно на момент проведения каждого конкретного документа. Например, необходимо выявить заказы покупателей, в которых был установлен резерв товара, но на дату оформления заказа свободный остаток на складе отсутствовал.
Попытка решить задачу «в лоб» через обращение к виртуальной таблице РегистрНакопления.Запасы.Остатки с передачей даты документа в качестве параметра соединения не работает. Разберем, почему возникает эта ошибка и какими способами можно правильно рассчитать остатки на динамические даты документов.
Рассмотрим типовую ошибку, которую часто допускают при составлении запроса:
ВЫБРАТЬ
ЗаказПокупателяЗапасы.Ссылка КАК Заказ,
ЗаказПокупателяЗапасы.Ссылка.Дата КАК Дата,
ЗаказПокупателяЗапасы.Номенклатура КАК Номенклатура,
ЗаказПокупателяЗапасы.Резерв КАК Резерв,
ЗапасыОстатки.КоличествоОстаток КАК КоличествоОстаток
ИЗ
Документ.ЗаказПокупателя.Запасы КАК ЗаказПокупателяЗапасы
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Запасы.Остатки(&Дата, ) КАК ЗапасыОстатки
ПО ЗаказПокупателяЗапасы.Номенклатура = ЗапасыОстатки.Номенклатура
ГДЕ
ЗаказПокупателяЗапасы.Резерв > 0
И ЕСТЬNULL(ЗапасыОстатки.КоличествоОстаток, 0) = 0
Проанализируем ограничения платформы 1С:Предприятие:
&Дата). Платформа формирует срез итогов один раз на указанную точку времени для всего запроса.ЗаказПокупателяЗапасы.Ссылка.Дата внутрь параметров виртуальной таблицы невозможно — синтаксис языка запросов 1С не поддерживает коррелированные подзапросы внутри параметров виртуальных таблиц.&Дата, таблица Остатки вернет текущие актуальные остатки на момент выполнения запроса, что исказит ретроспективный анализ.Если результат требуется вывести в виде отчета или обработать программно через процессор компоновки, наиболее производительным и надежным способом является механизм связей наборов данных в СКД.
Разберем по шагам настройку схемы:
ВЫБРАТЬ
ЗаказПокупателяЗапасы.Ссылка КАК Заказ,
ЗаказПокупателяЗапасы.Ссылка.Дата КАК Дата,
ЗаказПокупателяЗапасы.Номенклатура КАК Номенклатура,
ЗаказПокупателяЗапасы.Резерв КАК Резерв
ИЗ
Документ.ЗаказПокупателя.Запасы КАК ЗаказПокупателяЗапасы
ГДЕ
ЗаказПокупателяЗапасы.Резерв > 0
ВЫБРАТЬ
ЗапасыОстатки.Номенклатура КАК Номенклатура,
ЗапасыОстатки.КоличествоОстаток КАК КоличествоОстаток
ИЗ
РегистрНакопления.Запасы.Остатки(&Период, ) КАК ЗапасыОстатки
НаборДанных1, Приемник связи: НаборДанных2.Номенклатура = Номенклатура.Период, выражение источника — Дата.Платформа 1С автоматически сгруппирует даты заказов и выполнит пакетное получение остатков по виртуальной таблице на каждую уникальную дату, обеспечив максимальное быстродействие.
Если задачу необходимо решить строго в рамках одного запроса без использования СКД, применяют расчет через виртуальную таблицу ОстаткиИОбороты с периодичностью Регистратор.
Рассмотрим корректную схему такого запроса:
ВЫБРАТЬ
ЗаказПокупателяЗапасы.Ссылка КАК Ссылка,
ЗаказПокупателяЗапасы.Ссылка.Дата КАК Дата,
ЗаказПокупателяЗапасы.Номенклатура КАК Номенклатура,
ЗаказПокупателяЗапасы.Резерв КАК Резерв
ПОМЕСТИТЬ ВтЗаказы
ИЗ
Документ.ЗаказПокупателя.Запасы КАК ЗаказПокупателяЗапасы
ГДЕ
ЗаказПокупателяЗапасы.Резерв > 0
;
ВЫБРАТЬ
ОО.Период КАК Период,
ОО.Номенклатура КАК Номенклатура,
ОО.КоличествоКонечныйОстаток КАК Остаток
ПОМЕСТИТЬ ВтОстаткиИОбороты
ИЗ
РегистрНакопления.Запасы.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, Регистратор, Движения, ) КАК ОО
;
ВЫБРАТЬ
ВтЗаказы.Ссылка КАК Ссылка,
ВтЗаказы.Дата КАК Дата,
ВтЗаказы.Номенклатура КАК Номенклатура,
ВтЗаказы.Резерв КАК Резерв,
МАКСИМУМ(ВтОстаткиИОбороты.Период) КАК ПериодСреза
ПОМЕСТИТЬ ВтСрезДат
ИЗ
ВтЗаказы КАК ВтЗаказы
ЛЕВОЕ СОЕДИНЕНИЕ ВтОстаткиИОбороты КАК ВтОстаткиИОбороты
ПО ВтЗаказы.Номенклатура = ВтОстаткиИОбороты.Номенклатура
И ВтЗаказы.Дата >= ВтОстаткиИОбороты.Период
СГРУППИРОВАТЬ ПО
ВтЗаказы.Ссылка,
ВтЗаказы.Дата,
ВтЗаказы.Номенклатура,
ВтЗаказы.Резерв
;
ВЫБРАТЬ
ВтСрезДат.Ссылка КАК Заказ,
ВтСрезДат.Дата КАК ДатаЗаказа,
ВтСрезДат.Номенклатура КАК Номенклатура,
ВтСрезДат.Резерв КАК Резерв,
ЕСТЬNULL(ВтОстаткиИОбороты.Остаток, 0) КАК ОстатокНаДату
ИЗ
ВтСрезДат КАК ВтСрезДат
ЛЕВОЕ СОЕДИНЕНИЕ ВтОстаткиИОбороты КАК ВтОстаткиИОбороты
ПО ВтСрезДат.Номенклатура = ВтОстаткиИОбороты.Номенклатура
И ВтСрезДат.ПериодСреза = ВтОстаткиИОбороты.Период
ГДЕ
ЕСТЬNULL(ВтОстаткиИОбороты.Остаток, 0) = 0
Разберем логику работы алгоритма:
ВтЗаказы с перечнем строк документов, требующих проверки.ОстаткиИОбороты с периодичностью Регистратор. Важно: обязательно передаем параметры &НачалоПериода и &КонецПериода, чтобы исключить сканирование всей таблицы движений базы.МАКСИМУМ(Период) находим дату последнего движения по каждой номенклатуре, предшествующую дате заказа.В современных версиях платформы 1С:Предприятие доступен механизм оконных функций, который позволяет рассчитывать остатки на каждую дату без тяжелых группировок и вложенных соединений.
Проанализируем пример запроса:
ВЫБРАТЬ
Движения.Период КАК Период,
Движения.Регистратор КАК Регистратор,
Движения.Номенклатура КАК Номенклатура,
ВЫБОР
КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
ТОГДА Движения.Количество
ИНАЧЕ -Движения.Количество
КОНЕЦ КАК КоличествоИзменение
ПОМЕСТИТЬ ВтДвижения
ИЗ
РегистрНакопления.Запасы КАК Движения
ГДЕ
Движения.Период МЕЖДУ &НачалоПериода И &КонецПериода
;
ВЫБРАТЬ
ВтДвижения.Период КАК Период,
ВтДвижения.Регистратор КАК Регистратор,
ВтДвижения.Номенклатура КАК Номенклатура,
СУММА(ВтДвижения.КоличествоИзменение) НАД (
РАЗДЕЛИТЬ ПО ВтДвижения.Номенклатура
УПОРЯДОЧИТЬ ПО ВтДвижения.Период, ВтДвижения.Регистратор
) КАК ОстатокНаМомент
ПОМЕСТИТЬ ВтОстаткиПоВремени
ИЗ
ВтДвижения КАК ВтДвижения
После вычисления нарастающего итога полученная таблица легко соединяется с заказами по условию максимального момента времени до даты документа.
При контроле резервов и остатков следует учитывать прикладные особенности учетных систем (например, 1С:УНФ, 1С:ERP или 1С:КА):