При разработке отчетов и механизмов проверки доступности товаров под заказы и будущую аренду разработчики часто сталкиваются с ситуацией, когда запрос возвращает не движение резервов за указанный интервал, а накопительный остаток. В результате программа показывает резервы, которые были сформированы задолго до выбранного периода.
Рассмотрим подробно, почему возникает эта проблема, в чем заключается разница между ресурсами остатка и оборота, а также как правильно составить запрос к виртуальным таблицам платформы 1С.
Главная концептуальная ошибка заключается в путанице между накопительным остатком и движениями (оборотами) за период. Регистр накопления остатков хранит данные в виде входящего остатка, движений (приход/расход) и конечного остатка.
Поле КоличествоКонечныйОстаток виртуальной таблицы ОстаткиИОбороты возвращает итоговое количество ресурса, накопленное с начала ведения учета до верхней границы указанного периода. Если резерв был оформлен 20-го числа, а отчет формируется за период с 27-го по 28-е число, поле КоличествоКонечныйОстаток на 28-е число все равно будет включать в себя эти 140 штук, так как документ резервирования не был отменен.
Разберем основные задачи, которые решаются в зависимости от выбираемого ресурса:
КоличествоПриход.Остатки.Вторая распространенная ошибка — установка условий по измерениям в секции ГДЕ основного запроса вместо передачи их в параметры виртуальной таблицы.
Когда условие накладывается в секции ГДЕ, платформа 1С сначала полностью рассчитывает виртуальную таблицу по всей базе данных (выбирая данные по всем номенклатурным позициям и складам), и только потом отсекает лишние строки. Это приводит к существенному падению производительности запроса.
Кроме того, при использовании ЛЕВОЕ СОЕДИНЕНИЕ фильтрация левой таблицы в секции ГДЕ по полям правой таблицы превращает левое соединение во внутреннее, что может приводить к неверным результатам.
Проанализируем некорректный фрагмент кода:
ИЗ
РегистрНакопления.ЗапасыНаСкладах.Остатки(, Организация = &Организация) КАК ЗапасыНаСкладахОстатки
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Запасы.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, , , Организация = &Организация) КАК ЗапасыОстатки
ПО ЗапасыНаСкладахОстатки.СтруктурнаяЕдиница = ЗапасыОстатки.СтруктурнаяЕдиница
И ЗапасыНаСкладахОстатки.Номенклатура = ЗапасыОстатки.Номенклатура
ГДЕ
ЗапасыНаСкладахОстатки.Номенклатура = &Номенклатура
Чтобы запрос работал оптимально, условие Номенклатура = &Номенклатура должно передаваться внутрь параметров каждой виртуальной таблицы.
Если задача состоит в том, чтобы проверить движения резервов за период (были ли установлены или сняты резервы в интервале), перепишем запрос с использованием ресурса КоличествоПриход и передачей фильтров в параметры виртуальных таблиц:
ВЫБРАТЬ
ЗапасыНаСкладахОстатки.СтруктурнаяЕдиница КАК СтруктурнаяЕдиница,
ЗапасыНаСкладахОстатки.Номенклатура КАК Номенклатура,
ЕСТЬNULL(ЗапасыНаСкладахОстатки.КоличествоОстаток, 0) КАК ОстатокНаСкладе,
ЕСТЬNULL(ЗапасыОстатки.КоличествоПриход, 0) КАК ЗарезервированоЗаПериод,
ЕСТЬNULL(ЗапасыСпецРезерв.КоличествоПриход, 0) КАК ЗабронированоЗаПериод
ИЗ
РегистрНакопления.ЗапасыНаСкладах.Остатки(
&КонецПериода,
Организация = &Организация
И Номенклатура = &Номенклатура
) КАК ЗапасыНаСкладахОстатки
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Запасы.ОстаткиИОбороты(
&НачалоПериода,
&КонецПериода,
Авто,
,
Организация = &Организация
И Номенклатура = &Номенклатура
И СтруктурнаяЕдиница ССЫЛКА Справочник.СтруктурныеЕдиницы
И ЗаказПокупателя <> ЗНАЧЕНИЕ(Документ.ЗаказПокупателя.ПустаяСсылка)
) КАК ЗапасыОстатки
ПО ЗапасыНаСкладахОстатки.СтруктурнаяЕдиница = ЗапасыОстатки.СтруктурнаяЕдиница
И ЗапасыНаСкладахОстатки.Номенклатура = ЗапасыОстатки.Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Бронь.ОстаткиИОбороты(
&НачалоПериода,
&КонецПериода,
Авто,
,
Организация = &Организация
И Номенклатура = &Номенклатура
И СтруктурнаяЕдиница ССЫЛКА Справочник.СтруктурныеЕдиницы
И ЗаказПокупателя <> ЗНАЧЕНИЕ(Документ.ЗаказПокупателя.ПустаяСсылка)
) КАК ЗапасыСпецРезерв
ПО ЗапасыНаСкладахОстатки.СтруктурнаяЕдиница = ЗапасыСпецРезерв.СтруктурнаяЕдиница
И ЗапасыНаСкладахОстатки.Номенклатура = ЗапасыСпецРезерв.Номенклатура
Важный архитектурный нюанс: если виртуальные таблицы Запасы и Бронь хранят детализацию по регистраторам или реквизиту ЗаказПокупателя, но при этом соединяются только по Номенклатура и СтруктурнаяЕдиница, вы можете получить размножение (задвоение) строк и неверные суммы.
Чтобы избежать этой проблемы при проектировании сложных отчетов, рекомендуется использовать временные таблицы с предвартельной группировкой:
ВТ_ОстаткиСкладов.ВТ_Резервы и ВТ_Бронь, сгрупировав их по полям Номенклатура и СтруктурнаяЕдиница с помощью агрегатора СУММА().Соблюдение этих правил гарантирует высокую скорость выполнения запроса 1С и точность получаемых данных по свободным остаткам и резервам.