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