From daba4e4fc279017a471fdf1c3a273296404cb882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Thu, 16 Aug 2012 17:02:13 +0200 Subject: [PATCH] [IMP] point_of_sale: ImageCache and RegExp search bzr revid: fva@openerp.com-20120816150213-mdctwsjxiyjh4lxi --- addons/point_of_sale/static/src/js/pos_db.js | 76 ++++++++++--------- .../point_of_sale/static/src/js/pos_models.js | 2 +- .../static/src/js/pos_widgets.js | 72 +++++++++++++----- addons/point_of_sale/static/src/xml/pos.xml | 2 +- 4 files changed, 95 insertions(+), 57 deletions(-) diff --git a/addons/point_of_sale/static/src/js/pos_db.js b/addons/point_of_sale/static/src/js/pos_db.js index 0992c6601c2..41957de4bbe 100644 --- a/addons/point_of_sale/static/src/js/pos_db.js +++ b/addons/point_of_sale/static/src/js/pos_db.js @@ -20,8 +20,11 @@ function openerp_pos_db(instance, module){ this.category_ancestors = {}; this.category_childs = {}; this.category_parent = {}; + this.category_search_string = {}; }, - /* returns the category object from its id */ + /* returns the category object from its id. If you pass a list of id as parameters, you get + * a list of category objects. + */ get_category_by_id: function(categ_id){ if(categ_id instanceof Array){ var list = []; @@ -108,6 +111,13 @@ function openerp_pos_db(instance, module){ localStorage[this.name + '_' + store] = JSON.stringify(data); this.cache[store] = data; }, + _product_search_string: function(product){ + var str = '' + product.id + ':' + product.name; + if(product.ean13){ + str += '|' + product.ean13; + } + return str + '\n'; + }, add_products: function(products){ var stored_products = this.load('products',{}); var stored_categories = this.load('categories',{}); @@ -117,21 +127,35 @@ function openerp_pos_db(instance, module){ } for(var i = 0, len = products.length; i < len; i++){ var product = products[i]; + var search_string = this._product_search_string(product); var categ_id = product.pos_categ_id[0]; if(!stored_categories[categ_id]){ stored_categories[categ_id] = []; } stored_categories[categ_id].push(product.id); + + if(this.category_search_string[categ_id] === undefined){ + this.category_search_string[categ_id] = ''; + } + this.category_search_string[categ_id] += search_string; + var ancestors = this.get_category_ancestors_ids(categ_id) || []; for(var j = 0; j < ancestors.length; j++){ - if(! stored_categories[ancestors[j]]){ - stored_categories[ancestors[j]] = []; + var ancestor = ancestors[j]; + if(! stored_categories[ancestor]){ + stored_categories[ancestor] = []; } - stored_categories[ancestors[j]].push(product.id); + stored_categories[ancestor].push(product.id); + + if( this.category_search_string[ancestor] === undefined){ + this.category_search_string[ancestor] = ''; + } + this.category_search_string[ancestor] += search_string; } stored_products[product.id] = product; } + console.log(this.category_search_string); this.save('products',stored_products); this.save('categories',stored_categories); }, @@ -173,41 +197,23 @@ function openerp_pos_db(instance, module){ } return list; }, - /* returns as a parameter of the result_callback function a list of products with : + /* returns a list of products with : * - a category that is or is a child of category_id, - * - a field in fields that contains a value that contains the query - * If a search is started before the previous has returned, the previous search may be cancelled - * (and the corresponding result_callback never called) + * - a name, package or ean13 containing the query (case insensitive) */ - search_product_in_category: function(category_id, fields, query){ - var self = this; - var stored_categories = this.load('categories',{}); - var stored_products = this.load('products',{}); - var product_ids = stored_categories[category_id]; - var list = []; - var count = 0; - - query = query.toString().toLowerCase(); - - if(!(fields instanceof Array)){ - fields = [fields]; - } - for(var i = 0, len = product_ids.length; i < len && count < this.limit; i++){ - var product = stored_products[product_ids[i]]; - for(var j = 0, jlen = fields.length; j < jlen; j++){ - var field = product[fields[j]]; - if(field === null || field === undefined){ - continue; - } - field = field.toString().toLowerCase(); - if(field.indexOf(query) != -1){ - list.push(product); - count++; - break; - } + search_product_in_category: function(category_id, query){ + var re = RegExp("([0-9]+):.*?"+query,"gi"); + var results = []; + for(var i = 0; i < this.limit; i++){ + r = re.exec(this.category_search_string[category_id]); + if(r){ + var id = Number(r[1]); + results.push(this.get_product_by_id(id)); + }else{ + break; } } - return list; + return results; }, add_order: function(order){ var last_id = this.load('last_order_id',0); diff --git a/addons/point_of_sale/static/src/js/pos_models.js b/addons/point_of_sale/static/src/js/pos_models.js index 03219e1852c..5fdd172a15f 100644 --- a/addons/point_of_sale/static/src/js/pos_models.js +++ b/addons/point_of_sale/static/src/js/pos_models.js @@ -298,7 +298,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal //try to push an order to the server (new instance.web.Model('pos.order')).get_func('create_from_ui')([order]) .fail(function(unused, event){ - //don't show error popup if it fails (I guess, copy pasted from niv without understanding it completely) + //don't show error popup if it fails event.preventDefault(); console.error('Failed to send order:',order); self._flush(index+1); diff --git a/addons/point_of_sale/static/src/js/pos_widgets.js b/addons/point_of_sale/static/src/js/pos_widgets.js index ec8f66d72de..0abdb2c7d9f 100644 --- a/addons/point_of_sale/static/src/js/pos_widgets.js +++ b/addons/point_of_sale/static/src/js/pos_widgets.js @@ -1,6 +1,51 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sale var QWeb = instance.web.qweb; + // The ImageCache is used to hide the latency of the application cache on-disk access + // that causes annoying flickering on product pictures. Why the hell a simple access to + // the application cache involves such latency is beyond me, hopefully one day this can be + // removed. + module.ImageCache = instance.web.Class.extend({ + init: function(options){ + options = options || {}; + this.max_size = options.max_size || 100; + + this.cache = {}; + this.access_time = {}; + this.size = 0; + }, + // returns a DOM Image object from an url, and cache the last 100 (by default) results + get_image: function(url){ + var cached = this.cache[url]; + if(cached){ + this.access_time[url] = (new Date()).getTime(); + return cached; + }else{ + var img = new Image(); + img.src = url; + while(this.size >= this.max_size){ + var oldestUrl = null; + var oldestTime = (new Date()).getTime(); + for(var url in this.cache){ + var time = this.access_time[url]; + if(time <= oldestTime){ + oldestTime = time; + oldestUrl = url; + } + } + if(oldestUrl){ + delete this.cache[oldestUrl]; + delete this.access_time[oldestUrl]; + } + this.size--; + } + this.cache[url] = img; + this.access_time[url] = (new Date()).getTime(); + return img; + } + }, + }); + module.NumpadWidget = module.PosBaseWidget.extend({ template:'NumpadWidget', init: function(parent, options) { @@ -229,25 +274,16 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa this._super(parent,options); this.model = options.model; this.model.attributes.weight = options.weight; - this.next_screen = options.next_screen; - this.click_product_action = options.click_product_action; - }, - add_to_order: function(event) { - /* Preserve the category URL */ - event.preventDefault(); - return (this.pos.get('selectedOrder')).addProduct(this.model); - }, - set_weight: function(weight){ - this.model.attributes.weight = weight; - this.renderElement(); + this.next_screen = options.next_screen; //when a product is clicked, this screen is set + this.click_product_action = options.click_product_action; }, + // returns the url of the product thumbnail get_image_url: function() { - var url = '/web/binary/image?session_id='+instance.connection.session_id+'&model=product.product&field=image&id='+this.model.get('id'); - console.log('Requesting url:'+ url); - return url; + return '/web/binary/image?session_id='+instance.connection.session_id+'&model=product.product&field=image&id='+this.model.get('id'); }, renderElement: function() { this._super(); + this.$('img').replaceWith(this.pos_widget.image_cache.get_image(this.get_image_url())); var self = this; $("a", this.$element).click(function(e){ if(self.click_product_action){ @@ -480,7 +516,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa this.$('.searchbox input').keyup(function(){ query = $(this).val().toLowerCase(); if(query){ - var products = self.pos.db.search_product_in_category(self.category.id, ['name','ean13'], query); + var products = self.pos.db.search_product_in_category(self.category.id, query); self.pos.get('products').reset(products); self.$('.search-clear').fadeIn(); }else{ @@ -518,11 +554,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa self.renderElement(); }); }, - set_weight: function(weight){ - for(var i = 0; i < this.product_list.length; i++){ - this.product_list[i].set_weight(weight); - } - }, renderElement: function() { var self = this; this._super(); @@ -656,6 +687,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa this.leftpane_visible = true; this.leftpane_width = '440px'; this.cashier_controls_visible = true; + this.image_cache = new module.ImageCache(); // for faster products image display /* //Epileptic mode diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index 1800170cb1d..fb3d4afd963 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -357,7 +357,7 @@
  • - +