[hw_scanner] implementation of the server side component of the barcode scanner proxy
authorFrédéric van der Essen <fva@openerp.com>
Thu, 2 Jan 2014 14:15:19 +0000 (15:15 +0100)
committerFrédéric van der Essen <fva@openerp.com>
Thu, 2 Jan 2014 14:15:19 +0000 (15:15 +0100)
bzr revid: fva@openerp.com-20140102141519-jaqi8mcqff282c6n

addons/hw_escpos/controllers/logo_grayscale.png [deleted file]
addons/hw_escpos/controllers/main.py
addons/hw_proxy/controllers/main.py
addons/hw_scanner/__init__.py [new file with mode: 0644]
addons/hw_scanner/__openerp__.py [new file with mode: 0644]
addons/hw_scanner/controllers/__init__.py [new file with mode: 0644]
addons/hw_scanner/controllers/main.py [new file with mode: 0644]
addons/point_of_sale/point_of_sale.py
addons/point_of_sale/point_of_sale_view.xml
addons/point_of_sale/static/src/js/models.js

diff --git a/addons/hw_escpos/controllers/logo_grayscale.png b/addons/hw_escpos/controllers/logo_grayscale.png
deleted file mode 100644 (file)
index acc3fa4..0000000
Binary files a/addons/hw_escpos/controllers/logo_grayscale.png and /dev/null differ
index c403141..fbc7986 100644 (file)
@@ -123,7 +123,8 @@ class EscposDriver(hw_proxy.Proxy):
             eprint.text(receipt['company']['email'] + '\n')
         if check(receipt['company']['website']):
             eprint.text(receipt['company']['website'] + '\n')
-
+        if check(receipt['header']):
+            eprint.text(receipt['header']+'\n')
         if check(receipt['cashier']):
             eprint.text('-'*32+'\n')
             eprint.text('Served by '+receipt['cashier']+'\n')
@@ -177,6 +178,8 @@ class EscposDriver(hw_proxy.Proxy):
             eprint.text(printline(_('Taxes'),money(receipt['total_tax']),width=40, ratio=0.6))
 
         # Footer
