При работе в конфигурации 1С:Управление торговлей, редакция 11 (УТ 11) пользователи часто сталкиваются с неприятным поведением системы при попытке оформить документ Возврат товаров от клиента на основании ранее пробитого Чек ККМ. Программа выдает блокирующую ошибку: «Не заполнена колонка "Документ продажи" в строке X списка "Товары"». При этом визуально на форме документа такой колонки просто нет, что вводит операторов и кассиров в ступор.
В этой статье мы подробно разберем, почему возникает данная проблема, рассмотрим архитектуру розничных продаж в решениях 1С, изучим сбойный код конфигурации и найдем несколько вариантов решения — от методологически правильного изменения бизнес-процесса до технической доработки системы с помощью расширения.
Чтобы понять природу этой ошибки, нам необходимо проанализировать, как устроена схема розничной торговли в семействе конфигураций ERP/КА/УТ. В системе существует разделение на оперативный учет продаж в течение дня и финансовый учет по итогам закрытия кассовой смены.
Когда мы пытаемся создать документ Возврат товаров от клиента с видом операции «Возврат от розничного покупателя», система ожидает, что для каждой возвращаемой товарной позиции будет четко определен финансовый документ продажи. Этим документом должен выступать именно Отчет о розничных продажах (или Реализация товаров и услуг, если продажа была оптовой), но никак не сам Чек ККМ.
На уровне метаданных в табличной части «Товары» документа возврата существует реквизит ДокументРеализации (в интерфейсе он называется «Документ продажи»). Однако на управляемой форме при розничной операции разработчики 1С скрыли эту колонку, чтобы упростить интерфейс для пользователя и заполнять её автоматически программными методами.
Давайте заглянем под капот конфигурации и проанализируем модуль, отвечающий за заполнение документа возврата на основании чека. В процедурах обработки заполнения вызывается следующий фрагмент кода:
ДокументРеализации = РозничныеПродажи.ПолучитьОтчетОРозничныхПродажахПоЧекуККМ(ДокументОснование);
Для Каждого ТекСтрока Из ТаблицаТоваров Цикл
Если ТекСтрока.ТипНоменклатуры = Перечисления.ТипыНоменклатуры.Товар
Или ТекСтрока.ТипНоменклатуры = Перечисления.ТипыНоменклатуры.МногооборотнаяТара Тогда
НоваяСтрока = Товары.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, ТекСтрока);
НоваяСтрока.ДокументРеализации = ДокументРеализации;
НоваяСтрока.СпособОпределенияСебестоимости = ?(ЗначениеЗаполнено(НоваяСтрока.ДокументРеализации),
Перечисления.СпособыОпределенияСебестоимости.ИзДокументаПродажи,
Перечисления.СпособыОпределенияСебестоимости.ИзТекущегоДокумента);
КонецЕсли;
КонецЦикла;
Давайте детально разберем, как работает эта логика и в каких случаях она дает сбой:
РозничныеПродажи.ПолучитьОтчетОРозничныхПродажахПоЧекуККМ(ДокументОснование) пытается по переданной ссылке на Чек ККМ найти документ Отчет о розничных продажах, в который данный чек вошел при закрытии смены.Неопределено). В результате реквизит ДокументРеализации в строках таблицы товаров не заполняется. При попытке проведения документа платформа выполняет проверку заполнения обязательных реквизитов и выдает ошибку, так как колонка осталась пустой.Пользователи на форумах часто отмечают, что если удалить все автоматически заполненные строки в документе возврата, а затем нажать кнопку «Заполнить» -> «Добавить товары из документов продажи» (или воспользоваться механизмом подбора), то документ успешно проводится. Выясним, почему так происходит.
При использовании стандартного диалогового окна подбора система предлагает пользователю напрямую выбрать документ продажи из списка. В этот момент оператор явно указывает нужный Отчет о розничных продажах. Платформа напрямую записывает ссылку на этот ОРП в скрытый реквизит ДокументРеализации каждой строки. В обход проблемной функции автоматического поиска по документу-основанию скрытое поле оказывается заполненным, и документ успешно проходит все проверки при проведении.
Прежде чем переходить к изменению программного кода, нам необходимо убедиться, что пользователи правильно отражают операции возврата с точки зрения методологии 1С. В зависимости от того, когда происходит возврат, должны применяться разные цепочки документов:
Использовать документ Возврат товаров от клиента в этом случае методологически неверно. Правильный путь — открыть Рабочее место кассира (РМК), выбрать операцию «Возврат» и пробить «Чек ККМ на возврат» на основании исходного чека продажи. При закрытии смены система автоматически свернет продажи и возвраты, сформировав корректный итоговый ОРП.
В этом сценарии использование документа Возврат товаров от клиента абсолютно оправдано. Однако создавать его целесообразно не на основании исходного Чека ККМ, а на основании сформированного Отчета о розничных продажах, в который этот чек вошел. Это гарантирует корректное автоматическое заполнение всех аналитик и цен.
Если ваши бизнес-процессы требуют создания возвратов именно на основании чеков ККМ (например, при интеграции с внешними фронт-офисными системами) и конфигурация должна оставаться на поддержке, лучшим решением будет создание небольшого расширения конфигурации.
Мы можем применить два подхода в расширении:
Подход А. Вывод скрытой колонки на форму документа
Самое простое решение, не требующее написания сложного кода — сделать скрытую колонку видимой. Для этого в расширении находим форму документа ФормаДокументаВозвратТоваровОтКлиента, переносим элемент табличной части ТоварыДокументПродажи (или Товары.ДокументРеализации) на форму и настраиваем свойство видимости. Если автозаполнение не сработает, пользователь сможет вручную выбрать нужный ОРП прямо в строке табличной части.
Подход Б. Программная корректировка заполнения при вводе на основании
Мы можем перехватить процедуру заполнения документа возврата на основании Чека ККМ с помощью аннотации &После или &Вместо. Наша задача — научить систему искать документ-продажи более надежным способом, либо, если смена еще не закрыта, временно подставлять сам чек в качестве документа продажи (хотя для полноценного проведения по регистрам себестоимости всё же потребуется ОРП).
Рассмотрим пример кода для расширения модуля менеджера документа ВозвратТоваровОтКлиента:
&После("ЗаполнитьПоЧекуККМ")
Процедура Расш_ЗаполнитьПоЧекуККМ(ДокументОснование)
// Проверим, заполнена ли колонка ДокументРеализации в строках
Для Каждого СтрокаТовара Из ЭтотОбъект.Товары Цикл
Если Не ЗначениеЗаполнено(СтрокаТовара.ДокументРеализации) Тогда
// Попробуем альтернативный поиск ОРП через запрос к регистру сведений "НоменклатураДокументовПретензий"
// или напрямую через запрос к табличным частям существующих Отчетов о розничных продажах
НайденныйОРП = ПолучитьОРПАльтернативнымПутем(ДокументОснование);
Если ЗначениеЗаполнено(НайденныйОРП) Тогда
СтрокаТовара.ДокументРеализации = НайденныйОРП;
СтрокаТовара.СпособОпределенияСебестоимости = Перечисления.СпособыОпределенияСебестоимости.ИзДокументаПродажи;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
&НаСервере
Функция ПолучитьОРПАльтернативнымПутем(ЧекСсылка)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| ОРПТовары.Ссылка КАК Ссылка
|ИЗ
| Документ.ОтчетОРозничныхПродажах.Товары КАК ОРПТовары
|ГДЕ
| ОРПТовары.ДокументОбъект = &ЧекСсылка"; // Или иная связь в зависимости от релиза
Запрос.УстановитьПараметр("ЧекСсылка", ЧекСсылка);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
Возврат Выборка.Ссылка;
КонецЕсли;
Возврат Неопределено;
КонецФункции
Применение расширения позволяет решить проблему раз и навсегда без снятия конфигурации с поддержки и без необходимости регулярного ручного вмешательства пользователей в процесс переподбора позиций.