В практике работы с конфигурациями на обычных формах, такими как Управление торговлей 10.3, Комплексная автоматизация 1.1 или УПП 1.3, часто возникает неприятная ситуация: бухгалтер видит, что складские остатки верны, а ведомость по партиям «плывет». Проблема обнаруживается в документе ОтчетОРозничныхПродажах (ООРП). Если в течение смены были оформлены возвраты товаров, проданных в предыдущие дни, эти позиции попадают в отчет с отрицательным количеством. При проведении документа по регистру ТоварыНаСкладах движение (приход) формируется корректно, а вот регистр ПартииТоваровНаСкладах игнорирует эти строки. В результате — расхождение между двумя ключевыми регистрами.
Разберем подробно, почему так происходит, и какими способами можно восстановить порядок в учете.
Проанализируем логику, заложенную разработчиками 1С. Согласно методологии, документ ОтчетОРозничныхПродажах предназначен для отражения операций, произошедших в рамках одной кассовой смены. Это означает следующее:
ВозвратТоваровОтПокупателя.Однако современное кассовое ПО (например, Frontol) и требования 54-ФЗ позволяют кассиру делать «Чек на возврат» в любое время. При загрузке таких данных в 1С система формирует отрицательные строки в ООРП. Типовой механизм партионного учета просто не «обучен» искать партию списания за прошлые периоды внутри розничного отчета. Он ожидает только расход (продажу).
Выясним причину на уровне программного кода. Если мы заглянем в общий модуль УправлениеЗапасамиПартионныйУчет, то увидим, что при формировании движений по партиям система делает выборку данных из документа. Проблема кроется в процедуре СписаниеПартий. Рассмотрим фрагмент кода, на который часто натыкаются разработчики при отладке:
Для Каждого СтрокаПартииРаспределения ИЗ СтрокаДереваПартий.Строки Цикл
Если КоличествоОсталосьПогасить < 0 Тогда
Если ТипЗнч(СтруктураПараметров.Регистратор) = Тип("ДокументСсылка.ОтчетОРозничныхПродажах") Тогда
// Система видит минус и просто прекращает обработку этой строки для партий
Прервать;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Программа видит, что количество отрицательное, и понимает, что это не «продажа», которую нужно списать по методу FIFO/LIFO, а нечто иное, что в рамках данного документа не обрабатывается.
Прежде чем вносить изменения в конфигурацию, рассмотрим самый безопасный «штатный» способ. Если вы используете внешнюю кассовую программу, проверьте настройки обработки загрузки данных. Во многих типовых обменах существует опция «Разделять продажи и возвраты».
При включении этой настройки:
ОтчетОРозничныхПродажах.ВозвратТоваровОтПокупателя.Документ возврата умеет работать с партиями: в нем можно указать документ партии вручную или настроить автоматический поиск. Это полностью снимет проблему расхождений в регистрах без написания кода.
Если изменить бизнес-процесс невозможно и возвраты обязаны оставаться в ООРП, придется доработать механизм проведения. Наша задача — заставить систему при наличии минуса в строке ООРП найти документ продажи этого товара и вернуть его на ту же партию.
Посмотрим на пример реализации в общем модуле УправлениеЗапасамиПартионныйУчет. Нам нужно модифицировать процедуру ВыполнитьСписание, добавив обработку типа документа ООРП аналогично документу возврата.
// Вставляем проверку в процедуре ВыполнитьСписание
ИначеЕсли СтруктураПараметров.ТипЗначенияРегистратора = Тип("ДокументСсылка.ВозвратТоваровОтПокупателя")
ИЛИ СтруктураПараметров.ТипЗначенияРегистратора = Тип("ДокументСсылка.ОтчетОРозничныхПродажах") Тогда
// Определяем, нужно ли нам получать остатки (для возврата - нет, мы увеличиваем партию)
ТребуетсяПолучитьОстатки = НЕ (ТаблицаСписания[0].КодОперацииПартииТоваров = СтруктураПараметров.КодыОпераций.ВозвратОтПокупателя);
КонецЕсли;
Далее необходимо реализовать алгоритм поиска партии. Рассмотрим логику: если в строке документа Количество < 0, нам нужно найти последний Расход по регистру ПартииТоваровНаСкладах для этой номенклатуры и склада.
// Пример запроса для поиска партии продажи
ТекстЗапроса = "ВЫБРАТЬ ПЕРВЫЕ 1
| ПартииТоваровНаСкладах.Регистратор КАК Регистратор
|ИЗ
| РегистрНакопления.ПартииТоваровНаСкладах КАК ПартииТоваровНаСкладах
|ГДЕ
| ПартииТоваровНаСкладах.Номенклатура = &Номенклатура
| И ПартииТоваровНаСкладах.Склад = &Склад
| И ПартииТоваровНаСкладах.ВидДвижения = &ВидДвижения
| И ПартииТоваровНаСкладах.Период < &ДатаДокумента
|УПОРЯДОЧИТЬ ПО
| ПартииТоваровНаСкладах.Период УБЫВ";
Запрос = Новый Запрос(ТекстЗапроса);
Запрос.УстановитьПараметр("Номенклатура", СтрокаДокумента.Номенклатура);
Запрос.УстановитьПараметр("Склад", СтрокаДокумента.Склад);
Запрос.УстановитьПараметр("ВидДвижения", ВидДвиженияНакопления.Расход);
Запрос.УстановитьПараметр("ДатаДокумента", СтруктураПараметров.Регистратор.Дата);
Выборка = Запрос.Выполнить.Выбрать();
Если Выборка.Следующий() Тогда
СтрокаДокумента.ДокументПартии = Выборка.Регистратор;
КонецЕсли;
Важный нюанс: Такой подход вернет товар на последнюю списанную партию. В большинстве случаев для управленческого учета этого достаточно, чтобы закрыть количественный разрыв. Однако, если вы ведете строгий партионный учет по ФИФО в разрезе серий или ГТД, такая «угадайка» может привести к искажению себестоимости. Проанализируем риски: если товар был куплен по одной цене, а возвращается на партию с другой ценой, возникнет курсовая или суммовая разница.
Иногда проблема кроется не в самом списании партий, а в том, как документ подготавливает таблицу движений перед финальной записью. В конфигурациях УПП 1.3 или КА 1.1 стоит обратить внимание на процедуру ДвиженияПоРегиструСписанныеТовары.
Проанализируем ситуацию: если в документе есть несколько строк по одной номенклатуре (одна с плюсом, другая с минусом), система может некорректно их свернуть, потеряв аналитику партий. Проверьте, включена ли колонка СуммаЗадолженности (или аналогичные суммовые показатели) в список суммируемых колонок при сворачивании таблицы движений. Если её там нет, движения могут «схлопнуться» неправильно, и партионный учет не увидит необходимости восстанавливать партию по возврату.
// Пример корректировки списка колонок для свертки
СписокСуммируемыхКолонок = "Количество, ПроводкиСуммаСНДСРегл, ПроводкиСуммаНДСРегл, СуммаЗадолженности";
// ... код подготовки колонок ...
ТаблицаДвижений.Свернуть(СписокФиксируемыхКолонок, СписокСуммируемыхКолонок);
Проблема расхождения партий в Отчете о розничных продажах — это следствие конфликта между жесткой методологией 1С (одна смена — один отчет) и реалиями современной розницы. Для решения задачи мы рекомендуем:
ВозвратТоваровОтПокупателя).Помните, что любые изменения в общем модуле УправлениеЗапасамиПартионныйУчет влияют на проведение практически всех складских документов, поэтому после внесения правок необходимо провести тщательное тестирование на копии базы.