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