From b93becdf8c21b4afc1ff676b293373537222414f Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 8 Jul 2014 19:55:57 +0200 Subject: [PATCH 01/27] Initial check-in of the module hw_customer_display. --- hw_customer_display/__init__.py | 24 +++ hw_customer_display/__openerp__.py | 56 ++++++ hw_customer_display/controllers/__init__.py | 24 +++ hw_customer_display/controllers/main.py | 182 ++++++++++++++++++++ 4 files changed, 286 insertions(+) create mode 100644 hw_customer_display/__init__.py create mode 100644 hw_customer_display/__openerp__.py create mode 100644 hw_customer_display/controllers/__init__.py create mode 100644 hw_customer_display/controllers/main.py diff --git a/hw_customer_display/__init__.py b/hw_customer_display/__init__.py new file mode 100644 index 00000000..d362d317 --- /dev/null +++ b/hw_customer_display/__init__.py @@ -0,0 +1,24 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Hardware Customer Display module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# +# 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 . +# +############################################################################## + + +from . import controllers diff --git a/hw_customer_display/__openerp__.py b/hw_customer_display/__openerp__.py new file mode 100644 index 00000000..59ca8f19 --- /dev/null +++ b/hw_customer_display/__openerp__.py @@ -0,0 +1,56 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Hardware Customer Display module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# +# 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 . +# +############################################################################## + + +{ + 'name': 'Hardware Customer Display', + 'version': '0.1', + 'category': 'Hardware Drivers', + 'license': 'AGPL-3', + 'summary': 'Adds a support for Customer LCD in Point of Sale', + 'description': """ +Hardware Customer Display +========================= + +This module adds support for Customer Display in the Point of Sale. It has been tested with a Bixolon BCD-1100 (http://www.bixolon.com/html/en/product/product_detail.xhtml?prod_id=61), but should support most serial and USB-serial LCD displays out-of-the-box or with inheritance of a few functions. This module is designed to be installed on the *POSbox* (i.e. the proxy on which the USB devices are connected) and not on the Odoo server. + +The configuration of the hardware is done in the configuration file of the Odoo server of the POSbox. You should add the following entries in the configuration file: + +* customer_display_device_name (default = /dev/ttyUSB0) +* customer_display_device_rate (default = 9600) +* customer_display_device_timeout (default = 2 seconds) +* customer_display_device_rows (default = 2) +* customer_display_device_cols (default = 20) + +This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. + +Please contact Alexis de Lattre from Akretion for any help or question about this module. + """, + 'author': 'Akretion', + 'website': 'http://www.akretion.com', + 'depends': ['hw_proxy'], + 'external_dependencies': { + 'python' : ['serial', 'unidecode'], + }, + 'data': [], + 'active': False, +} diff --git a/hw_customer_display/controllers/__init__.py b/hw_customer_display/controllers/__init__.py new file mode 100644 index 00000000..76e27a99 --- /dev/null +++ b/hw_customer_display/controllers/__init__.py @@ -0,0 +1,24 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Hardware Customer Display module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# +# 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 . +# +############################################################################## + + +from . import main diff --git a/hw_customer_display/controllers/main.py b/hw_customer_display/controllers/main.py new file mode 100644 index 00000000..e3a314a2 --- /dev/null +++ b/hw_customer_display/controllers/main.py @@ -0,0 +1,182 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Hardware Customer Display module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# +# 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 . +# +############################################################################## + + +import logging +import simplejson +import time +from threading import Thread, Lock +from Queue import Queue +from unidecode import unidecode +from serial import Serial +import openerp.addons.hw_proxy.controllers.main as hw_proxy +from openerp import http +from openerp.tools.config import config + + +logger = logging.getLogger(__name__) + + +class CustomerDisplayDriver(Thread): + def __init__(self): + Thread.__init__(self) + self.queue = Queue() + self.lock = Lock() + self.status = {'status': 'connecting', 'messages': []} + self.device_name = config.get( + 'customer_display_device_name', '/dev/ttyUSB0') + self.device_rate = int(config.get( + 'customer_display_device_rate', 9600)) + self.device_timeout = int(config.get( + 'customer_display_device_timeout', 2)) + self.device_rows = int(config.get( + 'customer_display_device_rows', 2)) + self.device_cols = int(config.get( + 'customer_display_device_cols', 20)) + self.serial = False + + def get_status(self): + self.push_task('status') + return self.status + + def set_status(self, status, message=None): + if status == self.status['status']: + if message is not None and message != self.status['messages'][-1]: + self.status['messages'].append(message) + else: + self.status['status'] = status + if message: + self.status['messages'] = [message] + else: + self.status['messages'] = [] + + if status == 'error' and message: + logger.error('Display Error: '+message) + elif status == 'disconnected' and message: + logger.warning('Disconnected Display: '+message) + + def lockedstart(self): + with self.lock: + if not self.isAlive(): + self.daemon = True + self.start() + + def push_task(self, task, data=None): + self.lockedstart() + self.queue.put((time.time(), task, data)) + + def move_cursor(self, col, row): + # Bixolon spec : 11. "Move Cursor to Specified Position" + self.cmd_serial_write('\x1B\x6C' + chr(col) + chr(row)) + + def display_text(self, lines): + logger.debug( + "Preparing to send the following lines to LCD: %s" % lines) + if len(lines) > self.device_rows: + logger.error( + 'Odoo POS sends %d rows but LCD only has %d rows' + % (len(lines), self.device_rows)) + return + assert len(lines) <= self.device_rows, 'Too many lines' + lines_ascii = [] + for line in lines: + lines_ascii.append(unidecode(line)) + row = 0 + for dline in lines_ascii: + row += 1 + self.move_cursor(1, row) + if len(line) > self.device_cols: + logger.error( + 'Odoo POS sends %d characters but LCD only has %d cols' + % (len(line), self.device_cols)) + return + self.serial_write(dline) + + def setup_customer_display(self): + '''Set LCD cursor to off + If your LCD has different setup instruction(s), you should + inherit this function''' + # Bixolon spec : 35. "Set Cursor On/Off" + self.cmd_serial_write('\x1F\x43\x00') + logger.debug('LCD cursor set to off') + + def clear_customer_display(self): + '''If your LCD has different clearing instruction, you should inherit + this function''' + # Bixolon spec : 12. "Clear Display Screen and Clear String Mode" + self.cmd_serial_write('\x0C') + logger.debug('Customer display cleared') + + def cmd_serial_write(self, command): + '''If your LCD requires a prefix and/or suffix on all commands, + you should inherit this function''' + assert isinstance(command, str), 'command must be a string' + self.serial_write(command) + + def serial_write(self, text): + assert isinstance(text, str), 'text must be a string' + self.serial.write(text) + + def send_text_customer_display(self, text_to_display): + lines = simplejson.loads(text_to_display) + assert isinstance(lines, list), 'lines_list should be a list' + try: + logger.debug( + 'Opening serial port %s for customer display with baudrate %d' + % (self.device_name, self.device_rate)) + self.serial = Serial( + self.device_name, self.device_rate, + timeout=self.device_timeout) + logger.debug('serial.is_open = %s' % self.serial.isOpen()) + self.setup_customer_display() + self.clear_customer_display() + self.display_text(lines) + except Exception, e: + logger.error('Exception in serial connection: %s' % str(e)) + finally: + if self.serial: + logger.debug('Closing serial port for customer display') + self.serial.close() + + def run(self): + while True: + try: + timestamp, task, data = self.queue.get(True) + if task == 'display': + self.send_text_customer_display(data) + elif task == 'status': + pass + except Exception as e: + self.set_status('error', str(e)) + +driver = CustomerDisplayDriver() + +hw_proxy.drivers['customer_display'] = driver + + +class CustomerDisplayProxy(hw_proxy.Proxy): + @http.route( + '/hw_proxy/send_text_customer_display', type='json', auth='none', + cors='*') + def send_text_customer_display(self, text_to_display): + logger.debug('LCD: Call send_text_customer_display') + driver.push_task('display', text_to_display) From 62c156f9cabe6ad7cb5095b0359d31ae0126b121 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Wed, 9 Jul 2014 11:04:10 +0200 Subject: [PATCH 02/27] Add test script, to test the display without odoo server Update description Remove test on number of row/cols, because it is already tested on POS client JS code. --- hw_customer_display/__init__.py | 2 +- hw_customer_display/__openerp__.py | 14 ++-- hw_customer_display/controllers/__init__.py | 2 +- hw_customer_display/controllers/main.py | 28 ++++---- .../test-scripts/customer-display-test.py | 68 +++++++++++++++++++ 5 files changed, 90 insertions(+), 24 deletions(-) create mode 100755 hw_customer_display/test-scripts/customer-display-test.py diff --git a/hw_customer_display/__init__.py b/hw_customer_display/__init__.py index d362d317..0914de44 100644 --- a/hw_customer_display/__init__.py +++ b/hw_customer_display/__init__.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# Hardware Customer Display module for OpenERP +# Hardware Customer Display module for Odoo # Copyright (C) 2014 Akretion (http://www.akretion.com) # @author Alexis de Lattre # diff --git a/hw_customer_display/__openerp__.py b/hw_customer_display/__openerp__.py index 59ca8f19..ef05e105 100644 --- a/hw_customer_display/__openerp__.py +++ b/hw_customer_display/__openerp__.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# Hardware Customer Display module for OpenERP +# Hardware Customer Display module for Odoo # Copyright (C) 2014 Akretion (http://www.akretion.com) # @author Alexis de Lattre # @@ -26,22 +26,24 @@ 'version': '0.1', 'category': 'Hardware Drivers', 'license': 'AGPL-3', - 'summary': 'Adds a support for Customer LCD in Point of Sale', + 'summary': 'Adds support for Customer Display in the Point of Sale', 'description': """ Hardware Customer Display ========================= -This module adds support for Customer Display in the Point of Sale. It has been tested with a Bixolon BCD-1100 (http://www.bixolon.com/html/en/product/product_detail.xhtml?prod_id=61), but should support most serial and USB-serial LCD displays out-of-the-box or with inheritance of a few functions. This module is designed to be installed on the *POSbox* (i.e. the proxy on which the USB devices are connected) and not on the Odoo server. +This module adds support for Customer Display in the Point of Sale. This module is designed to be installed on the *POSbox* (i.e. the proxy on which the USB devices are connected) and not on the main Odoo server. On the main Odoo server, you should install the module *pos_customer_display*. The configuration of the hardware is done in the configuration file of the Odoo server of the POSbox. You should add the following entries in the configuration file: * customer_display_device_name (default = /dev/ttyUSB0) * customer_display_device_rate (default = 9600) * customer_display_device_timeout (default = 2 seconds) -* customer_display_device_rows (default = 2) -* customer_display_device_cols (default = 20) -This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. +The number of rows and cols of the Customer Display (usually 2 x 20) should be configured on the main Odoo server, in the menu Point of Sale > Configuration > Point of Sales. + +It has been tested with a Bixolon BCD-1100 (http://www.bixolon.com/html/en/product/product_detail.xhtml?prod_id=61), but should support most serial and USB-serial LCD displays out-of-the-box or with inheritance of a few functions. To setup the BCD-1100 on Linux, you will find some technical instructions on this page : http://techtuxwords.blogspot.fr/2012/12/linux-and-bixolon-bcd-1100.html + +This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. This module is part of the POS project of the Odoo Community Association http://odoo-community.org/. You are invited to become a member and/or get involved in the Association ! Please contact Alexis de Lattre from Akretion for any help or question about this module. """, diff --git a/hw_customer_display/controllers/__init__.py b/hw_customer_display/controllers/__init__.py index 76e27a99..7ae94c9e 100644 --- a/hw_customer_display/controllers/__init__.py +++ b/hw_customer_display/controllers/__init__.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# Hardware Customer Display module for OpenERP +# Hardware Customer Display module for Odoo # Copyright (C) 2014 Akretion (http://www.akretion.com) # @author Alexis de Lattre # diff --git a/hw_customer_display/controllers/main.py b/hw_customer_display/controllers/main.py index e3a314a2..6d40289b 100644 --- a/hw_customer_display/controllers/main.py +++ b/hw_customer_display/controllers/main.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# Hardware Customer Display module for OpenERP +# Hardware Customer Display module for Odoo # Copyright (C) 2014 Akretion (http://www.akretion.com) # @author Alexis de Lattre # @@ -48,10 +48,6 @@ class CustomerDisplayDriver(Thread): 'customer_display_device_rate', 9600)) self.device_timeout = int(config.get( 'customer_display_device_timeout', 2)) - self.device_rows = int(config.get( - 'customer_display_device_rows', 2)) - self.device_cols = int(config.get( - 'customer_display_device_cols', 20)) self.serial = False def get_status(self): @@ -91,12 +87,8 @@ class CustomerDisplayDriver(Thread): def display_text(self, lines): logger.debug( "Preparing to send the following lines to LCD: %s" % lines) - if len(lines) > self.device_rows: - logger.error( - 'Odoo POS sends %d rows but LCD only has %d rows' - % (len(lines), self.device_rows)) - return - assert len(lines) <= self.device_rows, 'Too many lines' + # We don't check the number of rows/cols here, because it has already + # been checked in the POS client in the JS code lines_ascii = [] for line in lines: lines_ascii.append(unidecode(line)) @@ -104,11 +96,6 @@ class CustomerDisplayDriver(Thread): for dline in lines_ascii: row += 1 self.move_cursor(1, row) - if len(line) > self.device_cols: - logger.error( - 'Odoo POS sends %d characters but LCD only has %d cols' - % (len(line), self.device_cols)) - return self.serial_write(dline) def setup_customer_display(self): @@ -137,6 +124,15 @@ class CustomerDisplayDriver(Thread): self.serial.write(text) def send_text_customer_display(self, text_to_display): + '''This function sends the data to the serial/usb port. + We open and close the serial connection on every message display. + Why ? + 1. Because it is not a problem for the customer display + 2. Because it is not a problem for performance, according to my tests + 3. Because it allows recovery on errors : you can unplug/replug the + customer display and it will work again on the next message without + problem + ''' lines = simplejson.loads(text_to_display) assert isinstance(lines, list), 'lines_list should be a list' try: diff --git a/hw_customer_display/test-scripts/customer-display-test.py b/hw_customer_display/test-scripts/customer-display-test.py new file mode 100755 index 00000000..ad3a50d4 --- /dev/null +++ b/hw_customer_display/test-scripts/customer-display-test.py @@ -0,0 +1,68 @@ +#! /usr/bin/python +# -*- encoding: utf-8 -*- +# Author : Alexis de Lattre +# The licence is in the file __openerp__.py +# This is a test script, that you can use if you want to test/play +# with the customer display independantly from the Odoo server +# It has been tested with a Bixolon BCD-1100 + +from serial import Serial +from unidecode import unidecode +import sys + +DEVICE = '/dev/ttyUSB0' +DEVICE_RATE = 9600 +DEVICE_COLS = 20 + + +def display_text(ser, line1, line2): + print "convert to ascii" + line1 = unidecode(line1) + line2 = unidecode(line2) + print "set lines to the right lenght (%s)" % DEVICE_COLS + for line in [line1, line2]: + if len(line) < DEVICE_COLS: + line += ' ' * (DEVICE_COLS - len(line)) + elif len(line) > DEVICE_COLS: + line = line[0:DEVICE_COLS] + assert len(line) == DEVICE_COLS, 'Wrong length' + print "try to clear display" + ser.write('\x0C') + print "clear done" + print "try to position at start of 1st line" + ser.write('\x1B\x6C' + chr(1) + chr(1)) + print "position done" + print "try to write 1st line" + ser.write(line1) + print "write 1st line done" + print "try to position at start of 2nd line" + ser.write('\x1B\x6C' + chr(1) + chr(2)) + print "position done" + print "try to write 2nd line" + ser.write(line2) + print "write done" + + +def open_close_display(line1, line2): + ser = False + try: + print "open serial port" + ser = Serial(DEVICE, DEVICE_RATE, timeout=2) + print "serial port open =", ser.isOpen() + print "try to set cursor to off" + ser.write('\x1F\x43\x00') + print "cursor set to off" + display_text(ser, line1, line2) + except Exception, e: + print "EXCEPTION e=", e + sys.exit(1) + finally: + if ser: + print "close serial port" + ser.close() + + +if __name__ == '__main__': + line1 = u'POS Code Sprint' + line2 = u'@ Akretion 2014/07' + open_close_display(line1, line2) From 58c570a2f7ddfe48b76e9a4d411a1aac8fa00c93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Au=C3=A9lien=20DUMAINE?= Date: Wed, 9 Jul 2014 14:20:20 +0200 Subject: [PATCH 03/27] Initial check-in of the module pos_customer_display. --- pos_customer_display/__init__.py | 1 + pos_customer_display/__openerp__.py | 46 +++ .../customer_display_view.xml | 16 ++ pos_customer_display/pos_customer_display.py | 46 +++ pos_customer_display/pos_customer_display.xml | 10 + .../static/src/js/customer_display.js | 264 ++++++++++++++++++ 6 files changed, 383 insertions(+) create mode 100755 pos_customer_display/__init__.py create mode 100755 pos_customer_display/__openerp__.py create mode 100644 pos_customer_display/customer_display_view.xml create mode 100644 pos_customer_display/pos_customer_display.py create mode 100755 pos_customer_display/pos_customer_display.xml create mode 100755 pos_customer_display/static/src/js/customer_display.js diff --git a/pos_customer_display/__init__.py b/pos_customer_display/__init__.py new file mode 100755 index 00000000..7dff50a7 --- /dev/null +++ b/pos_customer_display/__init__.py @@ -0,0 +1 @@ +import pos_customer_display diff --git a/pos_customer_display/__openerp__.py b/pos_customer_display/__openerp__.py new file mode 100755 index 00000000..3fb094b6 --- /dev/null +++ b/pos_customer_display/__openerp__.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# 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 . +# +############################################################################## + + +{ + 'name': 'POS Customer Display', + 'version': '0.1', + 'category': 'Point Of Sale', + 'summary': 'Manage Customer Display device from POS front end', + 'description': """ +POS Customer Display +==================== + +This module adds support for Customer Display in the Point of Sale. This module is designed to be installed on the *main Odoo server*. On the *POSbox*, you should install the module *hw_customer_display*. + +The number of rows and cols of the Customer Display (usually 2 x 20) should be configured on the main Odoo server, in the menu Point of Sale > Configuration > Point of Sales. + +It has been tested with a Bixolon BCD-1100 (http://www.bixolon.com/html/en/product/product_detail.xhtml?prod_id=61), but should support most serial and USB-serial LCD displays out-of-the-box or with inheritance of a few functions. To setup the BCD-1100 on Linux, you will find some technical instructions on this page : http://techtuxwords.blogspot.fr/2012/12/linux-and-bixolon-bcd-1100.html + +This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. This module is part of the POS project of the Odoo Community Association http://odoo-community.org/. You are invited to become a member and/or get involved in the Association ! + +Please contact Alexis de Lattre from Akretion for any help or question about this module. + """, + 'author': 'Aurélien DUMAINE', + 'depends': ['point_of_sale'], + 'data' : ['pos_customer_display.xml', + 'customer_display_view.xml'], +} diff --git a/pos_customer_display/customer_display_view.xml b/pos_customer_display/customer_display_view.xml new file mode 100644 index 00000000..80b238a6 --- /dev/null +++ b/pos_customer_display/customer_display_view.xml @@ -0,0 +1,16 @@ + + + + + pos.config.form.view.inherit + pos.config + + + + + + + + + + diff --git a/pos_customer_display/pos_customer_display.py b/pos_customer_display/pos_customer_display.py new file mode 100644 index 00000000..20eda083 --- /dev/null +++ b/pos_customer_display/pos_customer_display.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SP (). +# +# 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 . +# +############################################################################## + +import logging +import time + +from openerp import tools +from openerp.osv import fields, osv +from openerp.tools.translate import _ + +import openerp.addons.decimal_precision as dp +import openerp.addons.product.product + +_logger = logging.getLogger(__name__) + +class pos_config(osv.osv): + _name = 'pos.config' + _inherit = 'pos.config' + + _columns = { + 'iface_customer_display' : fields.boolean('Customer display', help="Display data on the customer display"), + 'customer_display_line_length' : fields.integer('Line length', help="Length of the LEDs lines of the customer display"), + } + _defaults = { + 'customer_display_line_length' : 20, + } + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/pos_customer_display/pos_customer_display.xml b/pos_customer_display/pos_customer_display.xml new file mode 100755 index 00000000..3aaa679e --- /dev/null +++ b/pos_customer_display/pos_customer_display.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/pos_customer_display/static/src/js/customer_display.js b/pos_customer_display/static/src/js/customer_display.js new file mode 100755 index 00000000..73dc914a --- /dev/null +++ b/pos_customer_display/static/src/js/customer_display.js @@ -0,0 +1,264 @@ +openerp.pos_customer_display = function(instance){ + module = instance.point_of_sale; + + var _t = instance.web._t; + var round_di = instance.web.round_decimals; + var round_pr = instance.web.round_precision + + + module.PosModel = module.PosModel.extend({ + prepare_text_customer_display: function(type, data){ + if (this.config.iface_customer_display != true) + return; + var line_length = this.config.customer_display_line_length || 20; + var currency_rounding = Math.ceil(Math.log(1.0 / this.currency.rounding) / Math.log(10)); + + if (type == 'addProduct'){ + // in order to not recompute qty in options..., we assume that the new ordeLine is the last of the collection + // addOrderline exists but is not called by addProduct, should we handle it ? + var line = this.get('selectedOrder').getLastOrderline(); + var price_unit = line.get_quantity() * line.get_unit_price() * (1.0 - (line.get_discount() / 100.0)); + price_unit = price_unit.toFixed(currency_rounding); + var l21 = line.get_quantity_str_with_unit() + ' x ' + price_unit; + var l22 = ' ' + line.get_display_price().toFixed(currency_rounding); + var lines_to_send = new Array( + this.proxy.complete_string_right(line.get_product().name, line_length), + this.proxy.complete_string_right(l21, line_length - l22.length) + l22 + ); + } else if (type == 'removeOrderline') { + // FIXME : first click on the backspace button set the amount to 0 + var line = data['line']; + var price_unit = line.get_quantity() * line.get_unit_price() * (1.0 - (line.get_discount() / 100.0)); + price_unit = price_unit.toFixed(currency_rounding); + var l21 = '-' + line.get_quantity_str_with_unit() + ' x ' + price_unit; + var l22 = ' ' + -1 * line.get_display_price().toFixed(currency_rounding); + var lines_to_send = new Array( + this.proxy.complete_string_right(line.get_product().name, line_length), + this.proxy.complete_string_right(l21, line_length - l22.length) + l22 + ); + } else if (type == 'addPaymentline') { + var cashregister = data['cashregister']; + var total = this.get('selectedOrder').getTotalTaxIncluded().toFixed(currency_rounding); + var lines_to_send = new Array( + this.proxy.complete_string_right(_t("TOTAL : "), line_length - 1 - total.length) + ' ' + total, + this.proxy.complete_string_right(_t("Paiement :"), line_length - 1 - cashregister.journal_id[1].length) + ' ' + cashregister.journal_id[1] + ); + + } else if (type == 'removePaymentline') { + var line = data['line']; + var amount = line.get_amount().toFixed(currency_rounding); + var lines_to_send = new Array( + this.proxy.complete_string_right(_t("Suppression paiement"), line_length), + this.proxy.complete_string_right(line.cashregister.journal_id[1] , line_length - 1- amount.length) + ' ' + amount + ); + } else if (type == 'pushOrder') { + var currentOrder = data['currentOrder']; + var paidTotal = currentOrder.getPaidTotal(); + var dueTotal = currentOrder.getTotalTaxIncluded(); + var remaining = dueTotal > paidTotal ? dueTotal - paidTotal : 0; + var change = paidTotal > dueTotal ? paidTotal - dueTotal : 0; + + var l1; + if (change == 0){ + l1 = this.proxy.complete_string_center(_t(""), line_length); + } else { + change = change.toFixed(currency_rounding); + l1 = this.proxy.complete_string_right(_t("YOUR CHANGE :"), line_length - 1 - change.length) + ' ' + change; + } + + var lines_to_send = new Array( + l1, + this.proxy.complete_string_center(_t("Next customer..."), line_length) + ); + } else if (type = 'closePOS') { + var lines_to_send = new Array( + this.proxy.complete_string_center(_t("Point of sale closed"), line_length), + this.proxy.complete_string_center(_t("***"), line_length) + ); + } else { + console.warn('Unknown message type'); + return; + } + + this.proxy.send_text_customer_display(lines_to_send, line_length); + }, + + }); + + + module.ProxyDevice = module.ProxyDevice.extend({ + send_text_customer_display: function(data, line_length){ + if (data[0].length != line_length || data[1].length != line_length){ + console.warn("Data components have to have " + line_length + " chars."); + console.log(data[0].length + " -> "+ data[0] + "\n" + data[1].length + " -> " + data[1]); + } else { + //alert(JSON.stringify(data)); + return this.message('send_text_customer_display', {'text_to_display' : JSON.stringify(data)}); + } + return; + }, + complete_string_right: function(string, length){ + if (string.length > length) + { + return string.substring(0,length); + } + else if (string.length < length) + { + while(string.length < length) + string = string+' '; + return string; + } + return string; + }, + complete_string_left: function(string, length){ + if (string.length > length) + { + return string.substring(0,length); + } + else if (string.length < length) + { + while(string.length < length) + string = ' '+string; + return string; + } + return string; + }, + complete_string_center: function(string, length){ + var self = this; + if (string.length > length) + { + return string.substring(0,length); + } + else if (string.length < length) + { + ini = (length - string.length)/2; + while(string.length < length - ini) + string = ' '+string; + while(string.length < length) + string = string + ' '; + return string; + } + return string; + }, + }); + + + var _super_setSmartStatus_ = module.ProxyStatusWidget.prototype.set_smart_status; + module.ProxyStatusWidget.prototype.set_smart_status = function(status){ + _super_setSmartStatus_.call(this, status); + if(status.status === 'connected'){ + var warning = false; + var msg = '' + if( this.pos.config.iface_customer_display){ + var customer_display = status.drivers.customer_display ? status.drivers.customer_display.status : false; + if( customer_display != 'connected' && customer_display != 'connecting'){ + warning = true; + msg = msg ? msg + ' & ' : msg; + msg += _t('Customer display'); + } + } + msg = msg ? msg + ' ' + _t('Offline') : msg; + this.set_status(warning ? 'warning' : 'connected', msg); + }else{ + this.set_status(status.status,''); + } + }; + + + var _super_addProduct_ = module.Order.prototype.addProduct; + module.Order.prototype.addProduct = function(product, options){ + _super_addProduct_.call(this, product, options); + this.pos.prepare_text_customer_display('addProduct', {'product' : product, 'options' : options}); + }; + + + var _super_removeOrderline_ = module.Order.prototype.removeOrderline; + module.Order.prototype.removeOrderline = function(line){ + this.pos.prepare_text_customer_display('removeOrderline', {'line' : line}); + _super_removeOrderline_.call(this, line); + }; + + var _super_removePaymentline_ = module.Order.prototype.removePaymentline; + module.Order.prototype.removePaymentline = function(line){ + this.pos.prepare_text_customer_display('removePaymentline', {'line' : line}); + _super_removePaymentline_.call(this, line); + }; + + var _super_addPaymentline_ = module.Order.prototype.addPaymentline; + module.Order.prototype.addPaymentline = function(cashregister){ + _super_addPaymentline_.call(this, cashregister); + this.pos.prepare_text_customer_display('addPaymentline', {'cashregister' : cashregister}); + }; + + + var _super_pushOrder_ = module.PosModel.prototype.push_order; + module.PosModel.prototype.push_order = function(currentOrder){ + _super_pushOrder_.call(this, currentOrder); + this.prepare_text_customer_display('pushOrder', {'currentOrder' : currentOrder}); + + }; + + + var _super_closePOS_ = module.PosWidget.prototype.close; + module.PosWidget.prototype.close = function(){ + this.pos.prepare_text_customer_display('closePOS', {}); + _super_closePOS_.call(this); + }; + +/* + var _super_updatePayment_ = module.PaymentScreenWidget.prototype.update_payment_summary; + module.PaymentScreenWidget.prototype.update_payment_summary = function(){ + _super_updatePayement_.call(this); + if( this.pos.config.iface_customer_display != true ) + return; + var currentOrder = this.pos.get('selectedOrder'); + var paidTotal = currentOrder.getPaidTotal(); + var dueTotal = currentOrder.getTotalTaxIncluded(); + var remaining = dueTotal > paidTotal ? dueTotal - paidTotal : 0; + var change = paidTotal > dueTotal ? paidTotal - dueTotal : 0; + + var currency_rounding = Math.ceil(Math.log(1.0 / this.pos.currency.rounding) / Math.log(10)); + var amount = line.get_amount().toFixed(currency_rounding); + var line_length = this.pos.config.customer_display_line_length || 20; + var data = new Array( + this.pos.proxy.complete_string_right(_t("Suppression paiement"), line_length), + this.pos.proxy.complete_string_right(line.cashregister.journal_id[1] , line_length - amount.length) + ' ' + amount + ); + this.pos.proxy.send_text_customer_display(data); + }; +*/ +/* + module.Paymentline = module.Paymentline.extend({ + set_pos: function(pos){ + this.pos = pos; + }, + }); + + module.PaypadButtonWidget = module.PaypadButtonWidget.extend({ + renderElement: function() { + var self = this; + this._super(); + + this.$el.click(function(){ + if (self.pos.get('selectedOrder').get('screen') === 'receipt'){ //TODO Why ? + console.warn('TODO should not get there...?'); + return; + } + self.pos.get('selectedOrder').addPaymentline(self.cashregister); + console.log('aurel'); + self.pos.get('selectedOrder').get('paymentLines').at(self.pos.get('selectedOrder').get('paymentLines').length -1).set_pos(self.pos); + console.log('aurel'); + self.pos_widget.screen_selector.set_current_screen('payment'); + }); + }, + }); + + var _super_setamountPaymentline_ = module.Paymentline.prototype.set_amount; + module.Paymentline.prototype.set_amount = function(amount){ + _super_setamountPaymentline_.call(this, amount); + var data = _t("Add paymentline : ") + this.cashregister.journal_id[1] + " " + this.get_amount() + " " + this.pos.get('selectedOrder').getDueLeft(); + alert(data); + this.pos.proxy.send_text_customer_display(data); + }; +*/ +}; From d9599e6c2fe07f3e6f29de5c825e374eaa2ec8d4 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Wed, 9 Jul 2014 18:56:38 +0200 Subject: [PATCH 04/27] Add module currency_iso_numeric. --- currency_iso_numeric/__init__.py | 24 + currency_iso_numeric/__openerp__.py | 48 ++ .../i18n/currency_iso_numeric.pot | 27 + currency_iso_numeric/i18n/fr.po | 27 + currency_iso_numeric/res_currency.py | 30 + currency_iso_numeric/res_currency_data.xml | 615 ++++++++++++++++++ currency_iso_numeric/res_currency_view.xml | 37 ++ 7 files changed, 808 insertions(+) create mode 100644 currency_iso_numeric/__init__.py create mode 100644 currency_iso_numeric/__openerp__.py create mode 100644 currency_iso_numeric/i18n/currency_iso_numeric.pot create mode 100644 currency_iso_numeric/i18n/fr.po create mode 100644 currency_iso_numeric/res_currency.py create mode 100644 currency_iso_numeric/res_currency_data.xml create mode 100644 currency_iso_numeric/res_currency_view.xml diff --git a/currency_iso_numeric/__init__.py b/currency_iso_numeric/__init__.py new file mode 100644 index 00000000..a013db0b --- /dev/null +++ b/currency_iso_numeric/__init__.py @@ -0,0 +1,24 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Currency ISO Numeric module for Odoo +# Copyright (C) 2014 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# +# 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 . +# +############################################################################## + + +from . import res_currency diff --git a/currency_iso_numeric/__openerp__.py b/currency_iso_numeric/__openerp__.py new file mode 100644 index 00000000..750df4c7 --- /dev/null +++ b/currency_iso_numeric/__openerp__.py @@ -0,0 +1,48 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Currency ISO Numeric module for Odoo +# Copyright (C) 2014 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# +# 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 . +# +############################################################################## + + +{ + 'name': 'Currency ISO Numeric', + 'version': '0.1', + 'category': 'Currency', + 'license': 'AGPL-3', + 'summary': 'Adds ISO 4217 numeric codes on currencies', + 'description': """ +Currency ISO Numeric +==================== + +This module adds a field *ISO Numeric Code* on currencies. This numeric ISO code is required by some applications ; for example, it is used in the Telium protocol for the communication between the Point of Sale and the credit card reader. + +This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. + +Please contact Alexis de Lattre from Akretion for any help or question about this module. + """, + 'author': 'Akretion', + 'website': 'http://www.akretion.com', + 'depends': ['base'], + 'data': [ + 'res_currency_data.xml', + 'res_currency_view.xml', + ], + 'active': False, +} diff --git a/currency_iso_numeric/i18n/currency_iso_numeric.pot b/currency_iso_numeric/i18n/currency_iso_numeric.pot new file mode 100644 index 00000000..04133e60 --- /dev/null +++ b/currency_iso_numeric/i18n/currency_iso_numeric.pot @@ -0,0 +1,27 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * currency_iso_numeric +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 8.0alpha1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-09 16:50+0000\n" +"PO-Revision-Date: 2014-07-09 16:50+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: currency_iso_numeric +#: model:ir.model,name:currency_iso_numeric.model_res_currency +msgid "Currency" +msgstr "" + +#. module: currency_iso_numeric +#: field:res.currency,iso_numeric:0 +msgid "ISO Numeric Code" +msgstr "" + diff --git a/currency_iso_numeric/i18n/fr.po b/currency_iso_numeric/i18n/fr.po new file mode 100644 index 00000000..9efba192 --- /dev/null +++ b/currency_iso_numeric/i18n/fr.po @@ -0,0 +1,27 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * currency_iso_numeric +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 8.0alpha1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-09 16:51+0000\n" +"PO-Revision-Date: 2014-07-09 16:51+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: currency_iso_numeric +#: model:ir.model,name:currency_iso_numeric.model_res_currency +msgid "Currency" +msgstr "Devise" + +#. module: currency_iso_numeric +#: field:res.currency,iso_numeric:0 +msgid "ISO Numeric Code" +msgstr "Code ISO numérique" + diff --git a/currency_iso_numeric/res_currency.py b/currency_iso_numeric/res_currency.py new file mode 100644 index 00000000..a52d119f --- /dev/null +++ b/currency_iso_numeric/res_currency.py @@ -0,0 +1,30 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Currency ISO Numeric module for Odoo +# Copyright (C) 2014 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# +# 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 . +# +############################################################################## + + +from openerp import models, fields + + +class Currency(models.Model): + _inherit = 'res.currency' + + iso_numeric = fields.Char(string='ISO Numeric Code', size=4) diff --git a/currency_iso_numeric/res_currency_data.xml b/currency_iso_numeric/res_currency_data.xml new file mode 100644 index 00000000..6efa4a18 --- /dev/null +++ b/currency_iso_numeric/res_currency_data.xml @@ -0,0 +1,615 @@ + + + + + + + + + + 971 + + + + 978 + + + + 008 + + + + 012 + + + + 840 + + + + 973 + + + + 951 + + + + 032 + + + + 051 + + + + 533 + + + + 036 + + + + 944 + + + + 044 + + + + 048 + + + + 050 + + + + 052 + + + + 974 + + + + 084 + + + + 952 + + + + 060 + + + + 064 + + + + 356 + + + + 068 + + + + 977 + + + + 072 + + + + 578 + + + + 986 + + + + 096 + + + + 975 + + + + 108 + + + + 116 + + + + 950 + + + + 124 + + + + 132 + + + + 136 + + + + 152 + + + + 156 + + + + 170 + + + + 174 + + + + 976 + + + + 554 + + + + 188 + + + + 191 + + + + 192 + + + + 532 + + + + 203 + + + + 208 + + + + 262 + + + + 214 + + + + 818 + + + + 222 + + + + 232 + + + + 230 + + + + 238 + + + + 242 + + + + 953 + + + + 270 + + + + 981 + + + + 936 + + + + 292 + + + + 320 + + + + 826 + + + + 324 + + + + 328 + + + + 332 + + + + 340 + + + + 344 + + + + 348 + + + + 352 + + + + 360 + + + + 364 + + + + 368 + + + + 376 + + + + 388 + + + + 392 + + + + 400 + + + + 398 + + + + 404 + + + + 408 + + + + 410 + + + + 414 + + + + 417 + + + + 418 + + + + 422 + + + + 426 + + + + 710 + + + + 430 + + + + 434 + + + + 756 + + + + 440 + + + + 446 + + + + 807 + + + + 969 + + + + 454 + + + + 458 + + + + 462 + + + + 478 + + + + 480 + + + + 484 + + + + 498 + + + + 496 + + + + 504 + + + + 943 + + + + 104 + + + + 516 + + + + 524 + + + + 558 + + + + 566 + + + + 512 + + + + 586 + + + + 590 + + + + 598 + + + + 600 + + + + 604 + + + + 608 + + + + 985 + + + + 634 + + + + 946 + + + + 643 + + + + 646 + + + + 654 + + + + 882 + + + + 678 + + + + 682 + + + + 941 + + + + 690 + + + + 694 + + + + 702 + + + + 090 + + + + 728 + + + + 144 + + + + 748 + + + + 752 + + + + 760 + + + + 901 + + + + 834 + + + + 764 + + + + 776 + + + + 780 + + + + 788 + + + + 949 + + + + 800 + + + + 980 + + + + 784 + + + + 858 + + + + 860 + + + + 548 + + + + 937 + + + + 704 + + + + 886 + + + + + diff --git a/currency_iso_numeric/res_currency_view.xml b/currency_iso_numeric/res_currency_view.xml new file mode 100644 index 00000000..cf0bbc9c --- /dev/null +++ b/currency_iso_numeric/res_currency_view.xml @@ -0,0 +1,37 @@ + + + + + + + + + + add.iso.numeric.res.currency.form + res.currency + + + + + + + + + + add.iso.numeric.res.currency.tree + res.currency + + + + + + + + + + + From 80603dc1635532434c2f21c25340909d6166d308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Au=C3=A9lien=20DUMAINE?= Date: Thu, 10 Jul 2014 17:23:30 +0200 Subject: [PATCH 05/27] Initial check-in of the module pos_payment_terminal. --- .../static/src/js/customer_display.js | 16 +++- pos_payment_terminal/__init__.py | 1 + pos_payment_terminal/__openerp__.py | 45 ++++++++++ pos_payment_terminal/pos_payment_terminal.py | 36 ++++++++ pos_payment_terminal/pos_payment_terminal.xml | 10 +++ .../pos_payment_terminal_view.xml | 25 ++++++ .../static/src/js/pos_payment_terminal.js | 86 +++++++++++++++++++ .../static/src/xml/pos_payment_terminal.xml | 16 ++++ 8 files changed, 231 insertions(+), 4 deletions(-) create mode 100755 pos_payment_terminal/__init__.py create mode 100755 pos_payment_terminal/__openerp__.py create mode 100644 pos_payment_terminal/pos_payment_terminal.py create mode 100755 pos_payment_terminal/pos_payment_terminal.xml create mode 100644 pos_payment_terminal/pos_payment_terminal_view.xml create mode 100755 pos_payment_terminal/static/src/js/pos_payment_terminal.js create mode 100644 pos_payment_terminal/static/src/xml/pos_payment_terminal.xml diff --git a/pos_customer_display/static/src/js/customer_display.js b/pos_customer_display/static/src/js/customer_display.js index 73dc914a..8a135570 100755 --- a/pos_customer_display/static/src/js/customer_display.js +++ b/pos_customer_display/static/src/js/customer_display.js @@ -41,15 +41,15 @@ openerp.pos_customer_display = function(instance){ var total = this.get('selectedOrder').getTotalTaxIncluded().toFixed(currency_rounding); var lines_to_send = new Array( this.proxy.complete_string_right(_t("TOTAL : "), line_length - 1 - total.length) + ' ' + total, - this.proxy.complete_string_right(_t("Paiement :"), line_length - 1 - cashregister.journal_id[1].length) + ' ' + cashregister.journal_id[1] + this.proxy.complete_string_right(_t("Payment :"), line_length - 1 - cashregister.journal_id[1].length) + ' ' + cashregister.journal_id[1] ); } else if (type == 'removePaymentline') { var line = data['line']; var amount = line.get_amount().toFixed(currency_rounding); var lines_to_send = new Array( - this.proxy.complete_string_right(_t("Suppression paiement"), line_length), - this.proxy.complete_string_right(line.cashregister.journal_id[1] , line_length - 1- amount.length) + ' ' + amount + this.proxy.complete_string_right(_t("Delete payment"), line_length), + this.proxy.complete_string_right(line.cashregister.journal_id[1] , line_length - 1 - amount.length) + ' ' + amount ); } else if (type == 'pushOrder') { var currentOrder = data['currentOrder']; @@ -80,6 +80,7 @@ openerp.pos_customer_display = function(instance){ return; } +// alert("aa" + line_length); this.proxy.send_text_customer_display(lines_to_send, line_length); }, @@ -88,11 +89,17 @@ openerp.pos_customer_display = function(instance){ module.ProxyDevice = module.ProxyDevice.extend({ send_text_customer_display: function(data, line_length){ + //FIXME : this function is call twice. The first time, it is not called by prepare_text_customer_display : WHY ? +// alert("bb" + line_length); + if (data[0].length != line_length) + console.warn(data[0].length + " " + data[0]); + if (data[1].length != line_length) + console.warn(data[1].length + " " + data[1]); if (data[0].length != line_length || data[1].length != line_length){ console.warn("Data components have to have " + line_length + " chars."); console.log(data[0].length + " -> "+ data[0] + "\n" + data[1].length + " -> " + data[1]); } else { - //alert(JSON.stringify(data)); +// alert(JSON.stringify(data)); return this.message('send_text_customer_display', {'text_to_display' : JSON.stringify(data)}); } return; @@ -143,6 +150,7 @@ openerp.pos_customer_display = function(instance){ }); + //FIXME : nothing append on customer display deconnection var _super_setSmartStatus_ = module.ProxyStatusWidget.prototype.set_smart_status; module.ProxyStatusWidget.prototype.set_smart_status = function(status){ _super_setSmartStatus_.call(this, status); diff --git a/pos_payment_terminal/__init__.py b/pos_payment_terminal/__init__.py new file mode 100755 index 00000000..45b85bcf --- /dev/null +++ b/pos_payment_terminal/__init__.py @@ -0,0 +1 @@ +import pos_payment_terminal diff --git a/pos_payment_terminal/__openerp__.py b/pos_payment_terminal/__openerp__.py new file mode 100755 index 00000000..67cb8a08 --- /dev/null +++ b/pos_payment_terminal/__openerp__.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# 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 . +# +############################################################################## + + +{ + 'name': 'POS Payment Terminal', + 'version': '0.1', + 'category': 'Point Of Sale', + 'summary': 'Manage Payment Terminal device from POS front end', + 'description': """ +POS Payment Terminal +==================== + +This module adds support for Payment Terminal in the Point of Sale. This module is designed to be installed on the *main Odoo server*. On the *POSbox*, you should install the module *hw_x* depending on the protocol implemented in your device. Ingenico devices support the Telium protocol implemented in the *hw_telium_payment_terminal* module. + +This module support two payment methods : cards and checks. The payment method should be configured on the main Odoo server, in the menu Point of Sale > Configuration > Payment Methods. + +This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. This module is part of the POS project of the Odoo Community Association http://odoo-community.org/. You are invited to become a member and/or get involved in the Association ! + +Please contact Alexis de Lattre from Akretion for any help or question about this module. + """, + 'author': 'Aurélien DUMAINE', + 'depends': ['point_of_sale'], + 'data' : ['pos_payment_terminal.xml', + 'pos_payment_terminal_view.xml'], + 'qweb': ['static/src/xml/pos_payment_terminal.xml'], +} diff --git a/pos_payment_terminal/pos_payment_terminal.py b/pos_payment_terminal/pos_payment_terminal.py new file mode 100644 index 00000000..3b4853ad --- /dev/null +++ b/pos_payment_terminal/pos_payment_terminal.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SP (). +# +# 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 . +# +############################################################################## + +from openerp import models, fields + +class account_journal(models.Model): + _name = 'account.journal' + _inherit = 'account.journal' + + payment_mode = fields.Selection((('card', 'Card'), ('check', 'Check')), 'Payment mode', help="Select the payment mode sent to the payment terminal") + +class pos_config(models.Model): + _name = 'pos.config' + _inherit = 'pos.config' + + iface_payment_terminal = fields.Boolean('Payment Terminal', help="A payment terminal is available on the Proxy") + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/pos_payment_terminal/pos_payment_terminal.xml b/pos_payment_terminal/pos_payment_terminal.xml new file mode 100755 index 00000000..32d81264 --- /dev/null +++ b/pos_payment_terminal/pos_payment_terminal.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/pos_payment_terminal/pos_payment_terminal_view.xml b/pos_payment_terminal/pos_payment_terminal_view.xml new file mode 100644 index 00000000..e55d2a34 --- /dev/null +++ b/pos_payment_terminal/pos_payment_terminal_view.xml @@ -0,0 +1,25 @@ + + + + + pos.config.form.view.inherit + pos.config + + + + + + + + + POS journal inherit + account.journal + + + + + + + + + diff --git a/pos_payment_terminal/static/src/js/pos_payment_terminal.js b/pos_payment_terminal/static/src/js/pos_payment_terminal.js new file mode 100755 index 00000000..6f7a866f --- /dev/null +++ b/pos_payment_terminal/static/src/js/pos_payment_terminal.js @@ -0,0 +1,86 @@ +openerp.pos_payment_terminal = function(instance){ + module = instance.point_of_sale; + + module.ProxyDevice = module.ProxyDevice.extend({ + payment_terminal_transaction_start: function(line, currency_iso, currency_iso_numeric){ + var data = {'amount' : line.get_amount(), + 'currency_iso' : currency_iso, + 'currency_iso_numeric' : currency_iso_numeric, + 'payment_mode' : line.cashregister.journal.payment_mode}; + alert(JSON.stringify(data)); + this.message('payment_terminal_transaction_start', {'payment_info' : JSON.stringify(data)}); + }, + }); + + //TODO make the button bigger and with better name + + var _super_PaymentScreenWidget_init_ = module.PaymentScreenWidget.prototype.init; + module.PaymentScreenWidget.prototype.init = function(parent, options){ + _super_PaymentScreenWidget_init_.call(this, parent, options); + self = this; + this.payment_terminal_transaction_start = function(event){ + var node = this; + while(node && !node.classList.contains('paymentline')){ + node = node.parentNode; + } + if(node){ + if (self.pos.config.iface_payment_terminal) + self.pos.proxy.payment_terminal_transaction_start(node.line, self.pos.currency.name, self.pos.currency.iso_numeric); + } + event.stopPropagation(); + }; + }; + + var _super_renderPaymentline_ = module.PaymentScreenWidget.prototype.render_paymentline; + module.PaymentScreenWidget.prototype.render_paymentline = function(line){ + var el_node = _super_renderPaymentline_.call(this, line); + if (line.cashregister.journal.payment_mode && this.pos.config.iface_payment_terminal){ + if (!this.pos.currency.name){ + var self = this; + var currencies = new instance.web.Model('res.currency').query(['name', 'iso_numeric']) + .filter([['id','=',this.pos.currency.id]]) + .all().then(function (currency) { + self.pos.currency.name = currency[0].name; + self.pos.currency.iso_numeric = currency[0].iso_numeric; + }); + } + el_node.querySelector('.payment-terminal-transaction-start') + .addEventListener('click', this.payment_terminal_transaction_start); + //.addEventListener('click', this.pos.proxy.payment_terminal_transaction_start(line, this.pos.currency.name, this.pos.currency.iso_numeric)); + } + return el_node; + }; + +/* + var _super_load_server_data_ = module.PosModel.prototype.load_server_data; + module.PosModel.prototype.load_server_data = function(){ + var loaded = _super_load_server_data_.call(this); + //FIXME : this is asynchronous, I can't assume the pos.currency loaded when we enter is this + this.pos.currency.name = new instance.web.Model('res.currency').query('name').filter([['id','=',this.pos_currency.id]]).all()[0] + return loaded; + }; +*/ + +/* + var _super_setSmartStatus_ = module.ProxyStatusWidget.prototype.set_smart_status; + module.ProxyStatusWidget.prototype.set_smart_status = function(status){ + _super_setSmartStatus_.call(this, status); + if(status.status === 'connected'){ + var warning = false; + var msg = '' + if(this.pos.config.iface_customer_display){ + var customer_display = status.drivers.customer_display ? status.drivers.customer_display.status : false; + if( customer_display != 'connected' && customer_display != 'connecting'){ + warning = true; + msg = msg ? msg + ' & ' : msg; + msg += _t('Customer display'); + } + } + msg = msg ? msg + ' ' + _t('Offline') : msg; + this.set_status(warning ? 'warning' : 'connected', msg); + }else{ + this.set_status(status.status,''); + } + }; +*/ +}; diff --git a/pos_payment_terminal/static/src/xml/pos_payment_terminal.xml b/pos_payment_terminal/static/src/xml/pos_payment_terminal.xml new file mode 100644 index 00000000..8467a816 --- /dev/null +++ b/pos_payment_terminal/static/src/xml/pos_payment_terminal.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + From 4f1fc6c235fd44ad44ca211f3804764aadcd1ebf Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Thu, 10 Jul 2014 20:39:07 +0200 Subject: [PATCH 06/27] Add module hw_telium_payment_terminal. --- hw_telium_payment_terminal/__init__.py | 24 ++ hw_telium_payment_terminal/__openerp__.py | 56 ++++ .../controllers/__init__.py | 24 ++ .../controllers/main.py | 277 ++++++++++++++++++ 4 files changed, 381 insertions(+) create mode 100644 hw_telium_payment_terminal/__init__.py create mode 100644 hw_telium_payment_terminal/__openerp__.py create mode 100644 hw_telium_payment_terminal/controllers/__init__.py create mode 100644 hw_telium_payment_terminal/controllers/main.py diff --git a/hw_telium_payment_terminal/__init__.py b/hw_telium_payment_terminal/__init__.py new file mode 100644 index 00000000..c535b703 --- /dev/null +++ b/hw_telium_payment_terminal/__init__.py @@ -0,0 +1,24 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Hardware Telium Payment Terminal module for Odoo +# Copyright (C) 2014 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# +# 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 . +# +############################################################################## + + +from . import controllers diff --git a/hw_telium_payment_terminal/__openerp__.py b/hw_telium_payment_terminal/__openerp__.py new file mode 100644 index 00000000..2ca39fc6 --- /dev/null +++ b/hw_telium_payment_terminal/__openerp__.py @@ -0,0 +1,56 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Hardware Telium Payment Terminal module for Odoo +# Copyright (C) 2014 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# +# 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 . +# +############################################################################## + + +{ + 'name': 'Hardware Telium Payment Terminal', + 'version': '0.1', + 'category': 'Hardware Drivers', + 'license': 'AGPL-3', + 'summary': 'Adds support for Payment Terminals using Telium protocol', + 'description': """ +Hardware Telium Payment Terminal +================================ + +This module adds support for Payment Terminals using Telium protocol in the Point of Sale. This module is designed to be installed on the *POSbox* (i.e. the proxy on which the USB devices are connected) and not on the main Odoo server. On the main Odoo server, you should install the module *pos_payment_terminal*. + +The configuration of the hardware is done in the configuration file of the Odoo server of the POSbox. You should add the following entries in the configuration file: + +* payment_terminal_device_name (default = /dev/ttyACM0) +* payment_terminal_device_rate (default = 9600) +* payment_terminal_device_timeout (default = 2 seconds) + +The Telium protocol is used by Ingenico and Sagem payment terminals. It is based on the Concert protocol, so it can probably work with payment terminals from other brands. It has been tested a an Ingenico EFTSmart4S terminal with Telim Manager version 37784503. The protocol E is implemented (we may implement the protocol E+ in the future). + +This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. This module is part of the POS project of the Odoo Community Association http://odoo-community.org/. You are invited to become a member and/or get involved in the Association ! + +Please contact Alexis de Lattre from Akretion for any help or question about this module. + """, + 'author': 'Akretion', + 'website': 'http://www.akretion.com', + 'depends': ['hw_proxy'], + 'external_dependencies': { + 'python' : ['serial'], + }, + 'data': [], + 'active': False, +} diff --git a/hw_telium_payment_terminal/controllers/__init__.py b/hw_telium_payment_terminal/controllers/__init__.py new file mode 100644 index 00000000..d91efc38 --- /dev/null +++ b/hw_telium_payment_terminal/controllers/__init__.py @@ -0,0 +1,24 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Hardware Telium Payment Terminal module for Odoo +# Copyright (C) 2014 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# +# 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 . +# +############################################################################## + + +from . import main diff --git a/hw_telium_payment_terminal/controllers/main.py b/hw_telium_payment_terminal/controllers/main.py new file mode 100644 index 00000000..31936292 --- /dev/null +++ b/hw_telium_payment_terminal/controllers/main.py @@ -0,0 +1,277 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Hardware Telium Payment Terminal module for Odoo +# Copyright (C) 2014 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# +# 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 . +# +############################################################################## + + +import logging +import simplejson +import time +import curses.ascii +from threading import Thread, Lock +from Queue import Queue +from serial import Serial +import openerp.addons.hw_proxy.controllers.main as hw_proxy +from openerp import http +from openerp.tools.config import config + + +logger = logging.getLogger(__name__) + + +class TeliumPaymentTerminalDriver(Thread): + def __init__(self): + Thread.__init__(self) + self.queue = Queue() + self.lock = Lock() + self.status = {'status': 'connecting', 'messages': []} + self.device_name = config.get( + 'telium_terminal_device_name', '/dev/ttyACM0') + self.device_rate = int(config.get( + 'telium_terminal_device_rate', 9600)) + self.serial = False + + def get_status(self): + self.push_task('status') + return self.status + + def set_status(self, status, message=None): + if status == self.status['status']: + if message is not None and message != self.status['messages'][-1]: + self.status['messages'].append(message) + else: + self.status['status'] = status + if message: + self.status['messages'] = [message] + else: + self.status['messages'] = [] + + if status == 'error' and message: + logger.error('Payment Terminal Error: '+message) + elif status == 'disconnected' and message: + logger.warning('Disconnected Terminal: '+message) + + def lockedstart(self): + with self.lock: + if not self.isAlive(): + self.daemon = True + self.start() + + def push_task(self, task, data=None): + self.lockedstart() + self.queue.put((time.time(), task, data)) + + def serial_write(self, text): + assert isinstance(text, str), 'text must be a string' + self.serial.write(text) + + def initialize_msg(self): + max_attempt = 3 + attempt_nr = 0 + while attempt_nr < max_attempt: + attempt_nr += 1 + self.send_one_byte_signal('ENQ') + if self.get_one_byte_answer('ACK'): + return True + else: + logger.warning("Terminal : SAME PLAYER TRY AGAIN") + self.send_one_byte_signal('EOT') + # Wait 1 sec between each attempt + time.sleep(1) + return False + + def send_one_byte_signal(self, signal): + ascii_names = curses.ascii.controlnames + assert signal in ascii_names, 'Wrong signal' + char = ascii_names.index(signal) + self.serial_write(chr(char)) + logger.debug('Signal %s sent to terminal' % signal) + + def get_one_byte_answer(self, expected_signal): + ascii_names = curses.ascii.controlnames + one_byte_read = self.serial.read(1) + expected_char = ascii_names.index(expected_signal) + if one_byte_read == chr(expected_char): + logger.debug("%s received from terminal" % expected_signal) + return True + else: + return False + + def prepare_data_to_send(self, payment_info_dict): + amount = payment_info_dict['amount'] + if payment_info_dict['payment_mode'] == 'check': + payment_mode = 'C' + elif payment_info_dict['payment_mode'] == 'card': + payment_mode = '1' + else: + logger.error( + "The payment mode '%s' is not supported" + % payment_info_dict['payment_mode']) + return False + data = { + 'pos_number': str(1).zfill(2), + 'answer_flag': '0', + 'transaction_type': '0', + 'payment_mode': payment_mode, + 'currency_numeric': + payment_info_dict['currency_iso_numeric'].zfill(3), + 'private': ' ' * 10, + 'delay': 'A011', + 'auto': 'B010', + 'amount_msg': ('%.0f' % (amount * 100)).zfill(8), + } + return data + + def generate_lrc(self, real_msg_with_etx): + lrc = 0 + for char in real_msg_with_etx: + lrc ^= ord(char) + return lrc + + def send_message(self, data): + '''We use protocol E+''' + ascii_names = curses.ascii.controlnames + real_msg = ( + data['pos_number'] + + data['amount_msg'] + + data['answer_flag'] + + data['payment_mode'] + + data['transaction_type'] + + data['currency_numeric'] + + data['private'] + + data['delay'] + + data['auto'] + ) + logger.debug('Real message to send = %s' % real_msg) + assert len(real_msg) == 34, 'Wrong length for protocol E+' + real_msg_with_etx = real_msg + chr(ascii_names.index('ETX')) + lrc = self.generate_lrc(real_msg_with_etx) + message = chr(ascii_names.index('STX')) + real_msg_with_etx + chr(lrc) + self.serial_write(message) + logger.info('Message sent to terminal') + + def compare_data_vs_answer(self, data, answer_data): + for field in [ + 'pos_number', 'amount_msg', + 'currency_numeric', 'private']: + if data[field] != answer_data[field]: + logger.warning( + "Field %s has value '%s' in data and value '%s' in answer" + % (field, data[field], answer_data[field])) + + def parse_terminal_answer(self, real_msg, data): + answer_data = { + 'pos_number': real_msg[0:2], + 'transaction_result': real_msg[2], + 'amount_msg': real_msg[3:11], + 'payment_mode': real_msg[11], + 'currency_numeric': real_msg[12:15], + 'private': real_msg[15:26], + } + logger.debug('answer_data = %s' % answer_data) + self.compare_data_vs_answer(data, answer_data) + return answer_data + + def get_answer_from_terminal(self, data): + ascii_names = curses.ascii.controlnames + full_msg_size = 1+2+1+8+1+3+10+1+1 + msg = self.serial.read(size=full_msg_size) + logger.debug('%d bytes read from terminal' % full_msg_size) + assert len(msg) == full_msg_size, 'Answer has a wrong size' + if msg[0] != chr(ascii_names.index('STX')): + logger.error( + 'The first byte of the answer from terminal should be STX') + if msg[-2] != chr(ascii_names.index('ETX')): + logger.error( + 'The byte before final of the answer from terminal ' + 'should be ETX') + lrc = msg[-1] + computed_lrc = chr(self.generate_lrc(msg[1:-1])) + if computed_lrc != lrc: + logger.error( + 'The LRC of the answer from terminal is wrong') + real_msg = msg[1:-2] + logger.debug('Real answer received = %s' % real_msg) + return self.parse_terminal_answer(real_msg, data) + + def transaction_start(self, payment_info): + '''This function sends the data to the serial/usb port. + ''' + payment_info_dict = simplejson.loads(payment_info) + assert isinstance(payment_info_dict, dict), \ + 'payment_info_dict should be a dict' + logger.debug("payment_info_dict = %s" % payment_info_dict) + try: + logger.debug( + 'Opening serial port %s for payment terminal with baudrate %d' + % (self.device_name, self.device_rate)) + # IMPORTANT : don't modify timeout=3 seconds + # This parameter is very important ; the Telium spec say + # that we have to wait to up 3 seconds to get LRC + self.serial = Serial( + self.device_name, self.device_rate, + timeout=3) + logger.debug('serial.is_open = %s' % self.serial.isOpen()) + if self.initialize_msg(): + data = self.prepare_data_to_send(payment_info_dict) + if not data: + return + self.send_message(data) + if self.get_one_byte_answer('ACK'): + self.send_one_byte_signal('EOT') + + logger.info("Now expecting answer from Terminal") + if self.get_one_byte_answer('ENQ'): + self.send_one_byte_signal('ACK') + answer_data = self.get_answer_from_terminal(data) + self.send_one_byte_signal('ACK') + if self.get_one_byte_answer('EOT'): + logger.info("Answer received from Terminal") + + except Exception, e: + logger.error('Exception in serial connection: %s' % str(e)) + finally: + if self.serial: + logger.debug('Closing serial port for payment terminal') + self.serial.close() + + def run(self): + while True: + try: + timestamp, task, data = self.queue.get(True) + if task == 'transaction_start': + self.transaction_start(data) + elif task == 'status': + pass + except Exception as e: + self.set_status('error', str(e)) + +driver = TeliumPaymentTerminalDriver() + +hw_proxy.drivers['telium_payment_terminal'] = driver + + +class TeliumPaymentTerminalProxy(hw_proxy.Proxy): + @http.route( + '/hw_proxy/payment_terminal_transaction_start', + type='json', auth='none', cors='*') + def payment_terminal_transaction_start(self, payment_info): + logger.debug('Telium: Call payment_terminal_transaction_start') + driver.push_task('transaction_start', payment_info) From 06eaeacce04ccccf1ebc9a944e9af376e1628dca Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Thu, 10 Jul 2014 20:40:01 +0200 Subject: [PATCH 07/27] Add dependancy on currency_iso_numeric. --- pos_payment_terminal/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pos_payment_terminal/__openerp__.py b/pos_payment_terminal/__openerp__.py index 67cb8a08..a6d5c5de 100755 --- a/pos_payment_terminal/__openerp__.py +++ b/pos_payment_terminal/__openerp__.py @@ -38,7 +38,7 @@ This module has been developped during a POS code sprint at Akretion France from Please contact Alexis de Lattre from Akretion for any help or question about this module. """, 'author': 'Aurélien DUMAINE', - 'depends': ['point_of_sale'], + 'depends': ['point_of_sale', 'currency_iso_numeric'], 'data' : ['pos_payment_terminal.xml', 'pos_payment_terminal_view.xml'], 'qweb': ['static/src/xml/pos_payment_terminal.xml'], From b9f64417454ce17055ae31c312d55db1a1bbcf5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Au=C3=A9lien=20DUMAINE?= Date: Fri, 11 Jul 2014 03:03:11 +0200 Subject: [PATCH 08/27] pos_customer_display bugfix : new massage on deleting orderLine and fixing unit_price computation on adding orderLine --- .../static/src/js/customer_display.js | 18 +++++++----------- .../static/src/js/pos_payment_terminal.js | 1 - 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/pos_customer_display/static/src/js/customer_display.js b/pos_customer_display/static/src/js/customer_display.js index 8a135570..ad2a9d06 100755 --- a/pos_customer_display/static/src/js/customer_display.js +++ b/pos_customer_display/static/src/js/customer_display.js @@ -17,7 +17,7 @@ openerp.pos_customer_display = function(instance){ // in order to not recompute qty in options..., we assume that the new ordeLine is the last of the collection // addOrderline exists but is not called by addProduct, should we handle it ? var line = this.get('selectedOrder').getLastOrderline(); - var price_unit = line.get_quantity() * line.get_unit_price() * (1.0 - (line.get_discount() / 100.0)); + var price_unit = line.get_unit_price() * (1.0 - (line.get_discount() / 100.0)); price_unit = price_unit.toFixed(currency_rounding); var l21 = line.get_quantity_str_with_unit() + ' x ' + price_unit; var l22 = ' ' + line.get_display_price().toFixed(currency_rounding); @@ -26,15 +26,11 @@ openerp.pos_customer_display = function(instance){ this.proxy.complete_string_right(l21, line_length - l22.length) + l22 ); } else if (type == 'removeOrderline') { - // FIXME : first click on the backspace button set the amount to 0 + // first click on the backspace button set the amount to 0 => we can't precise the deleted qunatity and price var line = data['line']; - var price_unit = line.get_quantity() * line.get_unit_price() * (1.0 - (line.get_discount() / 100.0)); - price_unit = price_unit.toFixed(currency_rounding); - var l21 = '-' + line.get_quantity_str_with_unit() + ' x ' + price_unit; - var l22 = ' ' + -1 * line.get_display_price().toFixed(currency_rounding); var lines_to_send = new Array( - this.proxy.complete_string_right(line.get_product().name, line_length), - this.proxy.complete_string_right(l21, line_length - l22.length) + l22 + this.proxy.complete_string_center(_t("Delete item"), line_length), + this.proxy.complete_string_center(line.get_product().name, line_length) ); } else if (type == 'addPaymentline') { var cashregister = data['cashregister']; @@ -48,7 +44,7 @@ openerp.pos_customer_display = function(instance){ var line = data['line']; var amount = line.get_amount().toFixed(currency_rounding); var lines_to_send = new Array( - this.proxy.complete_string_right(_t("Delete payment"), line_length), + this.proxy.complete_string_center(_t("Delete payment"), line_length), this.proxy.complete_string_right(line.cashregister.journal_id[1] , line_length - 1 - amount.length) + ' ' + amount ); } else if (type == 'pushOrder') { @@ -80,7 +76,7 @@ openerp.pos_customer_display = function(instance){ return; } -// alert("aa" + line_length); +// alert("In prepare_text_customer_display " + line_length); this.proxy.send_text_customer_display(lines_to_send, line_length); }, @@ -90,7 +86,7 @@ openerp.pos_customer_display = function(instance){ module.ProxyDevice = module.ProxyDevice.extend({ send_text_customer_display: function(data, line_length){ //FIXME : this function is call twice. The first time, it is not called by prepare_text_customer_display : WHY ? -// alert("bb" + line_length); +// alert("In sent_text_customer_display " + line_length); if (data[0].length != line_length) console.warn(data[0].length + " " + data[0]); if (data[1].length != line_length) diff --git a/pos_payment_terminal/static/src/js/pos_payment_terminal.js b/pos_payment_terminal/static/src/js/pos_payment_terminal.js index 6f7a866f..4281a9c0 100755 --- a/pos_payment_terminal/static/src/js/pos_payment_terminal.js +++ b/pos_payment_terminal/static/src/js/pos_payment_terminal.js @@ -46,7 +46,6 @@ openerp.pos_payment_terminal = function(instance){ } el_node.querySelector('.payment-terminal-transaction-start') .addEventListener('click', this.payment_terminal_transaction_start); - //.addEventListener('click', this.pos.proxy.payment_terminal_transaction_start(line, this.pos.currency.name, this.pos.currency.iso_numeric)); } return el_node; }; From c5f0f4221394301610e36dff97f32265f891082d Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Sat, 12 Jul 2014 00:13:07 +0200 Subject: [PATCH 09/27] Update description. --- hw_telium_payment_terminal/__openerp__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hw_telium_payment_terminal/__openerp__.py b/hw_telium_payment_terminal/__openerp__.py index 2ca39fc6..504f053c 100644 --- a/hw_telium_payment_terminal/__openerp__.py +++ b/hw_telium_payment_terminal/__openerp__.py @@ -37,9 +37,12 @@ The configuration of the hardware is done in the configuration file of the Odoo * payment_terminal_device_name (default = /dev/ttyACM0) * payment_terminal_device_rate (default = 9600) -* payment_terminal_device_timeout (default = 2 seconds) -The Telium protocol is used by Ingenico and Sagem payment terminals. It is based on the Concert protocol, so it can probably work with payment terminals from other brands. It has been tested a an Ingenico EFTSmart4S terminal with Telim Manager version 37784503. The protocol E is implemented (we may implement the protocol E+ in the future). +The Telium protocol is used by Ingenico and Sagem payment terminals. It is based on the Concert protocol, so it can probably work with payment terminals from other brands. This module implements the protocol E+ (and not the protocol E), so it requires a Telium Manager version 37783600 or superior. To get the version of the Telium Manager, press F > 0-TELIUM MANAGER > 2-Consultation > 4-Configuration > 2-Software > 1-TERMINAL > On Display > Telium Manager and then read the field *M20S*. + +You will need to configure your payment terminal to accept commands from the POS. On an Ingenico reader, press F > 0-TELIUM MANAGER > 5-Initialization > 1-Parameters > Cash Connection and then select *On* and then *USB*. After that, you should reboot the terminal. + +This module has been successfully tested with an Ingenico EFTSmart4S and an Ingenico EFTSmart2 2640 with Telim Manager version 37784503. This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. This module is part of the POS project of the Odoo Community Association http://odoo-community.org/. You are invited to become a member and/or get involved in the Association ! From 5cc93eef965363488fea6405bb47b82733af7f19 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Sat, 12 Jul 2014 00:29:04 +0200 Subject: [PATCH 10/27] Fix copyright headers PEP8 stuff --- hw_customer_display/__openerp__.py | 2 +- hw_telium_payment_terminal/__openerp__.py | 2 +- pos_customer_display/__init__.py | 2 +- pos_customer_display/__openerp__.py | 14 +++++----- pos_customer_display/pos_customer_display.py | 28 +++++++++---------- pos_customer_display/pos_customer_display.xml | 0 pos_payment_terminal/__init__.py | 2 +- pos_payment_terminal/__openerp__.py | 16 +++++------ pos_payment_terminal/pos_payment_terminal.py | 18 ++++++++---- 9 files changed, 44 insertions(+), 40 deletions(-) mode change 100755 => 100644 pos_customer_display/__init__.py mode change 100755 => 100644 pos_customer_display/__openerp__.py mode change 100755 => 100644 pos_customer_display/pos_customer_display.xml diff --git a/hw_customer_display/__openerp__.py b/hw_customer_display/__openerp__.py index ef05e105..b1f5e96e 100644 --- a/hw_customer_display/__openerp__.py +++ b/hw_customer_display/__openerp__.py @@ -51,7 +51,7 @@ Please contact Alexis de Lattre from Akretion for 'website': 'http://www.akretion.com', 'depends': ['hw_proxy'], 'external_dependencies': { - 'python' : ['serial', 'unidecode'], + 'python': ['serial', 'unidecode'], }, 'data': [], 'active': False, diff --git a/hw_telium_payment_terminal/__openerp__.py b/hw_telium_payment_terminal/__openerp__.py index 504f053c..21035e94 100644 --- a/hw_telium_payment_terminal/__openerp__.py +++ b/hw_telium_payment_terminal/__openerp__.py @@ -52,7 +52,7 @@ Please contact Alexis de Lattre from Akretion for 'website': 'http://www.akretion.com', 'depends': ['hw_proxy'], 'external_dependencies': { - 'python' : ['serial'], + 'python': ['serial'], }, 'data': [], 'active': False, diff --git a/pos_customer_display/__init__.py b/pos_customer_display/__init__.py old mode 100755 new mode 100644 index 7dff50a7..bb27ef75 --- a/pos_customer_display/__init__.py +++ b/pos_customer_display/__init__.py @@ -1 +1 @@ -import pos_customer_display +from . import pos_customer_display diff --git a/pos_customer_display/__openerp__.py b/pos_customer_display/__openerp__.py old mode 100755 new mode 100644 index 3fb094b6..3f0a7c1e --- a/pos_customer_display/__openerp__.py +++ b/pos_customer_display/__openerp__.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). +# POS Customer Display module for Odoo +# Copyright (C) 2014 Aurélien DUMAINE # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -33,14 +33,14 @@ This module adds support for Customer Display in the Point of Sale. This module The number of rows and cols of the Customer Display (usually 2 x 20) should be configured on the main Odoo server, in the menu Point of Sale > Configuration > Point of Sales. -It has been tested with a Bixolon BCD-1100 (http://www.bixolon.com/html/en/product/product_detail.xhtml?prod_id=61), but should support most serial and USB-serial LCD displays out-of-the-box or with inheritance of a few functions. To setup the BCD-1100 on Linux, you will find some technical instructions on this page : http://techtuxwords.blogspot.fr/2012/12/linux-and-bixolon-bcd-1100.html +It has been tested with a Bixolon BCD-1100 (http://www.bixolon.com/html/en/product/product_detail.xhtml?prod_id=61), but should support most serial and USB-serial LCD displays out-of-the-box, cf the module *hw_customer_display* for more info. This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. This module is part of the POS project of the Odoo Community Association http://odoo-community.org/. You are invited to become a member and/or get involved in the Association ! - -Please contact Alexis de Lattre from Akretion for any help or question about this module. """, 'author': 'Aurélien DUMAINE', 'depends': ['point_of_sale'], - 'data' : ['pos_customer_display.xml', - 'customer_display_view.xml'], + 'data' : [ + 'pos_customer_display.xml', + 'customer_display_view.xml', + ], } diff --git a/pos_customer_display/pos_customer_display.py b/pos_customer_display/pos_customer_display.py index 20eda083..d2bcb43e 100644 --- a/pos_customer_display/pos_customer_display.py +++ b/pos_customer_display/pos_customer_display.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SP (). +# POS Customer Display module for Odoo +# Copyright (C) 2014 Aurélien DUMAINE # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -20,27 +20,25 @@ ############################################################################## import logging -import time - -from openerp import tools -from openerp.osv import fields, osv -from openerp.tools.translate import _ - -import openerp.addons.decimal_precision as dp -import openerp.addons.product.product +from openerp.osv import fields, orm _logger = logging.getLogger(__name__) -class pos_config(osv.osv): + +class pos_config(orm.Model): _name = 'pos.config' _inherit = 'pos.config' - + _columns = { - 'iface_customer_display' : fields.boolean('Customer display', help="Display data on the customer display"), - 'customer_display_line_length' : fields.integer('Line length', help="Length of the LEDs lines of the customer display"), + 'iface_customer_display': fields.boolean( + 'Customer display', help="Display data on the customer display"), + 'customer_display_line_length': fields.integer( + 'Line length', + help="Length of the LEDs lines of the customer display"), } + _defaults = { - 'customer_display_line_length' : 20, + 'customer_display_line_length': 20, } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/pos_customer_display/pos_customer_display.xml b/pos_customer_display/pos_customer_display.xml old mode 100755 new mode 100644 diff --git a/pos_payment_terminal/__init__.py b/pos_payment_terminal/__init__.py index 45b85bcf..9e67b3d9 100755 --- a/pos_payment_terminal/__init__.py +++ b/pos_payment_terminal/__init__.py @@ -1 +1 @@ -import pos_payment_terminal +from . import pos_payment_terminal diff --git a/pos_payment_terminal/__openerp__.py b/pos_payment_terminal/__openerp__.py index a6d5c5de..9241fd02 100755 --- a/pos_payment_terminal/__openerp__.py +++ b/pos_payment_terminal/__openerp__.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). +# POS Payment Terminal module for Odoo +# Copyright (C) 2014 Aurélien DUMAINE # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -29,17 +29,17 @@ POS Payment Terminal ==================== -This module adds support for Payment Terminal in the Point of Sale. This module is designed to be installed on the *main Odoo server*. On the *POSbox*, you should install the module *hw_x* depending on the protocol implemented in your device. Ingenico devices support the Telium protocol implemented in the *hw_telium_payment_terminal* module. +This module adds support for Payment Terminal in the Point of Sale. This module is designed to be installed on the *main Odoo server*. On the *POSbox*, you should install the module *hw_x* depending on the protocol implemented in your device. Ingenico and Sagem devices support the Telium protocol implemented in the *hw_telium_payment_terminal* module. This module support two payment methods : cards and checks. The payment method should be configured on the main Odoo server, in the menu Point of Sale > Configuration > Payment Methods. This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. This module is part of the POS project of the Odoo Community Association http://odoo-community.org/. You are invited to become a member and/or get involved in the Association ! - -Please contact Alexis de Lattre from Akretion for any help or question about this module. """, 'author': 'Aurélien DUMAINE', 'depends': ['point_of_sale', 'currency_iso_numeric'], - 'data' : ['pos_payment_terminal.xml', - 'pos_payment_terminal_view.xml'], - 'qweb': ['static/src/xml/pos_payment_terminal.xml'], + 'data': [ + 'pos_payment_terminal.xml', + 'pos_payment_terminal_view.xml', + ], + 'qweb': ['static/src/xml/pos_payment_terminal.xml'], } diff --git a/pos_payment_terminal/pos_payment_terminal.py b/pos_payment_terminal/pos_payment_terminal.py index 3b4853ad..e6dc9ff7 100644 --- a/pos_payment_terminal/pos_payment_terminal.py +++ b/pos_payment_terminal/pos_payment_terminal.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SP (). +# POS Payment Terminal module for Odoo +# Copyright (C) 2014 Aurélien DUMAINE # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -21,16 +21,22 @@ from openerp import models, fields + class account_journal(models.Model): _name = 'account.journal' _inherit = 'account.journal' - - payment_mode = fields.Selection((('card', 'Card'), ('check', 'Check')), 'Payment mode', help="Select the payment mode sent to the payment terminal") + + payment_mode = fields.Selection( + (('card', 'Card'), ('check', 'Check')), 'Payment mode', + help="Select the payment mode sent to the payment terminal") + class pos_config(models.Model): _name = 'pos.config' _inherit = 'pos.config' - - iface_payment_terminal = fields.Boolean('Payment Terminal', help="A payment terminal is available on the Proxy") + + iface_payment_terminal = fields.Boolean( + 'Payment Terminal', + help="A payment terminal is available on the Proxy") # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From 44122e4ce3a36b6d0d157188746cf5e74ab9f77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Au=C3=A9lien=20DUMAINE?= Date: Sun, 20 Jul 2014 22:36:24 +0200 Subject: [PATCH 11/27] pos_payment_terminal : bigger button for start transaction with touchscreen --- pos_customer_display/static/src/js/customer_display.js | 4 ++-- pos_payment_terminal/pos_payment_terminal_view.xml | 6 ++++++ pos_payment_terminal/static/src/js/pos_payment_terminal.js | 2 +- .../static/src/xml/pos_payment_terminal.xml | 5 +++-- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/pos_customer_display/static/src/js/customer_display.js b/pos_customer_display/static/src/js/customer_display.js index ad2a9d06..fd21fe07 100755 --- a/pos_customer_display/static/src/js/customer_display.js +++ b/pos_customer_display/static/src/js/customer_display.js @@ -88,9 +88,9 @@ openerp.pos_customer_display = function(instance){ //FIXME : this function is call twice. The first time, it is not called by prepare_text_customer_display : WHY ? // alert("In sent_text_customer_display " + line_length); if (data[0].length != line_length) - console.warn(data[0].length + " " + data[0]); + console.warn(data[0].length + " -> " + data[0]); if (data[1].length != line_length) - console.warn(data[1].length + " " + data[1]); + console.warn(data[1].length + " ->" + data[1]); if (data[0].length != line_length || data[1].length != line_length){ console.warn("Data components have to have " + line_length + " chars."); console.log(data[0].length + " -> "+ data[0] + "\n" + data[1].length + " -> " + data[1]); diff --git a/pos_payment_terminal/pos_payment_terminal_view.xml b/pos_payment_terminal/pos_payment_terminal_view.xml index e55d2a34..dbcca88c 100644 --- a/pos_payment_terminal/pos_payment_terminal_view.xml +++ b/pos_payment_terminal/pos_payment_terminal_view.xml @@ -21,5 +21,11 @@ + + diff --git a/pos_payment_terminal/static/src/js/pos_payment_terminal.js b/pos_payment_terminal/static/src/js/pos_payment_terminal.js index 4281a9c0..9013da8d 100755 --- a/pos_payment_terminal/static/src/js/pos_payment_terminal.js +++ b/pos_payment_terminal/static/src/js/pos_payment_terminal.js @@ -7,7 +7,7 @@ openerp.pos_payment_terminal = function(instance){ 'currency_iso' : currency_iso, 'currency_iso_numeric' : currency_iso_numeric, 'payment_mode' : line.cashregister.journal.payment_mode}; - alert(JSON.stringify(data)); +// alert(JSON.stringify(data)); this.message('payment_terminal_transaction_start', {'payment_info' : JSON.stringify(data)}); }, }); diff --git a/pos_payment_terminal/static/src/xml/pos_payment_terminal.xml b/pos_payment_terminal/static/src/xml/pos_payment_terminal.xml index 8467a816..ce7459c4 100644 --- a/pos_payment_terminal/static/src/xml/pos_payment_terminal.xml +++ b/pos_payment_terminal/static/src/xml/pos_payment_terminal.xml @@ -1,12 +1,13 @@ - + From 8b598d562ce634dee8b5dcfec8445dd0622501c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Au=C3=A9lien=20DUMAINE?= Date: Mon, 21 Jul 2014 22:12:06 +0200 Subject: [PATCH 12/27] Add french translation --- pos_customer_display/i18n/fr.po | 108 ++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 pos_customer_display/i18n/fr.po diff --git a/pos_customer_display/i18n/fr.po b/pos_customer_display/i18n/fr.po new file mode 100644 index 00000000..9fa4cc09 --- /dev/null +++ b/pos_customer_display/i18n/fr.po @@ -0,0 +1,108 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * pos_customer_display +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 9.0alpha1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-21 10:50+0000\n" +"PO-Revision-Date: 2014-07-21 10:50+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:72 +#, python-format +msgid "***" +msgstr "***" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:161 +#: field:pos.config,iface_customer_display:0 +#, python-format +msgid "Customer display" +msgstr "Afficheur client" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:32 +#, python-format +msgid "Delete item" +msgstr "Suppression article" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:47 +#, python-format +msgid "Delete payment" +msgstr "Suppression paiement" + +#. module: pos_customer_display +#: help:pos.config,iface_customer_display:0 +msgid "Display data on the customer display" +msgstr "Afficher les info sur l'afficheur client" + +#. module: pos_customer_display +#: help:pos.config,customer_display_line_length:0 +msgid "Length of the LEDs lines of the customer display" +msgstr "Longueur des lignes de l'afficheur client, nombre de caractères" + +#. module: pos_customer_display +#: field:pos.config,customer_display_line_length:0 +msgid "Line length" +msgstr "Longueur des lignes" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:67 +#, python-format +msgid "Next customer..." +msgstr "Client suivant..." + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:164 +#, python-format +msgid "Offline" +msgstr "Hors ligne" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:40 +#, python-format +msgid "Payment :" +msgstr "Paiement :" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:71 +#, python-format +msgid "Point of sale closed" +msgstr "Caisse fermée" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:39 +#, python-format +msgid "TOTAL : " +msgstr "TOTAL : " + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:62 +#, python-format +msgid "YOUR CHANGE :" +msgstr "VOTRE MONNAIE :" + +#. module: pos_customer_display +#: model:ir.model,name:pos_customer_display.model_pos_config +msgid "pos.config" +msgstr "pos.config" + From 41e2497035f92c20d154820558894c9ca1236d29 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Mon, 1 Sep 2014 13:31:03 +0200 Subject: [PATCH 13/27] FIX Adapt JS code to recent changes in the code of POS v8 Update module description with my recent tests with new hardware --- hw_customer_display/__openerp__.py | 11 +++++++++-- hw_telium_payment_terminal/__openerp__.py | 6 +++++- .../static/src/js/customer_display.js | 8 ++++---- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/hw_customer_display/__openerp__.py b/hw_customer_display/__openerp__.py index b1f5e96e..3f295961 100644 --- a/hw_customer_display/__openerp__.py +++ b/hw_customer_display/__openerp__.py @@ -39,9 +39,16 @@ The configuration of the hardware is done in the configuration file of the Odoo * customer_display_device_rate (default = 9600) * customer_display_device_timeout (default = 2 seconds) -The number of rows and cols of the Customer Display (usually 2 x 20) should be configured on the main Odoo server, in the menu Point of Sale > Configuration > Point of Sales. +The number of cols of the Customer Display (usually 20) should be configured on the main Odoo server, in the menu Point of Sale > Configuration > Point of Sales. The number of rows is supposed to be 2. -It has been tested with a Bixolon BCD-1100 (http://www.bixolon.com/html/en/product/product_detail.xhtml?prod_id=61), but should support most serial and USB-serial LCD displays out-of-the-box or with inheritance of a few functions. To setup the BCD-1100 on Linux, you will find some technical instructions on this page : http://techtuxwords.blogspot.fr/2012/12/linux-and-bixolon-bcd-1100.html +It should support most serial and USB-serial LCD displays out-of-the-box or with inheritance of a few functions. + +It has been tested with: + +* Bixolon BCD-1100 (Datasheet : http://www.bixolon.com/html/en/product/product_detail.xhtml?prod_id=61) +* Bixolon BCD-1000 + +To setup the BCD-1100 on Linux, you will find some technical instructions on this page : http://techtuxwords.blogspot.fr/2012/12/linux-and-bixolon-bcd-1100.html This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. This module is part of the POS project of the Odoo Community Association http://odoo-community.org/. You are invited to become a member and/or get involved in the Association ! diff --git a/hw_telium_payment_terminal/__openerp__.py b/hw_telium_payment_terminal/__openerp__.py index 21035e94..fb98d352 100644 --- a/hw_telium_payment_terminal/__openerp__.py +++ b/hw_telium_payment_terminal/__openerp__.py @@ -42,7 +42,11 @@ The Telium protocol is used by Ingenico and Sagem payment terminals. It is based You will need to configure your payment terminal to accept commands from the POS. On an Ingenico reader, press F > 0-TELIUM MANAGER > 5-Initialization > 1-Parameters > Cash Connection and then select *On* and then *USB*. After that, you should reboot the terminal. -This module has been successfully tested with an Ingenico EFTSmart4S and an Ingenico EFTSmart2 2640 with Telim Manager version 37784503. +This module has been successfully tested with: + +* Ingenico EFTSmart4S +* Ingenico EFTSmart2 2640 with Telim Manager version 37784503 +* Ingenico i2200 cheque reader and writer This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. This module is part of the POS project of the Odoo Community Association http://odoo-community.org/. You are invited to become a member and/or get involved in the Association ! diff --git a/pos_customer_display/static/src/js/customer_display.js b/pos_customer_display/static/src/js/customer_display.js index fd21fe07..829e6407 100755 --- a/pos_customer_display/static/src/js/customer_display.js +++ b/pos_customer_display/static/src/js/customer_display.js @@ -22,7 +22,7 @@ openerp.pos_customer_display = function(instance){ var l21 = line.get_quantity_str_with_unit() + ' x ' + price_unit; var l22 = ' ' + line.get_display_price().toFixed(currency_rounding); var lines_to_send = new Array( - this.proxy.complete_string_right(line.get_product().name, line_length), + this.proxy.complete_string_right(line.get_product().display_name, line_length), this.proxy.complete_string_right(l21, line_length - l22.length) + l22 ); } else if (type == 'removeOrderline') { @@ -36,8 +36,8 @@ openerp.pos_customer_display = function(instance){ var cashregister = data['cashregister']; var total = this.get('selectedOrder').getTotalTaxIncluded().toFixed(currency_rounding); var lines_to_send = new Array( - this.proxy.complete_string_right(_t("TOTAL : "), line_length - 1 - total.length) + ' ' + total, - this.proxy.complete_string_right(_t("Payment :"), line_length - 1 - cashregister.journal_id[1].length) + ' ' + cashregister.journal_id[1] + this.proxy.complete_string_right(_t("TOTAL: "), line_length - 1 - total.length) + ' ' + total, + this.proxy.complete_string_right(_t("Payment:"), line_length - 1 - cashregister.journal_id[1].length) + ' ' + cashregister.journal_id[1] ); } else if (type == 'removePaymentline') { @@ -172,7 +172,7 @@ openerp.pos_customer_display = function(instance){ var _super_addProduct_ = module.Order.prototype.addProduct; module.Order.prototype.addProduct = function(product, options){ _super_addProduct_.call(this, product, options); - this.pos.prepare_text_customer_display('addProduct', {'product' : product, 'options' : options}); + this.pos.prepare_text_customer_display('addProduct', {'product' : product, 'options' : options}); }; From a564d3182ce9d274afe601e160fffd8d9b2b8f2e Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 23 Sep 2014 19:03:05 +0200 Subject: [PATCH 14/27] Update module description, with instructions for recent kernels --- hw_customer_display/__openerp__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hw_customer_display/__openerp__.py b/hw_customer_display/__openerp__.py index 3f295961..920994d0 100644 --- a/hw_customer_display/__openerp__.py +++ b/hw_customer_display/__openerp__.py @@ -49,6 +49,7 @@ It has been tested with: * Bixolon BCD-1000 To setup the BCD-1100 on Linux, you will find some technical instructions on this page : http://techtuxwords.blogspot.fr/2012/12/linux-and-bixolon-bcd-1100.html +If you have a kernel >= 3.12, you should also read this : http://www.leniwiec.org/en/2014/06/25/ubuntu-14-04lts-how-to-pass-id-vendor-and-id-product-to-ftdi_sio-driver/ This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. This module is part of the POS project of the Odoo Community Association http://odoo-community.org/. You are invited to become a member and/or get involved in the Association ! From 5411ca4d50d17e530047b78e330e1947703d3b15 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Wed, 22 Oct 2014 21:47:15 +0200 Subject: [PATCH 15/27] pos_payment_terminal: add missing css file pos_customer_display: FIX JS code and make it more robust --- .../static/src/js/customer_display.js | 351 ++++++++---------- .../static/src/css/pos_payment_terminal.css | 12 + 2 files changed, 165 insertions(+), 198 deletions(-) create mode 100644 pos_payment_terminal/static/src/css/pos_payment_terminal.css diff --git a/pos_customer_display/static/src/js/customer_display.js b/pos_customer_display/static/src/js/customer_display.js index 829e6407..6170a360 100755 --- a/pos_customer_display/static/src/js/customer_display.js +++ b/pos_customer_display/static/src/js/customer_display.js @@ -3,104 +3,103 @@ openerp.pos_customer_display = function(instance){ var _t = instance.web._t; var round_di = instance.web.round_decimals; - var round_pr = instance.web.round_precision + var round_pr = instance.web.round_precision; module.PosModel = module.PosModel.extend({ - prepare_text_customer_display: function(type, data){ - if (this.config.iface_customer_display != true) - return; - var line_length = this.config.customer_display_line_length || 20; - var currency_rounding = Math.ceil(Math.log(1.0 / this.currency.rounding) / Math.log(10)); - - if (type == 'addProduct'){ - // in order to not recompute qty in options..., we assume that the new ordeLine is the last of the collection - // addOrderline exists but is not called by addProduct, should we handle it ? - var line = this.get('selectedOrder').getLastOrderline(); - var price_unit = line.get_unit_price() * (1.0 - (line.get_discount() / 100.0)); - price_unit = price_unit.toFixed(currency_rounding); - var l21 = line.get_quantity_str_with_unit() + ' x ' + price_unit; - var l22 = ' ' + line.get_display_price().toFixed(currency_rounding); - var lines_to_send = new Array( - this.proxy.complete_string_right(line.get_product().display_name, line_length), - this.proxy.complete_string_right(l21, line_length - l22.length) + l22 - ); - } else if (type == 'removeOrderline') { - // first click on the backspace button set the amount to 0 => we can't precise the deleted qunatity and price - var line = data['line']; - var lines_to_send = new Array( - this.proxy.complete_string_center(_t("Delete item"), line_length), - this.proxy.complete_string_center(line.get_product().name, line_length) - ); - } else if (type == 'addPaymentline') { - var cashregister = data['cashregister']; - var total = this.get('selectedOrder').getTotalTaxIncluded().toFixed(currency_rounding); - var lines_to_send = new Array( - this.proxy.complete_string_right(_t("TOTAL: "), line_length - 1 - total.length) + ' ' + total, - this.proxy.complete_string_right(_t("Payment:"), line_length - 1 - cashregister.journal_id[1].length) + ' ' + cashregister.journal_id[1] - ); - - } else if (type == 'removePaymentline') { - var line = data['line']; - var amount = line.get_amount().toFixed(currency_rounding); - var lines_to_send = new Array( - this.proxy.complete_string_center(_t("Delete payment"), line_length), - this.proxy.complete_string_right(line.cashregister.journal_id[1] , line_length - 1 - amount.length) + ' ' + amount - ); - } else if (type == 'pushOrder') { - var currentOrder = data['currentOrder']; - var paidTotal = currentOrder.getPaidTotal(); - var dueTotal = currentOrder.getTotalTaxIncluded(); - var remaining = dueTotal > paidTotal ? dueTotal - paidTotal : 0; - var change = paidTotal > dueTotal ? paidTotal - dueTotal : 0; - - var l1; - if (change == 0){ - l1 = this.proxy.complete_string_center(_t(""), line_length); - } else { - change = change.toFixed(currency_rounding); - l1 = this.proxy.complete_string_right(_t("YOUR CHANGE :"), line_length - 1 - change.length) + ' ' + change; - } - - var lines_to_send = new Array( - l1, - this.proxy.complete_string_center(_t("Next customer..."), line_length) - ); - } else if (type = 'closePOS') { - var lines_to_send = new Array( - this.proxy.complete_string_center(_t("Point of sale closed"), line_length), - this.proxy.complete_string_center(_t("***"), line_length) - ); - } else { - console.warn('Unknown message type'); - return; - } + prepare_text_customer_display: function(type, data){ + if (this.config.iface_customer_display != true) + return; + var line_length = this.config.customer_display_line_length || 20; + var currency_rounding = Math.ceil(Math.log(1.0 / this.currency.rounding) / Math.log(10)); + + if (type == 'addProduct'){ + // in order to not recompute qty in options..., we assume that the new ordeLine is the last of the collection + // addOrderline exists but is not called by addProduct, should we handle it ? + var line = this.get('selectedOrder').getLastOrderline(); + var price_unit = line.get_unit_price() * (1.0 - (line.get_discount() / 100.0)); + price_unit = price_unit.toFixed(currency_rounding); + var l21 = line.get_quantity_str_with_unit() + ' x ' + price_unit; + var l22 = ' ' + line.get_display_price().toFixed(currency_rounding); + var lines_to_send = new Array( + this.proxy.complete_string_right(line.get_product().display_name, line_length), + this.proxy.complete_string_right(l21, line_length - l22.length) + l22 + ); + + } else if (type == 'removeOrderline') { + // first click on the backspace button set the amount to 0 => we can't precise the deleted qunatity and price + var line = data['line']; + var lines_to_send = new Array( + this.proxy.complete_string_center(_t("Delete item"), line_length), + this.proxy.complete_string_center(line.get_product().name, line_length) + ); + + } else if (type == 'addPaymentline') { + var cashregister = data['cashregister']; + var total = this.get('selectedOrder').getTotalTaxIncluded().toFixed(currency_rounding); + var lines_to_send = new Array( + this.proxy.complete_string_right(_t("TOTAL: "), line_length - 1 - total.length) + ' ' + total, + this.proxy.complete_string_right(_t("Payment:"), line_length - 1 - cashregister.journal_id[1].length) + ' ' + cashregister.journal_id[1] + ); + + } else if (type == 'removePaymentline') { + var line = data['line']; + var amount = line.get_amount().toFixed(currency_rounding); + var lines_to_send = new Array( + this.proxy.complete_string_center(_t("Delete payment"), line_length), + this.proxy.complete_string_right(line.cashregister.journal_id[1] , line_length - 1 - amount.length) + ' ' + amount + ); + + } else if (type == 'pushOrder') { + var order = data['order']; + var paidTotal = order.getPaidTotal(); + var dueTotal = order.getTotalTaxIncluded(); + var remaining = dueTotal > paidTotal ? dueTotal - paidTotal : 0; + var change = paidTotal > dueTotal ? paidTotal - dueTotal : 0; + + var l1; + if (change == 0){ + l1 = this.proxy.complete_string_center(_t(""), line_length); + } else { + change = change.toFixed(currency_rounding); + l1 = this.proxy.complete_string_right(_t("YOUR CHANGE:"), line_length - 1 - change.length) + ' ' + change; + } + + var lines_to_send = new Array( + l1, + this.proxy.complete_string_center(_t("Next customer..."), line_length) + ); + + } else if (type = 'closePOS') { + var lines_to_send = new Array( + this.proxy.complete_string_center(_t("Point of sale closed"), line_length), + this.proxy.complete_string_center(_t("***"), line_length) + ); + } else { + console.warn('Unknown message type'); + return; + } -// alert("In prepare_text_customer_display " + line_length); - this.proxy.send_text_customer_display(lines_to_send, line_length); - }, +// alert("In prepare_text_customer_display " + line_length); + this.proxy.send_text_customer_display(lines_to_send, line_length); + }, - }); + }); module.ProxyDevice = module.ProxyDevice.extend({ send_text_customer_display: function(data, line_length){ - //FIXME : this function is call twice. The first time, it is not called by prepare_text_customer_display : WHY ? -// alert("In sent_text_customer_display " + line_length); - if (data[0].length != line_length) - console.warn(data[0].length + " -> " + data[0]); - if (data[1].length != line_length) - console.warn(data[1].length + " ->" + data[1]); - if (data[0].length != line_length || data[1].length != line_length){ - console.warn("Data components have to have " + line_length + " chars."); - console.log(data[0].length + " -> "+ data[0] + "\n" + data[1].length + " -> " + data[1]); + //FIXME : this function is call twice. The first time, it is not called by prepare_text_customer_display : WHY ? + if (_.isEmpty(data) || data.lenght != 2 || data[0].length != line_length || data[1].length != line_length){ + console.warn("Bad Data argument. Data = " + data); + console.warn('Line_length = ' + line_length); } else { -// alert(JSON.stringify(data)); - return this.message('send_text_customer_display', {'text_to_display' : JSON.stringify(data)}); - } - return; +// alert(JSON.stringify(data)); + return this.message('send_text_customer_display', {'text_to_display' : JSON.stringify(data)}); + } }, complete_string_right: function(string, length){ + if (string) { if (string.length > length) { return string.substring(0,length); @@ -108,161 +107,117 @@ openerp.pos_customer_display = function(instance){ else if (string.length < length) { while(string.length < length) - string = string+' '; + string = string + ' '; return string; } - return string; + } + return string; }, complete_string_left: function(string, length){ - if (string.length > length) - { + if (string) { + if (string.length > length) + { return string.substring(0,length); - } - else if (string.length < length) - { + } + else if (string.length < length) + { while(string.length < length) - string = ' '+string; + string = ' ' + string; return string; + } } return string; }, complete_string_center: function(string, length){ - var self = this; - if (string.length > length) - { - return string.substring(0,length); - } - else if (string.length < length) - { - ini = (length - string.length)/2; - while(string.length < length - ini) - string = ' '+string; - while(string.length < length) - string = string + ' '; - return string; + if (string) { + if (string.length > length) + { + return string.substring(0, length); + } + else if (string.length < length) + { + ini = (length - string.length) / 2; + while(string.length < length - ini) + string = ' ' + string; + while(string.length < length) + string = string + ' '; + return string; + } } return string; }, - }); + }); - //FIXME : nothing append on customer display deconnection + //FIXME : nothing happen on customer display deconnection var _super_setSmartStatus_ = module.ProxyStatusWidget.prototype.set_smart_status; module.ProxyStatusWidget.prototype.set_smart_status = function(status){ _super_setSmartStatus_.call(this, status); - if(status.status === 'connected'){ - var warning = false; - var msg = '' - if( this.pos.config.iface_customer_display){ - var customer_display = status.drivers.customer_display ? status.drivers.customer_display.status : false; - if( customer_display != 'connected' && customer_display != 'connecting'){ - warning = true; - msg = msg ? msg + ' & ' : msg; - msg += _t('Customer display'); - } - } - msg = msg ? msg + ' ' + _t('Offline') : msg; - this.set_status(warning ? 'warning' : 'connected', msg); - }else{ - this.set_status(status.status,''); - } + if (status.status === 'connected') { + var warning = false; + var msg = ''; + if (this.pos.config.iface_customer_display) { + var customer_display = status.drivers.customer_display ? status.drivers.customer_display.status : false; + if (customer_display != 'connected' && customer_display != 'connecting') { + warning = true; + msg = msg ? msg + ' & ' : msg; + msg += _t('Customer display'); + } + } + msg = msg ? msg + ' ' + _t('Offline') : msg; + this.set_status(warning ? 'warning' : 'connected', msg); + } else { + this.set_status(status.status, ''); + } }; - var _super_addProduct_ = module.Order.prototype.addProduct; module.Order.prototype.addProduct = function(product, options){ - _super_addProduct_.call(this, product, options); - this.pos.prepare_text_customer_display('addProduct', {'product' : product, 'options' : options}); + res = _super_addProduct_.call(this, product, options); + if (product) { + this.pos.prepare_text_customer_display('addProduct', {'product' : product, 'options' : options}); + } + return res; }; - var _super_removeOrderline_ = module.Order.prototype.removeOrderline; module.Order.prototype.removeOrderline = function(line){ - this.pos.prepare_text_customer_display('removeOrderline', {'line' : line}); - _super_removeOrderline_.call(this, line); + if (line) { + this.pos.prepare_text_customer_display('removeOrderline', {'line' : line}); + } + return _super_removeOrderline_.call(this, line); }; var _super_removePaymentline_ = module.Order.prototype.removePaymentline; module.Order.prototype.removePaymentline = function(line){ - this.pos.prepare_text_customer_display('removePaymentline', {'line' : line}); - _super_removePaymentline_.call(this, line); + if (line) { + this.pos.prepare_text_customer_display('removePaymentline', {'line' : line}); + } + return _super_removePaymentline_.call(this, line); }; var _super_addPaymentline_ = module.Order.prototype.addPaymentline; module.Order.prototype.addPaymentline = function(cashregister){ - _super_addPaymentline_.call(this, cashregister); - this.pos.prepare_text_customer_display('addPaymentline', {'cashregister' : cashregister}); + res = _super_addPaymentline_.call(this, cashregister); + if (cashregister) { + this.pos.prepare_text_customer_display('addPaymentline', {'cashregister' : cashregister}); + } + return res; }; - var _super_pushOrder_ = module.PosModel.prototype.push_order; - module.PosModel.prototype.push_order = function(currentOrder){ - _super_pushOrder_.call(this, currentOrder); - this.prepare_text_customer_display('pushOrder', {'currentOrder' : currentOrder}); - + module.PosModel.prototype.push_order = function(order){ + res = _super_pushOrder_.call(this, order); + if (order) { + this.prepare_text_customer_display('pushOrder', {'order' : order}); + } + return res; }; - var _super_closePOS_ = module.PosWidget.prototype.close; module.PosWidget.prototype.close = function(){ this.pos.prepare_text_customer_display('closePOS', {}); - _super_closePOS_.call(this); + return _super_closePOS_.call(this); }; -/* - var _super_updatePayment_ = module.PaymentScreenWidget.prototype.update_payment_summary; - module.PaymentScreenWidget.prototype.update_payment_summary = function(){ - _super_updatePayement_.call(this); - if( this.pos.config.iface_customer_display != true ) - return; - var currentOrder = this.pos.get('selectedOrder'); - var paidTotal = currentOrder.getPaidTotal(); - var dueTotal = currentOrder.getTotalTaxIncluded(); - var remaining = dueTotal > paidTotal ? dueTotal - paidTotal : 0; - var change = paidTotal > dueTotal ? paidTotal - dueTotal : 0; - - var currency_rounding = Math.ceil(Math.log(1.0 / this.pos.currency.rounding) / Math.log(10)); - var amount = line.get_amount().toFixed(currency_rounding); - var line_length = this.pos.config.customer_display_line_length || 20; - var data = new Array( - this.pos.proxy.complete_string_right(_t("Suppression paiement"), line_length), - this.pos.proxy.complete_string_right(line.cashregister.journal_id[1] , line_length - amount.length) + ' ' + amount - ); - this.pos.proxy.send_text_customer_display(data); - }; -*/ -/* - module.Paymentline = module.Paymentline.extend({ - set_pos: function(pos){ - this.pos = pos; - }, - }); - - module.PaypadButtonWidget = module.PaypadButtonWidget.extend({ - renderElement: function() { - var self = this; - this._super(); - - this.$el.click(function(){ - if (self.pos.get('selectedOrder').get('screen') === 'receipt'){ //TODO Why ? - console.warn('TODO should not get there...?'); - return; - } - self.pos.get('selectedOrder').addPaymentline(self.cashregister); - console.log('aurel'); - self.pos.get('selectedOrder').get('paymentLines').at(self.pos.get('selectedOrder').get('paymentLines').length -1).set_pos(self.pos); - console.log('aurel'); - self.pos_widget.screen_selector.set_current_screen('payment'); - }); - }, - }); - - var _super_setamountPaymentline_ = module.Paymentline.prototype.set_amount; - module.Paymentline.prototype.set_amount = function(amount){ - _super_setamountPaymentline_.call(this, amount); - var data = _t("Add paymentline : ") + this.cashregister.journal_id[1] + " " + this.get_amount() + " " + this.pos.get('selectedOrder').getDueLeft(); - alert(data); - this.pos.proxy.send_text_customer_display(data); - }; -*/ }; diff --git a/pos_payment_terminal/static/src/css/pos_payment_terminal.css b/pos_payment_terminal/static/src/css/pos_payment_terminal.css new file mode 100644 index 00000000..bfec68ef --- /dev/null +++ b/pos_payment_terminal/static/src/css/pos_payment_terminal.css @@ -0,0 +1,12 @@ +.pos .payment-terminal-transaction-start button { + width: 150px; + height: 60px; + font-size: 18px; + cursor: pointer; + text-align:center; + box-sizing: border-box; + -moz-box-sizing: border-box; + left: 105%; + bottom: 10px; + position: absolute; +} From e7bfab5768a238792d1a4272bb2c32ed0bba0b83 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Thu, 23 Oct 2014 14:36:24 +0200 Subject: [PATCH 16/27] Use pycountry Add test script for credit card reader --- hw_telium_payment_terminal/__openerp__.py | 3 +- .../controllers/main.py | 13 +- .../test-scripts/telium-test.py | 219 ++++++++++++++++++ 3 files changed, 230 insertions(+), 5 deletions(-) create mode 100755 hw_telium_payment_terminal/test-scripts/telium-test.py diff --git a/hw_telium_payment_terminal/__openerp__.py b/hw_telium_payment_terminal/__openerp__.py index fb98d352..fcec8d33 100644 --- a/hw_telium_payment_terminal/__openerp__.py +++ b/hw_telium_payment_terminal/__openerp__.py @@ -56,8 +56,7 @@ Please contact Alexis de Lattre from Akretion for 'website': 'http://www.akretion.com', 'depends': ['hw_proxy'], 'external_dependencies': { - 'python': ['serial'], + 'python': ['serial', 'pycountry'], }, 'data': [], - 'active': False, } diff --git a/hw_telium_payment_terminal/controllers/main.py b/hw_telium_payment_terminal/controllers/main.py index 31936292..3dbd1b7f 100644 --- a/hw_telium_payment_terminal/controllers/main.py +++ b/hw_telium_payment_terminal/controllers/main.py @@ -28,6 +28,7 @@ import curses.ascii from threading import Thread, Lock from Queue import Queue from serial import Serial +import pycountry import openerp.addons.hw_proxy.controllers.main as hw_proxy from openerp import http from openerp.tools.config import config @@ -125,13 +126,19 @@ class TeliumPaymentTerminalDriver(Thread): "The payment mode '%s' is not supported" % payment_info_dict['payment_mode']) return False + cur_iso_letter = payment_info_dict['currency_iso'].upper() + try: + cur = pycountry.currencies.get(letter=cur_iso_letter) + cur_numeric = str(cur.numeric) + except: + logger.error("Currency %s is not recognized" % cur_iso_letter) + return False data = { 'pos_number': str(1).zfill(2), 'answer_flag': '0', 'transaction_type': '0', 'payment_mode': payment_mode, - 'currency_numeric': - payment_info_dict['currency_iso_numeric'].zfill(3), + 'currency_numeric': cur_numeric.zfill(3), 'private': ' ' * 10, 'delay': 'A011', 'auto': 'B010', @@ -240,7 +247,7 @@ class TeliumPaymentTerminalDriver(Thread): logger.info("Now expecting answer from Terminal") if self.get_one_byte_answer('ENQ'): self.send_one_byte_signal('ACK') - answer_data = self.get_answer_from_terminal(data) + self.get_answer_from_terminal(data) self.send_one_byte_signal('ACK') if self.get_one_byte_answer('EOT'): logger.info("Answer received from Terminal") diff --git a/hw_telium_payment_terminal/test-scripts/telium-test.py b/hw_telium_payment_terminal/test-scripts/telium-test.py new file mode 100755 index 00000000..67245558 --- /dev/null +++ b/hw_telium_payment_terminal/test-scripts/telium-test.py @@ -0,0 +1,219 @@ +#! /usr/bin/python +# -*- encoding: utf-8 -*- +############################################################################## +# +# Hardware Telium Test script +# Copyright (C) 2014 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# +# 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 . +# +############################################################################## + + +from serial import Serial +import curses.ascii +import time +import pycountry + + +DEVICE = '/dev/ttyACM0' +DEVICE_RATE = 9600 +PAYMENT_MODE = 'card' # 'card' ou 'check' +CURRENCY_ISO = 'EUR' +AMOUNT = 12.42 + + +def serial_write(serial, text): + assert isinstance(text, str), 'text must be a string' + serial.write(text) + + +def initialize_msg(serial): + max_attempt = 3 + attempt_nr = 0 + while attempt_nr < max_attempt: + attempt_nr += 1 + send_one_byte_signal(serial, 'ENQ') + if get_one_byte_answer(serial, 'ACK'): + return True + else: + print "Terminal : SAME PLAYER TRY AGAIN" + send_one_byte_signal(serial, 'EOT') + # Wait 1 sec between each attempt + time.sleep(1) + return False + + +def send_one_byte_signal(serial, signal): + ascii_names = curses.ascii.controlnames + assert signal in ascii_names, 'Wrong signal' + char = ascii_names.index(signal) + serial_write(serial, chr(char)) + print 'Signal %s sent to terminal' % signal + + +def get_one_byte_answer(serial, expected_signal): + ascii_names = curses.ascii.controlnames + one_byte_read = serial.read(1) + expected_char = ascii_names.index(expected_signal) + if one_byte_read == chr(expected_char): + print "%s received from terminal" % expected_signal + return True + else: + return False + + +def prepare_data_to_send(): + if PAYMENT_MODE == 'check': + payment_mode = 'C' + elif PAYMENT_MODE == 'card': + payment_mode = '1' + else: + print "The payment mode '%s' is not supported" % PAYMENT_MODE + return False + cur_iso_letter = CURRENCY_ISO.upper() + try: + cur = pycountry.currencies.get(letter=cur_iso_letter) + cur_numeric = str(cur.numeric) + except: + print "Currency %s is not recognized" % cur_iso_letter + return False + data = { + 'pos_number': str(1).zfill(2), + 'answer_flag': '0', + 'transaction_type': '0', + 'payment_mode': payment_mode, + 'currency_numeric': cur_numeric.zfill(3), + 'private': ' ' * 10, + 'delay': 'A011', + 'auto': 'B010', + 'amount_msg': ('%.0f' % (AMOUNT * 100)).zfill(8), + } + return data + + +def generate_lrc(real_msg_with_etx): + lrc = 0 + for char in real_msg_with_etx: + lrc ^= ord(char) + return lrc + + +def send_message(serial, data): + '''We use protocol E+''' + ascii_names = curses.ascii.controlnames + real_msg = ( + data['pos_number'] + + data['amount_msg'] + + data['answer_flag'] + + data['payment_mode'] + + data['transaction_type'] + + data['currency_numeric'] + + data['private'] + + data['delay'] + + data['auto'] + ) + print 'Real message to send = %s' % real_msg + assert len(real_msg) == 34, 'Wrong length for protocol E+' + real_msg_with_etx = real_msg + chr(ascii_names.index('ETX')) + lrc = generate_lrc(real_msg_with_etx) + message = chr(ascii_names.index('STX')) + real_msg_with_etx + chr(lrc) + serial_write(serial, message) + print 'Message sent to terminal' + + +def compare_data_vs_answer(data, answer_data): + for field in [ + 'pos_number', 'amount_msg', + 'currency_numeric', 'private']: + if data[field] != answer_data[field]: + print ( + "Field %s has value '%s' in data and value '%s' in answer" + % (field, data[field], answer_data[field])) + + +def parse_terminal_answer(real_msg, data): + answer_data = { + 'pos_number': real_msg[0:2], + 'transaction_result': real_msg[2], + 'amount_msg': real_msg[3:11], + 'payment_mode': real_msg[11], + 'currency_numeric': real_msg[12:15], + 'private': real_msg[15:26], + } + print 'answer_data = %s' % answer_data + compare_data_vs_answer(data, answer_data) + return answer_data + + +def get_answer_from_terminal(serial, data): + ascii_names = curses.ascii.controlnames + full_msg_size = 1+2+1+8+1+3+10+1+1 + msg = serial.read(size=full_msg_size) + print '%d bytes read from terminal' % full_msg_size + assert len(msg) == full_msg_size, 'Answer has a wrong size' + if msg[0] != chr(ascii_names.index('STX')): + print 'The first byte of the answer from terminal should be STX' + if msg[-2] != chr(ascii_names.index('ETX')): + print 'The byte before final of the answer from terminal should be ETX' + lrc = msg[-1] + computed_lrc = chr(generate_lrc(msg[1:-1])) + if computed_lrc != lrc: + print 'The LRC of the answer from terminal is wrong' + real_msg = msg[1:-2] + print 'Real answer received = %s' % real_msg + return parse_terminal_answer(real_msg, data) + + +def transaction_start(): + '''This function sends the data to the serial/usb port. + ''' + serial = False + try: + print( + 'Opening serial port %s for payment terminal with ' + 'baudrate %d' % (DEVICE, DEVICE_RATE)) + # IMPORTANT : don't modify timeout=3 seconds + # This parameter is very important ; the Telium spec say + # that we have to wait to up 3 seconds to get LRC + serial = Serial( + DEVICE, DEVICE_RATE, timeout=3) + print 'serial.is_open = %s' % serial.isOpen() + if initialize_msg(serial): + data = prepare_data_to_send() + if not data: + return + send_message(serial, data) + if get_one_byte_answer(serial, 'ACK'): + send_one_byte_signal(serial, 'EOT') + + print "Now expecting answer from Terminal" + if get_one_byte_answer(serial, 'ENQ'): + send_one_byte_signal(serial, 'ACK') + get_answer_from_terminal(serial, data) + send_one_byte_signal(serial, 'ACK') + if get_one_byte_answer(serial, 'EOT'): + print "Answer received from Terminal" + + except Exception, e: + print 'Exception in serial connection: %s' % str(e) + finally: + if serial: + print 'Closing serial port for payment terminal' + serial.close() + + +if __name__ == '__main__': + transaction_start() From 7f37501f2fefe7d4ec2c13de87836447afaa6072 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Thu, 23 Oct 2014 14:51:40 +0200 Subject: [PATCH 17/27] Remove currency_iso_numeric (replaced by pycountry) Adapt JS code of pos_payment_terminal Some cleanup --- currency_iso_numeric/__init__.py | 24 - currency_iso_numeric/__openerp__.py | 48 -- .../i18n/currency_iso_numeric.pot | 27 - currency_iso_numeric/i18n/fr.po | 27 - currency_iso_numeric/res_currency.py | 30 - currency_iso_numeric/res_currency_data.xml | 615 ------------------ currency_iso_numeric/res_currency_view.xml | 37 -- hw_customer_display/__openerp__.py | 1 - pos_payment_terminal/__openerp__.py | 2 +- pos_payment_terminal/pos_payment_terminal.py | 4 - .../static/src/js/pos_payment_terminal.js | 97 +-- 11 files changed, 32 insertions(+), 880 deletions(-) delete mode 100644 currency_iso_numeric/__init__.py delete mode 100644 currency_iso_numeric/__openerp__.py delete mode 100644 currency_iso_numeric/i18n/currency_iso_numeric.pot delete mode 100644 currency_iso_numeric/i18n/fr.po delete mode 100644 currency_iso_numeric/res_currency.py delete mode 100644 currency_iso_numeric/res_currency_data.xml delete mode 100644 currency_iso_numeric/res_currency_view.xml diff --git a/currency_iso_numeric/__init__.py b/currency_iso_numeric/__init__.py deleted file mode 100644 index a013db0b..00000000 --- a/currency_iso_numeric/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- encoding: utf-8 -*- -############################################################################## -# -# Currency ISO Numeric module for Odoo -# Copyright (C) 2014 Akretion (http://www.akretion.com) -# @author Alexis de Lattre -# -# 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 . -# -############################################################################## - - -from . import res_currency diff --git a/currency_iso_numeric/__openerp__.py b/currency_iso_numeric/__openerp__.py deleted file mode 100644 index 750df4c7..00000000 --- a/currency_iso_numeric/__openerp__.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- encoding: utf-8 -*- -############################################################################## -# -# Currency ISO Numeric module for Odoo -# Copyright (C) 2014 Akretion (http://www.akretion.com) -# @author Alexis de Lattre -# -# 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 . -# -############################################################################## - - -{ - 'name': 'Currency ISO Numeric', - 'version': '0.1', - 'category': 'Currency', - 'license': 'AGPL-3', - 'summary': 'Adds ISO 4217 numeric codes on currencies', - 'description': """ -Currency ISO Numeric -==================== - -This module adds a field *ISO Numeric Code* on currencies. This numeric ISO code is required by some applications ; for example, it is used in the Telium protocol for the communication between the Point of Sale and the credit card reader. - -This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. - -Please contact Alexis de Lattre from Akretion for any help or question about this module. - """, - 'author': 'Akretion', - 'website': 'http://www.akretion.com', - 'depends': ['base'], - 'data': [ - 'res_currency_data.xml', - 'res_currency_view.xml', - ], - 'active': False, -} diff --git a/currency_iso_numeric/i18n/currency_iso_numeric.pot b/currency_iso_numeric/i18n/currency_iso_numeric.pot deleted file mode 100644 index 04133e60..00000000 --- a/currency_iso_numeric/i18n/currency_iso_numeric.pot +++ /dev/null @@ -1,27 +0,0 @@ -# Translation of OpenERP Server. -# This file contains the translation of the following modules: -# * currency_iso_numeric -# -msgid "" -msgstr "" -"Project-Id-Version: OpenERP Server 8.0alpha1\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-07-09 16:50+0000\n" -"PO-Revision-Date: 2014-07-09 16:50+0000\n" -"Last-Translator: <>\n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" - -#. module: currency_iso_numeric -#: model:ir.model,name:currency_iso_numeric.model_res_currency -msgid "Currency" -msgstr "" - -#. module: currency_iso_numeric -#: field:res.currency,iso_numeric:0 -msgid "ISO Numeric Code" -msgstr "" - diff --git a/currency_iso_numeric/i18n/fr.po b/currency_iso_numeric/i18n/fr.po deleted file mode 100644 index 9efba192..00000000 --- a/currency_iso_numeric/i18n/fr.po +++ /dev/null @@ -1,27 +0,0 @@ -# Translation of OpenERP Server. -# This file contains the translation of the following modules: -# * currency_iso_numeric -# -msgid "" -msgstr "" -"Project-Id-Version: OpenERP Server 8.0alpha1\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-07-09 16:51+0000\n" -"PO-Revision-Date: 2014-07-09 16:51+0000\n" -"Last-Translator: <>\n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" - -#. module: currency_iso_numeric -#: model:ir.model,name:currency_iso_numeric.model_res_currency -msgid "Currency" -msgstr "Devise" - -#. module: currency_iso_numeric -#: field:res.currency,iso_numeric:0 -msgid "ISO Numeric Code" -msgstr "Code ISO numérique" - diff --git a/currency_iso_numeric/res_currency.py b/currency_iso_numeric/res_currency.py deleted file mode 100644 index a52d119f..00000000 --- a/currency_iso_numeric/res_currency.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- encoding: utf-8 -*- -############################################################################## -# -# Currency ISO Numeric module for Odoo -# Copyright (C) 2014 Akretion (http://www.akretion.com) -# @author Alexis de Lattre -# -# 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 . -# -############################################################################## - - -from openerp import models, fields - - -class Currency(models.Model): - _inherit = 'res.currency' - - iso_numeric = fields.Char(string='ISO Numeric Code', size=4) diff --git a/currency_iso_numeric/res_currency_data.xml b/currency_iso_numeric/res_currency_data.xml deleted file mode 100644 index 6efa4a18..00000000 --- a/currency_iso_numeric/res_currency_data.xml +++ /dev/null @@ -1,615 +0,0 @@ - - - - - - - - - - 971 - - - - 978 - - - - 008 - - - - 012 - - - - 840 - - - - 973 - - - - 951 - - - - 032 - - - - 051 - - - - 533 - - - - 036 - - - - 944 - - - - 044 - - - - 048 - - - - 050 - - - - 052 - - - - 974 - - - - 084 - - - - 952 - - - - 060 - - - - 064 - - - - 356 - - - - 068 - - - - 977 - - - - 072 - - - - 578 - - - - 986 - - - - 096 - - - - 975 - - - - 108 - - - - 116 - - - - 950 - - - - 124 - - - - 132 - - - - 136 - - - - 152 - - - - 156 - - - - 170 - - - - 174 - - - - 976 - - - - 554 - - - - 188 - - - - 191 - - - - 192 - - - - 532 - - - - 203 - - - - 208 - - - - 262 - - - - 214 - - - - 818 - - - - 222 - - - - 232 - - - - 230 - - - - 238 - - - - 242 - - - - 953 - - - - 270 - - - - 981 - - - - 936 - - - - 292 - - - - 320 - - - - 826 - - - - 324 - - - - 328 - - - - 332 - - - - 340 - - - - 344 - - - - 348 - - - - 352 - - - - 360 - - - - 364 - - - - 368 - - - - 376 - - - - 388 - - - - 392 - - - - 400 - - - - 398 - - - - 404 - - - - 408 - - - - 410 - - - - 414 - - - - 417 - - - - 418 - - - - 422 - - - - 426 - - - - 710 - - - - 430 - - - - 434 - - - - 756 - - - - 440 - - - - 446 - - - - 807 - - - - 969 - - - - 454 - - - - 458 - - - - 462 - - - - 478 - - - - 480 - - - - 484 - - - - 498 - - - - 496 - - - - 504 - - - - 943 - - - - 104 - - - - 516 - - - - 524 - - - - 558 - - - - 566 - - - - 512 - - - - 586 - - - - 590 - - - - 598 - - - - 600 - - - - 604 - - - - 608 - - - - 985 - - - - 634 - - - - 946 - - - - 643 - - - - 646 - - - - 654 - - - - 882 - - - - 678 - - - - 682 - - - - 941 - - - - 690 - - - - 694 - - - - 702 - - - - 090 - - - - 728 - - - - 144 - - - - 748 - - - - 752 - - - - 760 - - - - 901 - - - - 834 - - - - 764 - - - - 776 - - - - 780 - - - - 788 - - - - 949 - - - - 800 - - - - 980 - - - - 784 - - - - 858 - - - - 860 - - - - 548 - - - - 937 - - - - 704 - - - - 886 - - - - - diff --git a/currency_iso_numeric/res_currency_view.xml b/currency_iso_numeric/res_currency_view.xml deleted file mode 100644 index cf0bbc9c..00000000 --- a/currency_iso_numeric/res_currency_view.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - add.iso.numeric.res.currency.form - res.currency - - - - - - - - - - add.iso.numeric.res.currency.tree - res.currency - - - - - - - - - - - diff --git a/hw_customer_display/__openerp__.py b/hw_customer_display/__openerp__.py index 920994d0..206decba 100644 --- a/hw_customer_display/__openerp__.py +++ b/hw_customer_display/__openerp__.py @@ -62,5 +62,4 @@ Please contact Alexis de Lattre from Akretion for 'python': ['serial', 'unidecode'], }, 'data': [], - 'active': False, } diff --git a/pos_payment_terminal/__openerp__.py b/pos_payment_terminal/__openerp__.py index 9241fd02..0595a779 100755 --- a/pos_payment_terminal/__openerp__.py +++ b/pos_payment_terminal/__openerp__.py @@ -36,7 +36,7 @@ This module support two payment methods : cards and checks. The payment method s This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. This module is part of the POS project of the Odoo Community Association http://odoo-community.org/. You are invited to become a member and/or get involved in the Association ! """, 'author': 'Aurélien DUMAINE', - 'depends': ['point_of_sale', 'currency_iso_numeric'], + 'depends': ['point_of_sale'], 'data': [ 'pos_payment_terminal.xml', 'pos_payment_terminal_view.xml', diff --git a/pos_payment_terminal/pos_payment_terminal.py b/pos_payment_terminal/pos_payment_terminal.py index e6dc9ff7..a54252ad 100644 --- a/pos_payment_terminal/pos_payment_terminal.py +++ b/pos_payment_terminal/pos_payment_terminal.py @@ -23,7 +23,6 @@ from openerp import models, fields class account_journal(models.Model): - _name = 'account.journal' _inherit = 'account.journal' payment_mode = fields.Selection( @@ -32,11 +31,8 @@ class account_journal(models.Model): class pos_config(models.Model): - _name = 'pos.config' _inherit = 'pos.config' iface_payment_terminal = fields.Boolean( 'Payment Terminal', help="A payment terminal is available on the Proxy") - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/pos_payment_terminal/static/src/js/pos_payment_terminal.js b/pos_payment_terminal/static/src/js/pos_payment_terminal.js index 9013da8d..419a0b20 100755 --- a/pos_payment_terminal/static/src/js/pos_payment_terminal.js +++ b/pos_payment_terminal/static/src/js/pos_payment_terminal.js @@ -2,84 +2,49 @@ openerp.pos_payment_terminal = function(instance){ module = instance.point_of_sale; module.ProxyDevice = module.ProxyDevice.extend({ - payment_terminal_transaction_start: function(line, currency_iso, currency_iso_numeric){ - var data = {'amount' : line.get_amount(), - 'currency_iso' : currency_iso, - 'currency_iso_numeric' : currency_iso_numeric, - 'payment_mode' : line.cashregister.journal.payment_mode}; -// alert(JSON.stringify(data)); - this.message('payment_terminal_transaction_start', {'payment_info' : JSON.stringify(data)}); + payment_terminal_transaction_start: function(line, currency_iso){ + var data = {'amount' : line.get_amount(), + 'currency_iso' : currency_iso, + 'payment_mode' : line.cashregister.journal.payment_mode}; +// alert(JSON.stringify(data)); + this.message('payment_terminal_transaction_start', {'payment_info' : JSON.stringify(data)}); }, - }); + }); - //TODO make the button bigger and with better name + //TODO make the button bigger and with better name var _super_PaymentScreenWidget_init_ = module.PaymentScreenWidget.prototype.init; module.PaymentScreenWidget.prototype.init = function(parent, options){ _super_PaymentScreenWidget_init_.call(this, parent, options); - self = this; - this.payment_terminal_transaction_start = function(event){ - var node = this; - while(node && !node.classList.contains('paymentline')){ - node = node.parentNode; - } - if(node){ - if (self.pos.config.iface_payment_terminal) - self.pos.proxy.payment_terminal_transaction_start(node.line, self.pos.currency.name, self.pos.currency.iso_numeric); - } - event.stopPropagation(); - }; - }; + self = this; + this.payment_terminal_transaction_start = function(event){ + var node = this; + while (node && !node.classList.contains('paymentline')){ + node = node.parentNode; + } + if (node && !_.isEmpty(node.line) && self.pos.config.iface_payment_terminal){ + self.pos.proxy.payment_terminal_transaction_start(node.line, self.pos.currency.name); + } + event.stopPropagation(); + }; + }; var _super_renderPaymentline_ = module.PaymentScreenWidget.prototype.render_paymentline; module.PaymentScreenWidget.prototype.render_paymentline = function(line){ var el_node = _super_renderPaymentline_.call(this, line); if (line.cashregister.journal.payment_mode && this.pos.config.iface_payment_terminal){ - if (!this.pos.currency.name){ - var self = this; - var currencies = new instance.web.Model('res.currency').query(['name', 'iso_numeric']) - .filter([['id','=',this.pos.currency.id]]) - .all().then(function (currency) { - self.pos.currency.name = currency[0].name; - self.pos.currency.iso_numeric = currency[0].iso_numeric; - }); - } + if (!this.pos.currency.name){ + var self = this; + var currencies = new instance.web.Model('res.currency').query(['name']) + .filter([['id','=',this.pos.currency.id]]) + .all().then(function (currency) { + self.pos.currency.name = currency[0].name; + }); + } el_node.querySelector('.payment-terminal-transaction-start') - .addEventListener('click', this.payment_terminal_transaction_start); - } - return el_node; - }; - -/* - var _super_load_server_data_ = module.PosModel.prototype.load_server_data; - module.PosModel.prototype.load_server_data = function(){ - var loaded = _super_load_server_data_.call(this); - //FIXME : this is asynchronous, I can't assume the pos.currency loaded when we enter is this - this.pos.currency.name = new instance.web.Model('res.currency').query('name').filter([['id','=',this.pos_currency.id]]).all()[0] - return loaded; - }; -*/ - -/* - var _super_setSmartStatus_ = module.ProxyStatusWidget.prototype.set_smart_status; - module.ProxyStatusWidget.prototype.set_smart_status = function(status){ - _super_setSmartStatus_.call(this, status); - if(status.status === 'connected'){ - var warning = false; - var msg = '' - if(this.pos.config.iface_customer_display){ - var customer_display = status.drivers.customer_display ? status.drivers.customer_display.status : false; - if( customer_display != 'connected' && customer_display != 'connecting'){ - warning = true; - msg = msg ? msg + ' & ' : msg; - msg += _t('Customer display'); - } - } - msg = msg ? msg + ' ' + _t('Offline') : msg; - this.set_status(warning ? 'warning' : 'connected', msg); - }else{ - this.set_status(status.status,''); + .addEventListener('click', this.payment_terminal_transaction_start); } + return el_node; }; -*/ + }; From de293a8fd2ad2ac5430b9054f41c2aa919c61d1d Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Fri, 24 Oct 2014 12:05:08 +0200 Subject: [PATCH 18/27] [FIX] lenght -> length [IMP] Change is now displayed on the LCD when entered (avoid to keep displaying the change with the "next customer") Rename function names to easier understanding --- .../static/src/js/customer_display.js | 73 ++++++++++--------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/pos_customer_display/static/src/js/customer_display.js b/pos_customer_display/static/src/js/customer_display.js index 6170a360..aa58ed7e 100755 --- a/pos_customer_display/static/src/js/customer_display.js +++ b/pos_customer_display/static/src/js/customer_display.js @@ -2,8 +2,6 @@ openerp.pos_customer_display = function(instance){ module = instance.point_of_sale; var _t = instance.web._t; - var round_di = instance.web.round_decimals; - var round_pr = instance.web.round_precision; module.PosModel = module.PosModel.extend({ @@ -22,58 +20,51 @@ openerp.pos_customer_display = function(instance){ var l21 = line.get_quantity_str_with_unit() + ' x ' + price_unit; var l22 = ' ' + line.get_display_price().toFixed(currency_rounding); var lines_to_send = new Array( - this.proxy.complete_string_right(line.get_product().display_name, line_length), - this.proxy.complete_string_right(l21, line_length - l22.length) + l22 + this.proxy.align_left(line.get_product().display_name, line_length), + this.proxy.align_left(l21, line_length - l22.length) + l22 ); } else if (type == 'removeOrderline') { // first click on the backspace button set the amount to 0 => we can't precise the deleted qunatity and price var line = data['line']; var lines_to_send = new Array( - this.proxy.complete_string_center(_t("Delete item"), line_length), - this.proxy.complete_string_center(line.get_product().name, line_length) + this.proxy.align_center(_t("Delete item"), line_length), + this.proxy.align_center(line.get_product().name, line_length) ); } else if (type == 'addPaymentline') { var cashregister = data['cashregister']; var total = this.get('selectedOrder').getTotalTaxIncluded().toFixed(currency_rounding); var lines_to_send = new Array( - this.proxy.complete_string_right(_t("TOTAL: "), line_length - 1 - total.length) + ' ' + total, - this.proxy.complete_string_right(_t("Payment:"), line_length - 1 - cashregister.journal_id[1].length) + ' ' + cashregister.journal_id[1] + this.proxy.align_left(_t("TOTAL: "), line_length - 1 - total.length) + ' ' + total, + this.proxy.align_left(_t("Payment:"), line_length - 1 - cashregister.journal_id[1].length) + ' ' + cashregister.journal_id[1] ); } else if (type == 'removePaymentline') { var line = data['line']; var amount = line.get_amount().toFixed(currency_rounding); var lines_to_send = new Array( - this.proxy.complete_string_center(_t("Delete payment"), line_length), - this.proxy.complete_string_right(line.cashregister.journal_id[1] , line_length - 1 - amount.length) + ' ' + amount + this.proxy.align_center(_t("Delete payment"), line_length), + this.proxy.align_left(line.cashregister.journal_id[1] , line_length - 1 - amount.length) + ' ' + amount ); - } else if (type == 'pushOrder') { - var order = data['order']; - var paidTotal = order.getPaidTotal(); - var dueTotal = order.getTotalTaxIncluded(); - var remaining = dueTotal > paidTotal ? dueTotal - paidTotal : 0; - var change = paidTotal > dueTotal ? paidTotal - dueTotal : 0; - - var l1; - if (change == 0){ - l1 = this.proxy.complete_string_center(_t(""), line_length); - } else { - change = change.toFixed(currency_rounding); - l1 = this.proxy.complete_string_right(_t("YOUR CHANGE:"), line_length - 1 - change.length) + ' ' + change; - } + } else if (type == 'update_payment') { + var change = data['change']; + var lines_to_send = new Array( + this.proxy.align_left(_t("Your Change:"), line_length), + this.proxy.align_right(change, line_length) + ); + } else if (type == 'pushOrder') { var lines_to_send = new Array( - l1, - this.proxy.complete_string_center(_t("Next customer..."), line_length) + this.proxy.align_center(_t("Next customer"), line_length), + '' ); - } else if (type = 'closePOS') { - var lines_to_send = new Array( - this.proxy.complete_string_center(_t("Point of sale closed"), line_length), - this.proxy.complete_string_center(_t("***"), line_length) + } else if (type = 'closePOS') { + var lines_to_send = new Array( + this.proxy.align_center(_t("Point of sale closed"), line_length), + '' ); } else { console.warn('Unknown message type'); @@ -90,7 +81,7 @@ openerp.pos_customer_display = function(instance){ module.ProxyDevice = module.ProxyDevice.extend({ send_text_customer_display: function(data, line_length){ //FIXME : this function is call twice. The first time, it is not called by prepare_text_customer_display : WHY ? - if (_.isEmpty(data) || data.lenght != 2 || data[0].length != line_length || data[1].length != line_length){ + if (_.isEmpty(data) || data.length != 2 || data[0].length != line_length || data[1].length != line_length){ console.warn("Bad Data argument. Data = " + data); console.warn('Line_length = ' + line_length); } else { @@ -98,7 +89,7 @@ openerp.pos_customer_display = function(instance){ return this.message('send_text_customer_display', {'text_to_display' : JSON.stringify(data)}); } }, - complete_string_right: function(string, length){ + align_left: function(string, length){ if (string) { if (string.length > length) { @@ -113,7 +104,7 @@ openerp.pos_customer_display = function(instance){ } return string; }, - complete_string_left: function(string, length){ + align_right: function(string, length){ if (string) { if (string.length > length) { @@ -128,7 +119,7 @@ openerp.pos_customer_display = function(instance){ } return string; }, - complete_string_center: function(string, length){ + align_center: function(string, length){ if (string) { if (string.length > length) { @@ -214,6 +205,20 @@ openerp.pos_customer_display = function(instance){ return res; }; + var _super_update_payment_summary_ = module.PaymentScreenWidget.prototype.update_payment_summary; + module.PaymentScreenWidget.prototype.update_payment_summary = function(){ + res = _super_update_payment_summary_.call(this); + var currentOrder = this.pos.get('selectedOrder'); + var paidTotal = currentOrder.getPaidTotal(); + var dueTotal = currentOrder.getTotalTaxIncluded(); + var change = paidTotal > dueTotal ? paidTotal - dueTotal : 0; + if (change) { + change_rounded = change.toFixed(2); + this.pos.prepare_text_customer_display('update_payment', {'change': change_rounded}); + } + return res; + }; + var _super_closePOS_ = module.PosWidget.prototype.close; module.PosWidget.prototype.close = function(){ this.pos.prepare_text_customer_display('closePOS', {}); From 3deb9f864e817ed88527891790f079d25c5755e7 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Fri, 24 Oct 2014 14:24:31 +0200 Subject: [PATCH 19/27] FIX bugs Remove paiment method from LCD message Add missing POT file Update FR translation --- pos_customer_display/i18n/fr.po | 62 +++++------- .../i18n/pos_customer_display.pot | 96 +++++++++++++++++++ .../static/src/js/customer_display.js | 23 ++--- 3 files changed, 133 insertions(+), 48 deletions(-) create mode 100644 pos_customer_display/i18n/pos_customer_display.pot diff --git a/pos_customer_display/i18n/fr.po b/pos_customer_display/i18n/fr.po index 9fa4cc09..a74c7923 100644 --- a/pos_customer_display/i18n/fr.po +++ b/pos_customer_display/i18n/fr.po @@ -1,14 +1,14 @@ -# Translation of OpenERP Server. +# Translation of Odoo Server. # This file contains the translation of the following modules: # * pos_customer_display # msgid "" msgstr "" -"Project-Id-Version: OpenERP Server 9.0alpha1\n" +"Project-Id-Version: Odoo Server 8.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-07-21 10:50+0000\n" -"PO-Revision-Date: 2014-07-21 10:50+0000\n" -"Last-Translator: <>\n" +"POT-Creation-Date: 2014-10-24 12:05+0000\n" +"PO-Revision-Date: 2014-10-24 12:05+0000\n" +"Last-Translator: Alexis de Lattre \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,14 +17,14 @@ msgstr "" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:72 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:47 #, python-format -msgid "***" -msgstr "***" +msgid "Cancel Payment" +msgstr "Paiement annulé" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:161 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:157 #: field:pos.config,iface_customer_display:0 #, python-format msgid "Customer display" @@ -32,27 +32,20 @@ msgstr "Afficheur client" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:32 -#, python-format -msgid "Delete item" -msgstr "Suppression article" - -#. module: pos_customer_display -#. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:47 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:31 #, python-format -msgid "Delete payment" -msgstr "Suppression paiement" +msgid "Delete Item" +msgstr "Article supprimé" #. module: pos_customer_display #: help:pos.config,iface_customer_display:0 msgid "Display data on the customer display" -msgstr "Afficher les info sur l'afficheur client" +msgstr "Utiliser l'afficheur client" #. module: pos_customer_display #: help:pos.config,customer_display_line_length:0 msgid "Length of the LEDs lines of the customer display" -msgstr "Longueur des lignes de l'afficheur client, nombre de caractères" +msgstr "Longueur des lignes de l'afficheur client: nombre de caractères" #. module: pos_customer_display #: field:pos.config,customer_display_line_length:0 @@ -61,14 +54,14 @@ msgstr "Longueur des lignes" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:67 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:60 #, python-format -msgid "Next customer..." -msgstr "Client suivant..." +msgid "Next Customer" +msgstr "Client suivant" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:164 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:160 #, python-format msgid "Offline" msgstr "Hors ligne" @@ -77,32 +70,27 @@ msgstr "Hors ligne" #. openerp-web #: code:addons/pos_customer_display/static/src/js/customer_display.js:40 #, python-format -msgid "Payment :" +msgid "Payment:" msgstr "Paiement :" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:71 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:66 #, python-format -msgid "Point of sale closed" +msgid "Point of Sale Closed" msgstr "Caisse fermée" #. module: pos_customer_display #. openerp-web #: code:addons/pos_customer_display/static/src/js/customer_display.js:39 #, python-format -msgid "TOTAL : " +msgid "TOTAL: " msgstr "TOTAL : " #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:62 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:54 #, python-format -msgid "YOUR CHANGE :" -msgstr "VOTRE MONNAIE :" - -#. module: pos_customer_display -#: model:ir.model,name:pos_customer_display.model_pos_config -msgid "pos.config" -msgstr "pos.config" +msgid "Your Change:" +msgstr "Monnaie à rendre :" diff --git a/pos_customer_display/i18n/pos_customer_display.pot b/pos_customer_display/i18n/pos_customer_display.pot new file mode 100644 index 00000000..691f1480 --- /dev/null +++ b/pos_customer_display/i18n/pos_customer_display.pot @@ -0,0 +1,96 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_customer_display +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-10-24 12:04+0000\n" +"PO-Revision-Date: 2014-10-24 12:04+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:47 +#, python-format +msgid "Cancel Payment" +msgstr "" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:157 +#: field:pos.config,iface_customer_display:0 +#, python-format +msgid "Customer display" +msgstr "" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:31 +#, python-format +msgid "Delete Item" +msgstr "" + +#. module: pos_customer_display +#: help:pos.config,iface_customer_display:0 +msgid "Display data on the customer display" +msgstr "" + +#. module: pos_customer_display +#: help:pos.config,customer_display_line_length:0 +msgid "Length of the LEDs lines of the customer display" +msgstr "" + +#. module: pos_customer_display +#: field:pos.config,customer_display_line_length:0 +msgid "Line length" +msgstr "" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:60 +#, python-format +msgid "Next Customer" +msgstr "" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:160 +#, python-format +msgid "Offline" +msgstr "" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:40 +#, python-format +msgid "Payment:" +msgstr "" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:66 +#, python-format +msgid "Point of Sale Closed" +msgstr "" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:39 +#, python-format +msgid "TOTAL: " +msgstr "" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:54 +#, python-format +msgid "Your Change:" +msgstr "" + diff --git a/pos_customer_display/static/src/js/customer_display.js b/pos_customer_display/static/src/js/customer_display.js index aa58ed7e..55c453fe 100755 --- a/pos_customer_display/static/src/js/customer_display.js +++ b/pos_customer_display/static/src/js/customer_display.js @@ -28,23 +28,22 @@ openerp.pos_customer_display = function(instance){ // first click on the backspace button set the amount to 0 => we can't precise the deleted qunatity and price var line = data['line']; var lines_to_send = new Array( - this.proxy.align_center(_t("Delete item"), line_length), + this.proxy.align_center(_t("Delete Item"), line_length), this.proxy.align_center(line.get_product().name, line_length) ); } else if (type == 'addPaymentline') { - var cashregister = data['cashregister']; var total = this.get('selectedOrder').getTotalTaxIncluded().toFixed(currency_rounding); var lines_to_send = new Array( - this.proxy.align_left(_t("TOTAL: "), line_length - 1 - total.length) + ' ' + total, - this.proxy.align_left(_t("Payment:"), line_length - 1 - cashregister.journal_id[1].length) + ' ' + cashregister.journal_id[1] + this.proxy.align_left(_t("TOTAL: "), line_length), + this.proxy.align_right(total, line_length) ); } else if (type == 'removePaymentline') { var line = data['line']; var amount = line.get_amount().toFixed(currency_rounding); var lines_to_send = new Array( - this.proxy.align_center(_t("Delete payment"), line_length), + this.proxy.align_center(_t("Cancel Payment"), line_length), this.proxy.align_left(line.cashregister.journal_id[1] , line_length - 1 - amount.length) + ' ' + amount ); @@ -57,14 +56,14 @@ openerp.pos_customer_display = function(instance){ } else if (type == 'pushOrder') { var lines_to_send = new Array( - this.proxy.align_center(_t("Next customer"), line_length), - '' + this.proxy.align_center(_t("Next Customer"), line_length), + this.proxy.align_left(' ', line_length) ); } else if (type = 'closePOS') { var lines_to_send = new Array( - this.proxy.align_center(_t("Point of sale closed"), line_length), - '' + this.proxy.align_center(_t("Point of Sale Closed"), line_length), + this.proxy.align_left(' ', line_length) ); } else { console.warn('Unknown message type'); @@ -82,13 +81,13 @@ openerp.pos_customer_display = function(instance){ send_text_customer_display: function(data, line_length){ //FIXME : this function is call twice. The first time, it is not called by prepare_text_customer_display : WHY ? if (_.isEmpty(data) || data.length != 2 || data[0].length != line_length || data[1].length != line_length){ - console.warn("Bad Data argument. Data = " + data); - console.warn('Line_length = ' + line_length); + console.warn("send_text_customer_display: Bad Data argument. Data=" + data + ' line_length=' + line_length); } else { // alert(JSON.stringify(data)); return this.message('send_text_customer_display', {'text_to_display' : JSON.stringify(data)}); } }, + align_left: function(string, length){ if (string) { if (string.length > length) @@ -104,6 +103,7 @@ openerp.pos_customer_display = function(instance){ } return string; }, + align_right: function(string, length){ if (string) { if (string.length > length) @@ -119,6 +119,7 @@ openerp.pos_customer_display = function(instance){ } return string; }, + align_center: function(string, length){ if (string) { if (string.length > length) From 8499ce49723018705f0259b3c4d142ae52217b3e Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Fri, 24 Oct 2014 15:47:00 +0200 Subject: [PATCH 20/27] Add message on POS open Remove code that was not working FIX bug in removeOrderLine Update translation --- pos_customer_display/i18n/fr.po | 38 +++++-------- .../i18n/pos_customer_display.pot | 32 ++++------- .../static/src/js/customer_display.js | 56 +++++++++---------- 3 files changed, 53 insertions(+), 73 deletions(-) diff --git a/pos_customer_display/i18n/fr.po b/pos_customer_display/i18n/fr.po index a74c7923..d5332a88 100644 --- a/pos_customer_display/i18n/fr.po +++ b/pos_customer_display/i18n/fr.po @@ -6,9 +6,9 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 8.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-10-24 12:05+0000\n" -"PO-Revision-Date: 2014-10-24 12:05+0000\n" -"Last-Translator: Alexis de Lattre \n" +"POT-Creation-Date: 2014-10-24 13:45+0000\n" +"PO-Revision-Date: 2014-10-24 13:45+0000\n" +"Last-Translator: <>\n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,22 +17,19 @@ msgstr "" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:47 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:56 #, python-format msgid "Cancel Payment" msgstr "Paiement annulé" #. module: pos_customer_display -#. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:157 #: field:pos.config,iface_customer_display:0 -#, python-format msgid "Customer display" msgstr "Afficheur client" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:31 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:41 #, python-format msgid "Delete Item" msgstr "Article supprimé" @@ -54,42 +51,35 @@ msgstr "Longueur des lignes" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:60 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:69 #, python-format msgid "Next Customer" msgstr "Client suivant" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:160 -#, python-format -msgid "Offline" -msgstr "Hors ligne" - -#. module: pos_customer_display -#. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:40 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:81 #, python-format -msgid "Payment:" -msgstr "Paiement :" +msgid "Point of Sale Closed" +msgstr "Caisse fermée" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:66 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:75 #, python-format -msgid "Point of Sale Closed" -msgstr "Caisse fermée" +msgid "Point of Sale Open" +msgstr "Caisse ouverte" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:39 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:48 #, python-format msgid "TOTAL: " msgstr "TOTAL : " #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:54 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:63 #, python-format msgid "Your Change:" msgstr "Monnaie à rendre :" diff --git a/pos_customer_display/i18n/pos_customer_display.pot b/pos_customer_display/i18n/pos_customer_display.pot index 691f1480..fa783681 100644 --- a/pos_customer_display/i18n/pos_customer_display.pot +++ b/pos_customer_display/i18n/pos_customer_display.pot @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 8.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-10-24 12:04+0000\n" -"PO-Revision-Date: 2014-10-24 12:04+0000\n" +"POT-Creation-Date: 2014-10-24 13:44+0000\n" +"PO-Revision-Date: 2014-10-24 13:44+0000\n" "Last-Translator: <>\n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -17,22 +17,19 @@ msgstr "" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:47 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:56 #, python-format msgid "Cancel Payment" msgstr "" #. module: pos_customer_display -#. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:157 #: field:pos.config,iface_customer_display:0 -#, python-format msgid "Customer display" msgstr "" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:31 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:41 #, python-format msgid "Delete Item" msgstr "" @@ -54,42 +51,35 @@ msgstr "" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:60 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:69 #, python-format msgid "Next Customer" msgstr "" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:160 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:81 #, python-format -msgid "Offline" -msgstr "" - -#. module: pos_customer_display -#. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:40 -#, python-format -msgid "Payment:" +msgid "Point of Sale Closed" msgstr "" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:66 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:75 #, python-format -msgid "Point of Sale Closed" +msgid "Point of Sale Open" msgstr "" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:39 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:48 #, python-format msgid "TOTAL: " msgstr "" #. module: pos_customer_display #. openerp-web -#: code:addons/pos_customer_display/static/src/js/customer_display.js:54 +#: code:addons/pos_customer_display/static/src/js/customer_display.js:63 #, python-format msgid "Your Change:" msgstr "" diff --git a/pos_customer_display/static/src/js/customer_display.js b/pos_customer_display/static/src/js/customer_display.js index 55c453fe..fd404ebb 100755 --- a/pos_customer_display/static/src/js/customer_display.js +++ b/pos_customer_display/static/src/js/customer_display.js @@ -1,3 +1,13 @@ +/* + POS Customer display module for Odoo + Copyright (C) 2014 Aurélien DUMAINE + Copyright (C) 2014 Barroux Abbey (www.barroux.org) + @author: Aurélien DUMAINE + @author: Alexis de Lattre + @author: Father Odilon (Barroux Abbey) + The licence is in the file __openerp__.py +*/ + openerp.pos_customer_display = function(instance){ module = instance.point_of_sale; @@ -28,8 +38,8 @@ openerp.pos_customer_display = function(instance){ // first click on the backspace button set the amount to 0 => we can't precise the deleted qunatity and price var line = data['line']; var lines_to_send = new Array( - this.proxy.align_center(_t("Delete Item"), line_length), - this.proxy.align_center(line.get_product().name, line_length) + this.proxy.align_left(_t("Delete Item"), line_length), + this.proxy.align_right(line.get_product().display_name, line_length) ); } else if (type == 'addPaymentline') { @@ -43,8 +53,8 @@ openerp.pos_customer_display = function(instance){ var line = data['line']; var amount = line.get_amount().toFixed(currency_rounding); var lines_to_send = new Array( - this.proxy.align_center(_t("Cancel Payment"), line_length), - this.proxy.align_left(line.cashregister.journal_id[1] , line_length - 1 - amount.length) + ' ' + amount + this.proxy.align_left(_t("Cancel Payment"), line_length), + this.proxy.align_right(line.cashregister.journal_id[1] , line_length - 1 - amount.length) + ' ' + amount ); } else if (type == 'update_payment') { @@ -60,6 +70,12 @@ openerp.pos_customer_display = function(instance){ this.proxy.align_left(' ', line_length) ); + } else if (type == 'openPOS') { + var lines_to_send = new Array( + this.proxy.align_center(_t("Point of Sale Open"), line_length), + this.proxy.align_left(' ', line_length) + ); + } else if (type = 'closePOS') { var lines_to_send = new Array( this.proxy.align_center(_t("Point of Sale Closed"), line_length), @@ -70,8 +86,8 @@ openerp.pos_customer_display = function(instance){ return; } -// alert("In prepare_text_customer_display " + line_length); this.proxy.send_text_customer_display(lines_to_send, line_length); + //console.log('prepare_text_customer_display type=' + type + ' | l1=' + lines_to_send[0] + ' | l2=' + lines_to_send[1]); }, }); @@ -140,29 +156,6 @@ openerp.pos_customer_display = function(instance){ }, }); - - //FIXME : nothing happen on customer display deconnection - var _super_setSmartStatus_ = module.ProxyStatusWidget.prototype.set_smart_status; - module.ProxyStatusWidget.prototype.set_smart_status = function(status){ - _super_setSmartStatus_.call(this, status); - if (status.status === 'connected') { - var warning = false; - var msg = ''; - if (this.pos.config.iface_customer_display) { - var customer_display = status.drivers.customer_display ? status.drivers.customer_display.status : false; - if (customer_display != 'connected' && customer_display != 'connecting') { - warning = true; - msg = msg ? msg + ' & ' : msg; - msg += _t('Customer display'); - } - } - msg = msg ? msg + ' ' + _t('Offline') : msg; - this.set_status(warning ? 'warning' : 'connected', msg); - } else { - this.set_status(status.status, ''); - } - }; - var _super_addProduct_ = module.Order.prototype.addProduct; module.Order.prototype.addProduct = function(product, options){ res = _super_addProduct_.call(this, product, options); @@ -226,4 +219,11 @@ openerp.pos_customer_display = function(instance){ return _super_closePOS_.call(this); }; + var _super_proxy_start_ = module.ProxyStatusWidget.prototype.start; + module.ProxyStatusWidget.prototype.start = function(){ + res = _super_proxy_start_.call(this); + this.pos.prepare_text_customer_display('openPOS', {}); + return res; + }; + }; From 49224f98901285c236ab1377cbb54ebaf8254926 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Mon, 17 Nov 2014 13:09:21 +0100 Subject: [PATCH 21/27] Add Spanish translation by Alejandro Amador (thanks for your contribution !) Move CSS definition from pos_payment_terminal_view.xml to pos_payment_terminal.xml Small cleanups --- pos_customer_display/i18n/es.po | 87 +++++++++++++++++++ pos_payment_terminal/__init__.py | 0 pos_payment_terminal/__openerp__.py | 0 pos_payment_terminal/pos_payment_terminal.xml | 22 +++-- .../pos_payment_terminal_view.xml | 11 +-- 5 files changed, 106 insertions(+), 14 deletions(-) create mode 100644 pos_customer_display/i18n/es.po mode change 100755 => 100644 pos_payment_terminal/__init__.py mode change 100755 => 100644 pos_payment_terminal/__openerp__.py mode change 100755 => 100644 pos_payment_terminal/pos_payment_terminal.xml diff --git a/pos_customer_display/i18n/es.po b/pos_customer_display/i18n/es.po new file mode 100644 index 00000000..19a40b26 --- /dev/null +++ b/pos_customer_display/i18n/es.po @@ -0,0 +1,87 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_customer_display +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-11-17 11:00+0000\n" +"PO-Revision-Date: 2014-11-17 12:03+0100\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" +"Language: es\n" +"X-Generator: Poedit 1.6.10\n" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:56 +#, python-format +msgid "Cancel Payment" +msgstr "Pago Cancelado" + +#. module: pos_customer_display +#: field:pos.config,iface_customer_display:0 +msgid "Customer display" +msgstr "Visor cliente" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:41 +#, python-format +msgid "Delete Item" +msgstr "Producto Eliminado" + +#. module: pos_customer_display +#: help:pos.config,iface_customer_display:0 +msgid "Display data on the customer display" +msgstr "Mostrar información en el visor de cliente" + +#. module: pos_customer_display +#: help:pos.config,customer_display_line_length:0 +msgid "Length of the LEDs lines of the customer display" +msgstr "Longitud de las líneas LED en el visor de cliente" + +#. module: pos_customer_display +#: field:pos.config,customer_display_line_length:0 +msgid "Line length" +msgstr "Longitud de línea " + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:69 +#, python-format +msgid "Next Customer" +msgstr "Próximo Cliente" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:81 +#, python-format +msgid "Point of Sale Closed" +msgstr "Punto de Venta Cerrado" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:75 +#, python-format +msgid "Point of Sale Open" +msgstr "Punto de Venta Abierto" + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:48 +#, python-format +msgid "TOTAL: " +msgstr "TOTAL: " + +#. module: pos_customer_display +#. openerp-web +#: code:addons/pos_customer_display/static/src/js/customer_display.js:63 +#, python-format +msgid "Your Change:" +msgstr "Su Cambio:" diff --git a/pos_payment_terminal/__init__.py b/pos_payment_terminal/__init__.py old mode 100755 new mode 100644 diff --git a/pos_payment_terminal/__openerp__.py b/pos_payment_terminal/__openerp__.py old mode 100755 new mode 100644 diff --git a/pos_payment_terminal/pos_payment_terminal.xml b/pos_payment_terminal/pos_payment_terminal.xml old mode 100755 new mode 100644 index 32d81264..6f46ad01 --- a/pos_payment_terminal/pos_payment_terminal.xml +++ b/pos_payment_terminal/pos_payment_terminal.xml @@ -1,10 +1,18 @@ - - - + + + + + + + diff --git a/pos_payment_terminal/pos_payment_terminal_view.xml b/pos_payment_terminal/pos_payment_terminal_view.xml index dbcca88c..f04d4648 100644 --- a/pos_payment_terminal/pos_payment_terminal_view.xml +++ b/pos_payment_terminal/pos_payment_terminal_view.xml @@ -1,8 +1,9 @@ + - pos.config.form.view.inherit + pos.payment.terminal.config.form pos.config @@ -11,8 +12,9 @@ + - POS journal inherit + pos.payment.terminal.journal.form account.journal @@ -22,10 +24,5 @@ - From bafb4137bf0864cdb3721f7c64d6fccd7713d1e2 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 18 Nov 2014 18:12:51 +0100 Subject: [PATCH 22/27] Update ES translation by Alejandro Amador --- pos_customer_display/i18n/es.po | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pos_customer_display/i18n/es.po b/pos_customer_display/i18n/es.po index 19a40b26..fcb58692 100644 --- a/pos_customer_display/i18n/es.po +++ b/pos_customer_display/i18n/es.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: Odoo Server 8.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-11-17 11:00+0000\n" -"PO-Revision-Date: 2014-11-17 12:03+0100\n" +"PO-Revision-Date: 2014-11-17 18:33+0100\n" "Last-Translator: <>\n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -63,14 +63,14 @@ msgstr "Próximo Cliente" #: code:addons/pos_customer_display/static/src/js/customer_display.js:81 #, python-format msgid "Point of Sale Closed" -msgstr "Punto de Venta Cerrado" +msgstr "Punto Venta Cerrado" #. module: pos_customer_display #. openerp-web #: code:addons/pos_customer_display/static/src/js/customer_display.js:75 #, python-format msgid "Point of Sale Open" -msgstr "Punto de Venta Abierto" +msgstr "Punto Venta Abierto" #. module: pos_customer_display #. openerp-web From e1a8e3f2145c5101553807791b61fd4a53dcecb5 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 30 Dec 2014 17:28:59 +0100 Subject: [PATCH 23/27] [FIX] Vicious bug that is invisible alone, but becomes visible when used with the module pos_pay_invoice --- pos_payment_terminal/static/src/js/pos_payment_terminal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pos_payment_terminal/static/src/js/pos_payment_terminal.js b/pos_payment_terminal/static/src/js/pos_payment_terminal.js index 419a0b20..f63f6f3e 100755 --- a/pos_payment_terminal/static/src/js/pos_payment_terminal.js +++ b/pos_payment_terminal/static/src/js/pos_payment_terminal.js @@ -16,7 +16,7 @@ openerp.pos_payment_terminal = function(instance){ var _super_PaymentScreenWidget_init_ = module.PaymentScreenWidget.prototype.init; module.PaymentScreenWidget.prototype.init = function(parent, options){ _super_PaymentScreenWidget_init_.call(this, parent, options); - self = this; + var self = this; this.payment_terminal_transaction_start = function(event){ var node = this; while (node && !node.classList.contains('paymentline')){ From d3da132e02843da2efc19f840126cf05619532b5 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 6 Jan 2015 23:26:30 +0100 Subject: [PATCH 24/27] PEP8 --- hw_customer_display/__openerp__.py | 38 +++++++++++++++++------ hw_telium_payment_terminal/__openerp__.py | 33 ++++++++++++++++---- pos_customer_display/__openerp__.py | 27 +++++++++++----- pos_payment_terminal/__openerp__.py | 17 ++++++++-- 4 files changed, 89 insertions(+), 26 deletions(-) diff --git a/hw_customer_display/__openerp__.py b/hw_customer_display/__openerp__.py index 206decba..848f70fd 100644 --- a/hw_customer_display/__openerp__.py +++ b/hw_customer_display/__openerp__.py @@ -31,29 +31,49 @@ Hardware Customer Display ========================= -This module adds support for Customer Display in the Point of Sale. This module is designed to be installed on the *POSbox* (i.e. the proxy on which the USB devices are connected) and not on the main Odoo server. On the main Odoo server, you should install the module *pos_customer_display*. +This module adds support for Customer Display in the Point of Sale. +This module is designed to be installed on the *POSbox* (i.e. the +proxy on which the USB devices are connected) and not on the main +Odoo server. On the main Odoo server, you should install the module +*pos_customer_display*. -The configuration of the hardware is done in the configuration file of the Odoo server of the POSbox. You should add the following entries in the configuration file: +The configuration of the hardware is done in the configuration file of +the Odoo server of the POSbox. You should add the following entries in +the configuration file: * customer_display_device_name (default = /dev/ttyUSB0) * customer_display_device_rate (default = 9600) * customer_display_device_timeout (default = 2 seconds) -The number of cols of the Customer Display (usually 20) should be configured on the main Odoo server, in the menu Point of Sale > Configuration > Point of Sales. The number of rows is supposed to be 2. +The number of cols of the Customer Display (usually 20) should be +configured on the main Odoo server, in the menu Point of Sale > +Configuration > Point of Sales. The number of rows is supposed to be 2. -It should support most serial and USB-serial LCD displays out-of-the-box or with inheritance of a few functions. +It should support most serial and USB-serial LCD displays out-of-the-box +or with inheritance of a few functions. It has been tested with: -* Bixolon BCD-1100 (Datasheet : http://www.bixolon.com/html/en/product/product_detail.xhtml?prod_id=61) +* Bixolon BCD-1100 (Datasheet : + http://www.bixolon.com/html/en/product/product_detail.xhtml?prod_id=61) * Bixolon BCD-1000 -To setup the BCD-1100 on Linux, you will find some technical instructions on this page : http://techtuxwords.blogspot.fr/2012/12/linux-and-bixolon-bcd-1100.html -If you have a kernel >= 3.12, you should also read this : http://www.leniwiec.org/en/2014/06/25/ubuntu-14-04lts-how-to-pass-id-vendor-and-id-product-to-ftdi_sio-driver/ +To setup the BCD-1100 on Linux, you will find some technical instructions +on this page: +http://techtuxwords.blogspot.fr/2012/12/linux-and-bixolon-bcd-1100.html -This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. This module is part of the POS project of the Odoo Community Association http://odoo-community.org/. You are invited to become a member and/or get involved in the Association ! +If you have a kernel >= 3.12, you should also read this: +http://www.leniwiec.org/en/2014/06/25/ubuntu-14-04lts-how-to-pass-id-ven +dor-and-id-product-to-ftdi_sio-driver/ -Please contact Alexis de Lattre from Akretion for any help or question about this module. +This module has been developped during a POS code sprint at Akretion +France from July 7th to July 10th 2014. This module is part of the POS +project of the Odoo Community Association http://odoo-community.org/. +You are invited to become a member and/or get involved in the +Association ! + +This module has been written by Alexis de Lattre from Akretion +. """, 'author': 'Akretion', 'website': 'http://www.akretion.com', diff --git a/hw_telium_payment_terminal/__openerp__.py b/hw_telium_payment_terminal/__openerp__.py index fcec8d33..ca408d52 100644 --- a/hw_telium_payment_terminal/__openerp__.py +++ b/hw_telium_payment_terminal/__openerp__.py @@ -31,16 +31,32 @@ Hardware Telium Payment Terminal ================================ -This module adds support for Payment Terminals using Telium protocol in the Point of Sale. This module is designed to be installed on the *POSbox* (i.e. the proxy on which the USB devices are connected) and not on the main Odoo server. On the main Odoo server, you should install the module *pos_payment_terminal*. +This module adds support for credit card reader and checks printers +using Telium protocol in the Point of Sale. This module is designed to +be installed on the *POSbox* (i.e. the proxy on which the USB devices +are connected) and not on the main Odoo server. On the main Odoo server, +you should install the module *pos_payment_terminal*. -The configuration of the hardware is done in the configuration file of the Odoo server of the POSbox. You should add the following entries in the configuration file: +The configuration of the hardware is done in the configuration file of +the Odoo server of the POSbox. You should add the following entries in +the configuration file: * payment_terminal_device_name (default = /dev/ttyACM0) * payment_terminal_device_rate (default = 9600) -The Telium protocol is used by Ingenico and Sagem payment terminals. It is based on the Concert protocol, so it can probably work with payment terminals from other brands. This module implements the protocol E+ (and not the protocol E), so it requires a Telium Manager version 37783600 or superior. To get the version of the Telium Manager, press F > 0-TELIUM MANAGER > 2-Consultation > 4-Configuration > 2-Software > 1-TERMINAL > On Display > Telium Manager and then read the field *M20S*. +The Telium protocol is used by Ingenico and Sagem payment terminals. It +is based on the Concert protocol, so it can probably work with payment +terminals from other brands. This module implements the protocol E+ (and +not the protocol E), so it requires a Telium Manager version 37783600 +or superior. To get the version of the Telium Manager on an Ingenico +terminal press F > 0-TELIUM MANAGER > 2-Consultation > 4-Configuration +> 2-Software > 1-TERMINAL > On Display > Telium Manager and then read +the field *M20S*. -You will need to configure your payment terminal to accept commands from the POS. On an Ingenico reader, press F > 0-TELIUM MANAGER > 5-Initialization > 1-Parameters > Cash Connection and then select *On* and then *USB*. After that, you should reboot the terminal. +You will need to configure your payment terminal to accept commands +from the POS. On an Ingenico terminal press F > 0-TELIUM MANAGER > +5-Initialization > 1-Parameters > Cash Connection and then select *On* +and then *USB*. After that, you should reboot the terminal. This module has been successfully tested with: @@ -48,9 +64,14 @@ This module has been successfully tested with: * Ingenico EFTSmart2 2640 with Telim Manager version 37784503 * Ingenico i2200 cheque reader and writer -This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. This module is part of the POS project of the Odoo Community Association http://odoo-community.org/. You are invited to become a member and/or get involved in the Association ! +This module has been developped during a POS code sprint at Akretion +France from July 7th to July 10th 2014. This module is part of the POS +project of the Odoo Community Association http://odoo-community.org/. +You are invited to become a member and/or get involved in the +Association ! -Please contact Alexis de Lattre from Akretion for any help or question about this module. +This module has been written by Alexis de Lattre + from Akretion. """, 'author': 'Akretion', 'website': 'http://www.akretion.com', diff --git a/pos_customer_display/__openerp__.py b/pos_customer_display/__openerp__.py index 3f0a7c1e..584f3059 100644 --- a/pos_customer_display/__openerp__.py +++ b/pos_customer_display/__openerp__.py @@ -29,18 +29,29 @@ POS Customer Display ==================== -This module adds support for Customer Display in the Point of Sale. This module is designed to be installed on the *main Odoo server*. On the *POSbox*, you should install the module *hw_customer_display*. +This module adds support for Customer Display in the Point of Sale. This +module is designed to be installed on the *main Odoo server*. On the +*POSbox*, you should install the module *hw_customer_display*. -The number of rows and cols of the Customer Display (usually 2 x 20) should be configured on the main Odoo server, in the menu Point of Sale > Configuration > Point of Sales. +The number of rows and cols of the Customer Display (usually 2 x 20) +should be configured on the main Odoo server, in the menu Point of Sale +> Configuration > Point of Sales. -It has been tested with a Bixolon BCD-1100 (http://www.bixolon.com/html/en/product/product_detail.xhtml?prod_id=61), but should support most serial and USB-serial LCD displays out-of-the-box, cf the module *hw_customer_display* for more info. +It has been tested with a Bixolon BCD-1100 +(http://www.bixolon.com/html/en/product/product_detail.xhtml?prod_id=61), +but should support most serial and USB-serial LCD displays +out-of-the-box, cf the module *hw_customer_display* for more info. -This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. This module is part of the POS project of the Odoo Community Association http://odoo-community.org/. You are invited to become a member and/or get involved in the Association ! +This module has been developped during a POS code sprint at Akretion +France from July 7th to July 10th 2014. This module is part of the POS +project of the Odoo Community Association http://odoo-community.org/. +You are invited to become a member and/or get involved in the +Association ! """, 'author': 'Aurélien DUMAINE', 'depends': ['point_of_sale'], - 'data' : [ - 'pos_customer_display.xml', - 'customer_display_view.xml', - ], + 'data': [ + 'pos_customer_display.xml', + 'customer_display_view.xml', + ], } diff --git a/pos_payment_terminal/__openerp__.py b/pos_payment_terminal/__openerp__.py index 0595a779..ab46d83e 100644 --- a/pos_payment_terminal/__openerp__.py +++ b/pos_payment_terminal/__openerp__.py @@ -29,11 +29,22 @@ POS Payment Terminal ==================== -This module adds support for Payment Terminal in the Point of Sale. This module is designed to be installed on the *main Odoo server*. On the *POSbox*, you should install the module *hw_x* depending on the protocol implemented in your device. Ingenico and Sagem devices support the Telium protocol implemented in the *hw_telium_payment_terminal* module. +This module adds support for credit card reader and checks printer +in the Point of Sale. This module is designed to be installed on the +*main Odoo server*. On the *POSbox*, you should install the module +*hw_x* depending on the protocol implemented in your device. Ingenico +and Sagem devices support the Telium protocol implemented in the +*hw_telium_payment_terminal* module. -This module support two payment methods : cards and checks. The payment method should be configured on the main Odoo server, in the menu Point of Sale > Configuration > Payment Methods. +This module support two payment methods : cards and checks. The payment +method should be configured on the main Odoo server, in the menu Point +of Sale > Configuration > Payment Methods. -This module has been developped during a POS code sprint at Akretion France from July 7th to July 10th 2014. This module is part of the POS project of the Odoo Community Association http://odoo-community.org/. You are invited to become a member and/or get involved in the Association ! +This module has been developped during a POS code sprint at Akretion +France from July 7th to July 10th 2014. This module is part of the POS +project of the Odoo Community Association http://odoo-community.org/. +You are invited to become a member and/or get involved in the +Association ! """, 'author': 'Aurélien DUMAINE', 'depends': ['point_of_sale'], From 232b93a292fc058c56079f06a908cd308a94b91c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Thu, 15 Jan 2015 09:28:05 +0100 Subject: [PATCH 25/27] [FIX] fix travis add missing dependency --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 96ed724c..22bcf700 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ install: - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} - travis_install_nightly - printf '[options]\n\nrunning_env = dev' > ${HOME}/.openerp_serverrc + - pip install unidecode serial script: - travis_run_tests From 48da52a03c18ad41291a3ae78d62aca0fc8621df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Thu, 15 Jan 2015 09:53:21 +0100 Subject: [PATCH 26/27] [FIX] fix dependency installation for travis pyserial ;) not serial --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 22bcf700..1ac81de4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ install: - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} - travis_install_nightly - printf '[options]\n\nrunning_env = dev' > ${HOME}/.openerp_serverrc - - pip install unidecode serial + - pip install unidecode pyserial script: - travis_run_tests From e4676ed4e2a2bc34e679fb4c1ac7907abe7a744e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Thu, 15 Jan 2015 11:49:15 +0100 Subject: [PATCH 27/27] [FIX] fix travis add missing dependency pycountry --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1ac81de4..9cec74e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ install: - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} - travis_install_nightly - printf '[options]\n\nrunning_env = dev' > ${HOME}/.openerp_serverrc - - pip install unidecode pyserial + - pip install unidecode pyserial pycountry script: - travis_run_tests