initial work on odoo 12
This commit is contained in:
parent
5f8ef72c8b
commit
41cb41be5f
12
README.md
12
README.md
|
@ -5,18 +5,6 @@ Odoo PosBox in Docker. It additionally contains
|
|||
[pos-addons](https://github.com/it-projects-llc/pos-addons)
|
||||
for using receipt printers over the network.
|
||||
|
||||
## Running on Raspberry Pi
|
||||
|
||||
The official Docker images of Odoo are not multi-arch
|
||||
compatible, the image needs to be rebuilt on arm.
|
||||
|
||||
1. Change the `FROM` in the [Dockerfile](https://github.com/odoo/docker/blob/master/11.0/Dockerfile) to
|
||||
`FROM armhf/debian:stretch` and build it as `local/odoo:11`.
|
||||
1. Change the `FROM` in [odoo/Dockerfile](odoo/Dockerfile) to
|
||||
`local/odoo:11`.
|
||||
|
||||
Corresponding issue on GitHub: [Support for ARM64v8 #195](https://github.com/odoo/docker/issues/195).
|
||||
|
||||
## Backup configuration
|
||||
|
||||
Example contents of `backup.env`:
|
||||
|
|
|
@ -8,12 +8,16 @@ services:
|
|||
- 80:8069
|
||||
volumes:
|
||||
- odoo-web-data:/var/lib/odoo
|
||||
environment:
|
||||
- HOST=db
|
||||
- USER=odoo
|
||||
- PASSWORD=odoo
|
||||
networks:
|
||||
localnet:
|
||||
ipv4_address: 10.5.0.2
|
||||
posbox:
|
||||
build: ./posbox
|
||||
command: -- --load=web,hw_proxy,hw_posbox_homepage,hw_escpos,hw_screen,hw_printer_network
|
||||
iotbox:
|
||||
build: ./iotbox
|
||||
command: -- --load=web,hw_proxy,hw_posbox_homepage,hw_posbox_upgrade,hw_scale,hw_scanner,hw_escpos,hw_blackbox_be,hw_screen,hw_drivers,hw_printer_network
|
||||
ports:
|
||||
- 8070:8069
|
||||
- 8072:8072
|
||||
|
@ -23,8 +27,9 @@ services:
|
|||
localnet:
|
||||
ipv4_address: 10.5.0.3
|
||||
db:
|
||||
image: postgres:9.6
|
||||
image: postgres:10
|
||||
environment:
|
||||
- POSTGRES_DB=postgres
|
||||
- POSTGRES_PASSWORD=odoo
|
||||
- POSTGRES_USER=odoo
|
||||
- PGDATA=/var/lib/postgresql/data/pgdata
|
||||
|
@ -33,28 +38,28 @@ services:
|
|||
networks:
|
||||
localnet:
|
||||
ipv4_address: 10.5.0.4
|
||||
backup:
|
||||
build: ./backup
|
||||
environment:
|
||||
- PGHOST=db
|
||||
- PGUSER=odoo
|
||||
- PGPASSWORD=odoo
|
||||
- BACKUP_SCHEDULE=0 19 * * *
|
||||
env_file: backup.env
|
||||
volumes:
|
||||
- odoo-db-data:/data/pg_raw:ro
|
||||
- /home/pi:/data/home-pi:ro
|
||||
networks:
|
||||
localnet:
|
||||
ipv4_address: 10.5.0.5
|
||||
monitoring:
|
||||
build: ./monitoring
|
||||
command: checkup every 10m
|
||||
volumes:
|
||||
- ./checkup.json:/opt/checkup/checkup.json:ro
|
||||
networks:
|
||||
localnet:
|
||||
ipv4_address: 10.5.0.6
|
||||
# backup:
|
||||
# build: ./backup
|
||||
# environment:
|
||||
# - PGHOST=db
|
||||
# - PGUSER=odoo
|
||||
# - PGPASSWORD=odoo
|
||||
# - BACKUP_SCHEDULE=0 19 * * *
|
||||
# env_file: backup.env
|
||||
# volumes:
|
||||
# - odoo-db-data:/data/pg_raw:ro
|
||||
# - /home/pi:/data/home-pi:ro
|
||||
# networks:
|
||||
# localnet:
|
||||
# ipv4_address: 10.5.0.5
|
||||
# monitoring:
|
||||
# build: ./monitoring
|
||||
# command: checkup every 10m
|
||||
# volumes:
|
||||
# - ./checkup.json:/opt/checkup/checkup.json:ro
|
||||
# networks:
|
||||
# localnet:
|
||||
# ipv4_address: 10.5.0.6
|
||||
volumes:
|
||||
odoo-web-data:
|
||||
odoo-db-data:
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
FROM odoo:12
|
||||
|
||||
USER root
|
||||
|
||||
## Dependencies for iotbox
|
||||
## See also https://github.com/odoo/odoo/blob/master/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init_posbox_image.sh
|
||||
RUN set -x; apt-get update \
|
||||
&& apt-get -y install --no-install-recommends \
|
||||
bluez \
|
||||
cups \
|
||||
cups-ipp-utils \
|
||||
dbus \
|
||||
gcc \
|
||||
git \
|
||||
libcups2-dev \
|
||||
python3-babel \
|
||||
python3-dateutil \
|
||||
python3-dbus \
|
||||
python3-decorator \
|
||||
python3-dev \
|
||||
python3-docutils \
|
||||
python3-feedparser \
|
||||
python3-gi \
|
||||
python3-html2text \
|
||||
python3-jinja2 \
|
||||
python3-ldap3 \
|
||||
python3-libsass \
|
||||
python3-lxml \
|
||||
python3-mako \
|
||||
python3-mock \
|
||||
python3-netifaces \
|
||||
python3-openid \
|
||||
python3-passlib \
|
||||
python3-pil \
|
||||
python3-pip \
|
||||
python3-psutil \
|
||||
python3-psycopg2 \
|
||||
python3-pydot \
|
||||
python3-pyparsing \
|
||||
python3-pypdf2 \
|
||||
python3-pyscard \
|
||||
python3-qrcode \
|
||||
python3-reportlab \
|
||||
python3-requests \
|
||||
python3-serial \
|
||||
python3-simplejson \
|
||||
python3-simplejson \
|
||||
python3-tz \
|
||||
python3-unittest2 \
|
||||
python3-urllib3 \
|
||||
python3-vatnumber \
|
||||
python3-werkzeug \
|
||||
python3-wheel \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& mkdir -p /opt/posbox/addons \
|
||||
&& chown -R odoo.odoo /opt/posbox \
|
||||
&& mkdir /usr/lib/python3/dist-packages/odoo/addons/hw_drivers/drivers
|
||||
|
||||
RUN pip3 install \
|
||||
pyusb==1.0.0b1 \
|
||||
evdev \
|
||||
gatt \
|
||||
v4l2 \
|
||||
polib \
|
||||
pycups
|
||||
|
||||
# See https://bugs.launchpad.net/python-v4l2/+bug/1664158
|
||||
COPY python-v4l2-1664158-fix.patch /tmp
|
||||
RUN patch -p0 /usr/local/lib/python3.5/dist-packages/v4l2.py < /tmp/python-v4l2-1664158-fix.patch
|
||||
|
||||
#USER odoo
|
||||
|
||||
## Get pos-addons for pos_printer_network
|
||||
RUN git clone --depth=1 -b 12.0 https://github.com/it-projects-llc/pos-addons.git \
|
||||
/opt/posbox/addons
|
||||
|
||||
COPY odoo.conf /etc/odoo/odoo.conf
|
||||
COPY entrypoint.sh /
|
File diff suppressed because one or more lines are too long
|
@ -13,7 +13,7 @@ DB_ARGS=()
|
|||
function check_config() {
|
||||
param="$1"
|
||||
value="$2"
|
||||
if ! grep -q -E "^\s*\b${param}\b\s*=" "$OPENERP_SERVER" ; then
|
||||
if ! grep -q -E "^\s*\b${param}\b\s*=" "$ODOO_RC" ; then
|
||||
DB_ARGS+=("--${param}")
|
||||
DB_ARGS+=("${value}")
|
||||
fi;
|
||||
|
@ -24,16 +24,18 @@ check_config "db_user" "$USER"
|
|||
check_config "db_password" "$PASSWORD"
|
||||
|
||||
case "$1" in
|
||||
-- | openerp-server)
|
||||
-- | odoo)
|
||||
shift
|
||||
/etc/init.d/cups start
|
||||
/etc/init.d/dbus start
|
||||
if [[ "$1" == "scaffold" ]] ; then
|
||||
exec openerp-server "$@"
|
||||
exec odoo "$@"
|
||||
else
|
||||
exec openerp-server "$@" "${DB_ARGS[@]}"
|
||||
exec odoo "$@" "${DB_ARGS[@]}"
|
||||
fi
|
||||
;;
|
||||
-*)
|
||||
exec openerp-server "$@" "${DB_ARGS[@]}"
|
||||
exec odoo "$@" "${DB_ARGS[@]}"
|
||||
;;
|
||||
*)
|
||||
exec "$@"
|
|
@ -0,0 +1,4 @@
|
|||
[options]
|
||||
addons_path = /mnt/extra-addons,/opt/posbox/addons
|
||||
data_dir = /var/lib/odoo
|
||||
admin_passwd = S3Cur3Passw0rd
|
|
@ -0,0 +1,22 @@
|
|||
=== modified file 'v4l2.py'
|
||||
--- v4l2.py 2010-07-22 01:07:58 +0000
|
||||
+++ v4l2.py 2018-03-03 01:59:16 +0000
|
||||
@@ -194,7 +194,7 @@
|
||||
V4L2_BUF_TYPE_SLICED_VBI_OUTPUT,
|
||||
V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY,
|
||||
V4L2_BUF_TYPE_PRIVATE,
|
||||
-) = range(1, 9) + [0x80]
|
||||
+) = list(range(1, 9)) + [0x80]
|
||||
|
||||
|
||||
v4l2_ctrl_type = enum
|
||||
@@ -245,7 +245,7 @@
|
||||
V4L2_PRIORITY_INTERACTIVE,
|
||||
V4L2_PRIORITY_RECORD,
|
||||
V4L2_PRIORITY_DEFAULT,
|
||||
-) = range(0, 4) + [2]
|
||||
+) = list(range(0, 4)) + [2]
|
||||
|
||||
|
||||
class v4l2_rect(ctypes.Structure):
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
FROM local/odoo:11
|
||||
FROM odoo:12
|
||||
|
||||
USER root
|
||||
|
||||
|
@ -11,8 +11,8 @@ RUN apt-get update \
|
|||
USER odoo
|
||||
|
||||
## Get pos-addons for pos_printer_network
|
||||
RUN git clone --depth=1 -b 11.0 https://github.com/it-projects-llc/pos-addons.git \
|
||||
RUN git clone --depth=1 -b 12.0 https://github.com/it-projects-llc/pos-addons.git \
|
||||
/opt/posbox/addons
|
||||
|
||||
COPY odoo.conf /etc/odoo/odoo.conf
|
||||
COPY bootstrap.min.js /usr/lib/python3/dist-packages/odoo/addons/web/static/lib/bootstrap/js/
|
||||
#COPY bootstrap.min.js /usr/lib/python3/dist-packages/odoo/addons/web/static/lib/bootstrap/js/
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
FROM debian:jessie
|
||||
MAINTAINER Odoo S.A. <info@odoo.com>
|
||||
|
||||
# Install some deps, lessc and less-plugin-clean-css, and wkhtmltopdf
|
||||
RUN set -x; \
|
||||
apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
curl \
|
||||
git \
|
||||
node-clean-css \
|
||||
node-less \
|
||||
python-gevent \
|
||||
python-netifaces \
|
||||
python-pip \
|
||||
python-pyinotify \
|
||||
python-renderpm \
|
||||
python-support \
|
||||
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false -o APT::AutoRemove::SuggestsImportant=false npm \
|
||||
&& rm -rf /var/lib/apt/lists/* wkhtmltox.deb \
|
||||
&& pip install psycogreen==1.0
|
||||
|
||||
# Install Odoo
|
||||
ENV ODOO_VERSION 8.0
|
||||
ENV ODOO_RELEASE 20170815
|
||||
RUN set -x; \
|
||||
curl -o odoo.deb -SL http://nightly.odoo.com/${ODOO_VERSION}/nightly/deb/odoo_${ODOO_VERSION}.${ODOO_RELEASE}_all.deb \
|
||||
&& echo '5835e966a07e5684b4f7bcc39585276b0bb68254 odoo.deb' | sha1sum -c - \
|
||||
&& dpkg --force-depends -i odoo.deb \
|
||||
&& apt-get update \
|
||||
&& apt-get -y install -f --no-install-recommends \
|
||||
&& rm -rf /var/lib/apt/lists/* odoo.deb
|
||||
|
||||
# Copy entrypoint script and Odoo configuration file
|
||||
COPY ./entrypoint.sh /
|
||||
COPY ./openerp-server.conf /etc/odoo/
|
||||
RUN chown odoo /etc/odoo/openerp-server.conf
|
||||
|
||||
# Mount /var/lib/odoo to allow restoring filestore and /mnt/extra-addons for users addons
|
||||
RUN mkdir -p /mnt/extra-addons \
|
||||
&& chown -R odoo /mnt/extra-addons
|
||||
VOLUME ["/var/lib/odoo", "/mnt/extra-addons"]
|
||||
|
||||
# Expose Odoo services
|
||||
EXPOSE 8069 8071
|
||||
|
||||
# Set the default config file
|
||||
ENV OPENERP_SERVER /etc/odoo/openerp-server.conf
|
||||
|
||||
## Prepare for pos-addons / hw_printer_network
|
||||
RUN pip install -U pip setuptools \
|
||||
&& pip install python-escpos
|
||||
|
||||
# hw_printer_network is located in 11.0 branch, but works with earler versions too
|
||||
RUN git clone --depth=1 -b 11.0 https://github.com/it-projects-llc/pos-addons.git \
|
||||
/opt/posbox/addons
|
||||
|
||||
COPY hw_screen_main.py /usr/lib/python3/dist-packages/odoo/addons/hw_screen/controllers/main.py
|
||||
COPY hw_screen_main.py /usr/lib/python2.7/dist-packages/openerp/addons/hw_screen/controllers/main.py
|
||||
COPY hw_printer_network_controller.py /opt/posbox/addons/hw_printer_network/controllers/hw_printer_network_controller.py
|
||||
|
||||
# Set default user when running the container
|
||||
USER odoo
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["openerp-server"]
|
|
@ -1,209 +0,0 @@
|
|||
# Copyright 2017-2018 Dinar Gabbasov <https://it-projects.info/team/GabbasovDinar>
|
||||
# Copyright 2018 Tom Blauwendraat <tom@sunflowerweb.nl>
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from openerp import http
|
||||
import logging
|
||||
import time
|
||||
import socket
|
||||
import subprocess
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
try:
|
||||
from openerp.addons.hw_escpos.escpos import escpos
|
||||
from openerp.addons.hw_escpos.controllers.main import EscposProxy
|
||||
from openerp.addons.hw_escpos.controllers.main import EscposDriver
|
||||
from openerp.addons.hw_escpos.escpos.printer import Network
|
||||
import openerp.addons.hw_proxy.controllers.main as hw_proxy
|
||||
except ImportError:
|
||||
EscposProxy = object
|
||||
EscposDriver = object
|
||||
|
||||
|
||||
class PingProcess(threading.Thread):
|
||||
def __init__(self, ip):
|
||||
self.stdout = None
|
||||
self.stderr = None
|
||||
threading.Thread.__init__(self)
|
||||
self.status = 'offline'
|
||||
self.ip = ip
|
||||
self.stop = False
|
||||
|
||||
def run(self):
|
||||
while not self.stop:
|
||||
child = subprocess.Popen(
|
||||
["ping", "-c1", "-w5", self.ip],
|
||||
shell=False,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
)
|
||||
child.communicate()
|
||||
self.status = 'offline' if child.returncode else 'online'
|
||||
time.sleep(1)
|
||||
|
||||
def get_status(self):
|
||||
return self.status
|
||||
|
||||
def __del__(self):
|
||||
self.stop = True
|
||||
|
||||
|
||||
class EscposNetworkDriver(EscposDriver):
|
||||
|
||||
def __init__(self):
|
||||
self.network_printers = []
|
||||
self.ping_processes = {}
|
||||
self.printer_objects = {}
|
||||
super(EscposNetworkDriver, self).__init__()
|
||||
|
||||
def get_network_printer(self, ip, name=None):
|
||||
found_printer = False
|
||||
for printer in self.network_printers:
|
||||
if printer['ip'] == ip:
|
||||
found_printer = True
|
||||
if name:
|
||||
printer['name'] = name
|
||||
if printer['status'] == 'online':
|
||||
printer_object = self.printer_objects.get(ip, None)
|
||||
if not printer_object:
|
||||
try:
|
||||
printer_object = Network(ip)
|
||||
self.printer_objects[ip] = printer_object
|
||||
except socket.error:
|
||||
pass
|
||||
return printer
|
||||
if not found_printer:
|
||||
self.add_network_printer(ip, name)
|
||||
return None
|
||||
|
||||
def add_network_printer(self, ip, name=None):
|
||||
printer = dict(
|
||||
ip=ip,
|
||||
status='offline',
|
||||
name=name or 'Unnamed printer'
|
||||
)
|
||||
self.network_printers.append(printer) # dont return because offline
|
||||
self.start_pinging(ip)
|
||||
|
||||
def start_pinging(self, ip):
|
||||
pinger = PingProcess(ip)
|
||||
self.ping_processes[ip] = pinger
|
||||
pinger.start()
|
||||
|
||||
def update_driver_status(self):
|
||||
count = len([p for p in self.network_printers if p.get('status', None) == 'online'])
|
||||
if count:
|
||||
self.set_status('connected', '{} printer(s) Connected'.format(count))
|
||||
else:
|
||||
self.set_status('disconnected', 'Disconnected')
|
||||
|
||||
def run(self):
|
||||
if not escpos:
|
||||
_logger.error('ESC/POS cannot initialize, please verify system dependencies.')
|
||||
return
|
||||
while True:
|
||||
try:
|
||||
error = True
|
||||
timestamp, task, data = self.queue.get(True)
|
||||
if task == 'xml_receipt':
|
||||
error = False
|
||||
if timestamp >= (time.time() - 1 * 60 * 60):
|
||||
receipt, network_printer_ip = data
|
||||
printer_info = self.get_network_printer(network_printer_ip)
|
||||
printer = self.printer_objects.get(network_printer_ip, None)
|
||||
if printer_info and printer_info['status'] == 'online' and printer:
|
||||
_logger.info('Printing XML receipt on printer %s...', network_printer_ip)
|
||||
try:
|
||||
printer.receipt(receipt)
|
||||
except socket.error:
|
||||
printer.open()
|
||||
printer.receipt(receipt)
|
||||
_logger.info('Done printing XML receipt on printer %s', network_printer_ip)
|
||||
else:
|
||||
_logger.error('xml_receipt: printer offline!')
|
||||
# add a missed order to queue
|
||||
time.sleep(3)
|
||||
self.queue.put((timestamp, task, data))
|
||||
elif task == 'cashbox':
|
||||
printer_info = self.get_network_printer(data)
|
||||
printer = self.printer_objects.get(data, None)
|
||||
if printer_info and printer_info['status'] == 'online' and printer:
|
||||
_logger.info('Opening cashbox on printer %s...', data)
|
||||
try:
|
||||
printer.cashdraw(2)
|
||||
printer.cashdraw(5)
|
||||
except socket.error:
|
||||
printer.open()
|
||||
printer.cashdraw(2)
|
||||
printer.cashdraw(5)
|
||||
_logger.info('Done opening cashbox on printer %s', data)
|
||||
else:
|
||||
_logger.error('cashbox: printer offline!')
|
||||
elif task == 'printstatus':
|
||||
pass
|
||||
elif task == 'status':
|
||||
error = False
|
||||
for printer in self.network_printers:
|
||||
ip = printer['ip']
|
||||
pinger = self.ping_processes.get(ip, None)
|
||||
if pinger and pinger.isAlive():
|
||||
status = pinger.get_status()
|
||||
if status != printer['status']:
|
||||
# todo: use a lock?
|
||||
printer['status'] = status
|
||||
self.update_driver_status()
|
||||
else:
|
||||
self.start_pinging(ip)
|
||||
error = False
|
||||
except Exception as e:
|
||||
self.set_status('error', str(e))
|
||||
errmsg = str(e) + '\n' + '-'*60+'\n' + traceback.format_exc() + '-'*60 + '\n'
|
||||
_logger.error(errmsg)
|
||||
finally:
|
||||
if error:
|
||||
self.queue.put((timestamp, task, data))
|
||||
|
||||
|
||||
# Separate instance, mainloop and queue for network printers
|
||||
# original driver runs in parallel and deals with USB printers
|
||||
network_driver = EscposNetworkDriver()
|
||||
|
||||
hw_proxy.drivers['escpos_network'] = network_driver
|
||||
|
||||
# this will also start the message handling loop
|
||||
network_driver.push_task('printstatus')
|
||||
|
||||
|
||||
class UpdatedEscposProxy(EscposProxy):
|
||||
@http.route('/hw_proxy/print_xml_receipt', type='json', auth='none', cors='*')
|
||||
def print_xml_receipt(self, receipt, proxy=None):
|
||||
if proxy:
|
||||
_logger.info('print_xml_receipt proxy %s', proxy)
|
||||
network_driver.push_task('xml_receipt', (receipt, proxy))
|
||||
else:
|
||||
super(UpdatedEscposProxy, self).print_xml_receipt(receipt)
|
||||
|
||||
@http.route('/hw_proxy/open_cashbox', type='json', auth='none', cors='*')
|
||||
def open_cashbox(self, proxy=None):
|
||||
_logger.info('Open cashbox via network printer')
|
||||
network_driver.push_task('cashbox', '192.168.233.3')
|
||||
|
||||
@http.route('/hw_proxy/network_printers', type='json', auth='none', cors='*')
|
||||
def network_printers(self, network_printers=None):
|
||||
for printer in network_printers:
|
||||
network_driver.get_network_printer(printer['ip'], name=printer['name'])
|
||||
|
||||
@http.route('/hw_proxy/status_network_printers', type='json', auth='none', cors='*')
|
||||
def network_printers_status(self):
|
||||
return network_driver.network_printers
|
||||
|
||||
@http.route('/hw_proxy/without_usb', type='http', auth='none', cors='*')
|
||||
def without_usb(self):
|
||||
""" Old pos_printer_network module expects this to work """
|
||||
return "ping"
|
||||
|
|
@ -1,195 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2015 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
|
||||
from openerp import http
|
||||
from openerp.tools import config
|
||||
from openerp.addons.web.controllers import main as web
|
||||
from openerp.addons.hw_posbox_homepage.controllers import main as homepage
|
||||
|
||||
import logging
|
||||
import netifaces as ni
|
||||
import os
|
||||
from subprocess import call
|
||||
import time
|
||||
import threading
|
||||
|
||||
self_port = str(config['xmlrpc_port'] or 8069)
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class Homepage(homepage.PosboxHomepage):
|
||||
|
||||
def get_hw_screen_message(self):
|
||||
return """
|
||||
<p>
|
||||
If you need to display the current customer basket on another device, you can do it <a href='/point_of_sale/display'>here</a>.
|
||||
</p>
|
||||
"""
|
||||
|
||||
class HardwareScreen(web.Home):
|
||||
|
||||
event_data = threading.Event()
|
||||
pos_client_data = {'rendered_html': False,
|
||||
'ip_from': False}
|
||||
display_in_use = ''
|
||||
failure_count = {}
|
||||
|
||||
def _call_xdotools(self, keystroke):
|
||||
os.environ['DISPLAY'] = ":0.0"
|
||||
os.environ['XAUTHORITY'] = "/run/lightdm/pi/xauthority"
|
||||
try:
|
||||
call(['xdotool', 'key', keystroke])
|
||||
return "xdotool succeeded in stroking " + keystroke
|
||||
except:
|
||||
return "xdotool threw an error, maybe it is not installed on the posbox"
|
||||
|
||||
@http.route('/hw_proxy/display_refresh', type='json', auth='none', cors='*')
|
||||
def display_refresh(self):
|
||||
return self._call_xdotools('F5')
|
||||
|
||||
# POS CASHIER'S ROUTES
|
||||
@http.route('/hw_proxy/customer_facing_display', type='json', auth='none', cors='*')
|
||||
def update_user_facing_display(self, html=None):
|
||||
request_ip = http.request.httprequest.remote_addr
|
||||
if request_ip == HardwareScreen.pos_client_data.get('ip_from', ''):
|
||||
HardwareScreen.pos_client_data['rendered_html'] = html
|
||||
HardwareScreen.event_data.set()
|
||||
|
||||
return {'status': 'updated'}
|
||||
else:
|
||||
return {'status': 'failed'}
|
||||
|
||||
@http.route('/hw_proxy/take_control', type='json', auth='none', cors='*')
|
||||
def take_control(self, html=None):
|
||||
# ALLOW A CASHIER TO TAKE CONTROL OVER THE POSBOX, IN CASE OF MULTIPLE CASHIER PER POSBOX
|
||||
HardwareScreen.pos_client_data['rendered_html'] = html
|
||||
HardwareScreen.pos_client_data['ip_from'] = http.request.httprequest.remote_addr
|
||||
HardwareScreen.event_data.set()
|
||||
|
||||
return {'status': 'success',
|
||||
'message': 'You now have access to the display'}
|
||||
|
||||
@http.route('/hw_proxy/test_ownership', type='json', auth='none', cors='*')
|
||||
def test_ownership(self):
|
||||
if HardwareScreen.pos_client_data.get('ip_from') == http.request.httprequest.remote_addr:
|
||||
return {'status': 'OWNER'}
|
||||
else:
|
||||
return {'status': 'NOWNER'}
|
||||
|
||||
# POSBOX ROUTES (SELF)
|
||||
@http.route('/point_of_sale/display', type='http', auth='none')
|
||||
def render_main_display(self):
|
||||
return self._get_html()
|
||||
|
||||
@http.route('/point_of_sale/get_serialized_order', type='json', auth='none')
|
||||
def get_serialized_order(self):
|
||||
request_addr = http.request.httprequest.remote_addr
|
||||
result = HardwareScreen.pos_client_data
|
||||
if HardwareScreen.display_in_use and request_addr != HardwareScreen.display_in_use:
|
||||
if not HardwareScreen.failure_count.get(request_addr):
|
||||
HardwareScreen.failure_count[request_addr] = 0
|
||||
if HardwareScreen.failure_count[request_addr] > 0:
|
||||
time.sleep(10)
|
||||
HardwareScreen.failure_count[request_addr] += 1
|
||||
return {'rendered_html': """<div class="pos-customer_facing_display"><p>Not Authorized. Another browser is in use to display for the client. Please refresh.</p></div> """,
|
||||
'stop_longpolling': True,
|
||||
'ip_from': request_addr}
|
||||
|
||||
# IMPLEMENTATION OF LONGPOLLING
|
||||
# Times out 2 seconds before the JS request does
|
||||
if HardwareScreen.event_data.wait(28):
|
||||
HardwareScreen.event_data.clear()
|
||||
HardwareScreen.failure_count[request_addr] = 0
|
||||
return result
|
||||
return {'rendered_html': False,
|
||||
'ip_from': HardwareScreen.pos_client_data['ip_from']}
|
||||
|
||||
def _get_html(self):
|
||||
cust_js = None
|
||||
interfaces = ni.interfaces()
|
||||
my_ip = '127.0.0.1'
|
||||
HardwareScreen.display_in_use = http.request.httprequest.remote_addr
|
||||
|
||||
with open(os.path.join(os.path.dirname(__file__), "../static/src/js/worker.js")) as js:
|
||||
cust_js = js.read()
|
||||
|
||||
with open(os.path.join(os.path.dirname(__file__), "../static/src/css/cust_css.css")) as css:
|
||||
cust_css = css.read()
|
||||
|
||||
display_ifaces = ""
|
||||
for iface_id in interfaces:
|
||||
iface_obj = ni.ifaddresses(iface_id)
|
||||
ifconfigs = iface_obj.get(ni.AF_INET, [])
|
||||
for conf in ifconfigs:
|
||||
if conf.get('addr'):
|
||||
display_ifaces += "<tr><td>" + iface_id + "</td>"
|
||||
display_ifaces += "<td>" + conf.get('addr') + "</td>"
|
||||
display_ifaces += "<td>" + conf.get('netmask') + "</td></tr>"
|
||||
# What is my external IP ?
|
||||
if iface_id != 'lo':
|
||||
my_ip = conf.get('addr')
|
||||
|
||||
my_ip_port = my_ip + ":" + self_port
|
||||
|
||||
html = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title class="origin">Odoo -- Point of Sale</title>
|
||||
<script type="text/javascript" class="origin" src="http://192.168.25.132:8069/web/static/lib/jquery/jquery.js" >
|
||||
</script>
|
||||
<script type="text/javascript" class="origin">
|
||||
""" + cust_js + """
|
||||
</script>
|
||||
<link rel="stylesheet" class="origin" href="http://192.168.25.132:8069/web/static/lib/bootstrap/css/bootstrap.css" >
|
||||
</link>
|
||||
<script class="origin" src="http://192.168.25.132:8069/web/static/lib/bootstrap/js/bootstrap.min.js"></script>
|
||||
<style class="origin">
|
||||
""" + cust_css + """
|
||||
</style>
|
||||
</head>
|
||||
<head>
|
||||
<body class="original_body">
|
||||
<div hidden class="shadow"></div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
<h1>Odoo Point of Sale</h1>
|
||||
<h2>POSBox Client display</h2>
|
||||
<h3>My IPs</h3>
|
||||
<table id="table_ip" class="table table-condensed">
|
||||
<tr>
|
||||
<th>Interface</th>
|
||||
<th>IP</th>
|
||||
<th>Netmask</th>
|
||||
</tr>
|
||||
""" + display_ifaces + """
|
||||
</table>
|
||||
<p>The customer cart will be displayed here once a Point of Sale session is started.</p>
|
||||
<p>Odoo version 11 or above is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return html
|
|
@ -1,3 +0,0 @@
|
|||
[options]
|
||||
addons_path = /opt/posbox/addons
|
||||
data_dir = /var/lib/odoo
|
Reference in New Issue