+        if check(receipt['footer']):
+            eprint.text('\n'+receipt['footer']+'\n\n')
         eprint.text(receipt['name']+'\n')
         eprint.text(      str(receipt['date']['date']).zfill(2)
                     +'/'+ str(receipt['date']['month']+1).zfill(2)
index c3e01a3..9b8e87b 100644 (file)
@@ -136,6 +136,17 @@ class Proxy(http.Controller):
     def print_receipt(self, receipt):
         print 'print_receipt' + str(receipt)
 
+    @http.route('/hw_proxy/is_scanner_connected', type='json', auth='admin')
+    def print_receipt(self, receipt):
+        print 'is_scanner_connected?' 
+        return False
+
+    @http.route('/hw_proxy/scanner', type='json', auth='admin')
+    def print_receipt(self, receipt):
+        print 'scanner' 
+        time.sleep(10)
+        return ''
+
     @http.route('/hw_proxy/log', type='json', auth='admin')
     def log(self, arguments):
         _logger.info(' '.join(str(v) for v in arguments))
diff --git a/addons/hw_scanner/__init__.py b/addons/hw_scanner/__init__.py
new file mode 100644 (file)
index 0000000..a208bc1
--- /dev/null
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#    
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
+#
+##############################################################################
+
+import controllers
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/hw_scanner/__openerp__.py b/addons/hw_scanner/__openerp__.py
new file mode 100644 (file)
index 0000000..9007304
--- /dev/null
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+
+{
+    'name': 'Barcode Scanner Hardware Driver',
+    'version': '1.0',
+    'category': 'Hardware Drivers',
+    'sequence': 6,
+    'summary': 'Hardware Driver for Barcode Scanners',
+    'description': """
+Barcode Scanner Hardware Driver
+================================
+
+This module allows the web client to access a remotely installed barcode
+scanner, and is used by the posbox to provide barcode scanner support to the
+point of sale module. 
+
+""",
+    'author': 'OpenERP SA',
+    'depends': [],
+    'test': [
+    ],
+    'installable': True,
+    'auto_install': False,
+}
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/hw_scanner/controllers/__init__.py b/addons/hw_scanner/controllers/__init__.py
new file mode 100644 (file)
index 0000000..b5f0bcc
--- /dev/null
@@ -0,0 +1,3 @@
+import main
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/hw_scanner/controllers/main.py b/addons/hw_scanner/controllers/main.py
new file mode 100644 (file)
index 0000000..82e940c
--- /dev/null
@@ -0,0 +1,125 @@
+# -*- coding: utf-8 -*-
+import logging
+import os
+from os import listdir
+from os.path import join
+import openerp
+import openerp.addons.hw_proxy.controllers.main as hw_proxy
+from openerp.tools.translate import _
+
+from openerp import http
+from openerp.http import request
+
+_logger = logging.getLogger(__name__)
+
+from evdev import InputDevice, ecodes, categorize, list_devices
+from select import select
+
+class ScannerDriver(hw_proxy.Proxy):
+    def __init__(self):
+        self.input_dir = '/dev/input/by-id/'
+        self.keymap = {
+            2: ("1","!"),
+            3: ("2","@"),
+            4: ("3","#"),
+            5: ("4","$"),
+            6: ("5","%"),
+            7: ("6","^"),
+            8: ("7","&"),
+            9: ("8","*"),
+            10:("9","("), 
+            11:("0",")"), 
+            12:("-","_"), 
+            13:("=","+"), 
+            # 14 BACKSPACE
+            # 15 TAB 
+            16:("q","Q"), 
+            17:("w","W"),
+            18:("e","E"),
+            19:("r","R"),
+            20:("t","T"),
+            21:("y","Y"),
+            22:("u","U"),
+            23:("i","I"),
+            24:("o","O"),
+            25:("p","P"),
+            26:("[","{"),
+            27:("]","}"),
+            # 28 ENTER
+            # 29 LEFT_CTRL
+            30:("a","A"),
+            31:("s","S"),
+            32:("d","D"),
+            33:("f","F"),
+            34:("g","G"),
+            35:("h","H"),
+            36:("j","J"),
+            37:("k","K"),
+            38:("l","L"),
+            39:(";",":"),
+            40:("'","\""),
+            41:("`","~"),
+            # 42 LEFT SHIFT
+            43:("\\","|"),
+            44:("z","Z"),
+            45:("x","X"),
+            46:("c","C"),
+            47:("v","V"),
+            48:("b","B"),
+            49:("n","N"),
+            50:("m","M"),
+            51:(",","<"),
+            52:(".",">"),
+            53:("/","?"),
+            # 54 RIGHT SHIFT
+            57:(" "," "),
+        }
+
+    def get_device(self):
+        devices   = [ device for device in listdir(self.input_dir)]
+        keyboards = [ device for device in devices if 'kbd' in device ]
+        scanners  = [ device for device in devices if ('barcode' in device.lower()) or ('scanner' in device.lower()) ]
+        if len(scanners) > 0:
+            return InputDevice(join(self.input_dir,scanners[0]))
+        elif len(keyboards) > 0:
+            return InputDevice(join(self.input_dir,keyboards[0]))
+        else:
+            return None
+
+    @http.route('/hw_proxy/is_scanner_connected', type='http', auth='admin')
+    def is_scanner_connected(self):
+        return self.get_device() != None
+    
+    @http.route('/hw_proxy/scanner', type='http', auth='admin')
+    def scanner(self):
+        device = self.get_device()
+        barcode = []
+        shift   = False
+        if not device:
+            return ''
+        else:
+            device.grab()
+        while True:
+            r,w,x = select([device],[],[],10)
+            if len(r) == 0: # timeout
+                device.ungrab()
+                return ''
+            for event in device.read():
+                if event.type == ecodes.EV_KEY:
+                    if event.value == 1: # keydown events
+                        print categorize(event)
+                        if event.code in self.keymap: 
+                            if shift:
+                                barcode.append(self.keymap[event.code][1])
+                            else:
+                                barcode.append(self.keymap[event.code][0])
+                        elif event.code == 42 or event.code == 54: # SHIFT
+                            shift = True
+                        elif event.code == 28: # ENTER
+                            device.ungrab()
+                            return ''.join(barcode);
+                    elif event.value == 0: #keyup events
+                        if event.code == 42 or event.code == 54: # LEFT SHIFT
+                            shift = False
+
+        
index e34207c..78f5b0d 100644 (file)
@@ -58,13 +58,15 @@ class pos_config(osv.osv):
              help="Accounting journal used to post sales entries."),
         'iface_self_checkout' : fields.boolean('Self Checkout Mode',
              help="Check this if this point of sale should open by default in a self checkout mode. If unchecked, OpenERP uses the normal cashier mode by default."),
