1 function openerp_pos_db(instance, module){
4 /* The PosDB holds reference to data that is either
5 * - static: does not change between pos reloads
6 * - persistent : must stay between reloads ( orders )
9 module.PosDB = instance.web.Class.extend({
10 name: 'openerp_pos_db', //the prefix of the localstorage data
11 limit: 100, // the maximum number of results returned by a search
12 init: function(options){
13 options = options || {};
14 this.name = options.name || this.name;
15 this.limit = options.limit || this.limit;
18 this.name = this.name + '_' + options.uuid;
21 //cache the data in memory to avoid roundtrips to the localstorage
24 this.product_by_id = {};
25 this.product_by_barcode = {};
26 this.product_by_category_id = {};
28 this.partner_sorted = [];
29 this.partner_by_id = {};
30 this.partner_by_barcode = {};
31 this.partner_search_string = "";
32 this.partner_write_date = null;
34 this.category_by_id = {};
35 this.root_category_id = 0;
36 this.category_products = {};
37 this.category_ancestors = {};
38 this.category_childs = {};
39 this.category_parent = {};
40 this.category_search_string = {};
41 this.packagings_by_id = {};
42 this.packagings_by_product_tmpl_id = {};
43 this.packagings_by_barcode = {};
47 * sets an uuid to prevent conflict in locally stored data between multiple databases running
48 * in the same browser at the same origin (Doing this is not advised !)
50 set_uuid: function(uuid){
51 this.name = this.name + '_' + uuid;
54 /* returns the category object from its id. If you pass a list of id as parameters, you get
55 * a list of category objects.
57 get_category_by_id: function(categ_id){
58 if(categ_id instanceof Array){
60 for(var i = 0, len = categ_id.length; i < len; i++){
61 var cat = this.category_by_id[categ_id[i]];
65 console.error("get_category_by_id: no category has id:",categ_id[i]);
70 return this.category_by_id[categ_id];
73 /* returns a list of the category's child categories ids, or an empty list
74 * if a category has no childs */
75 get_category_childs_ids: function(categ_id){
76 return this.category_childs[categ_id] || [];
78 /* returns a list of all ancestors (parent, grand-parent, etc) categories ids
79 * starting from the root category to the direct parent */
80 get_category_ancestors_ids: function(categ_id){
81 return this.category_ancestors[categ_id] || [];
83 /* returns the parent category's id of a category, or the root_category_id if no parent.
84 * the root category is parent of itself. */
85 get_category_parent_id: function(categ_id){
86 return this.category_parent[categ_id] || this.root_category_id;
88 /* adds categories definitions to the database. categories is a list of categories objects as
89 * returned by the openerp server. Categories must be inserted before the products or the
90 * product/ categories association may (will) not work properly */
91 add_categories: function(categories){
93 if(!this.category_by_id[this.root_category_id]){
94 this.category_by_id[this.root_category_id] = {
95 id : this.root_category_id,
99 for(var i=0, len = categories.length; i < len; i++){
100 this.category_by_id[categories[i].id] = categories[i];
102 for(var i=0, len = categories.length; i < len; i++){
103 var cat = categories[i];
104 var parent_id = cat.parent_id[0] || this.root_category_id;
105 this.category_parent[cat.id] = cat.parent_id[0];
106 if(!this.category_childs[parent_id]){
107 this.category_childs[parent_id] = [];
109 this.category_childs[parent_id].push(cat.id);
111 function make_ancestors(cat_id, ancestors){
112 self.category_ancestors[cat_id] = ancestors;
114 ancestors = ancestors.slice(0);
115 ancestors.push(cat_id);
117 var childs = self.category_childs[cat_id] || [];
118 for(var i=0, len = childs.length; i < len; i++){
119 make_ancestors(childs[i], ancestors);
122 make_ancestors(this.root_category_id, []);
124 category_contains: function(categ_id, product_id) {
125 var product = this.product_by_id[product_id];
127 var cid = product.pos_categ_id[0];
128 while (cid && cid !== categ_id){
129 cid = this.category_parent[cid];
135 /* loads a record store from the database. returns default if nothing is found */
136 load: function(store,deft){
137 if(this.cache[store] !== undefined){
138 return this.cache[store];
140 var data = localStorage[this.name + '_' + store];
141 if(data !== undefined && data !== ""){
142 data = JSON.parse(data);
143 this.cache[store] = data;
149 /* saves a record store to the database */
150 save: function(store,data){
151 var str_data = JSON.stringify(data);
152 localStorage[this.name + '_' + store] = JSON.stringify(data);
153 this.cache[store] = data;
155 _product_search_string: function(product){
156 var str = '' + product.id + ':' + product.display_name;
157 if (product.barcode) {
158 str += '|' + product.barcode;
160 if (product.default_code) {
161 str += '|' + product.default_code.replace(':','');
163 if (product.description) {
164 str += '|' + product.description.replace(':','');
166 if (product.description_sale) {
167 str += '|' + product.description_sale.replace(':','');
169 var packagings = this.packagings_by_product_tmpl_id[product.product_tmpl_id] || [];
170 for (var i = 0; i < packagings.length; i++) {
171 str += '|' + packagings[i].barcode;
175 add_products: function(products){
176 var stored_categories = this.product_by_category_id;
178 if(!products instanceof Array){
179 products = [products];
181 for(var i = 0, len = products.length; i < len; i++){
182 var product = products[i];
183 var search_string = this._product_search_string(product);
184 var categ_id = product.pos_categ_id ? product.pos_categ_id[0] : this.root_category_id;
185 product.product_tmpl_id = product.product_tmpl_id[0];
186 if(!stored_categories[categ_id]){
187 stored_categories[categ_id] = [];
189 stored_categories[categ_id].push(product.id);
191 if(this.category_search_string[categ_id] === undefined){
192 this.category_search_string[categ_id] = '';
194 this.category_search_string[categ_id] += search_string;
196 var ancestors = this.get_category_ancestors_ids(categ_id) || [];
198 for(var j = 0, jlen = ancestors.length; j < jlen; j++){
199 var ancestor = ancestors[j];
200 if(! stored_categories[ancestor]){
201 stored_categories[ancestor] = [];
203 stored_categories[ancestor].push(product.id);
205 if( this.category_search_string[ancestor] === undefined){
206 this.category_search_string[ancestor] = '';
208 this.category_search_string[ancestor] += search_string;
210 this.product_by_id[product.id] = product;
212 this.product_by_barcode[product.barcode] = product;
216 add_packagings: function(packagings){
217 for(var i = 0, len = packagings.length; i < len; i++){
218 var pack = packagings[i];
219 this.packagings_by_id[pack.id] = pack;
220 if(!this.packagings_by_product_tmpl_id[pack.product_tmpl_id[0]]){
221 this.packagings_by_product_tmpl_id[pack.product_tmpl_id[0]] = [];
223 this.packagings_by_product_tmpl_id[pack.product_tmpl_id[0]].push(pack);
225 this.packagings_by_barcode[pack.barcode] = pack;
229 _partner_search_string: function(partner){
230 var str = '' + partner.id + ':' + partner.name;
232 str += '|' + partner.barcode;
235 str += '|' + partner.address;
238 str += '|' + partner.phone.split(' ').join('');
241 str += '|' + partner.mobile.split(' ').join('');
244 str += '|' + partner.email;
248 add_partners: function(partners){
249 var updated_count = 0;
250 var new_write_date = '';
251 for(var i = 0, len = partners.length; i < len; i++){
252 var partner = partners[i];
254 if ( this.partner_write_date &&
255 this.partner_by_id[partner.id] &&
256 new Date(this.partner_write_date).getTime() + 1000 >=
257 new Date(partner.write_date).getTime() ) {
258 // FIXME: The write_date is stored with milisec precision in the database
259 // but the dates we get back are only precise to the second. This means when
260 // you read partners modified strictly after time X, you get back partners that were
261 // modified X - 1 sec ago.
263 } else if ( new_write_date < partner.write_date ) {
264 new_write_date = partner.write_date;
266 if (!this.partner_by_id[partner.id]) {
267 this.partner_sorted.push(partner.id);
269 this.partner_by_id[partner.id] = partner;
274 this.partner_write_date = new_write_date || this.partner_write_date;
277 // If there were updates, we need to completely
278 // rebuild the search string and the barcode indexing
280 this.partner_search_string = "";
281 this.partner_by_barcode = {};
283 for (var id in this.partner_by_id) {
284 var partner = this.partner_by_id[id];
287 this.partner_by_barcode[partner.barcode] = partner;
289 partner.address = (partner.street || '') +', '+
290 (partner.zip || '') +' '+
291 (partner.city || '') +', '+
292 (partner.country_id[1] || '');
293 this.partner_search_string += this._partner_search_string(partner);
296 return updated_count;
298 get_partner_write_date: function(){
299 return this.partner_write_date;
301 get_partner_by_id: function(id){
302 return this.partner_by_id[id];
304 get_partner_by_barcode: function(barcode){
305 return this.partner_by_barcode[barcode];
307 get_partners_sorted: function(max_count){
308 max_count = max_count ? Math.min(this.partner_sorted.length, max_count) : this.partner_sorted.length;
310 for (var i = 0; i < max_count; i++) {
311 partners.push(this.partner_by_id[this.partner_sorted[i]]);
315 search_partner: function(query){
317 query = query.replace(/[\[\]\(\)\+\*\?\.\-\!\&\^\$\|\~\_\{\}\:\,\\\/]/g,'.');
318 query = query.replace(' ','.+');
319 var re = RegExp("([0-9]+):.*?"+query,"gi");
324 for(var i = 0; i < this.limit; i++){
325 var r = re.exec(this.partner_search_string);
327 var id = Number(r[1]);
328 results.push(this.get_partner_by_id(id));
335 /* removes all the data from the database. TODO : being able to selectively remove data */
336 clear: function(stores){
337 for(var i = 0, len = arguments.length; i < len; i++){
338 localStorage.removeItem(this.name + '_' + arguments[i]);
341 /* this internal methods returns the count of properties in an object. */
342 _count_props : function(obj){
344 for(var prop in obj){
345 if(obj.hasOwnProperty(prop)){
351 get_product_by_id: function(id){
352 return this.product_by_id[id];
354 get_product_by_barcode: function(barcode){
355 if(this.product_by_barcode[barcode]){
356 return this.product_by_barcode[barcode];
358 var pack = this.packagings_by_barcode[barcode];
360 return this.product_by_id[pack.product_tmpl_id[0]];
364 get_product_by_category: function(category_id){
365 var product_ids = this.product_by_category_id[category_id];
368 for (var i = 0, len = Math.min(product_ids.length, this.limit); i < len; i++) {
369 list.push(this.product_by_id[product_ids[i]]);
374 /* returns a list of products with :
375 * - a category that is or is a child of category_id,
376 * - a name, package or barcode containing the query (case insensitive)
378 search_product_in_category: function(category_id, query){
380 query = query.replace(/[\[\]\(\)\+\*\?\.\-\!\&\^\$\|\~\_\{\}\:\,\\\/]/g,'.');
381 query = query.replace(' ','.+');
382 var re = RegExp("([0-9]+):.*?"+query,"gi");
387 for(var i = 0; i < this.limit; i++){
388 var r = re.exec(this.category_search_string[category_id]);
390 var id = Number(r[1]);
391 results.push(this.get_product_by_id(id));
400 add_order: function(order){
401 var order_id = order.uid;
402 var orders = this.load('orders',[]);
404 // if the order was already stored, we overwrite its data
405 for(var i = 0, len = orders.length; i < len; i++){
406 if(orders[i].id === order_id){
407 orders[i].data = order;
408 this.save('orders',orders);
413 orders.push({id: order_id, data: order});
414 this.save('orders',orders);
417 remove_order: function(order_id){
418 var orders = this.load('orders',[]);
419 orders = _.filter(orders, function(order){
420 return order.id !== order_id;
422 this.save('orders',orders);
424 remove_all_orders: function(){
425 this.save('orders',[]);
427 get_orders: function(){
428 return this.load('orders',[]);
430 get_order: function(order_id){
431 var orders = this.get_orders();
432 for(var i = 0, len = orders.length; i < len; i++){
433 if(orders[i].id === order_id){
441 save_unpaid_order: function(order){
442 var order_id = order.uid;
443 var orders = this.load('unpaid_orders',[]);
444 var serialized = order.export_as_JSON();
446 for (var i = 0; i < orders.length; i++) {
447 if (orders[i].id === order_id){
448 orders[i].data = serialized;
449 this.save('unpaid_orders',orders);
454 orders.push({id: order_id, data: serialized});
455 this.save('unpaid_orders',orders);
458 remove_unpaid_order: function(order){
459 var orders = this.load('unpaid_orders',[]);
460 orders = _.filter(orders, function(o){
461 return o.id !== order.uid;
463 this.save('unpaid_orders',orders);
465 remove_all_unpaid_orders: function(){
466 this.save('unpaid_orders',[]);
468 get_unpaid_orders: function(){
469 var saved = this.load('unpaid_orders',[]);
471 for (var i = 0; i < saved.length; i++) {
472 orders.push(saved[i].data);