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)
|
[pos-addons](https://github.com/it-projects-llc/pos-addons)
|
||||||
for using receipt printers over the network.
|
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
|
## Backup configuration
|
||||||
|
|
||||||
Example contents of `backup.env`:
|
Example contents of `backup.env`:
|
||||||
|
|
|
@ -8,12 +8,16 @@ services:
|
||||||
- 80:8069
|
- 80:8069
|
||||||
volumes:
|
volumes:
|
||||||
- odoo-web-data:/var/lib/odoo
|
- odoo-web-data:/var/lib/odoo
|
||||||
|
environment:
|
||||||
|
- HOST=db
|
||||||
|
- USER=odoo
|
||||||
|
- PASSWORD=odoo
|
||||||
networks:
|
networks:
|
||||||
localnet:
|
localnet:
|
||||||
ipv4_address: 10.5.0.2
|
ipv4_address: 10.5.0.2
|
||||||
posbox:
|
iotbox:
|
||||||
build: ./posbox
|
build: ./iotbox
|
||||||
command: -- --load=web,hw_proxy,hw_posbox_homepage,hw_escpos,hw_screen,hw_printer_network
|
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:
|
ports:
|
||||||
- 8070:8069
|
- 8070:8069
|
||||||
- 8072:8072
|
- 8072:8072
|
||||||
|
@ -23,8 +27,9 @@ services:
|
||||||
localnet:
|
localnet:
|
||||||
ipv4_address: 10.5.0.3
|
ipv4_address: 10.5.0.3
|
||||||
db:
|
db:
|
||||||
image: postgres:9.6
|
image: postgres:10
|
||||||
environment:
|
environment:
|
||||||
|
- POSTGRES_DB=postgres
|
||||||
- POSTGRES_PASSWORD=odoo
|
- POSTGRES_PASSWORD=odoo
|
||||||
- POSTGRES_USER=odoo
|
- POSTGRES_USER=odoo
|
||||||
- PGDATA=/var/lib/postgresql/data/pgdata
|
- PGDATA=/var/lib/postgresql/data/pgdata
|
||||||
|
@ -33,28 +38,28 @@ services:
|
||||||
networks:
|
networks:
|
||||||
localnet:
|
localnet:
|
||||||
ipv4_address: 10.5.0.4
|
ipv4_address: 10.5.0.4
|
||||||
backup:
|
# backup:
|
||||||
build: ./backup
|
# build: ./backup
|
||||||
environment:
|
# environment:
|
||||||
- PGHOST=db
|
# - PGHOST=db
|
||||||
- PGUSER=odoo
|
# - PGUSER=odoo
|
||||||
- PGPASSWORD=odoo
|
# - PGPASSWORD=odoo
|
||||||
- BACKUP_SCHEDULE=0 19 * * *
|
# - BACKUP_SCHEDULE=0 19 * * *
|
||||||
env_file: backup.env
|
# env_file: backup.env
|
||||||
volumes:
|
# volumes:
|
||||||
- odoo-db-data:/data/pg_raw:ro
|
# - odoo-db-data:/data/pg_raw:ro
|
||||||
- /home/pi:/data/home-pi:ro
|
# - /home/pi:/data/home-pi:ro
|
||||||
networks:
|
# networks:
|
||||||
localnet:
|
# localnet:
|
||||||
ipv4_address: 10.5.0.5
|
# ipv4_address: 10.5.0.5
|
||||||
monitoring:
|
# monitoring:
|
||||||
build: ./monitoring
|
# build: ./monitoring
|
||||||
command: checkup every 10m
|
# command: checkup every 10m
|
||||||
volumes:
|
# volumes:
|
||||||
- ./checkup.json:/opt/checkup/checkup.json:ro
|
# - ./checkup.json:/opt/checkup/checkup.json:ro
|
||||||
networks:
|
# networks:
|
||||||
localnet:
|
# localnet:
|
||||||
ipv4_address: 10.5.0.6
|
# ipv4_address: 10.5.0.6
|
||||||
volumes:
|
volumes:
|
||||||
odoo-web-data:
|
odoo-web-data:
|
||||||
odoo-db-data:
|
odoo-db-data:
|
||||||
|
|
78
iotbox/Dockerfile
Normal file
78
iotbox/Dockerfile
Normal file
|
@ -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 /
|
6
iotbox/bootstrap.min.js
vendored
Normal file
6
iotbox/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -13,7 +13,7 @@ DB_ARGS=()
|
||||||
function check_config() {
|
function check_config() {
|
||||||
param="$1"
|
param="$1"
|
||||||
value="$2"
|
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+=("--${param}")
|
||||||
DB_ARGS+=("${value}")
|
DB_ARGS+=("${value}")
|
||||||
fi;
|
fi;
|
||||||
|
@ -24,16 +24,18 @@ check_config "db_user" "$USER"
|
||||||
check_config "db_password" "$PASSWORD"
|
check_config "db_password" "$PASSWORD"
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
-- | openerp-server)
|
-- | odoo)
|
||||||
shift
|
shift
|
||||||
|
/etc/init.d/cups start
|
||||||
|
/etc/init.d/dbus start
|
||||||
if [[ "$1" == "scaffold" ]] ; then
|
if [[ "$1" == "scaffold" ]] ; then
|
||||||
exec openerp-server "$@"
|
exec odoo "$@"
|
||||||
else
|
else
|
||||||
exec openerp-server "$@" "${DB_ARGS[@]}"
|
exec odoo "$@" "${DB_ARGS[@]}"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
-*)
|
-*)
|
||||||
exec openerp-server "$@" "${DB_ARGS[@]}"
|
exec odoo "$@" "${DB_ARGS[@]}"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
exec "$@"
|
exec "$@"
|
4
iotbox/odoo.conf
Normal file
4
iotbox/odoo.conf
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[options]
|
||||||
|
addons_path = /mnt/extra-addons,/opt/posbox/addons
|
||||||
|
data_dir = /var/lib/odoo
|
||||||
|
admin_passwd = S3Cur3Passw0rd
|
22
iotbox/python-v4l2-1664158-fix.patch
Normal file
22
iotbox/python-v4l2-1664158-fix.patch
Normal file
|
@ -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
|
USER root
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@ RUN apt-get update \
|
||||||
USER odoo
|
USER odoo
|
||||||
|
|
||||||
## Get pos-addons for pos_printer_network
|
## 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
|
/opt/posbox/addons
|
||||||
|
|
||||||
COPY odoo.conf /etc/odoo/odoo.conf
|
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 a new issue