-        'iface_cashdrawer' : fields.boolean('Cashdrawer Interface'),
-        'iface_payment_terminal' : fields.boolean('Payment Terminal Interface'),
-        'iface_electronic_scale' : fields.boolean('Electronic Scale Interface'),
-        'iface_vkeyboard' : fields.boolean('Virtual KeyBoard Interface'),
-        'iface_print_via_proxy' : fields.boolean('Print via Proxy'),
+        'iface_cashdrawer' : fields.boolean('Cashdrawer',help="Automatically open the cashdrawer"),
+        'iface_payment_terminal' : fields.boolean('Payment Terminal', help="Enables Payment Terminal integration"),
+        'iface_electronic_scale' : fields.boolean('Electronic Scale', help="Enables Electronic Scale integration"),
+        'iface_vkeyboard' : fields.boolean('Virtual KeyBoard', help="Enables an integrated Virtual Keyboard"),
+        'iface_print_via_proxy' : fields.boolean('Print via Proxy', help="Bypass browser printing and prints via the hardware proxy"),
         'iface_invoicing': fields.boolean('Invoicing',help='Enables invoice generation from the Point of Sale'),
         'iface_big_scrollbars': fields.boolean('Large Scrollbars',help='For imprecise industrial touchscreens'),
+        'receipt_header': fields.text('Receipt Header',help="A short text that will be inserted as a header in the printed receipt"),
+        'receipt_footer': fields.text('Receipt Footer',help="A short text that will be inserted as a footer in the printed receipt"),
 
         'state' : fields.selection(POS_CONFIG_STATE, 'Status', required=True, readonly=True),
         'sequence_id' : fields.many2one('ir.sequence', 'Order IDs Sequence', readonly=True,
index bdd59f9..af98eb6 100644 (file)
                                 <field name="cash_control" />
                             </tree>
                         </field>
-                        <group string="Material Interfaces" >
+                        <group string="Settings" >
                             <group>
                                 <field name="iface_self_checkout" />
                                 <field name="iface_cashdrawer" />
                             <group>
                                 <field name="iface_electronic_scale" />
                                 <field name="iface_vkeyboard" />
-                                <field name="iface_print_via_proxy" />
                                 <field name="iface_big_scrollbars" />
                             </group>
                         </group>
+                        <group string="Receipt" >
+                            <field name="iface_print_via_proxy" />
+                            <field name="receipt_header" placeholder="A custom receipt header message"/>
+                            <field name="receipt_footer" placeholder="A custom receipt header footage"/>
+                        </group>
                     </sheet>
 
                 </form>
index 2d2965f..7ac2f42 100644 (file)
@@ -138,6 +138,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
                          'iface_self_checkout', 'iface_led', 'iface_cashdrawer',
                          'iface_payment_terminal', 'iface_electronic_scale', 'iface_barscan', 'iface_vkeyboard',
                          'iface_print_via_proxy','iface_cashdrawer','iface_invoicing','iface_big_scrollbars',
+                         'receipt_header','receipt_footer',
                          'state','sequence_id','session_ids'],
                         [['id','=', self.pos_session.config_id[0]]]
                     );
@@ -869,6 +870,8 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
                 client: client ? client.name : null ,
                 invoice_id: null,   //TODO
                 cashier: cashier ? cashier.name : null,
+                header: this.pos.config.receipt_header || '',
+                footer: this.pos.config.receipt_footer || '',
                 precision: {
                     price: 2,
                     money: 2,