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