From b3a3454a2ffab9042c021204d284ac25c5f56aef Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Wed, 2 Mar 2016 08:10:00 +0100 Subject: [PATCH 1/8] [FIX] stock: duplicated term in .pot --- addons/stock/i18n/stock.pot | 6 ------ 1 file changed, 6 deletions(-) diff --git a/addons/stock/i18n/stock.pot b/addons/stock/i18n/stock.pot index a271946b791..e4643d321ca 100644 --- a/addons/stock/i18n/stock.pot +++ b/addons/stock/i18n/stock.pot @@ -51,12 +51,6 @@ msgstr "" msgid " (reserved)" msgstr "" -#. module: stock -#: code:addons/stock/stock.py:2954 -#, python-format -msgid "You cannot have two inventory adjustements in state 'in Progess' with the same product(%s), same location(%s), same package, same owner and same lot. Please first validate the first inventory adjustement with this product before creating another one." -msgstr "" - #. module: stock #: code:addons/stock/stock.py:3090 #, python-format From acf027ac98cd775e533cc0a0eb208754ba900576 Mon Sep 17 00:00:00 2001 From: Joren Van Onder Date: Wed, 2 Mar 2016 09:31:57 +0100 Subject: [PATCH 2/8] [IMP] hw_scale: allow third party USB to serial interfaces We support the Mettler Toledo scales which have their own built-in USB to serial interface. Due to continuing issues with that built-in interface we will also support the scale configured in raw RS-232 mode which seems to be more reliable. This means that in order to connect the scale you'll need a third party USB to serial interface (unless you have built-in serial interfaces, but the POSBox doesn't). The main difficulty this poses is that using this approach we cannot use the name of the interface to find the device. When using the built-in interface of the scale the interface would identify with an ID containing 'mettler' and 'toledo'. When using a third party interface the ID will instead contain information about the third party interface. To fix this we use a probe-based approach, probing every available serial interface until we find one that returns a response to our probe. This approach will work with both third party interfaces and the built-in interface of the scale. Contrary to probe-based approach used in hw_blackbox_be this one is slightly more complicated because hw_scale is written in such a way that it is 'plug and play', which means that as long as the module is running it will continually try to find a scale. This is fine, but we don't want to keep sending probes to eg. Fiscal Data Modules, which could lead to issues. Therefore we will only probe every device once. When we lose an existing, confirmed connection to a scale we will however keep retrying to connect to that particular device. --- addons/hw_scale/controllers/main.py | 58 +++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/addons/hw_scale/controllers/main.py b/addons/hw_scale/controllers/main.py index b03fbc0ae97..68ec1126c01 100644 --- a/addons/hw_scale/controllers/main.py +++ b/addons/hw_scale/controllers/main.py @@ -33,6 +33,8 @@ class Scale(Thread): self.weight = 0 self.weight_info = 'ok' self.device = None + self.probed_device_paths = [] + self.path_to_scale = '' def lockedstart(self): with self.lock: @@ -61,27 +63,53 @@ class Scale(Thread): elif status == 'disconnected' and message: _logger.warning('Disconnected Scale: '+message) + def _get_raw_response(self, connection): + response = "" + while True: + byte = connection.read(1) + + if byte: + response += byte + else: + return response + def get_device(self): try: if not os.path.exists(self.input_dir): self.set_status('disconnected','Scale Not Found') return None devices = [ device for device in listdir(self.input_dir)] - scales = [ device for device in devices if ('mettler' in device.lower()) or ('toledo' in device.lower()) ] - if len(scales) > 0: - print join(self.input_dir,scales[0]) - self.set_status('connected','Connected to '+scales[0]) - return serial.Serial(join(self.input_dir,scales[0]), - baudrate = 9600, - bytesize = serial.SEVENBITS, - stopbits = serial.STOPBITS_ONE, - parity = serial.PARITY_EVEN, - #xonxoff = serial.XON, - timeout = 0.02, - writeTimeout= 0.02) - else: - self.set_status('disconnected','Scale Not Found') - return None + + if len(devices) > 0: + for device in devices: + path = self.input_dir + device + + # don't keep probing devices that are not a scale, + # only keep probing if in the past the device was + # confirmed to be a scale + if path not in self.probed_device_paths or path == self.path_to_scale: + _logger.debug('Probing: ' + path) + connection = serial.Serial(path, + baudrate = 9600, + bytesize = serial.SEVENBITS, + stopbits = serial.STOPBITS_ONE, + parity = serial.PARITY_EVEN, + timeout = 0.02, + writeTimeout= 0.02) + + connection.write("W") + self.probed_device_paths.append(path) + + if self._get_raw_response(connection): + _logger.debug(path + ' is scale') + self.path_to_scale = path + self.set_status('connected','Connected to '+device) + return connection + else: + _logger.debug('Already probed: ' + path) + + self.set_status('disconnected','Scale Not Found') + return None except Exception as e: self.set_status('error',str(e)) return None From 290eb4ea2e734f0a24e87d9ff82589cd4a9ed81a Mon Sep 17 00:00:00 2001 From: Joren Van Onder Date: Wed, 2 Mar 2016 09:51:04 +0100 Subject: [PATCH 3/8] [FIX] hw_blackbox_be, hw_scale: don't rely on serial ID to be unique After plugging in multiple identical serial to USB interfaces, only the last one will be available under /dev/serial/by-id/ because they'll have identical IDs. Instead use /dev/serial/by-path/ which does not have this issue. --- addons/hw_blackbox_be/controllers/main.py | 2 +- addons/hw_scale/controllers/main.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/hw_blackbox_be/controllers/main.py b/addons/hw_blackbox_be/controllers/main.py index fbc7664cbc0..8db84fbbffa 100644 --- a/addons/hw_blackbox_be/controllers/main.py +++ b/addons/hw_blackbox_be/controllers/main.py @@ -37,7 +37,7 @@ class Blackbox(Thread): # request. The first device to give an answer that makes sense # wins. def _find_device_path_by_probing(self): - path = "/dev/serial/by-id/" + path = "/dev/serial/by-path/" probe_message = self._wrap_low_level_message_around("S000") try: diff --git a/addons/hw_scale/controllers/main.py b/addons/hw_scale/controllers/main.py index 68ec1126c01..4d12b2aa825 100644 --- a/addons/hw_scale/controllers/main.py +++ b/addons/hw_scale/controllers/main.py @@ -29,7 +29,7 @@ class Scale(Thread): self.lock = Lock() self.scalelock = Lock() self.status = {'status':'connecting', 'messages':[]} - self.input_dir = '/dev/serial/by-id/' + self.input_dir = '/dev/serial/by-path/' self.weight = 0 self.weight_info = 'ok' self.device = None From c23eb232675554b4952d3898a76470d5d659fe82 Mon Sep 17 00:00:00 2001 From: Joren Van Onder Date: Wed, 2 Mar 2016 16:27:31 +0100 Subject: [PATCH 4/8] [FIX] hw_scale: use a larger timeout during initial probe I'm not sure why the serial timeout is so aggressive, but I assume there is a reason for it. During probing however, we shouldn't use such an aggressive timeout because if we miss the device during initial startup we'll never find it since we only probe devices once. --- addons/hw_scale/controllers/main.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/addons/hw_scale/controllers/main.py b/addons/hw_scale/controllers/main.py index 4d12b2aa825..730b9bb9c68 100644 --- a/addons/hw_scale/controllers/main.py +++ b/addons/hw_scale/controllers/main.py @@ -94,8 +94,8 @@ class Scale(Thread): bytesize = serial.SEVENBITS, stopbits = serial.STOPBITS_ONE, parity = serial.PARITY_EVEN, - timeout = 0.02, - writeTimeout= 0.02) + timeout = 1, + writeTimeout = 1) connection.write("W") self.probed_device_paths.append(path) @@ -104,6 +104,8 @@ class Scale(Thread): _logger.debug(path + ' is scale') self.path_to_scale = path self.set_status('connected','Connected to '+device) + connection.timeout = 0.02 + connection.writeTimeout = 0.02 return connection else: _logger.debug('Already probed: ' + path) From 13f1426bf564a91c6cdbfb6e6aa8522efdd549f3 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Thu, 3 Mar 2016 10:59:40 +0100 Subject: [PATCH 5/8] [FIX] report: UPC-A barcodes drawing It looks like reportlab doesn't handle well UPC-A barcodes. As UPC-A barcodes became EAN13 barcodes when adding a `0` to the beginning of the barcode, and the render is the same, we ask reportlab a EAN13 barcode type when a UPC-A barcode is asked, making sure to add the `0` automatically first. From Wikipedia: ``` The EAN was developed as a superset of UPC, adding an extra digit to the beginning of every UPC number. [...] the original rules for UPC are treated as a '0' if read as EAN-13. A UPC barcode XXXXXXXXXXXX therefore is the EAN-13 barcode 0XXXXXXXXXXXX. It is possible to prefix a UPC barcode with a 0; they become EAN-13 rather than UPC-A. This does not change the check digit. All point-of-sale systems can now understand both equally. ``` opw-670560 --- addons/report/controllers/main.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addons/report/controllers/main.py b/addons/report/controllers/main.py index 56236990316..ba8016f6ea1 100644 --- a/addons/report/controllers/main.py +++ b/addons/report/controllers/main.py @@ -85,6 +85,10 @@ class ReportController(Controller): :param humanreadable: Accepted values: 0 (default) or 1. 1 will insert the readable value at the bottom of the output image """ + if type == 'UPCA' and len(value) in (11, 12, 13): + type = 'EAN13' + if len(value) in (11, 12): + value = '0%s' % value try: width, height, humanreadable = int(width), int(height), bool(humanreadable) barcode = createBarcodeDrawing( From 14282f1659149cd2d0e06f9442a37e069a2deb58 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Tue, 26 Jan 2016 17:22:39 +0100 Subject: [PATCH 6/8] [FIX] report, website_report: barcode widget Up to this revision, barcodes set in reports are generated using the controller `/report/barcode`, using a `img` HTML tag, e.g. (from `report_location_barcode`) ``` `` This `/report/barcode` route is set as `auth='user'`, meaning the route can only be accessed by signed in users. When wkhtmltopdf prints a report containing such a barcode, it calls this `report/barcode` route, making sure to pass the request session in the cookies, so wkhtmltopdf uses the same session than the one of the user. This is needed, as only users can access this `report/barcode/` route. This session is passed in `report.py`, in the method `_run_wkhtmltopdf`, thanks to the --cookie wkhtmltopdf parameter. Nevertheless, if a report is printed through the website front-end, as public (without a signed in user), the request session is not associated to a signed in user, and therefore the route `/report/barcode` refuses the access / redirects to the login, making it impossible to print a report containing a barcode without being signed in. This even if the report is printed as `sudo` through a controller (as this is the session of the not signed in user which is passed, not a session associated to `sudo). Fixes #10621 opw-667797 --- addons/report/controllers/main.py | 11 +---------- addons/report/models/__init__.py | 1 + addons/report/models/ir_qweb.py | 21 +++++++++++++++++++++ addons/report/models/report.py | 16 ++++++++++++++++ addons/website_report/__init__.py | 1 + addons/website_report/ir_qweb.py | 9 +++++++++ 6 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 addons/report/models/ir_qweb.py create mode 100644 addons/website_report/ir_qweb.py diff --git a/addons/report/controllers/main.py b/addons/report/controllers/main.py index ba8016f6ea1..1fcdd7c4ad3 100644 --- a/addons/report/controllers/main.py +++ b/addons/report/controllers/main.py @@ -85,17 +85,8 @@ class ReportController(Controller): :param humanreadable: Accepted values: 0 (default) or 1. 1 will insert the readable value at the bottom of the output image """ - if type == 'UPCA' and len(value) in (11, 12, 13): - type = 'EAN13' - if len(value) in (11, 12): - value = '0%s' % value try: - width, height, humanreadable = int(width), int(height), bool(humanreadable) - barcode = createBarcodeDrawing( - type, value=value, format='png', width=width, height=height, - humanReadable = humanreadable - ) - barcode = barcode.asString('png') + barcode = request.registry['report'].barcode(type, value, width=width, height=height, humanreadable=humanreadable) except (ValueError, AttributeError): raise exceptions.HTTPException(description='Cannot convert into barcode.') diff --git a/addons/report/models/__init__.py b/addons/report/models/__init__.py index d21f03e5bcb..4566e3a43d3 100644 --- a/addons/report/models/__init__.py +++ b/addons/report/models/__init__.py @@ -1,3 +1,4 @@ import report import report_paperformat import abstract_report +import ir_qweb diff --git a/addons/report/models/ir_qweb.py b/addons/report/models/ir_qweb.py new file mode 100644 index 00000000000..4802586de50 --- /dev/null +++ b/addons/report/models/ir_qweb.py @@ -0,0 +1,21 @@ +from reportlab.graphics.barcode import createBarcodeDrawing + +from openerp.osv import osv +from openerp.addons.base.ir.ir_qweb import HTMLSafe + + +class BarcodeConverter(osv.AbstractModel): + """ ``barcode`` widget rendering, inserts a data:uri-using image tag in the + document. May be overridden by e.g. the website module to generate links + instead. + """ + _name = 'ir.qweb.field.barcode' + _inherit = 'ir.qweb.field' + + def value_to_html(self, cr, uid, value, field, options=None, context=None): + barcode_type = options.get('type', 'Code128') + barcode = self.pool['report'].barcode( + barcode_type, + value, + **dict((key, value) for key, value in options.items() if key in ['width', 'height', 'humanreadable'])) + return HTMLSafe('' % ('png', barcode.encode('base64'))) diff --git a/addons/report/models/report.py b/addons/report/models/report.py index aa6b6d143b1..3c7d19fcc3a 100644 --- a/addons/report/models/report.py +++ b/addons/report/models/report.py @@ -41,6 +41,7 @@ from contextlib import closing from distutils.version import LooseVersion from functools import partial from pyPdf import PdfFileWriter, PdfFileReader +from reportlab.graphics.barcode import createBarcodeDrawing #-------------------------------------------------------------------------- @@ -574,3 +575,18 @@ class Report(osv.Model): stream.close() return merged_file_path + + def barcode(self, barcode_type, value, width=600, height=100, humanreadable=0): + if barcode_type == 'UPCA' and len(value) in (11, 12, 13): + barcode_type = 'EAN13' + if len(value) in (11, 12): + value = '0%s' % value + try: + width, height, humanreadable = int(width), int(height), bool(humanreadable) + barcode = createBarcodeDrawing( + barcode_type, value=value, format='png', width=width, height=height, + humanReadable=humanreadable + ) + return barcode.asString('png') + except (ValueError, AttributeError): + raise ValueError("Cannot convert into barcode.") diff --git a/addons/website_report/__init__.py b/addons/website_report/__init__.py index a0526c7c636..6737fad41b6 100644 --- a/addons/website_report/__init__.py +++ b/addons/website_report/__init__.py @@ -1,2 +1,3 @@ import controllers import report +import ir_qweb diff --git a/addons/website_report/ir_qweb.py b/addons/website_report/ir_qweb.py new file mode 100644 index 00000000000..3123e13e254 --- /dev/null +++ b/addons/website_report/ir_qweb.py @@ -0,0 +1,9 @@ +from openerp.osv import orm + + +class Barcode(orm.AbstractModel): + _name = 'website.qweb.field.barcode' + _inherit = ['website.qweb.field', 'ir.qweb.field.barcode'] + + def from_html(self, cr, uid, model, field, element, context=None): + return None From e0555a66e14fd3824860f1dc87c3b14ea6aa9535 Mon Sep 17 00:00:00 2001 From: eLBati Date: Fri, 8 Jan 2016 18:08:49 +0100 Subject: [PATCH 7/8] [FIX] account: `amount_currency` when grouping invoice lines when using a journal with 'Group Invoice Lines' and issuing an invoice with foreign currency with more than 1 line (of the same product, or no product), the amount currency in the journal item associated to the merged invoice lines was wrong. It took into account the `amount_currency` of the first line only, ignoring the `amount_currency` of the remaining merged lines. opw-670972 Closes #10375 --- addons/account/account_invoice.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 90d4d7fd954..7a0d737d847 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -779,6 +779,7 @@ class account_invoice(models.Model): line2[tmp]['debit'] = (am > 0) and am or 0.0 line2[tmp]['credit'] = (am < 0) and -am or 0.0 line2[tmp]['tax_amount'] += l['tax_amount'] + line2[tmp]['amount_currency'] += l['amount_currency'] line2[tmp]['analytic_lines'] += l['analytic_lines'] else: line2[tmp] = l From 40fdb841ae909304f65785b4c580331a6db59df6 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 3 Mar 2016 17:38:55 +0100 Subject: [PATCH 8/8] [FIX] auth_signup: readonly empty login field on signup Closes #9367 Fixes #11180 --- addons/auth_signup/res_users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py index 39ef46f1b78..0d20342febd 100644 --- a/addons/auth_signup/res_users.py +++ b/addons/auth_signup/res_users.py @@ -165,7 +165,7 @@ class res_partner(osv.Model): if partner.user_ids: res['login'] = partner.user_ids[0].login else: - res['email'] = partner.email or '' + res['login'] = res['email'] = partner.email or '' return res class res_users(osv.Model):