Анализ работы внешней обработки сервиса МодульКасса применительно к задаче фискализации чеков при доставке
В статье разберу работу внешней обработки для работы с МодульКасса, опишу недочеты и применимость для организации доставки, предложу собственный вариант реализации.
- Описание
- Подробнее
Описание
Вводные
- Нужно организовать выгрузку заказов из УТ 11 в МодульКасса
- Нужно организовать загрузку факта фискализации в УТ 11
- Есть обработка предоставляемая сервисом использующая HTTP API
- Есть документация описывающая HTTP API, высылается по запросу в тех поддержку.
Если дополнительно попросить, до можно получить ссылку на полную документацию https://drive.google.com/file/d/12f0TH3Vf8Hou3kF6bzz0onXWzZb3p_kV/view
Обзор текущего функционала
Общие настройки
Обмен реализован в виде внешней обработки, вариант с расширением пока не используют потому что сложнее объяснять пользователям как его подключать.
В СведенияОВнешнейОбработке() установлен "БезопасныйРежим = Истина" с описанием разрешений, это хорошо.
Разрешение = РаботаВБезопасномРежиме.РазрешениеНаИспользованиеИнтернетРесурса("HTTPS","service.modulpos.ru",443,"Доступ к API сервисов МодульКассы для выгрузки товарного каталога и загрузки данных о продажах.");
ПараметрыРегистрации.Разрешения.Добавить(Разрешение);
Разрешение = РаботаВБезопасномРежиме.РазрешениеНаИспользованиеИнтернетРесурса("HTTPS","update.avanpos.com",443,"Доступ к сведениям об актуальной версии обработки и файлам обновлений.");
ПараметрыРегистрации.Разрешения.Добавить(Разрешение);
Настройки хранят в ХранилищеОбщихНастроек, с указание пользователя из ИмяПользователя(), при запуске фоновым заданием настройки могут не подхватится.
Хороший вариант сохранять в реквизит внешней обработки, где НастройкаСсылка это элемент спр. ДополнительныеОтчетыИОбработки
обНастройка = НастройкаСсылка.ПолучитьОбъект();
обНастройка.ХранилищеНастроек = Новый ХранилищеЗначения(СохраняемоеЗначение);
обНастройка.Записать();
Вызов обращений реализован через экспортные процедуры при этом в них дублируется
Если Не ЗначениеЗаполнено(ИмяПользователя) ИЛИ Не ЗначениеЗаполнено(Пароль) Тогда
Возврат;
КонецЕсли;
Оптимальнее делать вызовы через ВыполнитьКоманду(), в которой делать необходимые проверки, работает если форма не тяжелая, т.к. делается вызов серверного метода.
В коде формы будет конструкция
&НаСервере
Процедура ВыполнитьКоманду(ИмяКоманды)
ОбъектОбработки = РеквизитФормыВЗначение("Объект");
Если ЗначениеЗаполнено(Параметры.ДополнительнаяОбработкаСсылка) Тогда
ОбъектОбработки.СохранитьНастройки(Параметры.ДополнительнаяОбработкаСсылка);
КонецЕсли;
ОбъектОбработки.ВыполнитьКоманду(ИмяКоманды, Новый Структура("ДополнительнаяОбработкаСсылка", Параметры.ДополнительнаяОбработкаСсылка));
ЗначениеВДанныеФормы(ОбъектОбработки, Объект);
КонецПроцедуры
&НаКлиенте
Процедура ОтправитьОстатки(Команда)
ВыполнитьКоманду("ОтправитьОстатки");
КонецПроцедуры
В HTTP запрос добавляется информация о версии модуля и текущей конфигурации.
Заголовки.Вставить("X-1C-VERSION", "ModulKassa " + Версия() + " Config " + Метаданные.Представление() + " " + Метаданные.Версия);
В обработке используется API v1, документация на v2. Код вытаскиваю копипастой, возможны перекосы.
Работа со справочниками
При старте обработка не работает пока не создаст дополнительные свойства для номенклатуры и характеристик и не заполнит их кодами элементов. Это без проверки того что функционал может быть отключен. Возможно перестраховываются от ошибок состыковки номенклатуры при загрузке оплат.
Использование уникальных идентификаторов справочников возможно решит проблему, вернусь к вопросу при разборе загрузки оплат.
На стороне сервиса есть справочник торговых точек, api/v1/retail-points (в документации описания к нему не нашел), в обработке выделена ТЧ под состыковку со справочником Кассы ККМ.
В ТЧ "ТорговыеТочки" дополнительно вынесены поля "Организация" и "Склад" с возможностью редактировать, хотя у спр. "КассыККМ" владелец тип "Организация" и присутствует реквизит "Склад". Пользователь может организовать перекос по данным указав неверную связку. Что делать если функционал розничной торговли отключен пока не понятно.
Система выдает ошибки:
Отключены розничные продажи!
Включить розничные продажи можно в настройках
"НСИ и администрирование - Настройка НСИ и разделов - Продажи"
Отключено использование нескольких касс ККМ!
Включить использование можно в настройках
"НСИ и администрирование - Настройка НСИ и разделов - Продажи"
При выгрузке заказов выведено поле на какую "Торговую точку" (по сути ККМ) выгружать, что вносит путаницу (грузить на обе нет смысла). В заказе присутствует поле "Касса", используя которое можно фильтровать ККМ на которые будет делаться выгрузка заказа.
Экваринговый терминал на уровне заказа клиента не установить но его можно вынести в ТЧ "ТорговыеТочки" и получать через кассу, сейчас он вынесен отдельным реквизитом, как работать если терминалов несколько непонятно.
Выгрузка заказов
Выгружаются заказы по тем строкам которые выделены в ТЧ, фильтров нет, что неудобно. Про выбор торговых точек написал выше.
"Костылем" является проверка
Если КоличествоВыбранныхТТ >1 Тогда
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Заказ не может находиться сразу нескольких торговых точках, только на одной, исправьте");
Возврат;
КонецЕсли;
Которая "обходится" повторной выгрузкой заказа на другую точку.
Так же проверка
Если НЕ СтрокаТЧ.Проведен Тогда
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(СокрЛП(СтрокаТЧ) + " не проведен, выгрузка невозможна");
Продолжить;
КонецЕсли;
решается фильтром на форме.
При выгрузке заказа выгружаются коды и наименование номенклатуры, ограничения по длине кода нет, на сколько понял грузить каталог товаров перед выгрузкой заказов не обязательно.
РазделительХарактеристики = "~";
КодТовараМодульКассы = КодТовараМодульКассы + РазделительХарактеристики + КодХарактеристики;
Внешний ключ понадобился для функционал загрузки номенклатуры в 1С созданного в сервисе, в моем случае не нужен.
Запрос.УстановитьПараметр("Значение1", "МК" + СтрокаПозиция.inventCode); // товары, загруженные ранее из МодульКассы
При выгрузке строки
1. ориентир идет на количество и единицу измерения номенклатуры, без анализа количества упаковок и единицы измерения упаковки.
2. не заполняется inventoryType (Тип товара), видимо расчет на предварительную выгрузку каталога.
Заполняется реквизит которого нет в документации "Данные.Вставить("moneyPositions", ДанныеmoneyPositions);"
после успешной выгрузки guid заказа во внешней системе фиксируется в доп свойстве "МодульКасса: ID внешнего чека", но при повторной выгрузке не проверяется. В какую именно ККМ было выгружено не фиксируется.
Загрузка заказов
Выборка заказов идет по дате "Заказ.Дата >= ТекущаяДата()-30*24*60*60"
Вычисляется номер смены из доп свойства ""ModulPOS: Номер смены" ,Кассы ККМ или странным условием в котором "забыли" реализацию товаров и странности с оптимизацией.
|ГДЕ
| ДополнительныеСведения.Свойство.Заголовок = &Наименование
| И (ВЫРАЗИТЬ(ДополнительныеСведения.Объект КАК Документ.ОтчетОРозничныхПродажах).КассаККМ = &КассаККМ
| ИЛИ ВЫРАЗИТЬ(ДополнительныеСведения.Объект КАК Документ.ВозвратТоваровОтКлиента).Склад = &Склад)";
Перебираются торговые точки по каждой делается запрос, в документации он не описан
Данные = ModulPOSВыполнитьGETЗапрос(
"api/v1/retail-point/" + СтрокаТЧ.ИдентификаторModulPOS
+ "/shift" + "?start=" + Формат(НомерСмены-1, "ЧГ=0")
+ "&count=1");
То что номера смен могут накладываться не учитывается.
Далее проверяются чеки в документации так же нет описания
// Определим списки заказов и продаж без заказов
ДанныеЧека = ModulPOSВыполнитьGETЗапрос("api/v1/retail-point/" + СтрокаТЧ.ИдентификаторModulPOS
+ "/shift/" + Данные.data[0].id + "/cashdoc")
При оплате картой создается документ постубление безначлиных ДС, что некорректно, типовые механизмы УТ используют док. "ОперацияПоПлатежнойКарте"
Если аПолученоКартой >0 Тогда
Если СоздаватьПоступлениеБезналичныхДС Тогда
... Документы.ПоступлениеБезналичныхДенежныхСредств.СоздатьДокумент();
Далее создается документ ООрП, Возврат от клиента. В общем процедура на 600 строк с дублирующимися циклами и набором потенциальных ошибок.
Проверок на то что документы уже создан не увидел, потенциально могут быть дубликаты.
Промежуточный итог
Для моей задачи с доставкой данная обработка не подходит. С уважением к "//rio" который поддерживает эту обработку.
Реализация своей обработки
Специфика реализации API со стороны сервиса
В системе существует 2 версии API, вторую писали для выделения работы с заказами. Состыковку типов документов и статусов между v1 и v2 еще не проверил.
Тестового контура нет.
Ответы HTTP 200 на ошибочные ситуации не редкость, поэтому нужно добавлять проверку на пустую строку в теле ответа сервера.
Номер документа уникален в рамках сервиса, поэтому если у ваз 2 заказа с одинаковым номером от разных годов, то при отправке второго получаем 409 ошибку.
Дату можно отправлять в формате 2019-09-18T14:41:04Z, хотя в документации указано с корректировкой на часовой пояс.
При получения списка документов в id "добавили" тип налогообложения, "id": "fdba0c9f-32b3-4fa0-b598-1b8dcb445ca9 — COMMON", хотя в документации нормальный GUID.
Структура данных со стороны 1С.
Реализовал в виде расширения. Для хранения общих настроек использовал справочник.
Для хранения связки по торговым точкам создал справочник "мк_ТорговыеТочки" в котором указываю кассу и экваринговый терминал, при тестировании загрузки данных возможно добавлю значения по умолчанию для заполнения документов.