summaryrefslogtreecommitdiff
path: root/addons/point_of_sale/static/src/js/db.js
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/point_of_sale/static/src/js/db.js
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/point_of_sale/static/src/js/db.js')
-rw-r--r--addons/point_of_sale/static/src/js/db.js556
1 files changed, 556 insertions, 0 deletions
diff --git a/addons/point_of_sale/static/src/js/db.js b/addons/point_of_sale/static/src/js/db.js
new file mode 100644
index 00000000..ca0afd28
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/db.js
@@ -0,0 +1,556 @@
+odoo.define('point_of_sale.DB', function (require) {
+"use strict";
+
+var core = require('web.core');
+var utils = require('web.utils');
+/* The PosDB holds reference to data that is either
+ * - static: does not change between pos reloads
+ * - persistent : must stay between reloads ( orders )
+ */
+
+var PosDB = core.Class.extend({
+ name: 'openerp_pos_db', //the prefix of the localstorage data
+ limit: 100, // the maximum number of results returned by a search
+ init: function(options){
+ options = options || {};
+ this.name = options.name || this.name;
+ this.limit = options.limit || this.limit;
+
+ if (options.uuid) {
+ this.name = this.name + '_' + options.uuid;
+ }
+
+ //cache the data in memory to avoid roundtrips to the localstorage
+ this.cache = {};
+
+ this.product_by_id = {};
+ this.product_by_barcode = {};
+ this.product_by_category_id = {};
+
+ this.partner_sorted = [];
+ this.partner_by_id = {};
+ this.partner_by_barcode = {};
+ this.partner_search_string = "";
+ this.partner_write_date = null;
+
+ this.category_by_id = {};
+ this.root_category_id = 0;
+ this.category_products = {};
+ this.category_ancestors = {};
+ this.category_childs = {};
+ this.category_parent = {};
+ this.category_search_string = {};
+ },
+
+ /**
+ * sets an uuid to prevent conflict in locally stored data between multiple PoS Configs. By
+ * using the uuid of the config the local storage from other configs will not get effected nor
+ * loaded in sessions that don't belong to them.
+ *
+ * @param {string} uuid Unique identifier of the PoS Config linked to the current session.
+ */
+ set_uuid: function(uuid){
+ this.name = this.name + '_' + uuid;
+ },
+
+ /* 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 = [];
+ for(var i = 0, len = categ_id.length; i < len; i++){
+ var cat = this.category_by_id[categ_id[i]];
+ if(cat){
+ list.push(cat);
+ }else{
+ console.error("get_category_by_id: no category has id:",categ_id[i]);
+ }
+ }
+ return list;
+ }else{
+ return this.category_by_id[categ_id];
+ }
+ },
+ /* returns a list of the category's child categories ids, or an empty list
+ * if a category has no childs */
+ get_category_childs_ids: function(categ_id){
+ return this.category_childs[categ_id] || [];
+ },
+ /* returns a list of all ancestors (parent, grand-parent, etc) categories ids
+ * starting from the root category to the direct parent */
+ get_category_ancestors_ids: function(categ_id){
+ return this.category_ancestors[categ_id] || [];
+ },
+ /* returns the parent category's id of a category, or the root_category_id if no parent.
+ * the root category is parent of itself. */
+ get_category_parent_id: function(categ_id){
+ return this.category_parent[categ_id] || this.root_category_id;
+ },
+ /* adds categories definitions to the database. categories is a list of categories objects as
+ * returned by the openerp server. Categories must be inserted before the products or the
+ * product/ categories association may (will) not work properly */
+ add_categories: function(categories){
+ var self = this;
+ if(!this.category_by_id[this.root_category_id]){
+ this.category_by_id[this.root_category_id] = {
+ id : this.root_category_id,
+ name : 'Root',
+ };
+ }
+ categories.forEach(function(cat){
+ self.category_by_id[cat.id] = cat;
+ });
+ categories.forEach(function(cat){
+ var parent_id = cat.parent_id[0];
+ if(!(parent_id && self.category_by_id[parent_id])){
+ parent_id = self.root_category_id;
+ }
+ self.category_parent[cat.id] = parent_id;
+ if(!self.category_childs[parent_id]){
+ self.category_childs[parent_id] = [];
+ }
+ self.category_childs[parent_id].push(cat.id);
+ });
+ function make_ancestors(cat_id, ancestors){
+ self.category_ancestors[cat_id] = ancestors;
+
+ ancestors = ancestors.slice(0);
+ ancestors.push(cat_id);
+
+ var childs = self.category_childs[cat_id] || [];
+ for(var i=0, len = childs.length; i < len; i++){
+ make_ancestors(childs[i], ancestors);
+ }
+ }
+ make_ancestors(this.root_category_id, []);
+ },
+ category_contains: function(categ_id, product_id) {
+ var product = this.product_by_id[product_id];
+ if (product) {
+ var cid = product.pos_categ_id[0];
+ while (cid && cid !== categ_id){
+ cid = this.category_parent[cid];
+ }
+ return !!cid;
+ }
+ return false;
+ },
+ /* loads a record store from the database. returns default if nothing is found */
+ load: function(store,deft){
+ if(this.cache[store] !== undefined){
+ return this.cache[store];
+ }
+ var data = localStorage[this.name + '_' + store];
+ if(data !== undefined && data !== ""){
+ data = JSON.parse(data);
+ this.cache[store] = data;
+ return data;
+ }else{
+ return deft;
+ }
+ },
+ /* saves a record store to the database */
+ save: function(store,data){
+ localStorage[this.name + '_' + store] = JSON.stringify(data);
+ this.cache[store] = data;
+ },
+ _product_search_string: function(product){
+ var str = product.display_name;
+ if (product.barcode) {
+ str += '|' + product.barcode;
+ }
+ if (product.default_code) {
+ str += '|' + product.default_code;
+ }
+ if (product.description) {
+ str += '|' + product.description;
+ }
+ if (product.description_sale) {
+ str += '|' + product.description_sale;
+ }
+ str = product.id + ':' + str.replace(/:/g,'') + '\n';
+ return str;
+ },
+ add_products: function(products){
+ var stored_categories = this.product_by_category_id;
+
+ if(!products instanceof Array){
+ products = [products];
+ }
+ for(var i = 0, len = products.length; i < len; i++){
+ var product = products[i];
+ if (product.id in this.product_by_id) continue;
+ if (product.available_in_pos){
+ var search_string = utils.unaccent(this._product_search_string(product));
+ var categ_id = product.pos_categ_id ? product.pos_categ_id[0] : this.root_category_id;
+ product.product_tmpl_id = product.product_tmpl_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, jlen = ancestors.length; j < jlen; j++){
+ var ancestor = ancestors[j];
+ if(! stored_categories[ancestor]){
+ stored_categories[ancestor] = [];
+ }
+ 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;
+ }
+ }
+ this.product_by_id[product.id] = product;
+ if(product.barcode){
+ this.product_by_barcode[product.barcode] = product;
+ }
+ }
+ },
+ _partner_search_string: function(partner){
+ var str = partner.name || '';
+ if(partner.barcode){
+ str += '|' + partner.barcode;
+ }
+ if(partner.address){
+ str += '|' + partner.address;
+ }
+ if(partner.phone){
+ str += '|' + partner.phone.split(' ').join('');
+ }
+ if(partner.mobile){
+ str += '|' + partner.mobile.split(' ').join('');
+ }
+ if(partner.email){
+ str += '|' + partner.email;
+ }
+ if(partner.vat){
+ str += '|' + partner.vat;
+ }
+ str = '' + partner.id + ':' + str.replace(':', '').replace(/\n/g, ' ') + '\n';
+ return str;
+ },
+ add_partners: function(partners){
+ var updated_count = 0;
+ var new_write_date = '';
+ var partner;
+ for(var i = 0, len = partners.length; i < len; i++){
+ partner = partners[i];
+
+ var local_partner_date = (this.partner_write_date || '').replace(/^(\d{4}-\d{2}-\d{2}) ((\d{2}:?){3})$/, '$1T$2Z');
+ var dist_partner_date = (partner.write_date || '').replace(/^(\d{4}-\d{2}-\d{2}) ((\d{2}:?){3})$/, '$1T$2Z');
+ if ( this.partner_write_date &&
+ this.partner_by_id[partner.id] &&
+ new Date(local_partner_date).getTime() + 1000 >=
+ new Date(dist_partner_date).getTime() ) {
+ // FIXME: The write_date is stored with milisec precision in the database
+ // but the dates we get back are only precise to the second. This means when
+ // you read partners modified strictly after time X, you get back partners that were
+ // modified X - 1 sec ago.
+ continue;
+ } else if ( new_write_date < partner.write_date ) {
+ new_write_date = partner.write_date;
+ }
+ if (!this.partner_by_id[partner.id]) {
+ this.partner_sorted.push(partner.id);
+ }
+ this.partner_by_id[partner.id] = partner;
+
+ updated_count += 1;
+ }
+
+ this.partner_write_date = new_write_date || this.partner_write_date;
+
+ if (updated_count) {
+ // If there were updates, we need to completely
+ // rebuild the search string and the barcode indexing
+
+ this.partner_search_string = "";
+ this.partner_by_barcode = {};
+
+ for (var id in this.partner_by_id) {
+ partner = this.partner_by_id[id];
+
+ if(partner.barcode){
+ this.partner_by_barcode[partner.barcode] = partner;
+ }
+ partner.address = (partner.street ? partner.street + ', ': '') +
+ (partner.zip ? partner.zip + ', ': '') +
+ (partner.city ? partner.city + ', ': '') +
+ (partner.state_id ? partner.state_id[1] + ', ': '') +
+ (partner.country_id ? partner.country_id[1]: '');
+ this.partner_search_string += this._partner_search_string(partner);
+ }
+
+ this.partner_search_string = utils.unaccent(this.partner_search_string);
+ }
+ return updated_count;
+ },
+ get_partner_write_date: function(){
+ return this.partner_write_date || "1970-01-01 00:00:00";
+ },
+ get_partner_by_id: function(id){
+ return this.partner_by_id[id];
+ },
+ get_partner_by_barcode: function(barcode){
+ return this.partner_by_barcode[barcode];
+ },
+ get_partners_sorted: function(max_count){
+ max_count = max_count ? Math.min(this.partner_sorted.length, max_count) : this.partner_sorted.length;
+ var partners = [];
+ for (var i = 0; i < max_count; i++) {
+ partners.push(this.partner_by_id[this.partner_sorted[i]]);
+ }
+ return partners;
+ },
+ search_partner: function(query){
+ try {
+ query = query.replace(/[\[\]\(\)\+\*\?\.\-\!\&\^\$\|\~\_\{\}\:\,\\\/]/g,'.');
+ query = query.replace(/ /g,'.+');
+ var re = RegExp("([0-9]+):.*?"+utils.unaccent(query),"gi");
+ }catch(e){
+ return [];
+ }
+ var results = [];
+ for(var i = 0; i < this.limit; i++){
+ var r = re.exec(this.partner_search_string);
+ if(r){
+ var id = Number(r[1]);
+ results.push(this.get_partner_by_id(id));
+ }else{
+ break;
+ }
+ }
+ return results;
+ },
+ /* removes all the data from the database. TODO : being able to selectively remove data */
+ clear: function(){
+ for(var i = 0, len = arguments.length; i < len; i++){
+ localStorage.removeItem(this.name + '_' + arguments[i]);
+ }
+ },
+ /* this internal methods returns the count of properties in an object. */
+ _count_props : function(obj){
+ var count = 0;
+ for(var prop in obj){
+ if(obj.hasOwnProperty(prop)){
+ count++;
+ }
+ }
+ return count;
+ },
+ get_product_by_id: function(id){
+ return this.product_by_id[id];
+ },
+ get_product_by_barcode: function(barcode){
+ if(this.product_by_barcode[barcode]){
+ return this.product_by_barcode[barcode];
+ } else {
+ return undefined;
+ }
+ },
+ get_product_by_category: function(category_id){
+ var product_ids = this.product_by_category_id[category_id];
+ var list = [];
+ if (product_ids) {
+ for (var i = 0, len = Math.min(product_ids.length, this.limit); i < len; i++) {
+ list.push(this.product_by_id[product_ids[i]]);
+ }
+ }
+ return list;
+ },
+ /* returns a list of products with :
+ * - a category that is or is a child of category_id,
+ * - a name, package or barcode containing the query (case insensitive)
+ */
+ search_product_in_category: function(category_id, query){
+ try {
+ query = query.replace(/[\[\]\(\)\+\*\?\.\-\!\&\^\$\|\~\_\{\}\:\,\\\/]/g,'.');
+ query = query.replace(/ /g,'.+');
+ var re = RegExp("([0-9]+):.*?"+utils.unaccent(query),"gi");
+ }catch(e){
+ return [];
+ }
+ var results = [];
+ for(var i = 0; i < this.limit; i++){
+ var 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 results;
+ },
+ /* from a product id, and a list of category ids, returns
+ * true if the product belongs to one of the provided category
+ * or one of its child categories.
+ */
+ is_product_in_category: function(category_ids, product_id) {
+ if (!(category_ids instanceof Array)) {
+ category_ids = [category_ids];
+ }
+ var cat = this.get_product_by_id(product_id).pos_categ_id[0];
+ while (cat) {
+ for (var i = 0; i < category_ids.length; i++) {
+ if (cat == category_ids[i]) { // The == is important, ids may be strings
+ return true;
+ }
+ }
+ cat = this.get_category_parent_id(cat);
+ }
+ return false;
+ },
+
+ /* paid orders */
+ add_order: function(order){
+ var order_id = order.uid;
+ var orders = this.load('orders',[]);
+
+ // if the order was already stored, we overwrite its data
+ for(var i = 0, len = orders.length; i < len; i++){
+ if(orders[i].id === order_id){
+ orders[i].data = order;
+ this.save('orders',orders);
+ return order_id;
+ }
+ }
+
+ // Only necessary when we store a new, validated order. Orders
+ // that where already stored should already have been removed.
+ this.remove_unpaid_order(order);
+
+ orders.push({id: order_id, data: order});
+ this.save('orders',orders);
+ return order_id;
+ },
+ remove_order: function(order_id){
+ var orders = this.load('orders',[]);
+ orders = _.filter(orders, function(order){
+ return order.id !== order_id;
+ });
+ this.save('orders',orders);
+ },
+ remove_all_orders: function(){
+ this.save('orders',[]);
+ },
+ get_orders: function(){
+ return this.load('orders',[]);
+ },
+ get_order: function(order_id){
+ var orders = this.get_orders();
+ for(var i = 0, len = orders.length; i < len; i++){
+ if(orders[i].id === order_id){
+ return orders[i];
+ }
+ }
+ return undefined;
+ },
+
+ /* working orders */
+ save_unpaid_order: function(order){
+ var order_id = order.uid;
+ var orders = this.load('unpaid_orders',[]);
+ var serialized = order.export_as_JSON();
+
+ for (var i = 0; i < orders.length; i++) {
+ if (orders[i].id === order_id){
+ orders[i].data = serialized;
+ this.save('unpaid_orders',orders);
+ return order_id;
+ }
+ }
+
+ orders.push({id: order_id, data: serialized});
+ this.save('unpaid_orders',orders);
+ return order_id;
+ },
+ remove_unpaid_order: function(order){
+ var orders = this.load('unpaid_orders',[]);
+ orders = _.filter(orders, function(o){
+ return o.id !== order.uid;
+ });
+ this.save('unpaid_orders',orders);
+ },
+ remove_all_unpaid_orders: function(){
+ this.save('unpaid_orders',[]);
+ },
+ get_unpaid_orders: function(){
+ var saved = this.load('unpaid_orders',[]);
+ var orders = [];
+ for (var i = 0; i < saved.length; i++) {
+ orders.push(saved[i].data);
+ }
+ return orders;
+ },
+ /**
+ * Return the orders with requested ids if they are unpaid.
+ * @param {array<number>} ids order_ids.
+ * @return {array<object>} list of orders.
+ */
+ get_unpaid_orders_to_sync: function(ids){
+ var saved = this.load('unpaid_orders',[]);
+ var orders = [];
+ saved.forEach(function(o) {
+ if (ids.includes(o.id) && (o.data.server_id || o.data.lines.length)){
+ orders.push(o);
+ }
+ });
+ return orders;
+ },
+ /**
+ * Add a given order to the orders to be removed from the server.
+ *
+ * If an order is removed from a table it also has to be removed from the server to prevent it from reapearing
+ * after syncing. This function will add the server_id of the order to a list of orders still to be removed.
+ * @param {object} order object.
+ */
+ set_order_to_remove_from_server: function(order){
+ if (order.server_id !== undefined) {
+ var to_remove = this.load('unpaid_orders_to_remove',[]);
+ to_remove.push(order.server_id);
+ this.save('unpaid_orders_to_remove', to_remove);
+ }
+ },
+ /**
+ * Get a list of server_ids of orders to be removed.
+ * @return {array<number>} list of server_ids.
+ */
+ get_ids_to_remove_from_server: function(){
+ return this.load('unpaid_orders_to_remove',[]);
+ },
+ /**
+ * Remove server_ids from the list of orders to be removed.
+ * @param {array<number>} ids
+ */
+ set_ids_removed_from_server: function(ids){
+ var to_remove = this.load('unpaid_orders_to_remove',[]);
+
+ to_remove = _.filter(to_remove, function(id){
+ return !ids.includes(id);
+ });
+ this.save('unpaid_orders_to_remove', to_remove);
+ },
+ set_cashier: function(cashier) {
+ // Always update if the user is the same as before
+ this.save('cashier', cashier || null);
+ },
+ get_cashier: function() {
+ return this.load('cashier');
+ }
+});
+
+return PosDB;
+
+});
+