Почему в «Отчете о розничных продажах» не списываются партии при возврате товара и как это исправить?

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

В практике работы с конфигурациями на обычных формах, такими как Управление торговлей 10.3, Комплексная автоматизация 1.1 или УПП 1.3, часто возникает неприятная ситуация: бухгалтер видит, что складские остатки верны, а ведомость по партиям «плывет». Проблема обнаруживается в документе ОтчетОРозничныхПродажах (ООРП). Если в течение смены были оформлены возвраты товаров, проданных в предыдущие дни, эти позиции попадают в отчет с отрицательным количеством. При проведении документа по регистру ТоварыНаСкладах движение (приход) формируется корректно, а вот регистр ПартииТоваровНаСкладах игнорирует эти строки. В результате — расхождение между двумя ключевыми регистрами.

Разберем подробно, почему так происходит, и какими способами можно восстановить порядок в учете.

Анализ причины: методология против практики

Проанализируем логику, заложенную разработчиками 1С. Согласно методологии, документ ОтчетОРозничныхПродажах предназначен для отражения операций, произошедших в рамках одной кассовой смены. Это означает следующее:

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

Однако современное кассовое ПО (например, Frontol) и требования 54-ФЗ позволяют кассиру делать «Чек на возврат» в любое время. При загрузке таких данных в 1С система формирует отрицательные строки в ООРП. Типовой механизм партионного учета просто не «обучен» искать партию списания за прошлые периоды внутри розничного отчета. Он ожидает только расход (продажу).

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


Для Каждого СтрокаПартииРаспределения ИЗ СтрокаДереваПартий.Строки Цикл
    Если КоличествоОсталосьПогасить < 0 Тогда
        Если ТипЗнч(СтруктураПараметров.Регистратор) = Тип("ДокументСсылка.ОтчетОРозничныхПродажах") Тогда
            // Система видит минус и просто прекращает обработку этой строки для партий
            Прервать; 
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

Программа видит, что количество отрицательное, и понимает, что это не «продажа», которую нужно списать по методу FIFO/LIFO, а нечто иное, что в рамках данного документа не обрабатывается.

Решение 1: Изменение бизнес-процесса и настроек обмена

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

При включении этой настройки:

Документ возврата умеет работать с партиями: в нем можно указать документ партии вручную или настроить автоматический поиск. Это полностью снимет проблему расхождений в регистрах без написания кода.

Решение 2: Программная доработка для автоматического подбора партий

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

Посмотрим на пример реализации в общем модуле УправлениеЗапасамиПартионныйУчет. Нам нужно модифицировать процедуру ВыполнитьСписание, добавив обработку типа документа ООРП аналогично документу возврата.


// Вставляем проверку в процедуре ВыполнитьСписание
ИначеЕсли СтруктураПараметров.ТипЗначенияРегистратора = Тип("ДокументСсылка.ВозвратТоваровОтПокупателя")
    ИЛИ СтруктураПараметров.ТипЗначенияРегистратора = Тип("ДокументСсылка.ОтчетОРозничныхПродажах") Тогда
    
    // Определяем, нужно ли нам получать остатки (для возврата - нет, мы увеличиваем партию)
    ТребуетсяПолучитьОстатки = НЕ (ТаблицаСписания[0].КодОперацииПартииТоваров = СтруктураПараметров.КодыОпераций.ВозвратОтПокупателя);
КонецЕсли;

Далее необходимо реализовать алгоритм поиска партии. Рассмотрим логику: если в строке документа Количество < 0, нам нужно найти последний Расход по регистру ПартииТоваровНаСкладах для этой номенклатуры и склада.


// Пример запроса для поиска партии продажи
ТекстЗапроса = "ВЫБРАТЬ ПЕРВЫЕ 1
               |    ПартииТоваровНаСкладах.Регистратор КАК Регистратор
               |ИЗ
               |    РегистрНакопления.ПартииТоваровНаСкладах КАК ПартииТоваровНаСкладах
               |ГДЕ
               |    ПартииТоваровНаСкладах.Номенклатура = &Номенклатура
               |    И ПартииТоваровНаСкладах.Склад = &Склад
               |    И ПартииТоваровНаСкладах.ВидДвижения = &ВидДвижения
               |    И ПартииТоваровНаСкладах.Период < &ДатаДокумента
               |УПОРЯДОЧИТЬ ПО
               |    ПартииТоваровНаСкладах.Период УБЫВ";

Запрос = Новый Запрос(ТекстЗапроса);
Запрос.УстановитьПараметр("Номенклатура", СтрокаДокумента.Номенклатура);
Запрос.УстановитьПараметр("Склад", СтрокаДокумента.Склад);
Запрос.УстановитьПараметр("ВидДвижения", ВидДвиженияНакопления.Расход);
Запрос.УстановитьПараметр("ДатаДокумента", СтруктураПараметров.Регистратор.Дата);

Выборка = Запрос.Выполнить.Выбрать();
Если Выборка.Следующий() Тогда
    СтрокаДокумента.ДокументПартии = Выборка.Регистратор;
КонецЕсли;

Важный нюанс: Такой подход вернет товар на последнюю списанную партию. В большинстве случаев для управленческого учета этого достаточно, чтобы закрыть количественный разрыв. Однако, если вы ведете строгий партионный учет по ФИФО в разрезе серий или ГТД, такая «угадайка» может привести к искажению себестоимости. Проанализируем риски: если товар был куплен по одной цене, а возвращается на партию с другой ценой, возникнет курсовая или суммовая разница.

Решение 3: Исправление группировки в УПП (для продвинутых пользователей)

Иногда проблема кроется не в самом списании партий, а в том, как документ подготавливает таблицу движений перед финальной записью. В конфигурациях УПП 1.3 или КА 1.1 стоит обратить внимание на процедуру ДвиженияПоРегиструСписанныеТовары.

Проанализируем ситуацию: если в документе есть несколько строк по одной номенклатуре (одна с плюсом, другая с минусом), система может некорректно их свернуть, потеряв аналитику партий. Проверьте, включена ли колонка СуммаЗадолженности (или аналогичные суммовые показатели) в список суммируемых колонок при сворачивании таблицы движений. Если её там нет, движения могут «схлопнуться» неправильно, и партионный учет не увидит необходимости восстанавливать партию по возврату.


// Пример корректировки списка колонок для свертки
СписокСуммируемыхКолонок = "Количество, ПроводкиСуммаСНДСРегл, ПроводкиСуммаНДСРегл, СуммаЗадолженности";
// ... код подготовки колонок ...
ТаблицаДвижений.Свернуть(СписокФиксируемыхКолонок, СписокСуммируемыхКолонок);

Подведем итоги

Проблема расхождения партий в Отчете о розничных продажах — это следствие конфликта между жесткой методологией 1С (одна смена — один отчет) и реалиями современной розницы. Для решения задачи мы рекомендуем:

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

Помните, что любые изменения в общем модуле УправлениеЗапасамиПартионныйУчет влияют на проведение практически всех складских документов, поэтому после внесения правок необходимо провести тщательное тестирование на копии базы.

← На главную