-
- 1. Добавление товара в чек
- 2. Удаление товара из чека
- 3. Применение скидки на чек
- 4. Получить итоговый чек при переходе к оплате
- 5. Разделить чек по позициям на несколько чеков (группы печати)
- 6. Получить все подключенные пинпады
- 7. Оплатить мультичек на пинпаде
- 8. Записать в итоговый чек экстра данные интеграции
-
- 1. Выбран тип оплаты
- 2. Оплата документа уже выполнена, но печать еще не запущена
- 3. Окрываем кассовый ящик
- 4. Изымаем деньги из кассы
- 5. Открываем карточку редактирования товара
- 6. Закрываем документ продажи/возврата
- 7. Добавляем позицию в чек
- 8. Удаляем позицию из чека
- 9. Удаляем чек (отмена всего чека)
Общая структура проекта:
client.yaml — файл с описанием разрешений для приложения, указаниями файлов, адреса view, для отображения и т.д.
Папка client:
-
daemons— папка, в которой находятся файлы js, которые будут выполнятся асинхронно (в примере отслеживаются события кассы)
Примечание! При отправке события в daemon из терминала, daemons не передает callback в приложение терминала, он работает только с внутренними данными самого приложения, т.е. результат работы daemon необходимо сохранять отдельно, например, в storage, чтобы затем им воспользоваться; -
uiPlugins— папка, в которой находятся файлы js, которые выполняются перед отображениемWebView(может не отображаться); -
view— папка с html файлами, стилями, скриптами и т.д., которые будут отображены вWebView.
Примечание! Вызыватьviewне обязательно если нет необходимости отображать UI, но вызвать метод интерфейсаnavigation.pushNext()для передачи результатов в терминал необходимо, даже если WebView не вызывается;
Файл архива клиента распаковывается в папку assets андроид приложения.
В корне должен находиться файл с описанием структуры проекта client.yaml.
Пример содержания:
version: 2
versionName: "1.0.1"
packageName: Test
appName: "testApp"
appUUID: "2e6dc4b8-fdac-48c1-8a1a-ade402863947"
iconColor: "#0f70b7"
capabilities:
- inventory
- storage
- http
- event-bus
- receipts
daemons:
- name: check
events:
- evo.receipt.opened
- evo.receipt.productAdded
- evo.receipt.productRemoved
- evo.receipt.closed
- evo.receipt.clear
behavior: check-daemon.js
plugins:
- name: discount
moments:
- evo.payments.process
- evo.payments.beforePrintReceipt
point: before
behavior: before-receipt-fixed.js
views:
- name: discount-loader
header: "Подождите"
source: client/views/discount-loader/view.html
styles:
- "*.css"
- name: launcher
header: "Подождите"
source: client/views/discount-loader/view.html
styles:
- "*.css"
Где:
version: 2 — Код версии приложения (инкремент этого параметра значит, что приложение изменилось и его необходимо обновить на терминалах)
versionName: "1.0.1" — Версия приложения (версия служит просто для отображения пользователю, можно писать что угодно)
packageName: org.example.myApp — Имя пакета (используйте правила соглашения об именовании – https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html)
appName: "testApp" — Отображаемое пользователю имя приложения
appUUID: "2e6dc4b8-fdac-48c1-8a1a-ade402863947" — UUID приложения (его можно посмотреть в адресной строке приложения на dev.evotor.ru в личном кабинете разработчика)
iconColor: "#0f70b7" — Цвет иконки приложения интеграции на терминале Evotor, если она размещена на рабочем столе (см. описание views, чтобы поместить иконку приложения на главный экран терминала)
capabilities: — Разрешения на использование интерфейсов, которые запрашивает приложение
- inventory
- storage
- http
- event-bus
- receipts
daemons: — Процессы-демоны (скрипт, реагирующший на события, указанные в списке events)
- name: check — Имя
events: — События, на которые он подписан
- evo.receipt.opened — чек открыт
- evo.receipt.productAdded — товар добавлен
- evo.receipt.productRemoved — товар удален
- evo.receipt.closed — чек закрыт
- evo.receipt.clear — чек удален
behavior: check-daemon.js — скрипт, который будет запущен как реакция на событие демонов (Внутри handleEvent - void метод, который должен вызываться из скрипта чтобы реагировать на событие терминала. Где параметры: handleEvent(integrationEvent), и где integrationEvent - это js объект, содержащий информацию о событии.)
plugins: — собирает и подготавливает данные для отображения пользователя и вызывает WebView (реагирует на события, указанные в списке moments)
- name: discount — Имя
moments: — События, на которые он подписан
- evo.payments.process — переход на экран оплаты чека (переход к оплате чека), на этом этапе чек уже сформирован
- evo.payments.beforePrintReceipt — когда оплата уже проведена, а печать еще не началась
point: before — признак вызова plugins до завершения события moments (пока работает только before)
behavior: before-receipt-fixed.js — скрипт, который будет запущен как реакция на событие плагинов (Внутри HandleMoment - void метод, который должен вызываться из скрипта чтобы реагировать на событие терминала. Где параметры: handleMoment(integrationContext, navigation).)
views: — список html загружаются внутрь WebView, которые затем можно отобразить пользователю в UI
- name: karamba — Любое название view, по которому к нему можно обратиться
header: "Подождите" — Заголовок окна
source: client/views/discount-loader/view.html — Полный путь к html файлу
styles: — список стилей которые должны быть подключены
- "*.css" — может подключить все файлы
- name: launcher — Если необходимо запускать WebView с главного экрана, необходимо в названии написать launcher, но он может быть только один (только одна иконка на главном экране приложения)
header: "Подождите" — Заголовок, при отображении в WebView
source: client/views/discount-loader/view.html — Полный путь к html файлу
styles:
- "*.css"
Для использования возможности работы с сервером посредством http запросов, в файле скрипта используется java объект, контекст которого передается в Java Script.
Работа в js с ним осуществляется, как с обычным js объектом.
Перед использованием API HTTP, необходимо явно указать необходимость использования данного функционала в скрипте:
В начале js скрипта необходимо получить этот объект с помощью:
var http = require('http')
И добавить в client.yaml соответствующий capability.
Далее в коде, после инициализации, можно вызывать метод этого объекта для отправки запроса, например:
function generateSuggestions(items) {
var response = http.send({
method : "POST",
path : "recommendations",
body : items
})
var jsonObject = JSON.parse(response)
}
Где:
var response = http.send({ — Вызов метода
method : "POST", — Тип запроса (POST/GET/PUT/DELETE/HEAD)
path : "recommendations", — URL на сервере
body : items — Тело запроса (тип данных для WebView — только строка, а в Demons или UIPlugin любой тип данных)
var jsonObject = JSON.parse(response) — В ответе получаем строку, которую приводим к JSON объекту
(в следующей версии будет передаваться сразу объект)
Данный функционал возможно использовать не только в сервисах-демонах, в WebView данный код тоже будет работать.
Отображение WebView происходит при вызове у интерфейса navigation метода:
pushView(...)
куда передается полный путь к html файлу страницы из view.source для открытия, где поддерживается использование css, javascript.
Работа с Java интерфейсами внутри WebView несколько отличается от работы с ними в процессах-демонах:
Доступные интерфейсы в WebView:
navigation — Работа с навигацией
jsData — Интерфейс данных интеграции для передачи в webView
Receipt — Работа с чеком
RPC — Доступ для отправки http запросов
storage — Работа с БД интеграции
Logger — Работа с логированием данных
Для работы с ними нет необходимости получать их через синтаксис required, они уже интегрированы в WebView.
Работать с ними можно как с локальными переменными, без их объявления.
API navigation используется для работы с WebView.
Для использования функционала, необходимо явно указать о намерении использования в скрипте, работа происходит через java объект, контекст которого передается в Java Script, далее работа с ним ведется, как с обычным js объектом.
Для инициализации объекта, в начале скрипта указываем:
Для работы с интерфейсом в сервисе-демоне:
var navigation = require('navigation');
Для работы с интерфейсом в WebView: ничего указывать не нужно, по умолчанию, интерфейс навигации уже передан во WebView, для использования обращаемся к нему, как к уже созданному объекту с именем navigation, объявлять его не нужно.
Доступные функции в интерфейсе:
pushNext
pushView
Где:
pushNext() — используется для перехода к следующему экрану:
При открытом WebView — закрывает его и возвращает пользователя в EvoPos, при этом в кассу передается стек операций, который представляет из себя набор действий: добавление товара в чек, удаление товара из чека, применение скидки к чеку или к отдельно выбранному товару.
pushView(String name, String data) — где:
viewLocation— адрес html страницы для открытия вWebViewdata— строка для данных, которые должны быть переданы вWebView, обычно используется json формат
Пример работы:
navigation.pushView("client/views/suggestion-list/view.html", {
suggestions: suggestedProducts,
receipt: receipt
});
Для получения данных в открывшемся WebView используется интерфейс jsData, имеющий метод getData(), возвращающий данные в строковом представлении, переданные через метод pushView().
Пример использования:
var passedData = JSON.parse(jsData.getData());
storage – система хранения данных в формате ключ – значение.
Для работы с API необходимо в манифесте приложения указать:
capabilities:
- storage
Объект для работы с API вызывается функцией:
var storage = require('storage')
Далее используются две функции:
- Сохранение данных:
storage.set(key, value)
Функция возвращает true в случае успешного сохранения, false если произошла ошибка.
key и value – строковые переменные
- Получение данных:
storage.get(key)
Функция возвращает строковую переменную ранее записанную в хранилище, либо null, если значение не было найдено.
key – строковая переменная
API inventory используется для доступа к базе данных устройства, конкретнее, к таблице, содержащей информацию о товарах.
Для использования функционала, необходимо явно указать о намерении использования в скрипте.
Работа происходит через java объект, контекст которого передается в Java Script, далее работа с ним ведется, как с обычным js объектом.
Для инициализации объекта, в начале скрипта указываем:
var inventory = require('inventory');
После этого, мы имеем доступ к методам этого объекта, на данный момент, реализован метод, для получения информации по конкретному товару в базе данных устройства.
Для ее получения, необходимо передать в функцию уникальный uuid товара.
Например:
function getProduct(productUID){
return inventory.getProduct(productUID);
}
Результатом работы функции будет JSON объект в строковом представлении, вида:
{
"ID":"136",
"UUID":"1196da34-e4a8-4915-8e92-bd7792875d76",
"CODE":"4",
"CODE_UPPER_CASE":"4",
"NAME":"вино апсны",
"NAME_UPPER_CASE":"ВИНО АПСНЫ",
"IS_GROUP":"0",
"IS_FAVORITE":"0",
"MEASURE_ID":"1",
"PRICE_OUT":"20000",
"COST_PRICE":"0",
"QUANTITY":"-1000",
"TAX_NUMBER":"VAT_18",
"ABBREVIATION":"ВНП",
"TILE_COLOR":"-26624",
"TYPE":"NORMAL",
"ALCOHOL_BY_VOLUME":"0",
"ALCOHOL_PRODUCT_KIND_CODE":"0",
"TARE_VOLUME":"0",
"SELL_FORBIDDEN":"0",
"DESCRIPTION":"",
"ARTICLE_NUMBER":"",
"ARTICLE_NUMBER_UPPER_CASE":""
}Если, товар с указанным uuid не будет найден в базе, то результатом будет строка с пустым JSON объектом:
{
}
Данный функционал возможно использовать не только в сервисах-демонах, в WebView данный код тоже будет работать.
Функционал для логирования
Объект, через который осуществляется логгирование получается функцией require:
var logger = require('logger')
Далее у объекта вызывается функция:
logger.log(value)
После выполнения функции в logcat устройства будет выведена строка value
Управление чеком доступно через receipt api, для этого используем метод:
receipt.addPosition(uuid: String) — добавление товара в чек
Управление чеком доступно через receipt api, для этого используем метод:
receipt.removePosition(uuid: String) — удаление из чека товара, который уже был добавлен через 'addPosition'
Управление чеком доступно через receipt api, для этого используем метод:
receipt.applyReceiptDiscountPercent(discount: Double) — применение скидки ко всему чеку, процентное значение
Для получения всего чека используем:
receipt.getReceipt()
Примечание: для использования receipt.getReceipt() нужно декларировать var receipt = require('receiptControl')
Возвращает строку в JSON формате (формат данных содержимого json - строки) :
{
"receiptData": {
"totalSum": "218.50",
"discountPercents": "0.000000",
"totalSumWithoutDiscount": "218.50",
"positionDiscountSum": "0.00",
"positionsCount": "4",
"extraData": "{}"
},
"receiptPositions": [{
"uuid": "070efd1b-4f53-401a-b5c1-cb31b5e9072d",
"type": "NORMAL",
"code": "1",
"measure": "шт",
"price": "56",
"priceWithDiscount": "56",
"quantity": "1"
}, {
"uuid": "9d2fdacd-969c-41f2-b360-a5ebbddcbe9e",
"type": "NORMAL",
"code": "131",
"measure": "шт",
"price": "30.5",
"priceWithDiscount": "30.5",
"quantity": "5"
}, {
"uuid": "7c916c19-756d-4b3d-806e-63c090080a3d",
"type": "NORMAL",
"code": "129",
"measure": "шт",
"price": "9",
"priceWithDiscount": "9",
"quantity": "1"
}, {
"uuid": "9dd36300-647e-4863-81b2-eb9591bc599b",
"type": "NORMAL",
"code": "127",
"measure": "шт",
"price": "1",
"priceWithDiscount": "1",
"quantity": "1"
}]
}Группы печати, где для задания группы печати на позицию, используется метод:
edited
receipt.addPositionPrintGroup(JSON String)
в качестве параметра
var addPositionPrintGroup = {
"uuid": "e82a113b-0d76-424f-9ad5-c6595ca57770",
"code": "4", — код товара
"printGroupId" : "4dc2-3fcd",
"printGroupIsFiscal" : true,
"printGroupOrgName" : "Andrew",
"printGroupOrgInn": "88005553535",
"printGroupPaymentSum" : "123.00"
};
edited
Где:
"uuid": "e82a113b-0d76-424f-9ad5-c6595ca57770" — uuid товара
"code": "4" — код товара
"printGroupId" : "4dc2-3fcd" — id группы печати
"printGroupIsFiscal" : true — признак фискальности
"printGroupOrgName" : "Andrew" — имя группы
"printGroupOrgInn": "88005553535" — ИНН
"printGroupPaymentSum" : "123.00"— Сумма товаров в группе
Devices.getAvailableDevicesIds()
Возвращает массив данных со всеми пин-падами, подключенными к терминалу:
Где поля, описывающие каждый платежный терминал:
deviceId — id устройства
userDescription — Пользовательское описание
model — Модель устройства
vendor — Производитель устройства
receipt.addPaymentOperation(JSON String)
Принимает на вход принимает JSON, который описывает структуру оплаты для разбиения общего чека на платежи:
var paymentOperation = {
"uuid": "1ef45g5",
"deviceUUID": "smth",
"paymentSum" : "123.00",
"isCashless" : true,
"printGroups" : [
{
"printGroupId" : "4dc2-3fcd",
"printGroupIsFiscal" : true,
"printGroupOrgName" : "Andrew",
"printGroupPaymentSum" : "123.00",
"printGroupOrgInn": "88005553535"
}
]
};
Где:
"uuid": "1ef45g5" — уникальный идентификатор для каждой оплаты (генерируется со стороны интеграции - уникальность должна гарантироваться в рамках одного чека, а тип данных String дает возможность использовать любые сгенерированные последовательности знаков)
"deviceUUID": "smth" — идентификатор устройства, на котором выполняется оплата (идентификатор, получаемый через наше апи 6. "Получить все подключенные пинпады")
"paymentSum" : "123.00" — сумма платежа
"isCashless" : true — признак картой/нал (true, если используется пин-пад, проставляется со стороны приложения. В событие evo.payments.process содержатся данные о типе оплаты, поле "isCashless")
"printGroups" : — список групп печати в оплате
Все необходимые данные интеграция может добавить в нужном ей формате как дополнительную информацию по документу чека продажи:
receipt.addExtraReceiptData(String extraData)
Прилетает событие:
action evo.payments.process
Где есть признак, является ли оплата безналичной:
isCashless - тип boolean
Прилетает событие:
evo.payments.beforePrintReceipt
Нужно на него подписаться
Прилетает событие:
evo.cashDrawer.opened
Нужно на него подписаться
Прилетает событие:
evo.cashOperations.cashOut
Нужно на него подписаться
Прилетает событие:
evo.commodity.cardOpened
Нужно на него подписаться
Прилетает событие:
evo.receipt.closed
Нужно на него подписаться
Прилетает событие:
evo.receipt.productAdded
Нужно на него подписаться
Прилетает событие:
evo.receipt.productRemoved
Нужно на него подписаться
Прилетает событие:
evo.receipt.clear
Нужно на него подписаться