Как убрать дублирование номенклатуры в запросах 1С при соединении остатков и цен

Программист 1С v8.3 (Обычные формы) Управленческий учет Торговля и дистрибуция
← На главную

В практике разработки на платформе 1С:Предприятие 8 часто возникает ситуация, когда при получении данных об остатках товаров вместе с их ценами, одна и та же номенклатура начинает «задваиваться» или даже «затраиваться». Это создает серьезные проблемы при формировании отчетов, расчете себестоимости или выводе прайс-листов. Разберем подробно, почему это происходит и как правильно построить запрос, чтобы исключить повторение позиций.

Анализ причин возникновения дублей

Прежде чем исправлять код, давайте проанализируем типичную ситуацию. В исходном запросе выполняется ЛЕВОЕ СОЕДИНЕНИЕ трех различных источников данных: остатков товаров на складах, остатков партий и цен номенклатуры контрагентов. Дублирование строк в таких случаях чаще всего вызвано следующими факторами:

  1. Разная детализация в регистрах остатков: Если в регистре «Товары на складах» товар хранится в разрезе складов, а в регистре «Партии товаров» — в разрезе складов и документов поступления, то при соединении по номенклатуре каждая строка из первой таблицы размножится на количество партий из второй.
  2. Неуказанные измерения в регистре цен: Виртуальная таблица СрезПоследних для цен часто содержит скрытые измерения, такие как «Тип цен», «Контрагент», «Валюта» или «Единица измерения». Если связь настроена только по номенклатуре, запрос вернет все актуальные цены для всех поставщиков и всех валют одновременно.
  3. Характеристики номенклатуры: Если для товара ведется учет по характеристикам, но в условии соединения (ПО) они пропущены, платформа соединит остаток без характеристики с ценами для всех возможных характеристик этого товара.
  4. Складской учет: Если в итоговой выборке присутствует поле Склад, то номенклатура будет повторяться столько раз, на скольких складах она числится в остатке.

Шаг 1. Оптимизация получения остатков

Рассмотрим, как правильно объединить данные из двух регистров остатков. Использование ЛЕВОЕ СОЕДИНЕНИЕ между виртуальными таблицами остатков считается «тяжелой» и потенциально ошибочной операцией, если ключи (измерения) не совпадают идеально. Вместо этого проанализируем подход с использованием временных таблиц и группировки.

Вместо прямого соединения в одном пакете, сначала поместим сгруппированные остатки во временную таблицу. Важно перенести все отборы в параметры виртуальной таблицы — это значительно ускорит работу и уменьшит объем обрабатываемых данных.


ВЫБРАТЬ
    ОстаткиТоваров.Номенклатура КАК Номенклатура,
    ОстаткиТоваров.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
    ОстаткиТоваров.Склад КАК Склад,
    СУММА(ОстаткиТоваров.КоличествоОстаток) КАК Количество
ПОМЕСТИТЬ ВТ_Количество
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, {Склад.*}) КАК ОстаткиТоваров
ГРУППИРОВАТЬ ПО
    ОстаткиТоваров.Номенклатура,
    ОстаткиТоваров.ХарактеристикаНоменклатуры,
    ОстаткиТоваров.Склад;

Шаг 2. Корректное соединение с ценами

Теперь выясним, почему соединение с регистром цен вызывает дубли. Основная ошибка заключается в обращении к полю ТипЦен.Наименование. В справочнике может быть несколько типов цен с названием «Закупочная» (например, от разных поставщиков или созданных по ошибке). Правильнее использовать ссылку на конкретный элемент или отфильтровать по измерениям регистра.

Проанализируем структуру регистра ЦеныНоменклатурыКонтрагентов. Помимо номенклатуры и характеристики, там часто присутствуют измерения ТипЦен и Валюта. Если мы не укажем их в связи или в параметрах виртуальной таблицы, мы получим «декартово произведение» строк.

Рассмотрим правильный способ соединения во втором пакете запроса:


ВЫБРАТЬ
    ВТОстатки.Номенклатура,
    ВТОстатки.ХарактеристикаНоменклатуры,
    ВТОстатки.Склад,
    ВТОстатки.Количество,
    ВЫБОР
        КОГДА ВТОстатки.Сумма = 0 ТОГДА ЕСТЬNULL(Цены.Цена, 0) * ВТОстатки.Количество
        ИНАЧЕ ВТОстатки.Сумма
    КОНЕЦ КАК ИтоговаяСумма,
    ЕСТЬNULL(Цены.Цена, 0) КАК Цена
ИЗ
    ВТОстатки КАК ВТОстатки
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатурыКонтрагентов.СрезПоследних(&Период, ТипЦен = &НужныйТипЦен) КАК Цены
    ПО ВТОстатки.Номенклатура = Цены.Номенклатура
    И ВТОстатки.ХарактеристикаНоменклатуры = Цены.ХарактеристикаНоменклатуры

Шаг 3. Учет единиц измерения и характеристик

Если после выполнения предыдущих шагов номенклатура все равно повторяется, обратим внимание на единицы хранения. У одной номенклатуры может быть установлена цена за «штуку», а в остатках она числится в «коробках». Если регистр сведений хранит цены в разрезе упаковок, это измерение также необходимо добавить в условие ПО.

Проанализируем ситуацию с характеристиками. Даже если вы не используете характеристики активно, поле ХарактеристикаНоменклатуры может содержать значение ПустаяСсылка. В запросе необходимо гарантировать, что мы соединяем пустые значения с пустыми, иначе NULL может вести себя непредсказуемо при сложных соединениях.

Важный совет: Если вам не принципиально видеть склад в отчете, уберите его из списка ВЫБРАТЬ и из ГРУППИРОВАТЬ ПО. Это автоматически схлопнет строки по номенклатуре, суммируя количество со всех складов.

Итоговая стратегия решения проблемы

Чтобы раз и навсегда избавиться от дублей, придерживайтесь следующего алгоритма при написании запроса:

  1. Используйте временные таблицы: Разделите получение количественных остатков, стоимостных остатков (партий) и цен на разные этапы.
  2. Группируйте данные на каждом этапе: Перед помещением во временную таблицу всегда делайте СГРУППИРОВАТЬ ПО всем полям, которые не являются ресурсами.
  3. Параметры виртуальных таблиц — ваш приоритет: Всегда передавайте ТипЦен, Организацию или Контрагента в параметры (второй аргумент функции СрезПоследних или Остатки), а не в секцию ГДЕ.
  4. Связь по всем измерениям: Проверьте, что в секции ПО указаны все общие измерения таблиц. Если в таблице цен есть «Тип цен», он должен быть зафиксирован либо в параметрах таблицы, либо в условии связи.
  5. Проверка на NULL: При использовании ЛЕВОЕ СОЕДИНЕНИЕ всегда обрабатывайте возможные пустые значения функции ЕСТЬNULL(), особенно в арифметических операциях.

Соблюдение этих правил гарантирует, что каждая позиция номенклатуры будет выведена ровно один раз для заданного набора измерений, а данные о количестве и стоимости останутся корректными.

← На главную