Часто начинающие разработчики 1С сталкиваются с ситуацией, когда при создании отчета по остаткам и ценам одна и та же номенклатура выводится несколькими строками с разными ценами, даже если выбран один конкретный тип цен (например, «Оптовая»). Это создает путаницу и делает отчет некорректным. Разберем детально причины возникновения этой проблемы и способы её решения на примере конфигурации «Управление торговлей».
Рассмотрим типичную ситуацию. В запросе соединяются две таблицы: остатки товаров на складах и актуальные цены. На первый взгляд запрос выглядит логично, но в результате мы видим картину, когда для одного товара на одном складе выводится три строки с ценами 190, 220 и 170 рублей. При этом остаток в каждой строке дублируется.
Проанализируем исходный код запроса, который часто становится причиной такой ошибки:
ВЫБРАТЬ РАЗЛИЧНЫЕ
ТоварыНаСкладахОстатки.Склад.Ссылка КАК Склад,
ТоварыНаСкладахОстатки.Номенклатура.НаименованиеПолное КАК Наименование,
ЦеныНоменклатурыСрезПоследних.Цена,
ТоварыНаСкладахОстатки.КоличествоОстаток КАК Остаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыНаСкладахОстатки
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК ЦеныНоменклатурыСрезПоследних
ПО ТоварыНаСкладахОстатки.Номенклатура.Ссылка = ЦеныНоменклатурыСрезПоследних.Номенклатура.Ссылка
ГДЕ
ЦеныНоменклатурыСрезПоследних.ТипЦен.Ссылка = &Ссылка
Главная причина, по которой возникают подобные дубли, — это наличие в системе характеризок номенклатуры (цвет, размер, модель). Если в настройках программы включен учет по характеристикам, то цена в регистре ЦеныНоменклатуры устанавливается не просто на товар, а на связку «Номенклатура + Характеристика».
В приведенном выше запросе соединение (JOIN) происходит только по полю Номенклатура. Это означает, что если для одного товара создано три разные характеристики и для каждой установлена своя цена, запрос подтянет все три записи из регистра цен к одной записи остатка. Система «не понимает», какую именно цену выбрать, и выводит все доступные варианты.
Решение: необходимо обязательно добавлять характеристику в условия соединения таблиц. Если мы выводим остатки, то и цена должна соответствовать конкретной характеристике товара на складе.
ПО ТоварыНаСкладахОстатки.Номенклатура = ЦеныНоменклатурыСрезПоследних.Номенклатура
И ТоварыНаСкладахОстатки.Характеристика = ЦеныНоменклатурыСрезПоследних.Характеристика
Посмотрим на блок ГДЕ в исходном запросе. Фильтрация по типу цен происходит после того, как таблицы были соединены. Это не только замедляет работу отчета, но и может приводить к логическим ошибкам при работе со срезом последних данных.
Проанализируем правильный подход: фильтры по дате и типу цен должны передаваться непосредственно в параметры виртуальной таблицы СрезПоследних. Это гарантирует, что система сначала выберет актуальные цены нужного типа, а уже потом начнет соединять их с остатками.
Правильный вариант обращения к таблице цен:
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ТипЦен = &ВыбранныйТипЦен) КАК ЦеныНоменклатурыСрезПоследних
В коде новичков часто встречается конструкция Склад.Ссылка или ТипЦен.Ссылка. Это важный технический момент. Поля Склад, Номенклатура, ТипЦен в регистрах уже являются ссылочными типами. Когда вы добавляете .Ссылка, 1С выполняет дополнительную операцию в базе данных (обращение к таблице справочника), чтобы получить значение, которое и так доступно в исходной таблице. Это снижает производительность и загромождает код.
Рекомендация: всегда пишите просто ТоварыНаСкладахОстатки.Склад вместо ТоварыНаСкладахОстатки.Склад.Ссылка.
Использование ВНУТРЕННЕЕ СОЕДИНЕНИЕ (INNER JOIN) приводит к тому, что если у товара есть остаток, но на него еще не установлена цена, этот товар вообще исчезнет из отчета. В управленческих отчетах это критично — менеджер не увидит товар в наличии.
Решение: используйте ЛЕВОЕ СОЕДИНЕНИЕ. В этом случае в отчет попадут все товары, имеющиеся на остатках, а в колонке «Цена» будет пустое значение (NULL), если цена не задана. Чтобы вместо пустоты выводить ноль, можно использовать функцию ЕСТЬNULL(ЦеныНоменклатурыСрезПоследних.Цена, 0).
С учетом всех разобранных ошибок, составим оптимальный и правильный запрос для получения отчета без дублей:
ВЫБРАТЬ
ТоварыНаСкладахОстатки.Склад КАК Склад,
ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
ТоварыНаСкладахОстатки.Характеристика КАК Характеристика,
ЕСТЬNULL(ЦеныНоменклатурыСрезПоследних.Цена, 0) КАК Цена,
ЦеныНоменклатурыСрезПоследних.ТипЦен КАК ТипЦены,
ТоварыНаСкладахОстатки.КоличествоОстаток КАК Остаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, ) КАК ТоварыНаСкладахОстатки
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ТипЦен = &ТипЦен) КАК ЦеныНоменклатурыСрезПоследних
ПО ТоварыНаСкладахОстатки.Номенклатура = ЦеныНоменклатурыСрезПоследних.Номенклатура
И ТоварыНаСкладахОстатки.Характеристика = ЦеныНоменклатурыСрезПоследних.Характеристика
Валюта. Если его не учесть в соединении или фильтре, дубли вернутся.СрезПоследних не поможет, и потребуется использовать механизмы программного расчета цен или СКД (Систему компоновки данных).Подведем итог. Чтобы избавиться от «лишних» цен в отчете, всегда проверяйте, по всем ли ключевым измерениям регистра сведений (Номенклатура, Характеристика, ТипЦен, Валюта) выполнено соединение и установлены ли фильтры в параметрах виртуальных таблиц. Это обеспечит точность данных и высокую скорость формирования отчета.