diff --git a/addons/point_of_sale/static/src/js/chrome.js b/addons/point_of_sale/static/src/js/chrome.js index 2e9d6f17885..69aa3c7f902 100644 --- a/addons/point_of_sale/static/src/js/chrome.js +++ b/addons/point_of_sale/static/src/js/chrome.js @@ -481,7 +481,7 @@ openerp.point_of_sale.load_chrome = function load_chrome(instance, module){ //mo } var popup = $(QWeb.render('ErrorTracebackPopupWidget',{ - widget: { title: title , body: body }, + widget: { options: {title: title , body: body }}, })); popup.find('.button').click(function(){ diff --git a/addons/point_of_sale/static/src/js/gui.js b/addons/point_of_sale/static/src/js/gui.js index 4627abab60b..20e754b07e5 100644 --- a/addons/point_of_sale/static/src/js/gui.js +++ b/addons/point_of_sale/static/src/js/gui.js @@ -169,6 +169,11 @@ openerp.point_of_sale.load_gui = function load_gui(instance, module) { } }, + // is there an active popup ? + has_popup: function() { + return !!this.current_popup; + }, + /* ---- Gui: ACCESS CONTROL ---- */ // A Generic UI that allow to select a user from a list. @@ -280,6 +285,21 @@ openerp.point_of_sale.load_gui = function load_gui(instance, module) { }); }, + /* ---- Gui: SOUND ---- */ + + play_sound: function(sound) { + var src = ''; + if (sound === 'error') { + src = "/point_of_sale/static/src/sounds/error.wav"; + } else if (sound === 'bell') { + src = "/point_of_sale/static/src/sounds/bell.wav"; + } else { + console.error('Unknown sound: ',sound); + return; + } + $('body').append(''); + }, + /* ---- Gui: KEYBOARD INPUT ---- */ // This is a helper to handle numpad keyboard input. @@ -325,6 +345,12 @@ openerp.point_of_sale.load_gui = function load_gui(instance, module) { } } + // End of input buffer at 12 characters. + if (newbuf.length > buffer.length && newbuf.length > 12) { + this.play_sound('bell'); + return buffer.slice(0); + } + return newbuf; }, }); diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 7441e25682b..71d593961de 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -284,7 +284,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo }, },{ model: 'pos.category', - fields: ['id','name','parent_id','child_id','image'], + fields: ['id','name','parent_id','child_id'], domain: null, loaded: function(self, categories){ self.db.add_categories(categories); @@ -874,6 +874,8 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo this.price = json.price_unit; this.set_discount(json.discount); this.set_quantity(json.qty); + this.id = json.id; + orderline_id = Math.max(this.id+1,orderline_id) }, clone: function(){ var orderline = new module.Orderline({},{ @@ -910,6 +912,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo // product's unity of measure properties. Quantities greater than zero will not get // rounded to zero set_quantity: function(quantity){ + this.order.assert_editable(); if(quantity === 'remove'){ this.order.remove_orderline(this); return; @@ -989,6 +992,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo } }, merge: function(orderline){ + this.order.assert_editable(); this.set_quantity(this.get_quantity() + orderline.get_quantity()); }, export_as_JSON: function() { @@ -997,6 +1001,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo price_unit: this.get_unit_price(), discount: this.get_discount(), product_id: this.get_product().id, + id: this.id, }; }, //used to create a json of the ticket, to be sent to the printer @@ -1017,6 +1022,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo }, // changes the base price of the product for this orderline set_unit_price: function(price){ + this.order.assert_editable(); this.price = round_di(parseFloat(price) || 0, this.pos.dp['Product Price']); this.trigger('change',this); }, @@ -1145,6 +1151,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo module.Paymentline = Backbone.Model.extend({ initialize: function(attributes, options) { this.pos = options.pos; + this.order = options.order; this.amount = 0; this.selected = false; if (options.json) { @@ -1161,6 +1168,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo }, //sets the amount of money on this payment line set_amount: function(value){ + this.order.assert_editable(); this.amount = round_di(parseFloat(value) || 0, this.pos.currency.decimals); this.trigger('change',this); }, @@ -1225,6 +1233,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo this.orderlines = new module.OrderlineCollection(); this.paymentlines = new module.PaymentlineCollection(); this.pos_session_id = this.pos.pos_session.id; + this.finalized = false; // if true, cannot be modified. this.set({ client: null }); @@ -1282,7 +1291,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo var paymentlines = json.statement_ids; for (var i = 0; i < paymentlines.length; i++) { var paymentline = paymentlines[i][2]; - var newpaymentline = new module.Paymentline({},{pos: this.pos, json: paymentline}); + var newpaymentline = new module.Paymentline({},{pos: this.pos, order: this, json: paymentline}); this.paymentlines.add(newpaymentline); if (i === paymentlines.length - 1) { @@ -1431,8 +1440,14 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo get_name: function() { return this.name; }, + assert_editable: function() { + if (this.finalized) { + throw new Error('Finalized Order cannot be modified'); + } + }, /* ---- Order Lines --- */ add_orderline: function(line){ + this.assert_editable(); if(line.order){ line.order.remove_orderline(line); } @@ -1456,10 +1471,12 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo return this.orderlines.at(this.orderlines.length -1); }, remove_orderline: function( line ){ + this.assert_editable(); this.orderlines.remove(line); this.select_orderline(this.get_last_orderline()); }, add_product: function(product, options){ + this.assert_editable(); options = options || {}; var attr = JSON.parse(JSON.stringify(product)); attr.pos = this.pos; @@ -1514,7 +1531,8 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo }, /* ---- Payment Lines --- */ add_paymentline: function(cashregister) { - var newPaymentline = new module.Paymentline({},{cashregister:cashregister, pos: this.pos}); + this.assert_editable(); + var newPaymentline = new module.Paymentline({},{order: this, cashregister:cashregister, pos: this.pos}); if(cashregister.journal.type !== 'cash' || this.pos.config.iface_precompute_cash){ newPaymentline.set_amount( Math.max(this.get_due(),0) ); } @@ -1526,6 +1544,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo return this.paymentlines.models; }, remove_paymentline: function(line){ + this.assert_editable(); if(this.selected_paymentline === line){ this.select_paymentline(undefined); } @@ -1701,6 +1720,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo }, /* ---- Invoice --- */ set_to_invoice: function(to_invoice) { + this.assert_editable(); this.to_invoice = to_invoice; }, is_to_invoice: function(){ @@ -1709,6 +1729,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo /* ---- Client / Customer --- */ // the client related to the current order. set_client: function(client){ + this.assert_editable(); this.set('client',client); }, get_client: function(){ diff --git a/addons/point_of_sale/static/src/js/popups.js b/addons/point_of_sale/static/src/js/popups.js index e52d9be9669..e0db66f5392 100644 --- a/addons/point_of_sale/static/src/js/popups.js +++ b/addons/point_of_sale/static/src/js/popups.js @@ -87,9 +87,7 @@ openerp.point_of_sale.load_popups = function load_popups(instance, module) { template:'ErrorPopupWidget', show: function(options){ this._super(options); - - $('body').append(''); - + this.gui.play_sound('error'); }, }); module.Gui.define_popup({name:'error', widget:module.ErrorPopupWidget}); @@ -138,7 +136,7 @@ openerp.point_of_sale.load_popups = function load_popups(instance, module) { show: function(options){ options = options || {}; var self = this; - this._super(); + this._super(options); this.list = options.list || []; this.renderElement(); @@ -167,19 +165,16 @@ openerp.point_of_sale.load_popups = function load_popups(instance, module) { show: function(options){ options = options || {}; var self = this; - this._super(); + this._super(options); this.renderElement(); this.$('input,textarea').focus(); - - this.$('.button.confirm').click(function(){ - }); }, click_confirm: function(){ - var value = self.$('input,textarea').val(); - self.gui.close_popup(); + var value = this.$('input,textarea').val(); + this.gui.close_popup(); if( this.options.confirm ){ - this.options.confirm.call(self,value); + this.options.confirm.call(this,value); } }, }); @@ -197,7 +192,7 @@ openerp.point_of_sale.load_popups = function load_popups(instance, module) { show: function(options){ options = options || {}; var self = this; - this._super(); + this._super(options); this.inputbuffer = options.value || ''; this.renderElement(); diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js index 9c9149b4fe3..0232fb6aebe 100644 --- a/addons/point_of_sale/static/src/js/screens.js +++ b/addons/point_of_sale/static/src/js/screens.js @@ -833,6 +833,9 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // }); }, button_click: function(){}, + highlight: function(highlight){ + this.$el.toggleClass('highlight',!!highlight); + }, }); /* -------- The Product Screen -------- */ @@ -1439,6 +1442,11 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // var newbuf = this.gui.numpad_input(this.inputbuffer, input, {'firstinput': this.firstinput}); this.firstinput = (newbuf.length === 0); + + // popup block inputs to prevent sneak editing. + if (this.gui.has_popup()) { + return; + } if (newbuf !== this.inputbuffer) { this.inputbuffer = newbuf; @@ -1572,6 +1580,10 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // self.click_invoice(); }); + this.$('.js_cashdrawer').click(function(){ + self.pos.proxy.open_cashbox(); + }); + }, show: function(){ this.pos.get_order().clean_empty_paymentlines(); @@ -1626,7 +1638,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // // Check if the order is paid, then sends it to the backend, // and complete the sale process - validate_order: function() { + validate_order: function(force_validation) { var self = this; var order = this.pos.get_order(); @@ -1671,6 +1683,26 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // } } + // if the change is too large, it's probably an input error, make the user confirm. + if (!force_validation && (order.get_total_with_tax() * 1000 < order.get_total_paid())) { + this.gui.show_popup('confirm',{ + title: _t('Please Confirm Large Amount'), + body: _t('Are you sure that the customer wants to pay') + + ' ' + + this.format_currency(order.get_total_paid()) + + ' ' + + _t('for an order of') + + ' ' + + this.format_currency(order.get_total_with_tax()) + + ' ' + + _t('? Clicking "Confirm" will validate the payment.'), + confirm: function() { + self.validate_order('confirm'); + }, + }); + return; + } + if (order.is_paid_with_cash() && this.pos.config.iface_cashdrawer) { this.pos.proxy.open_cashbox(); diff --git a/addons/point_of_sale/static/src/sounds/bell.wav b/addons/point_of_sale/static/src/sounds/bell.wav new file mode 100644 index 00000000000..660779c51f2 Binary files /dev/null and b/addons/point_of_sale/static/src/sounds/bell.wav differ diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index 5547a4c70f7..955cde86837 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -596,7 +596,7 @@ -
+
Open Cashbox
diff --git a/addons/pos_restaurant/static/src/js/notes.js b/addons/pos_restaurant/static/src/js/notes.js index 2d60b66d62b..116f9c3709a 100644 --- a/addons/pos_restaurant/static/src/js/notes.js +++ b/addons/pos_restaurant/static/src/js/notes.js @@ -47,7 +47,7 @@ openerp.pos_restaurant.load_notes = function(instance,module){ var line = this.pos.get_order().get_selected_orderline(); if (line) { this.gui.show_popup('textarea',{ - message: _t('Orderline Note'), + title: _t('Add Note'), value: line.get_note(), confirm: function(note) { line.set_note(note); diff --git a/addons/pos_restaurant/static/src/js/splitbill.js b/addons/pos_restaurant/static/src/js/splitbill.js index 70714418430..cc640599512 100644 --- a/addons/pos_restaurant/static/src/js/splitbill.js +++ b/addons/pos_restaurant/static/src/js/splitbill.js @@ -167,7 +167,7 @@ openerp.pos_restaurant.load_splitbill = function(instance, module){ template: 'SplitbillButton', button_click: function(){ if(this.pos.get_order().get_orderlines().length > 0){ - self.gui.show_screen('splitbill'); + this.gui.show_screen('splitbill'); } }, });