From d72d280a04d82e14edb3d5420ea658f71a3c640c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Wed, 22 Apr 2015 14:32:43 +0200 Subject: [PATCH] [IMP] point_of_sale: import / export of the point of sale offline state into / from text files. --- addons/point_of_sale/static/src/css/pos.css | 21 ++- addons/point_of_sale/static/src/js/chrome.js | 36 ++++-- addons/point_of_sale/static/src/js/gui.js | 24 ++++ addons/point_of_sale/static/src/js/models.js | 129 +++++++++++++++++++ addons/point_of_sale/static/src/js/popups.js | 11 +- addons/point_of_sale/static/src/xml/pos.xml | 100 +++++++------- 6 files changed, 250 insertions(+), 71 deletions(-) diff --git a/addons/point_of_sale/static/src/css/pos.css b/addons/point_of_sale/static/src/css/pos.css index 4daab2ffad9..9926beb09ba 100644 --- a/addons/point_of_sale/static/src/css/pos.css +++ b/addons/point_of_sale/static/src/css/pos.css @@ -819,7 +819,7 @@ td { background: url("../img/search.png") no-repeat 15px; background-color: white; border-radius: 20px; - font-family: Lato; + font-family: "Lato","Lucida Grande", Helvetica, Verdana, Arial; font-size: 13px; } .pos .searchbox input:focus { @@ -2062,6 +2062,8 @@ td { border-radius: 3px; box-shadow: 0px 10px 20px rgba(0,0,0,0.4); z-index:1200; + font-family: 'Lato'; + font-family: Lato; } .pos .popup .body { font-weight: normal; @@ -2240,6 +2242,23 @@ td { display: inline-block; } +.pos .popup .body ul, +.pos .popup ul.body { + text-align: left; + margin-left: 1em; +} +.pos .popup .body li { + text-indent: 1em; +} +.pos .popup .body li:before { + content: '—'; + position: relative; + font-size: 0.6em; + left: -1em; + bottom: 0.2em; +} + + /* ********* The Webkit Scrollbar ********* */ .pos *::-webkit-scrollbar{ diff --git a/addons/point_of_sale/static/src/js/chrome.js b/addons/point_of_sale/static/src/js/chrome.js index 0be0953fbd6..76f86879812 100644 --- a/addons/point_of_sale/static/src/js/chrome.js +++ b/addons/point_of_sale/static/src/js/chrome.js @@ -254,22 +254,16 @@ var DebugWidget = PosBaseWidget.extend({ this.$('.button.barcode').click(function(){ self.pos.barcode_reader.scan(self.$('input.ean').val()); }); - this.$('.button.show_orders').click(function(){ - self.gui.show_popup('unsent-orders'); - }); this.$('.button.delete_orders').click(function(){ self.gui.show_popup('confirm',{ - 'title': _t('Delete Unsent Orders ?'), - 'body': _t('This operation will permanently destroy all unsent orders from the local storage. You will lose all the data. This operation cannot be undone.'), + 'title': _t('Delete Paid Orders ?'), + 'body': _t('This operation will permanently destroy all paid orders from the local storage. You will lose all the data. This operation cannot be undone.'), confirm: function(){ self.pos.db.remove_all_orders(); self.pos.set({synch: { state:'connected', pending: 0 }}); }, }); }); - this.$('.button.show_unpaid_orders').click(function(){ - self.gui.show_popup('unpaid-orders'); - }); this.$('.button.delete_unpaid_orders').click(function(){ self.gui.show_popup('confirm',{ 'title': _t('Delete Unpaid Orders ?'), @@ -280,6 +274,32 @@ var DebugWidget = PosBaseWidget.extend({ }, }); }); + + this.$('.button.export_unpaid_orders').click(function(){ + self.gui.download_file(self.pos.export_unpaid_orders(), + "unpaid_orders_" + (new Date()).toUTCString().replace(/\ /g,'_') + '.json'); + }); + + this.$('.button.export_paid_orders').click(function() { + self.gui.download_file(self.pos.export_paid_orders(), + "paid_orders_" + (new Date()).toUTCString().replace(/\ /g,'_') + '.json'); + }); + + this.$('.button.import_orders input').on('change', function(event) { + var file = event.target.files[0]; + + if (file) { + var reader = new FileReader(); + + reader.onload = function(event) { + var report = self.pos.import_orders(event.target.result); + self.gui.show_popup('orderimport',{report:report}); + }; + + reader.readAsText(file); + } + }); + _.each(this.events, function(name){ self.pos.proxy.add_notification(name,function(){ self.$('.event.'+name).stop().clearQueue().css({'background-color':'#6CD11D'}); diff --git a/addons/point_of_sale/static/src/js/gui.js b/addons/point_of_sale/static/src/js/gui.js index c37ed951e5c..48021560642 100644 --- a/addons/point_of_sale/static/src/js/gui.js +++ b/addons/point_of_sale/static/src/js/gui.js @@ -299,6 +299,30 @@ var Gui = core.Class.extend({ $('body').append(''); }, + /* ---- Gui: FILE I/O ---- */ + + // This will make the browser download 'contents' as a + // file named 'name' + // if 'contents' is not a string, it is converted into + // a JSON representation of the contents. + download_file: function(contents, name) { + var URL = window.URL || window.webkitURL; + + if (typeof contents !== 'string') { + contents = JSON.stringify(contents,null,2); + } + + var blob = new Blob([contents]); + + var evt = document.createEvent("HTMLEvents"); + evt.initEvent("click"); + + $("",{ + download: name || 'document.txt', + href: URL.createObjectURL(blob), + }).get(0).dispatchEvent(evt); + }, + /* ---- Gui: KEYBOARD INPUT ---- */ // This is a helper to handle numpad keyboard input. diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index a7a9d0cf9f6..ad2edf147ee 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -140,6 +140,14 @@ exports.PosModel = Backbone.Model.extend({ // used during loading such as object ids, etc. models: [ { + label: 'version', + loaded: function(self){ + return session.rpc('/web/webclient/version_info',{}).done(function(version) { + self.version = version; + }); + }, + + },{ model: 'res.users', fields: ['name','company_id'], ids: function(self){ return [session.uid]; }, @@ -773,6 +781,127 @@ exports.PosModel = Backbone.Model.extend({ } return true; }, + + // Exports the paid orders (the ones waiting for internet connection) + export_paid_orders: function() { + return JSON.stringify({ + 'paid_orders': this.db.get_orders(), + 'session': this.pos_session.name, + 'session_id': this.pos_session.id, + 'date': (new Date()).toUTCString(), + 'version': this.version.server_version_info, + },null,2); + }, + + // Exports the unpaid orders (the tabs) + export_unpaid_orders: function() { + return JSON.stringify({ + 'unpaid_orders': this.db.get_unpaid_orders(), + 'session': this.pos_session.name, + 'session_id': this.pos_session.id, + 'date': (new Date()).toUTCString(), + 'version': this.version.server_version_info, + },null,2); + }, + + // This imports paid or unpaid orders from a json file whose + // contents are provided as the string str. + // It returns a report of what could and what could not be + // imported. + import_orders: function(str) { + var json = JSON.parse(str); + var report = { + // Number of paid orders that were imported + paid: 0, + // Number of unpaid orders that were imported + unpaid: 0, + // Orders that were not imported because they already exist (uid conflict) + unpaid_skipped_existing: 0, + // Orders that were not imported because they belong to another session + unpaid_skipped_session: 0, + // The list of session ids to which skipped orders belong. + unpaid_skipped_sessions: [], + }; + + if (json.paid_orders) { + for (var i = 0; i < json.paid_orders.length; i++) { + this.db.add_order(json.paid_orders[i].data); + } + report.paid = json.paid_orders.length; + this.push_order(); + } + + if (json.unpaid_orders) { + + var orders = []; + var existing = this.get_order_list(); + var existing_uids = {}; + var skipped_sessions = {}; + + for (var i = 0; i < existing.length; i++) { + existing_uids[existing[i].uid] = true; + } + + for (var i = 0; i < json.unpaid_orders.length; i++) { + var order = json.unpaid_orders[i]; + if (order.pos_session_id !== this.pos_session.id) { + report.unpaid_skipped_session += 1; + skipped_sessions[order.pos_session_id] = true; + } else if (existing_uids[order.uid]) { + report.unpaid_skipped_existing += 1; + } else { + orders.push(new exports.Order({},{ + pos: this, + json: order, + })); + } + } + + orders = orders.sort(function(a,b){ + return a.sequence_number - b.sequence_number; + }); + + if (orders.length) { + report.unpaid = orders.length; + this.get('orders').add(orders); + } + + report.unpaid_skipped_sessions = _.keys(skipped_sessions); + } + + return report; + }, + + _load_orders: function(){ + var jsons = this.db.get_unpaid_orders(); + var orders = []; + var not_loaded_count = 0; + + for (var i = 0; i < jsons.length; i++) { + var json = jsons[i]; + if (json.pos_session_id === this.pos_session.id) { + orders.push(new exports.Order({},{ + pos: this, + json: json, + })); + } else { + not_loaded_count += 1; + } + } + + if (not_loaded_count) { + console.info('There are '+not_loaded_count+' locally saved unpaid orders belonging to another session'); + } + + orders = orders.sort(function(a,b){ + return a.sequence_number - b.sequence_number; + }); + + if (orders.length) { + this.get('orders').add(orders); + } + }, + }); // Add fields to the list of read fields when a model is loaded diff --git a/addons/point_of_sale/static/src/js/popups.js b/addons/point_of_sale/static/src/js/popups.js index 1b22d46fe3d..3fc7b089795 100644 --- a/addons/point_of_sale/static/src/js/popups.js +++ b/addons/point_of_sale/static/src/js/popups.js @@ -221,14 +221,9 @@ var PasswordPopupWidget = NumberPopupWidget.extend({ }); gui.define_popup({name:'password', widget: PasswordPopupWidget}); -var UnsentOrdersPopupWidget = ConfirmPopupWidget.extend({ - template: 'UnsentOrdersPopupWidget', +var OrderImportPopupWidget = PopupWidget.extend({ + template: 'OrderImportPopupWidget', }); -gui.define_popup({name:'unsent-orders', widget: UnsentOrdersPopupWidget}); - -var UnpaidOrdersPopupWidget = ConfirmPopupWidget.extend({ - template: 'UnpaidOrdersPopupWidget', -}); -gui.define_popup({name:'unpaid-orders', widget: UnpaidOrdersPopupWidget}); +gui.define_popup({name:'orderimport', widget: OrderImportPopupWidget}); }); diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index 98dc3e464d5..06a5bb74545 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -920,52 +920,6 @@ - - - - - - - -