[IMP] pos, hw_drivers, hw_posbox_homepage: Six terminals
Add support for Six payment terminals. TaskId: 1932416 Linked to odoo/enterprise#4435 -- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr closes odoo/odoo#33673 Signed-off-by: Quentin Lejeune (qle) <qle@odoo.com>
This commit is contained in:
@@ -19,6 +19,7 @@ from glob import glob
|
||||
from base64 import b64decode
|
||||
from pathlib import Path
|
||||
import socket
|
||||
import ctypes
|
||||
|
||||
from odoo import http, _
|
||||
from odoo.modules.module import get_resource_path
|
||||
@@ -379,6 +380,7 @@ class Manager(Thread):
|
||||
while 1:
|
||||
updated_devices = self.usb_loop()
|
||||
updated_devices.update(self.video_loop())
|
||||
updated_devices.update(mpdm.devices)
|
||||
updated_devices.update(bt_devices)
|
||||
updated_devices.update(socket_devices)
|
||||
updated_devices.update(self.serial_loop())
|
||||
@@ -441,11 +443,46 @@ class SocketManager(Thread):
|
||||
iot_device = IoTDevice(type('', (), {'dev': dev}), 'socket')
|
||||
socket_devices[addr[0]] = iot_device
|
||||
|
||||
class MPDManager(Thread):
|
||||
def __init__(self):
|
||||
super(MPDManager, self).__init__()
|
||||
self.devices = {}
|
||||
self.mpd_session = ctypes.c_void_p()
|
||||
|
||||
def run(self):
|
||||
eftapi.EFT_CreateSession(ctypes.byref(self.mpd_session))
|
||||
eftapi.EFT_PutDeviceId(self.mpd_session, terminal_id.encode())
|
||||
while True:
|
||||
if self.terminal_connected(terminal_id):
|
||||
self.devices[terminal_id] = IoTDevice(terminal_id, 'mpd')
|
||||
elif terminal_id in self.devices:
|
||||
self.devices = {}
|
||||
time.sleep(20)
|
||||
|
||||
def terminal_connected(self, terminal_id):
|
||||
eftapi.EFT_QueryStatus(self.mpd_session)
|
||||
eftapi.EFT_Complete(self.mpd_session, 1) # Needed to read messages from driver
|
||||
device_status = ctypes.c_long()
|
||||
eftapi.EFT_GetDeviceStatusCode(self.mpd_session, ctypes.byref(device_status))
|
||||
return device_status.value in [0, 1]
|
||||
|
||||
|
||||
conn = cups_connection()
|
||||
PPDs = conn.getPPDs()
|
||||
printers = conn.getPrinters()
|
||||
cups_lock = Lock() # We can only make one call to Cups at a time
|
||||
|
||||
mpdm = MPDManager()
|
||||
terminal_id = read_file_first_line('odoo-six-payment-terminal.conf')
|
||||
if terminal_id:
|
||||
try:
|
||||
subprocess.check_output(["pidof", "eftdvs"]) # Check if MPD server is running
|
||||
except subprocess.CalledProcessError:
|
||||
subprocess.Popen(["eftdvs", "/ConfigDir", "/usr/share/eftdvs/"]) # Start MPD server
|
||||
eftapi = ctypes.CDLL("eftapi.so") # Library given by Six
|
||||
mpdm.daemon = True
|
||||
mpdm.start()
|
||||
|
||||
m = Manager()
|
||||
m.daemon = True
|
||||
m.start()
|
||||
|
||||
@@ -53,6 +53,7 @@ wifi_config_template = jinja_env.get_template('wifi_config.html')
|
||||
driver_list_template = jinja_env.get_template('driver_list.html')
|
||||
remote_connect_template = jinja_env.get_template('remote_connect.html')
|
||||
configure_wizard_template = jinja_env.get_template('configure_wizard.html')
|
||||
six_payment_terminal_template = jinja_env.get_template('six_payment_terminal.html')
|
||||
|
||||
class IoTboxHomepage(web.Home):
|
||||
|
||||
@@ -72,6 +73,12 @@ class IoTboxHomepage(web.Home):
|
||||
statuses[driver] = hw_proxy.drivers[driver].get_status()
|
||||
return statuses
|
||||
|
||||
def get_six_terminal(self):
|
||||
path = Path.home() / 'odoo-six-payment-terminal.conf'
|
||||
if path.exists():
|
||||
return path.read_text()
|
||||
return 'Not Configured'
|
||||
|
||||
def get_homepage_data(self):
|
||||
hostname = str(socket.gethostname())
|
||||
mac = get_mac()
|
||||
@@ -113,6 +120,7 @@ class IoTboxHomepage(web.Home):
|
||||
'mac': ":".join(i + next(h) for i in h),
|
||||
'iot_device_status': iot_device,
|
||||
'server_status': get_odoo_server_url() or 'Not Configured',
|
||||
'six_terminal': self.get_six_terminal(),
|
||||
'network_status': network,
|
||||
}
|
||||
|
||||
@@ -296,3 +304,30 @@ class IoTboxHomepage(web.Home):
|
||||
return 'starting with ' + auth_token
|
||||
else:
|
||||
return 'already running'
|
||||
|
||||
@http.route('/six_payment_terminal', type='http', auth='none', cors='*', csrf=False)
|
||||
def six_payment_terminal(self):
|
||||
return six_payment_terminal_template.render({
|
||||
'title': 'Six Payment Terminal',
|
||||
'breadcrumb': 'Six Payment Terminal',
|
||||
'terminalId': self.get_six_terminal(),
|
||||
})
|
||||
|
||||
@http.route('/six_payment_terminal_add', type='http', auth='none', cors='*', csrf=False)
|
||||
def add_six_payment_terminal(self, terminal_id):
|
||||
subprocess.check_call(["sudo", "mount", "-o", "remount,rw", "/"])
|
||||
path = Path.home() / 'odoo-six-payment-terminal.conf'
|
||||
path.write_text(terminal_id)
|
||||
subprocess.check_call(["sudo", "service", "odoo", "restart"])
|
||||
subprocess.check_call(["sudo", "mount", "-o", "remount,ro", "/"])
|
||||
return 'http://' + get_ip() + ':8069'
|
||||
|
||||
@http.route('/six_payment_terminal_clear', type='http', auth='none', cors='*', csrf=False)
|
||||
def clear_six_payment_terminal(self):
|
||||
subprocess.check_call(["sudo", "mount", "-o", "remount,rw", "/"])
|
||||
path = Path.home() / 'odoo-six-payment-terminal.conf'
|
||||
if path.exists():
|
||||
path.unlink()
|
||||
subprocess.check_call(["sudo", "service", "odoo", "restart"])
|
||||
subprocess.check_call(["sudo", "mount", "-o", "remount,ro", "/"])
|
||||
return "<meta http-equiv='refresh' content='0; url=http://" + get_ip() + ":8069'>"
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
font-weight: bold;
|
||||
vertical-align: top;
|
||||
width: 30%;
|
||||
text-align: left;
|
||||
}
|
||||
.device-status {
|
||||
margin: 6px 0;
|
||||
@@ -109,6 +110,12 @@
|
||||
<td class="heading">Server</td>
|
||||
<td><a href='{{ server_status }}' target=_blank>{{ server_status }}<a class="btn btn-sm float-right" href='/server'>configure</a></td>
|
||||
</tr>
|
||||
{% if server_status != "Not Configured" %}
|
||||
<tr>
|
||||
<td class="heading">Six payment terminal</td>
|
||||
<td>{{ six_terminal }} <a class="btn btn-sm float-right" href='/six_payment_terminal'>configure</a></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td class="heading">IOT Device</td>
|
||||
<td>
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
{% extends "layout.html" %}
|
||||
{% from "loading.html" import loading_block_ui %}
|
||||
{% block head %}
|
||||
<script>
|
||||
var _onQueryDone = function () {
|
||||
$('.message-status').html('Updated configuration <br> Refreshing page');
|
||||
setTimeout(function () {
|
||||
location.reload();
|
||||
}, 30000);
|
||||
}
|
||||
$(document).ready(function () {
|
||||
$('#terminal-id').submit(function(e){
|
||||
e.preventDefault();
|
||||
$('.loading-block').removeClass('o_hide');
|
||||
$.ajax({
|
||||
url: '/six_payment_terminal_add',
|
||||
type: 'post',
|
||||
data: $('#terminal-id').serialize(),
|
||||
}).always(_onQueryDone);
|
||||
});
|
||||
$('#terminal-clear').submit(function(e){
|
||||
e.preventDefault();
|
||||
$('.loading-block').removeClass('o_hide');
|
||||
$.ajax({
|
||||
url: '/six_payment_terminal_clear',
|
||||
type: 'get',
|
||||
}).always(_onQueryDone);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<h2 class="text-center">Six Payment Terminal</h2>
|
||||
<p>
|
||||
Set the Terminal ID (TID) of the terminal you want to use.
|
||||
</p>
|
||||
<form id="terminal-id" action='/six_payment_terminal_add' method='POST'>
|
||||
<table align="center">
|
||||
<tr>
|
||||
<td>Terminal ID</td>
|
||||
<td><input type="text" name="terminal_id"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td/>
|
||||
<td><input class="btn" type="submit" value="Connect"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
{{ loading_block_ui(loading_message) }}
|
||||
</form>
|
||||
{% if terminalId %}
|
||||
<p class="text-center font-small">
|
||||
Current Terminal Id: <strong>{{ terminalId }}</strong>
|
||||
</p>
|
||||
<div class="text-center font-small" style="margin: 10px auto;">
|
||||
You can clear the terminal configuration
|
||||
<form id="terminal-clear" style="display: inline-block;margin-left: 4px;">
|
||||
<input class="btn btn-sm" type="submit" value="Clear"/>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -1945,12 +1945,7 @@ var PaymentScreenWidget = ScreenWidget.extend({
|
||||
}
|
||||
|
||||
var lines = order.get_paymentlines();
|
||||
var due = order.get_due();
|
||||
var extradue = 0;
|
||||
if (due && lines.length && due !== order.get_due(lines[lines.length-1])) {
|
||||
extradue = due;
|
||||
}
|
||||
|
||||
var extradue = this.compute_extradue(order);
|
||||
|
||||
this.$('.paymentlines-container').empty();
|
||||
var lines = $(QWeb.render('PaymentScreen-Paymentlines', {
|
||||
@@ -1970,6 +1965,14 @@ var PaymentScreenWidget = ScreenWidget.extend({
|
||||
|
||||
lines.appendTo(this.$('.paymentlines-container'));
|
||||
},
|
||||
compute_extradue: function (order) {
|
||||
var lines = order.get_paymentlines();
|
||||
var due = order.get_due();
|
||||
if (due && lines.length && due !== order.get_due(lines[lines.length-1])) {
|
||||
return due;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
click_paymentmethods: function(id) {
|
||||
var payment_method = this.pos.payment_methods_by_id[id]
|
||||
this.pos.get_order().add_paymentline(payment_method);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/Trace logstd /TraceDir /var/log/eftdvs
|
||||
@@ -121,6 +121,9 @@ PIP_TO_INSTALL="
|
||||
|
||||
pip3 install ${PIP_TO_INSTALL}
|
||||
|
||||
# Dowload MPD server and library for Six terminals
|
||||
wget 'https://nightly.odoo.com/master/iotbox/eftdvs' -P /usr/local/bin/
|
||||
wget 'https://nightly.odoo.com/master/iotbox/eftapi.so' -P /usr/lib/
|
||||
|
||||
groupadd usbusers
|
||||
usermod -a -G usbusers pi
|
||||
|
||||
@@ -5,14 +5,12 @@
|
||||
<link rel="stylesheet" href="/web/static/lib/fontawesome/css/font-awesome.css"/>
|
||||
<link rel="stylesheet" href="/point_of_sale/static/src/css/pos.css" id="pos-stylesheet"/>
|
||||
<link rel="stylesheet" href="/point_of_sale/static/src/css/keyboard.css"/>
|
||||
<link rel="stylesheet" href="/point_of_sale/static/src/css/pos_receipts.css"/>
|
||||
|
||||
<link rel="stylesheet" type="text/less" href="/web/static/src/scss/fontawesome_overridden.scss"/>
|
||||
|
||||
<script type="text/javascript" src="/point_of_sale/static/lib/backbone/backbone.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/lib/waitfont.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/lib/sha1.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/lib/html2canvas.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/main.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/db.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/models.js"></script>
|
||||
@@ -24,7 +22,6 @@
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/gui.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/popups.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/screens.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/printers.js"></script>
|
||||
</template>
|
||||
|
||||
<template id="customer_facing_display_html" >
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
<template id="assets_backend" name="hr assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" type="text/scss" href="/point_of_sale/static/src/scss/pos_dashboard.scss"/>
|
||||
<link rel="stylesheet" href="/point_of_sale/static/src/css/pos_receipts.css"/>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/tours/point_of_sale.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/printers.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/lib/html2canvas.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
attrs="{'invisible' : ['|', ('cash_control', '=', False), ('state', '!=', 'opening_control')]}" class="oe_highlight"/>
|
||||
<button name="open_frontend_cb" type="object" string="Continue Selling" states="opened"
|
||||
class="oe_highlight"/>
|
||||
<button name="action_pos_session_closing_control" type="object" string="End of Session"
|
||||
<button id="end_session_opened" name="action_pos_session_closing_control" type="object" string="End of Session"
|
||||
attrs="{'invisible' : ['|', ('cash_control', '=', False),('state', '!=', 'opened')]}"
|
||||
class="oe_highlight"/>
|
||||
<button name="action_pos_session_closing_control" type="object" string="Validate Closing & Post Entries"
|
||||
<button id="validate_opened" name="action_pos_session_closing_control" type="object" string="Validate Closing & Post Entries"
|
||||
attrs="{'invisible' : ['|', ('cash_control', '=', True),('state', '!=', 'opened')]}"
|
||||
class="oe_highlight"/>
|
||||
<button name="action_pos_session_validate" type="object" string="Validate Closing & Post Entries" states="closing_control"
|
||||
<button id="validate_closing_control" name="action_pos_session_validate" type="object" string="Validate Closing & Post Entries" states="closing_control"
|
||||
class="oe_highlight"/>
|
||||
|
||||
<field name="state" widget="statusbar" statusbar_visible="new_session,opening_control,opened,closing_control,closed" nolabel="1" />
|
||||
|
||||
Reference in New Issue
Block a user