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

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

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

Анализ исходной проблемы: почему запрос к документам — это ошибка

Рассмотрим ситуацию, представленную в вопросе. Автор пытается объединить данные из двух документов через ОБЪЕДИНИТЬ ВСЕ и затем соединить их со срезом последних цен. Проанализируем основные риски такого подхода:

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

Правильный подход: использование регистра «Выручка и себестоимость продаж»

В современных конфигурациях (УТ 11, ERP 2, КА 2) для этих целей предназначен регистр накопления ВыручкаИСебестоимостьПродаж. Он уже содержит агрегированные данные по продажам и возвратам (возвраты в нем отражаются со знаком минус или сторно), а также учитывает все корректировки.

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

Шаг 1: Подготовка данных о продажах

Вместо объединения документов, мы обращаемся к оборотам регистра. Проанализируем пример кода:


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

Использование временной таблицы с индексацией по полям связи (Номенклатура, Характеристика) — это залог скорости. Обратите внимание, что мы используем виртуальную таблицу Обороты, которая работает значительно быстрее, чем чтение физической таблицы регистра.

Шаг 2: Получение уникального списка товаров для цен

Чтобы не перегружать СУБД при получении цен, мы выберем только те позиции, которые реально продавались в выбранном периоде. Это оптимизирует работу виртуальной таблицы СрезПоследних.


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ВТ_Продажи.Номенклатура КАК Номенклатура,
    ВТ_Продажи.Характеристика КАК Характеристика
ПОМЕСТИТЬ ВТ_СписокТоваров
ИЗ
    ВТ_Продажи КАК ВТ_Продажи
;

Шаг 3: Соединение с закупочными ценами

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


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

Тонкости работы со срезом последних

Проанализируем ситуацию с характеристиками. Если в вашей базе учет по характеристикам включен выборочно, может возникнуть проблема: цена установлена на Номенклатуру (характеристика пустая), а в продаже указана конкретная характеристика. В этом случае обычное соединение по обоим полям вернет NULL.

Для решения этой проблемы можно использовать конструкцию ВЫБОР или повторное соединение. Рассмотрим пример приоритетного получения цены:

  1. Сначала пытаемся найти цену с учетом характеристики.
  2. Если цена не найдена, берем цену, установленную на номенклатуру в целом (где характеристика — пустая ссылка).

Оптимизация агрегатных функций

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

ВЫБОР КОГДА СУММА(Количество) = 0 ТОГДА 0 ИНАЧЕ СУММА(Сумма) / СУММА(Количество) КОНЕЦ КАК СредняяЦенаПродажи

Это обеспечит корректность данных даже при наличии множества строк в одном документе или при сворачивании данных за большой период.

Резюме

Для создания надежного и быстрого отчета по продажам следуйте этим правилам:

  1. Забудьте про документы как источник данных для отчетов по товародвижению. Используйте регистры накопления.
  2. Используйте временные таблицы для промежуточных данных. Это делает запрос читаемым и позволяет СУБД построить оптимальный план выполнения.
  3. Индексируйте временные таблицы по полям, по которым планируется соединение (Номенклатура, Склад, Контрагент).
  4. Всегда указывайте дату в параметрах виртуальных таблиц СрезПоследних. Пустая дата означает получение цен на «сейчас», что редко подходит для исторических отчетов.
  5. Не суммируйте несуммируемое. Цены, ставки налогов и коэффициенты единиц измерения нельзя складывать — их нужно использовать как множители или вычислять среднее значение.

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

← На главную