-
- 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 страницы для открытия вWebView
data
— строка для данных, которые должны быть переданы в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
Нужно на него подписаться