[IMP] point_of_sale: new payment screen is now operational

This commit is contained in:
Frederic van der Essen
2014-08-07 17:28:46 +02:00
parent ec29736801
commit 1f39a7d640
5 changed files with 211 additions and 376 deletions
+15 -3
View File
@@ -996,7 +996,7 @@ td {
.pos .payment-numpad {
display: inline-block;
width: 230px;
width: 232px;
margin-top: 8px;
margin-right: 8px;
float: right;
@@ -1024,7 +1024,7 @@ td {
border-radius: 3px;
}
.pos .paymentline{
font-size: 18px;
font-size: 22px;
}
.pos .paymentline.selected {
font-size: 22px;
@@ -1035,13 +1035,25 @@ td {
background: white;
color: #6EC89B;
outline: 3px blue;
border: solid 4px #6EC89B;
box-shadow: 0px 0px 0px 3px #6EC89B;
position: relative;
border-radius: 3px;
}
.pos .paymentline > *{
padding: 8px;
}
.pos .paymentline .col-due,
.pos .paymentline .col-tendered,
.pos .paymentline .col-change {
min-width: 80px;
}
.pos .paymentline .col-change.highlight {
background: rgb(184, 152, 204);
}
.pos .paymentline .col-name {
font-size: 16px;
}
.pos .paymentline .delete-button{
cursor: pointer;
text-align: center;
@@ -1030,6 +1030,14 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
isPaid: function(){
return this.getDueLeft() === 0;
},
isPaidWithCash: function(){
return !!this.get('paymentLines').find( function(pl){
return pl.cashregister.journal.type === 'cash';
});
},
finalize: function(){
this.destroy();
},
// sets the type of receipt 'receipt'(default) or 'invoice'
set_receipt_type: function(type){
this.receipt_type = type;
+151 -317
View File
@@ -783,9 +783,8 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
module.ReceiptScreenWidget = module.ScreenWidget.extend({
template: 'ReceiptScreenWidget',
show_numpad: true,
show_leftpane: true,
show_numpad: false,
show_leftpane: false,
show: function(){
this._super();
@@ -849,24 +848,102 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
});
module.NewPaymentScreenWidget = module.ScreenWidget.extend({
template: 'NewPaymentScreenWidget',
module.PaymentScreenWidget = module.ScreenWidget.extend({
template: 'PaymentScreenWidget',
back_screen: 'product',
next_screen: 'receipt',
show_leftpane: false,
show_numpad: false,
init: function(parent, options) {
var self = this;
this._super(parent, options);
this.pos.bind('change:selectedOrder',function(){
this.bind_events();
this.renderElement();
this.watch_order_changes();
},this);
this.watch_order_changes();
this.inputbuffer = "";
this.keyboard_handler = function(event){
var key = '';
if ( event.keyCode === 13 ) { // Enter
self.validate_order();
} else if ( event.keyCode === 190 ) { // Dot
key = '.';
} else if ( event.keyCode === 46 ) { // Delete
key = 'CLEAR';
} else if ( event.keyCode === 8 ) { // Backspace
key = 'BACKSPACE';
event.preventDefault(); // Prevents history back nav
} else if ( event.keyCode >= 48 && event.keyCode <= 57 ){ // Numbers
key = '' + (event.keyCode - 48);
} else if ( event.keyCode >= 96 && event.keyCode <= 105 ){ // Numpad Numbers
key = '' + (event.keyCode - 96);
} else if ( event.keyCode === 189 || event.keyCode === 109 ) { // Minus
key = '-';
} else if ( event.keyCode === 107 ) { // Plus
key = '+';
}
self.payment_input(key);
};
},
click_numpad: function(button){
console.log(button);
// resets the current input buffer
reset_input: function(){
this.inputbuffer = "";
},
// handle both keyboard and numpad input. Accepts
// a string that represents the key pressed.
payment_input: function(input) {
var oldbuf = this.inputbuffer.slice(0);
if (input === '.') {
if (!this.inputbuffer.length || this.inputbuffer === '-') {
this.inputbuffer += "0.";
} else if (this.inputbuffer.indexOf('.') < 0){
this.inputbuffer = this.inputbuffer + '.';
}
} else if (input === 'CLEAR') {
this.inputbuffer = "";
} else if (input === 'BACKSPACE') {
this.inputbuffer = this.inputbuffer.substring(0,this.inputbuffer.length - 1);
} else if (input === '+') {
if ( this.inputbuffer[0] === '-' ) {
this.inputbuffer = this.inputbuffer.substring(1,this.inputbuffer.length);
}
} else if (input === '-') {
if ( this.inputbuffer[0] === '-' ) {
this.inputbuffer = this.inputbuffer.substring(1,this.inputbuffer.length);
} else {
this.inputbuffer = '-' + this.inputbuffer;
}
} else if (input[0] === '+' && !isNaN(parseFloat(input))) {
this.inputbuffer = '' + ((parseFloat(this.inputbuffer) || 0) + parseFloat(input));
} else if (!isNaN(parseInt(input))) {
this.inputbuffer += input;
}
if (this.inputbuffer !== oldbuf) {
var order = this.pos.get_order();
if (order.selected_paymentline) {
order.selected_paymentline.set_amount(parseFloat(this.inputbuffer));
this.order_changes();
this.render_paymentlines();
this.$('.paymentline.selected .edit').text(this.inputbuffer);
}
}
},
click_numpad: function(button) {
this.payment_input(button.data('action'));
},
render_numpad: function() {
var self = this;
var numpad = $(QWeb.render('PaymentScreen-Numpad', { widget:this }));
numpad.on('click','button',function(){
self.click_numpad($(this));
});
return numpad;
},
click_delete_paymentline: function(cid){
@@ -874,6 +951,18 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
for ( var i = 0; i < lines.length; i++ ) {
if (lines[i].cid === cid) {
this.pos.get_order().removePaymentline(lines[i]);
this.reset_input();
this.render_paymentlines();
return;
}
}
},
click_paymentline: function(cid){
var lines = this.pos.get_order().get('paymentLines').models;
for ( var i = 0; i < lines.length; i++ ) {
if (lines[i].cid === cid) {
this.pos.get_order().selectPaymentline(lines[i]);
this.reset_input();
this.render_paymentlines();
return;
}
@@ -894,10 +983,14 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
lines.on('click','.delete-button',function(){
self.click_delete_paymentline($(this).data('cid'));
});
lines.on('click','.paymentline',function(){
self.click_paymentline($(this).data('cid'));
});
lines.appendTo(this.$('.paymentlines-container'));
},
click_paymentmethods: function(id){
click_paymentmethods: function(id) {
var cashregister = null;
for ( var i = 0; i < this.pos.cashregisters.length; i++ ) {
if ( this.pos.cashregisters[i].journal_id[0] === id ){
@@ -906,6 +999,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
}
}
this.pos.get_order().addPaymentline( cashregister );
this.reset_input();
this.render_paymentlines();
},
render_paymentmethods: function() {
@@ -932,355 +1026,95 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
self.pos_widget.screen_selector.back();
});
this.$('.next').click(function(){
self.validate_order();
});
this.$('.js_invoice').click(function(){
self.validate_order({invoice: true});
});
},
show: function(){
this.pos.get_order().clean_empty_paymentlines();
this.reset_input();
this.render_paymentlines();
window.document.body.addEventListener('keydown',this.keyboard_handler);
this._super();
},
});
module.PaymentScreenWidget = module.ScreenWidget.extend({
template: 'PaymentScreenWidget',
back_screen: 'products',
next_screen: 'receipt',
init: function(parent, options) {
hide: function(){
window.document.body.removeEventListener('keydown',this.keyboard_handler);
this._super();
},
// sets up listeners to watch for order changes
watch_order_changes: function() {
var self = this;
this._super(parent,options);
this.pos.bind('change:selectedOrder',function(){
this.bind_events();
this.renderElement();
},this);
this.bind_events();
this.line_delete_handler = function(event){
var node = this;
while(node && !node.classList.contains('paymentline')){
node = node.parentNode;
}
if(node){
self.pos.get('selectedOrder').removePaymentline(node.line)
}
event.stopPropagation();
};
this.line_change_handler = function(event){
var node = this;
while(node && !node.classList.contains('paymentline')){
node = node.parentNode;
}
if(node){
node.line.set_amount(this.value);
}
};
this.line_click_handler = function(event){
var node = this;
while(node && !node.classList.contains('paymentline')){
node = node.parentNode;
}
if(node){
self.pos.get('selectedOrder').selectPaymentline(node.line);
}
};
this.hotkey_handler = function(event){
if(event.which === 13){
self.validate_order();
}else if(event.which === 27){
self.back();
}
};
},
show: function(){
this._super();
var self = this;
this.enable_numpad();
this.focus_selected_line();
document.body.addEventListener('keyup', this.hotkey_handler);
this.add_action_button({
label: _t('Back'),
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
click: function(){
self.back();
},
});
this.add_action_button({
label: _t('Validate'),
name: 'validation',
icon: '/point_of_sale/static/src/img/icons/png48/validate.png',
click: function(){
self.validate_order();
},
});
if( this.pos.config.iface_invoicing ){
this.add_action_button({
label: 'Invoice',
name: 'invoice',
icon: '/point_of_sale/static/src/img/icons/png48/invoice.png',
click: function(){
self.validate_order({invoice: true});
},
});
}
if( this.pos.config.iface_cashdrawer ){
this.add_action_button({
label: _t('Cash'),
name: 'cashbox',
icon: '/point_of_sale/static/src/img/open-cashbox.png',
click: function(){
self.pos.proxy.open_cashbox();
},
});
}
this.update_payment_summary();
},
close: function(){
this._super();
this.disable_numpad();
document.body.removeEventListener('keyup',this.hotkey_handler);
},
remove_empty_lines: function(){
var order = this.pos.get('selectedOrder');
var lines = order.get('paymentLines').models.slice(0);
for(var i = 0; i < lines.length; i++){
var line = lines[i];
if(line.get_amount() === 0){
order.removePaymentline(line);
}
}
},
back: function() {
this.remove_empty_lines();
this.pos_widget.screen_selector.set_current_screen(this.back_screen);
},
bind_events: function() {
var order = this.pos.get_order();
if(this.old_order){
this.old_order.unbind(null,null,this);
}
var order = this.pos.get('selectedOrder');
order.bind('change:selected_paymentline',this.focus_selected_line,this);
order.bind('all',function(){
self.order_changes();
});
this.old_order = order;
if(this.old_paymentlines){
this.old_paymentlines.unbind(null,null,this);
}
var paymentlines = order.get('paymentLines');
paymentlines.bind('add', this.add_paymentline, this);
paymentlines.bind('change:selected', this.rerender_paymentline, this);
paymentlines.bind('change:amount', function(line){
if(!line.selected && line.node){
line.node.value = line.amount.toFixed(2);
}
this.update_payment_summary();
},this);
paymentlines.bind('remove', this.remove_paymentline, this);
paymentlines.bind('all', this.update_payment_summary, this);
this.old_paymentlines = paymentlines;
if(this.old_orderlines){
this.old_orderlines.unbind(null,null,this);
}
var orderlines = order.get('orderLines');
orderlines.bind('all', this.update_payment_summary, this);
this.old_orderlines = orderlines;
},
focus_selected_line: function(){
var line = this.pos.get('selectedOrder').selected_paymentline;
if(line){
var input = line.node.querySelector('input');
if(!input){
return;
}
var value = input.value;
input.focus();
if(this.numpad_state){
this.numpad_state.reset();
}
if(Number(value) === 0){
input.value = '';
}else{
input.value = value;
input.select();
}
// called when the order is changed, used to show if
// the order is paid or not
order_changes: function(){
var order = this.pos.get_order();
console.log('change!');
if (order.isPaid()) {
self.$('.next').addClass('highlight');
}else{
self.$('.next').removeClass('highlight');
}
},
add_paymentline: function(line) {
var list_container = this.el.querySelector('.payment-lines');
list_container.appendChild(this.render_paymentline(line));
if(this.numpad_state){
this.numpad_state.reset();
}
},
render_paymentline: function(line){
var el_html = openerp.qweb.render('Paymentline',{widget: this, line: line});
el_html = _.str.trim(el_html);
var el_node = document.createElement('tbody');
el_node.innerHTML = el_html;
el_node = el_node.childNodes[0];
el_node.line = line;
el_node.querySelector('.paymentline-delete')
.addEventListener('click', this.line_delete_handler);
el_node.addEventListener('click', this.line_click_handler);
el_node.querySelector('input')
.addEventListener('keyup', this.line_change_handler);
line.node = el_node;
return el_node;
},
rerender_paymentline: function(line){
var old_node = line.node;
var new_node = this.render_paymentline(line);
old_node.parentNode.replaceChild(new_node,old_node);
},
remove_paymentline: function(line){
line.node.parentNode.removeChild(line.node);
line.node = undefined;
},
renderElement: function(){
this._super();
var paymentlines = this.pos.get('selectedOrder').get('paymentLines').models;
var list_container = this.el.querySelector('.payment-lines');
for(var i = 0; i < paymentlines.length; i++){
list_container.appendChild(this.render_paymentline(paymentlines[i]));
}
this.update_payment_summary();
},
update_payment_summary: function() {
var currentOrder = this.pos.get('selectedOrder');
var paidTotal = currentOrder.getPaidTotal();
var dueTotal = currentOrder.getTotalTaxIncluded();
var remaining = dueTotal > paidTotal ? dueTotal - paidTotal : 0;
var change = paidTotal > dueTotal ? paidTotal - dueTotal : 0;
this.$('.payment-due-total').html(this.format_currency(dueTotal));
this.$('.payment-paid-total').html(this.format_currency(paidTotal));
this.$('.payment-remaining').html(this.format_currency(remaining));
this.$('.payment-change').html(this.format_currency(change));
if(currentOrder.selected_orderline === undefined){
remaining = 1; // What is this ?
}
if(this.pos_widget.action_bar){
this.pos_widget.action_bar.set_button_disabled('validation', !this.is_paid());
this.pos_widget.action_bar.set_button_disabled('invoice', !this.is_paid());
}
},
is_paid: function(){
var currentOrder = this.pos.get('selectedOrder');
return (currentOrder.getTotalTaxIncluded() < 0.000001
|| currentOrder.getPaidTotal() + 0.000001 >= currentOrder.getTotalTaxIncluded());
},
// Check if the order is paid, then sends it to the backend,
// and complete the sale process
validate_order: function(options) {
var self = this;
options = options || {};
var currentOrder = this.pos.get('selectedOrder');
var order = this.pos.get_order();
if(!this.is_paid()){
if (!order.isPaid() || this.invoicing) {
return;
}
if( this.pos.config.iface_cashdrawer
&& this.pos.get('selectedOrder').get('paymentLines').find( function(pl){
return pl.cashregister.journal.type === 'cash';
})){
if (order.isPaidWithCash() && this.pos.config.iface_cashdrawer) {
this.pos.proxy.open_cashbox();
}
if(options.invoice){
// deactivate the validation button while we try to send the order
this.pos_widget.action_bar.set_button_disabled('validation',true);
this.pos_widget.action_bar.set_button_disabled('invoice',true);
var invoiced = this.pos.push_and_invoice_order(currentOrder);
if (options.invoice) {
var invoiced = this.pos.push_and_invoice_order(order);
this.invoicing = true;
invoiced.fail(function(error){
if(error === 'error-no-client'){
self.invoicing = false;
if (error === 'error-no-client') {
self.pos_widget.screen_selector.show_popup('error-no-client');
}else{
} else {
self.pos_widget.screen_selector.show_popup('error-invoice-transfer');
}
self.pos_widget.action_bar.set_button_disabled('validation',false);
self.pos_widget.action_bar.set_button_disabled('invoice',false);
});
invoiced.done(function(){
self.pos_widget.action_bar.set_button_disabled('validation',false);
self.pos_widget.action_bar.set_button_disabled('invoice',false);
self.pos.get('selectedOrder').destroy();
self.invoicing = false;
order.finalize();
});
}else{
this.pos.push_order(currentOrder)
if(this.pos.config.iface_print_via_proxy){
} else {
this.pos.push_order(order)
if (this.pos.config.iface_print_via_proxy) {
var receipt = currentOrder.export_for_printing();
this.pos.proxy.print_receipt(QWeb.render('XmlReceipt',{
receipt: receipt, widget: self,
}));
this.pos.get('selectedOrder').destroy(); //finish order and go back to scan screen
}else{
order.finalize(); //finish order and go back to scan screen
} else {
this.pos_widget.screen_selector.set_current_screen(this.next_screen);
}
}
// hide onscreen (iOS) keyboard
setTimeout(function(){
document.activeElement.blur();
$("input").blur();
},250);
},
enable_numpad: function(){
this.disable_numpad(); //ensure we don't register the callbacks twice
this.numpad_state = this.pos_widget.numpad.state;
if(this.numpad_state){
this.numpad_state.reset();
this.numpad_state.changeMode('payment');
this.numpad_state.bind('set_value', this.set_value, this);
this.numpad_state.bind('change:mode', this.set_mode_back_to_payment, this);
}
},
disable_numpad: function(){
if(this.numpad_state){
this.numpad_state.unbind('set_value', this.set_value);
this.numpad_state.unbind('change:mode',this.set_mode_back_to_payment);
}
},
set_mode_back_to_payment: function() {
this.numpad_state.set({mode: 'payment'});
},
set_value: function(val) {
var selected_line =this.pos.get('selectedOrder').selected_paymentline;
if(selected_line){
selected_line.set_amount(val);
selected_line.node.querySelector('input').value = selected_line.amount.toFixed(2);
}
},
});
}
@@ -1067,7 +1067,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this.receipt_screen = new module.ReceiptScreenWidget(this, {});
this.receipt_screen.appendTo(this.$('.screens'));
this.payment_screen = new module.NewPaymentScreenWidget(this, {});
this.payment_screen = new module.PaymentScreenWidget(this, {});
this.payment_screen.appendTo(this.$('.screens'));
this.clientlist_screen = new module.ClientListScreenWidget(this, {});
+36 -55
View File
@@ -423,19 +423,29 @@
<t t-foreach='paymentlines' t-as='line'>
<t t-if='line.selected'>
<tr class='paymentline selected'>
<td> <t t-esc='widget.format_currency_no_symbol(order.getDueLeft(line))' /> </td>
<td class='edit'> <t t-esc='line.get_amount()' /> </td>
<td> <t t-esc='widget.format_currency_no_symbol(order.getChange(line))' /> </td>
<td> <t t-esc='line.name' /> </td>
<td class='col-due'> <t t-esc='widget.format_currency_no_symbol(order.getDueLeft(line))' /> </td>
<td class='col-tendered edit'> <t t-esc='line.get_amount()' /> </td>
<t t-if='order.getChange(line)'>
<td class='col-change highlight' > <t t-esc='widget.format_currency_no_symbol(order.getChange(line))' /> </td>
</t>
<t t-if='!order.getChange(line)'>
<td class='col-change' ></td>
</t>
<td class='col-name' > <t t-esc='line.name' /> </td>
<td class='delete-button' t-att-data-cid='line.cid'> <i class='fa fa-times-circle' /> </td>
</tr>
</t>
<t t-if='!line.selected'>
<tr class='paymentline'>
<td> <t t-esc='widget.format_currency_no_symbol(order.getDueLeft(line))' /> </td>
<td> <t t-esc='widget.format_currency_no_symbol(line.get_amount())' /> </td>
<td> <t t-esc='widget.format_currency_no_symbol(order.getChange(line))' /> </td>
<td> <t t-esc='line.name' /> </td>
<tr class='paymentline' t-att-data-cid='line.cid'>
<td class='col-due'> <t t-esc='widget.format_currency_no_symbol(order.getDueLeft(line))' /> </td>
<td class='col-tendered'> <t t-esc='widget.format_currency_no_symbol(line.get_amount())' /> </td>
<td class='col-change'>
<t t-if='order.getChange(line)'>
<t t-esc='widget.format_currency_no_symbol(order.getChange(line))' />
</t>
</td>
<td class='col-name'> <t t-esc='line.name' /> </td>
<td class='delete-button' t-att-data-cid='line.cid'> <i class='fa fa-times-circle' /> </td>
</tr>
</t>
@@ -448,25 +458,25 @@
<t t-name="PaymentScreen-Numpad">
<div class="numpad">
<button class="input-button number-char">1</button>
<button class="input-button number-char">2</button>
<button class="input-button number-char">3</button>
<button class="mode-button" data-action='currency' data-value='5' >5€</button>
<button class="input-button number-char" data-action='1'>1</button>
<button class="input-button number-char" data-action='2'>2</button>
<button class="input-button number-char" data-action='3'>3</button>
<button class="mode-button" data-action='+5'>+5</button>
<br />
<button class="input-button number-char">4</button>
<button class="input-button number-char">5</button>
<button class="input-button number-char">6</button>
<button class="mode-button" data-action='currency' data-value='10' >10€</button>
<button class="input-button number-char" data-action='4'>4</button>
<button class="input-button number-char" data-action='5'>5</button>
<button class="input-button number-char" data-action='6'>6</button>
<button class="mode-button" data-action='+10'>+10</button>
<br />
<button class="input-button number-char">7</button>
<button class="input-button number-char">8</button>
<button class="input-button number-char">9</button>
<button class="mode-button" data-action='currency' data-value='20' >20€</button>
<button class="input-button number-char" data-action='7'>7</button>
<button class="input-button number-char" data-action='8'>8</button>
<button class="input-button number-char" data-action='9'>9</button>
<button class="mode-button" data-action='+20'>+20</button>
<br />
<button class="input-button numpad-char" data-action='clear' >C</button>
<button class="input-button number-char">0</button>
<button class="input-button number-char">.</button>
<button class="input-button numpad-backspace" data-action='backspace' >
<button class="input-button numpad-char" data-action='CLEAR' >C</button>
<button class="input-button number-char" data-action='0'>0</button>
<button class="input-button number-char" data-action='.'>.</button>
<button class="input-button numpad-backspace" data-action='BACKSPACE' >
<img src="/point_of_sale/static/src/img/backspace.png" width="24" height="21" />
</button>
<br />
@@ -483,7 +493,7 @@
</div>
</t>
<t t-name="NewPaymentScreenWidget">
<t t-name="PaymentScreenWidget">
<div class='payment-screen screen'>
<div class='screen-content'>
<div class='top-content'>
@@ -530,35 +540,6 @@
</t>
<t t-name="PaymentScreenWidget">
<div class="payment-screen screen touch-scrollable">
<div class="pos-payment-container">
<div class='payment-due-total'></div>
<div class='payment-lines'></div>
<div class='payment-info'>
<div class="infoline">
<span class='left-block'>
Paid:
</span>
<span class="right-block payment-paid-total"></span>
</div>
<div class="infoline">
<span class='left-block'>
Remaining:
</span>
<span class="right-block payment-remaining"></span>
</div>
<div class="infoline bigger" >
<span class='left-block'>
Change:
</span>
<span class="right-block payment-change"></span>
</div>
</div>
</div>
</div>
</t>
<t t-name="ReceiptScreenWidget">
<div class="receipt-screen screen touch-scrollable" >
<div class="pos-step-container">