From a9226e3d5bb4123842dd7baa352dedd24f94416f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Thu, 6 Feb 2014 15:05:24 +0100 Subject: [PATCH 01/14] [FIX] hw_scanner, hw_escpos: prevent driver threads from deadlocking when woken by two simultaneous requests bzr revid: fva@openerp.com-20140206140524-ur51ghcpdzwxv1yr --- addons/hw_escpos/controllers/main.py | 12 +++++++++--- addons/hw_scanner/controllers/main.py | 19 ++++++++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/addons/hw_escpos/controllers/main.py b/addons/hw_escpos/controllers/main.py index 52307318870..318f117456c 100644 --- a/addons/hw_escpos/controllers/main.py +++ b/addons/hw_escpos/controllers/main.py @@ -11,7 +11,7 @@ import math import md5 import openerp.addons.hw_proxy.controllers.main as hw_proxy import subprocess -from threading import Thread +from threading import Thread, Lock from Queue import Queue, Empty try: @@ -39,6 +39,7 @@ class EscposDriver(Thread): def __init__(self): Thread.__init__(self) self.queue = Queue() + self.lock = Lock() self.status = {'status':'connecting', 'messages':[]} def connected_usb_devices(self): @@ -47,6 +48,12 @@ class EscposDriver(Thread): if usb.core.find(idVendor=device['vendor'], idProduct=device['product']) != None: connected.append(device) return connected + + def lockedstart(self): + self.lock.acquire() + if not self.isAlive(): + self.start() + self.lock.release() def get_escpos_printer(self): try: @@ -113,8 +120,7 @@ class EscposDriver(Thread): _logger.error(e); def push_task(self,task, data = None): - if not self.isAlive(): - self.start() + self.lockedstart() self.queue.put((time.time(),task,data)) def print_receipt_body(self,eprint,receipt): diff --git a/addons/hw_scanner/controllers/main.py b/addons/hw_scanner/controllers/main.py index 105856a3e28..8fbdae62db6 100644 --- a/addons/hw_scanner/controllers/main.py +++ b/addons/hw_scanner/controllers/main.py @@ -4,7 +4,7 @@ import os import time from os import listdir from os.path import join -from threading import Thread +from threading import Thread, Lock from select import select from Queue import Queue, Empty @@ -26,6 +26,7 @@ except ImportError: class Scanner(Thread): def __init__(self): Thread.__init__(self) + self.lock = Lock() self.status = {'status':'connecting', 'messages':[]} self.input_dir = '/dev/input/by-id/' self.barcodes = Queue() @@ -86,6 +87,12 @@ class Scanner(Thread): 57:(" "," "), } + def lockedstart(self): + self.lock.acquire() + if not self.isAlive(): + self.start() + self.lock.release() + def set_status(self, status, message = None): if status == self.status['status']: if message != None and message != self.status['messages'][-1]: @@ -102,8 +109,6 @@ class Scanner(Thread): elif status == 'disconnected' and message: _logger.warning('Disconnected Barcode Scanner: '+message) - - def get_device(self): try: if not evdev: @@ -135,6 +140,8 @@ class Scanner(Thread): busy reading another barcode """ + self.lockedstart() + while True: try: timestamp, barcode = self.barcodes.get(True, 5) @@ -144,8 +151,7 @@ class Scanner(Thread): return '' def get_status(self): - if not s.isAlive(): - s.start() + self.lockedstart() return self.status def run(self): @@ -209,7 +215,6 @@ hw_proxy.drivers['scanner'] = s class ScannerDriver(hw_proxy.Proxy): @http.route('/hw_proxy/scanner', type='json', auth='none', cors='*') def scanner(self): - if not s.isAlive(): - s.start() return s.get_barcode() + From ec4dd7389bb5e4b6d6613dcd9cce47a41382d56f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Thu, 6 Feb 2014 15:06:45 +0100 Subject: [PATCH 02/14] [IMP] point_of_sale: try harder to connect to proxy when the ip is already known bzr revid: fva@openerp.com-20140206140645-uo82x3tf60wlhytj --- addons/point_of_sale/static/src/js/devices.js | 144 +++++++++++------- 1 file changed, 88 insertions(+), 56 deletions(-) diff --git a/addons/point_of_sale/static/src/js/devices.js b/addons/point_of_sale/static/src/js/devices.js index 8eb32d63873..683cfaeb08b 100644 --- a/addons/point_of_sale/static/src/js/devices.js +++ b/addons/point_of_sale/static/src/js/devices.js @@ -162,25 +162,37 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal }, // find a proxy and connects to it. for options see find_proxy + // - force_ip : only try to connect to the specified ip. + // - port: what port to listen to (default 8069) + // - progress(fac) : callback for search progress ( fac in [0,1] ) autoconnect: function(options){ var self = this; this.set_connection_status('connecting',{}); + var found_url = new $.Deferred(); var success = new $.Deferred(); - this.find_proxy(options) - .then(function(proxies){ - if(proxies.length > 0){ - self.connect(proxies[0]) - .then(function(){ - success.resolve(); - },function(){ - self.set_connection_status('disconnected'); - success.reject(); - }); - }else{ - self.set_connection_status('disconnected'); - success.reject(); - } + + if ( options.force_ip ){ + // if the ip is forced by server config, bailout on fail + found_url = this.try_hard_to_connect(options.force_ip, options) + }else if( localStorage['hw_proxy_url'] ){ + // try harder when we remember a good proxy url + found_url = this.try_hard_to_connect(localStorage['hw_proxy_url'], options) + .then(null,function(){ + return self.find_proxy(options); + }); + }else{ + // just find something quick + found_url = this.find_proxy(options); + } + + success = found_url.then(function(url){ + return self.connect(url); }); + + success.fail(function(){ + self.set_connection_status('disconnected'); + }); + return success; }, @@ -217,10 +229,50 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal } }, - // returns as a deferred a list of valid hosts urls that can be used as proxy. + // try several time to connect to a known proxy url + try_hard_to_connect: function(url,options){ + options = options || {}; + var port = ':' + (options.port || '8069'); + + this.set_connection_status('connecting'); + + if(url.indexOf('//') < 0){ + url = 'http://'+url; + } + + if(url.indexOf(':',5) < 0){ + url = url+port; + } + + // try real hard to connect to url, with a 1sec timeout and up to 'retries' retries + function try_real_hard_to_connect(url, retries, done){ + + done = done || new $.Deferred(); + + var c = $.ajax({ + url: url + '/hw_proxy/hello', + method: 'GET', + timeout: 1000, + }) + .done(function(){ + done.resolve(url); + }) + .fail(function(){ + if(retries > 0){ + try_real_hard_to_connect(url,retries-1,done); + }else{ + done.reject(); + } + }); + return done; + } + + return try_real_hard_to_connect(url,3); + }, + + // returns as a deferred a valid host url that can be used as proxy. // options: // - port: what port to listen to (default 8069) - // - force_ip : limit the search to the specified ip // - progress(fac) : callback for search progress ( fac in [0,1] ) find_proxy: function(options){ options = options || {}; @@ -228,36 +280,17 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal var port = ':' + (options.port || '8069'); var urls = []; var found = false; - var proxies = []; - var done = new $.Deferred(); var parallel = 8; + var done = new $.Deferred(); // will be resolved with the proxies valid urls var threads = []; var progress = 0; - this.set_connection_status('connecting'); - if(options.force_ip){ - var url = options.force_ip; - if(url.indexOf('//') < 0){ - url = 'http://'+url; - } - if(url.indexOf(':',5) < 0){ - url = url+port; - } - urls.push(url); - }else{ - if(localStorage['hw_proxy_url']){ - urls.push(localStorage['hw_proxy_url']); - } - - urls.push('http://localhost'+port); - - for(var i = 0; i < 256; i++){ - urls.push('http://192.168.0.'+i+port); - urls.push('http://192.168.1.'+i+port); - urls.push('http://192.168.2.'+i+port); - urls.push('http://10.0.0.'+i+port); - } + urls.push('http://localhost'+port); + for(var i = 0; i < 256; i++){ + urls.push('http://192.168.0.'+i+port); + urls.push('http://192.168.1.'+i+port); + urls.push('http://10.0.0.'+i+port); } var prog_inc = 1/urls.length; @@ -269,40 +302,39 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal } } - function thread(url,done){ - if(!url){ + function thread(done){ + var url = urls.shift(); + + done = done || new $.Deferred(); + + if( !url || found || !self.searching_for_proxy ){ done.resolve(); + return done; } + var c = $.ajax({ url: url + '/hw_proxy/hello', method: 'GET', - timeout: 300, + timeout: 400, }).done(function(){ found = true; update_progress(); - proxies.push(url); done.resolve(url); }) .fail(function(){ update_progress(); - var next_url = urls.shift(); - if(found ||! self.searching_for_proxy || !next_url){ - done.resolve(); - }else{ - thread(next_url,done); - } + thread(done); }); + return done; } this.searching_for_proxy = true; - for(var i = 0; i < Math.min(parallel,urls.length); i++){ - threads.push(thread(urls.shift(),new $.Deferred())); + for(var i = 0, len = Math.min(parallel,urls.length); i < len; i++){ + threads.push(thread()); } - var done = new $.Deferred(); - $.when.apply($,threads).then(function(){ var urls = []; for(var i = 0; i < arguments.length; i++){ @@ -310,7 +342,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal urls.push(arguments[i]); } } - done.resolve(urls); + done.resolve(urls[0]); }); return done; From 008e3c1ab01ced4302a648c2ff55ac8a209898be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Thu, 6 Feb 2014 15:36:48 +0100 Subject: [PATCH 03/14] [FIX] point_of_sale: receipt header & footer could overflow outside of ticket bzr revid: fva@openerp.com-20140206143648-z1bsknelzznf0jqj --- addons/point_of_sale/static/src/xml/pos.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index 748fcbfee1b..d0cd945d4f4 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -635,9 +635,9 @@ Shop:

-
+                
-
+
@@ -711,9 +711,9 @@

-
+                
-
+
From a78af30755bab161e07330a552e3495b0a751e2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Thu, 6 Feb 2014 16:14:32 +0100 Subject: [PATCH 04/14] [FIX] hw_escpos: company logo was not centered on the first receipt bzr revid: fva@openerp.com-20140206151432-avaz87emjzqyvu9t --- addons/hw_escpos/controllers/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/hw_escpos/controllers/main.py b/addons/hw_escpos/controllers/main.py index 318f117456c..b7b254d4bd5 100644 --- a/addons/hw_escpos/controllers/main.py +++ b/addons/hw_escpos/controllers/main.py @@ -163,6 +163,7 @@ class EscposDriver(Thread): # Receipt Header if receipt['company']['logo']: + eprint.set(align='center') eprint.print_base64_image(receipt['company']['logo']) eprint.text('\n') else: From 356010c3575316fc1dff0dfeb4d1038f9c399f21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Fri, 7 Feb 2014 17:16:47 +0100 Subject: [PATCH 05/14] [FIX] point_of_sale: totals were not rendrered on firefox due to use of nonstandard innerText dom property bzr revid: fva@openerp.com-20140207161647-rgeymwt4ksei2cod --- addons/point_of_sale/static/src/js/widgets.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/point_of_sale/static/src/js/widgets.js b/addons/point_of_sale/static/src/js/widgets.js index f2a254593d1..a2f0759d1a1 100644 --- a/addons/point_of_sale/static/src/js/widgets.js +++ b/addons/point_of_sale/static/src/js/widgets.js @@ -258,8 +258,8 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa var total = order ? order.getTotalTaxIncluded() : 0; var taxes = order ? total - order.getTotalTaxExcluded() : 0; - this.el.querySelector('.summary .total > .value').innerText = this.format_currency(total); - this.el.querySelector('.summary .total .subentry .value').innerText = this.format_currency(taxes); + this.el.querySelector('.summary .total > .value').textContent = this.format_currency(total); + this.el.querySelector('.summary .total .subentry .value').textContent = this.format_currency(taxes); }, }); From 234aa3928584fe1a83def860df1ae9d706b02ea3 Mon Sep 17 00:00:00 2001 From: jke-openerp Date: Fri, 7 Feb 2014 18:16:37 +0100 Subject: [PATCH 06/14] [FIX] Fix bug when an event with date_startand date_stop (and not datetime as calendar) have always 1 days in more. bzr revid: jke@openerp.com-20140207171637-j37cnt63cywstrtj --- addons/web_calendar/static/src/js/web_calendar.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/addons/web_calendar/static/src/js/web_calendar.js b/addons/web_calendar/static/src/js/web_calendar.js index b283f8b5cb1..ef84f8a2784 100644 --- a/addons/web_calendar/static/src/js/web_calendar.js +++ b/addons/web_calendar/static/src/js/web_calendar.js @@ -492,11 +492,7 @@ openerp.web_calendar = function(instance) { all_day = this.all_day ? evt[this.all_day] : false, res_computed_text = '', the_title = '', - attendees = []; - - if (this.date_stop && this.fields[this.date_stop].type == 'date') { - date_stop.addDay(1); - } + attendees = []; if (this.info_fields) { var temp_ret = {}; From 0821af1af96c0ff59770a32c03b0bd5cdf960c39 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 10 Feb 2014 13:34:16 +0100 Subject: [PATCH 07/14] [FIX] close autocomplete listing on input Closing previously occured on search request (so that a user wouldn't be able to select "old" data on new search request), but ``search`` is only triggered after the search delay. Worked when delay was 0, with it being moved to 250 a user can get results matching the previous search instead of the current one. Trigger a closing of the current results list on any ``input`` event, which is when text is entered in any of the searchview's ``InputView``. bzr revid: xmo@openerp.com-20140210123416-cfc0x24bfkiv4lse --- addons/web/static/src/js/search.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index fa9bae9969d..903a9f4683e 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -473,12 +473,9 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea * Sets up search view's view-wide auto-completion widget */ setup_global_completion: function () { - var self = this; - var autocomplete = this.$el.autocomplete({ source: this.proxy('complete_global_search'), select: this.proxy('select_completion'), - search: function () { self.$el.autocomplete('close'); }, focus: function (e) { e.preventDefault(); }, html: true, autoFocus: true, @@ -486,6 +483,10 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea delay: 250, }).data('autocomplete'); + this.$el.on('input', function () { + this.$el.autocomplete('close'); + }.bind(this)); + // MonkeyPatch autocomplete instance _.extend(autocomplete, { _renderItem: function (ul, item) { From e95eb140a74487a91a4c657c3616e4ceb10ea9b4 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 10 Feb 2014 14:05:04 +0100 Subject: [PATCH 08/14] [CHG] website: fp wants image 'change' buttons to be btn-sm bzr revid: xmo@openerp.com-20140210130504-hejvay436emh7f36 --- addons/website/static/src/js/website.editor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js index 1e8246366a9..7e867dca685 100644 --- a/addons/website/static/src/js/website.editor.js +++ b/addons/website/static/src/js/website.editor.js @@ -638,7 +638,7 @@ image_dialog(editor, new CKEDITOR.dom.element(previous)); $image_button.hide(); previous = null; - }); + }, 'btn-sm'); // previous is the state of the button-trigger: it's the // currently-ish hovered element which can trigger a button showing. From d4c7de4b03c7f21f79bdad97ff4475e367572c61 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 10 Feb 2014 14:28:55 +0100 Subject: [PATCH 09/14] [FIX] work around distribute_branding distribute_branding is applied after view inheritance is applied, and does not take it in account. As a result, altering an existing view "from the top" (e.g. adding a div element preceding an existing div) will alter the xpath leading to whatever element we need to deal with. Move fakebar injection to the bottom of the body, where it can't have any impact on preceding node paths. bzr revid: xmo@openerp.com-20140210132855-lwh1ow1tq1e0pidp --- addons/website/views/website_templates.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index bf61f64a1b9..9073ccb7a4a 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -224,7 +224,7 @@ padding-top: 51px; - +