Как правильно добавить колонку Цена в отчет 1С и избежать «размножения» количества

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

При разработке отчетов в системе 1С:Предприятие одной из самых частых задач является объединение данных из разных документов или табличных частей. Например, когда нам нужно вывести список товаров из документа ПеремещениеТоваров и дополнить его ценами из документа УсловияПоставокПоДоговорамКонтрагентов. Однако на этом пути часто встречается ловушка: итоговое количество товара в отчете начинает магическим образом увеличиваться, становясь в несколько раз больше реального. В этой статье мы разберем, почему так происходит и как построить запрос правильно.

Разберем причину проблемы: Декартово произведение

Проанализируем ситуацию, описанную в теме. Автор столкнулся с тем, что вместо суммы 72 (32 + 40) отчет выдает 288. Почему это происходит? Выясним причину: когда мы выполняем ВНУТРЕННЕЕ СОЕДИНЕНИЕ табличной части товаров с таблицей цен только по полю Номенклатура, система ищет совпадения. Если в документе условий поставок одна и та же номенклатура встречается несколько раз (например, для разных упаковок, разных характеристик или просто по ошибке), то каждая строка из «Перемещения» соединится с каждой строкой из «Условий».

Посмотрим на пример: если в перемещении была 1 строка с количеством 40, а в условиях поставок для этой номенклатуры нашлось 4 строки с разными ценами, то после соединения мы получим 4 строки, в каждой из которых будет количество 40. При суммировании мы получим 160 вместо 40. Это и называется «размножением» строк или неявным созданием декартова произведения.

Решение №1: Обязательное использование характеристик

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

Рассмотрим правильное условие связи в конструкторе запроса:


ПО ПеремещениеТоваровТовары.Номенклатура = УсловияПоставокПоДоговорамКонтрагентовНоменклатураДоговора.Номенклатура
    И ПеремещениеТоваровТовары.ХарактеристикаНоменклатуры = УсловияПоставокПоДоговорамКонтрагентовНоменклатураДоговора.ХарактеристикаНоменклатуры

Важный момент: если поле ХарактеристикаНоменклатуры может быть пустым, то простое сравнение может не сработать (так как NULL не всегда равен пустому значению в разных СУБД). В таких случаях рекомендуется использовать функцию ЕСТЬNULL или проверку на пустую ссылку.

Решение №2: Использование временных таблиц (Лучшая практика)

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

  1. Сгруппируем данные перемещений: Сначала выберем все нужные нам товары из перемещений, просуммируем количество и сгруппируем их по номенклатуре и характеристике. Полученный результат сохраним во временную таблицу.
  2. Подготовим таблицу цен: Если в документе условий поставок возможны дубли, их также нужно предварительно сгруппировать (например, выбрав МАКСИМУМ(Цена) или СРЕДНЕЕ(Цена)) во второй временной таблице.
  3. Финальное соединение: В итоговом запросе мы соединяем наши сгруппированные «чистые» данные.

Посмотрим на пример кода такого запроса:


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

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Данные.Номенклатура,
    ВТ_Данные.ХарактеристикаНоменклатуры,
    ВТ_Данные.Количество,
    ЕСТЬNULL(УсловияЦен.Цена, 0) КАК Цена
ИЗ
    ВТ_ДанныеПеремещения КАК ВТ_Данные
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.УсловияПоставокПоДоговорамКонтрагентов.НоменклатураДоговора КАК УсловияЦен
        ПО ВТ_Данные.Номенклатура = УсловияЦен.Номенклатура
            И ВТ_Данные.ХарактеристикаНоменклатуры = УсловияЦен.ХарактеристикаНоменклатуры
ГДЕ
    УсловияЦен.Ссылка = &СсылкаУсловия

Решение №3: Тип соединения и производительность

Проанализируем еще два важных технических аспекта, упомянутых в обсуждении:

1. Левое соединение вместо внутреннего: Автор исходного сообщения использовал ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Это ошибка для отчета, так как если для какого-то товара цена в условиях не заведена, этот товар просто исчезнет из итогового отчета. Использование ЛЕВОЕ СОЕДИНЕНИЕ позволит увидеть все перемещения, а в колонке цены будет пусто или ноль, что гораздо информативнее.

2. Оператор В ИЕРАРХИИ: В запросе использовалось условие В ИЕРАРХИИ(&Ссылка). Выясним причину, почему это может тормозить отчет: этот оператор заставляет систему проверять все уровни вложенности. Если вы передаете в параметр просто список ссылок на документы, используйте обычный оператор В (&СписокСсылок). Это значительно ускорит выполнение запроса на больших объемах данных.

Почему лучше использовать регистры?

Хотя автор темы настаивал на получении цены именно из документа условий, с точки зрения архитектуры 1С это не совсем правильно. Рассмотрим, почему:

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

← На главную