Почему запрос количества проданного товара в 1С:Торговля и Склад 7.7 завышает данные и как это исправить?

Программист 1С v7.7 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

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

Разбор ошибок в исходном запросе к регистру «ПартииТоваров»

Проанализируем типовую ситуацию: разработчик пытается получить количество проданного товара из остаточного регистра ПартииТоваров следующим кодом:


Процедура Сформировать()
    Перем Запрос, ТекстЗапроса, Таб;
    Запрос = СоздатьОбъект("Запрос");
    ТекстЗапроса = 
    "//{{ЗАПРОС(Сформировать)
    |Период с ВыбНачПериода по ВыбКонПериода;
    |Товар = Регистр.ПартииТоваров.Товар;
    |Фирма = Регистр.ПартииТоваров.Фирма;
    |ОстатокТовара=Регистр.ПартииТоваров.ОстатокТовара;
    |Группировка Товар все; 
    |Функция РасхОстатокТовара=Расход(ОстатокТовара);
    |Функция ПрихОстатокТовара=Приход(ОстатокТовара);
    |"//}}ЗАПРОС
    ; 
    Если ПустоеЗначение(ВыбГруппа)=0 тогда
        ТекстЗапроса = ТекстЗапроса+ " 
        |Условие (Товар в ВыбГруппа);";
    КонецЕсли;
    ...
КонецПроцедуры

Выясним главные причины, по которым данный подход приводит к ошибочным результатам:

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

Способ 1. Использование оборотного регистра «Продажи» (Рекомендуемый подход)

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

Рассмотрим, как правильно составить запрос к регистру Продажи с выводом структуры иерархии (по группам справочника):


Процедура Сформировать()
    Перем Запрос, ТекстЗапроса, Таб;
    
    Запрос = СоздатьОбъект("Запрос");
    
    ТекстЗапроса = 
    "//{{ЗАПРОС(Сформировать)
    |Период с ВыбНачПериода по ВыбКонПериода;
    |Номенклатура = Регистр.Продажи.Номенклатура;
    |Количество = Регистр.Продажи.Количество;
    |Функция ВсегоКоличество = Сумма(Количество);
    |Группировка Номенклатура;
    |"//}}ЗАПРОС
    ;
    
    Если ПустоеЗначение(ВыбГруппа) = 0 Тогда
        ТекстЗапроса = ТекстЗапроса + "
        |Условие (Номенклатура в ВыбГруппа);";
    КонецЕсли;
    
    Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
        Возврат;
    КонецЕсли;
    
    Таб = СоздатьОбъект("Таблица");
    Таб.ИсходнаяТаблица("Сформировать");
    Таб.ВывестиСекцию("Заголовок");
    
    Пока Запрос.Группировка(1) = 1 Цикл
        ПечКоличество = Строка(Запрос.ВсегоКоличество) + " шт";
        
        Если Запрос.Номенклатура.ЭтоГруппа() = 1 Тогда
            Таб.ВывестиСекцию("Группа");
        Иначе
            Таб.ВывестиСекцию("Товар");
        КонецЕсли;
    КонецЦикла;
    
    Таб.ТолькоПросмотр(1);
    Таб.Показать("Отчет по продажам", "");
КонецПроцедуры

Посмотрим на особенности этого решения:

  1. Мы используем функцию Сумма(Количество) по измерению регистра Продажи, что гарантирует точный подсчет проданного количества.
  2. Ключевое слово все в предложении Группировка Номенклатура; убрано. Теперь в результат попадут только те товары и их родительские группы, по которым реально происходили продажи за указанный период.
  3. Метод ЭтоГруппа() позволяет корректно разграничить строки печатной формы на группы и конкретные элементы номенклатуры.

Способ 2. Корректный запрос по регистру «ПартииТоваров» с отбором операций

Если решение задачи требует обязательно использовать регистр ПартииТоваров (например, когда необходимо проанализировать движение остатков конкретных партий в привязке к фирмам или складам), разберем, как отсечь лишние движения.

Чтобы исключить удвоение и лишние обороты, добавим в запрос отбор по статусу партии и коду операции:


ТекстЗапроса = 
"//{{ЗАПРОС(Сформировать)
|Период с ВыбНачПериода по ВыбКонПериода;
|Товар = Регистр.ПартииТоваров.Товар;
|Статус = Регистр.ПартииТоваров.СтатусПартии;
|ОстатокТовара = Регистр.ПартииТоваров.ОстатокТовара;
|Функция ПроданоКоличество = Расход(ОстатокТовара);
|Группировка Товар;
|Условие (Статус = Перечисление.СтатусыПартии.Купленный);
|"//}}ЗАПРОС
;

При таком подходе:

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

← На главную