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