2012-05-03 17:15:23 +00:00
// this file contains the screens definitions. Screens are the
// content of the right pane of the pos, containing the main functionalities.
// screens are contained in the PosWidget, in pos_widget.js
// all screens are present in the dom at all time, but only one is shown at the
// same time.
//
// transition between screens is made possible by the use of the screen_selector,
// which is responsible of hiding and showing the screens, as well as maintaining
// the state of the screens between different orders.
//
// all screens inherit from ScreenWidget. the only addition from the base widgets
// are show() and hide() which shows and hides the screen but are also used to
// bind and unbind actions on widgets and devices. The screen_selector guarantees
// that only one screen is shown at the same time and that show() is called after all
// hide()s
2012-05-14 14:03:40 +00:00
function openerp _pos _screens ( instance , module ) { //module is instance.point_of_sale
2013-02-18 05:54:02 +00:00
var QWeb = instance . web . qweb ,
_t = instance . web . _t ;
2012-05-03 17:15:23 +00:00
2014-04-28 15:33:26 +00:00
var round _pr = instance . web . round _precision
2012-05-03 17:15:23 +00:00
module . ScreenSelector = instance . web . Class . extend ( {
init : function ( options ) {
this . pos = options . pos ;
2012-05-04 09:48:25 +00:00
2012-05-03 17:15:23 +00:00
this . screen _set = options . screen _set || { } ;
2012-05-04 09:48:25 +00:00
2012-05-06 21:34:39 +00:00
this . popup _set = options . popup _set || { } ;
2014-06-13 14:19:05 +00:00
this . default _screen = options . default _screen ;
2012-05-04 09:48:25 +00:00
2012-05-06 21:34:39 +00:00
this . current _popup = null ;
2014-06-13 14:19:05 +00:00
this . current _mode = options . default _mode || 'cashier' ;
2012-05-04 09:48:25 +00:00
2012-06-11 14:04:41 +00:00
this . current _screen = null ;
2012-05-16 13:01:42 +00:00
2012-05-03 17:15:23 +00:00
for ( screen _name in this . screen _set ) {
2012-06-11 14:04:41 +00:00
this . screen _set [ screen _name ] . hide ( ) ;
2012-05-03 17:15:23 +00:00
}
2012-05-06 21:34:39 +00:00
for ( popup _name in this . popup _set ) {
this . popup _set [ popup _name ] . hide ( ) ;
}
2014-06-13 14:19:05 +00:00
this . pos . get ( 'selectedOrder' ) . set _screen _data ( {
'screen' : this . default _screen ,
2012-05-04 09:48:25 +00:00
} ) ;
2012-05-03 17:15:23 +00:00
this . pos . bind ( 'change:selectedOrder' , this . load _saved _screen , this ) ;
} ,
add _screen : function ( screen _name , screen ) {
screen . hide ( ) ;
this . screen _set [ screen _name ] = screen ;
return this ;
} ,
2014-06-13 14:19:05 +00:00
show _popup : function ( name , options ) {
2012-05-06 21:34:39 +00:00
if ( this . current _popup ) {
this . close _popup ( ) ;
}
this . current _popup = this . popup _set [ name ] ;
2014-06-13 14:19:05 +00:00
this . current _popup . show ( options ) ;
2012-05-06 21:34:39 +00:00
} ,
close _popup : function ( ) {
if ( this . current _popup ) {
2012-09-11 10:18:30 +00:00
this . current _popup . close ( ) ;
2012-05-06 21:34:39 +00:00
this . current _popup . hide ( ) ;
this . current _popup = null ;
}
} ,
2012-05-03 17:15:23 +00:00
load _saved _screen : function ( ) {
2012-05-06 21:34:39 +00:00
this . close _popup ( ) ;
2012-05-08 12:37:01 +00:00
var selectedOrder = this . pos . get ( 'selectedOrder' ) ;
2014-07-28 09:33:24 +00:00
// FIXME : this changing screen behaviour is sometimes confusing ...
this . set _current _screen ( selectedOrder . get _screen _data ( 'screen' ) || this . default _screen , null , 'refresh' ) ;
//this.set_current_screen(this.default_screen,null,'refresh');
2012-05-08 12:37:01 +00:00
2012-05-04 09:48:25 +00:00
} ,
set _user _mode : function ( user _mode ) {
if ( user _mode !== this . current _mode ) {
2012-05-06 21:34:39 +00:00
this . close _popup ( ) ;
2012-05-04 09:48:25 +00:00
this . current _mode = user _mode ;
this . load _saved _screen ( ) ;
2012-05-03 17:15:23 +00:00
}
} ,
2012-05-08 12:37:01 +00:00
get _user _mode : function ( ) {
return this . current _mode ;
} ,
2012-07-12 17:00:13 +00:00
set _current _screen : function ( screen _name , params , refresh ) {
2012-05-03 17:15:23 +00:00
var screen = this . screen _set [ screen _name ] ;
2012-07-12 17:00:13 +00:00
if ( ! screen ) {
console . error ( "ERROR: set_current_screen(" + screen _name + ") : screen not found" ) ;
}
2012-05-03 17:15:23 +00:00
2012-05-06 21:34:39 +00:00
this . close _popup ( ) ;
2014-06-13 14:19:05 +00:00
var order = this . pos . get ( 'selectedOrder' ) ;
var old _screen _name = order . get _screen _data ( 'screen' ) ;
order . set _screen _data ( 'screen' , screen _name ) ;
if ( params ) {
order . set _screen _data ( 'params' , params ) ;
}
if ( screen _name !== old _screen _name ) {
order . set _screen _data ( 'previous-screen' , old _screen _name ) ;
2012-05-04 09:48:25 +00:00
}
2014-06-13 14:19:05 +00:00
if ( refresh || screen !== this . current _screen ) {
2012-05-03 17:15:23 +00:00
if ( this . current _screen ) {
2012-06-11 14:04:41 +00:00
this . current _screen . close ( ) ;
2012-05-03 17:15:23 +00:00
this . current _screen . hide ( ) ;
}
this . current _screen = screen ;
this . current _screen . show ( ) ;
}
} ,
2014-06-13 14:19:05 +00:00
get _current _screen : function ( ) {
return this . pos . get ( 'selectedOrder' ) . get _screen _data ( 'screen' ) || this . default _screen ;
} ,
back : function ( ) {
var previous = this . pos . get ( 'selectedOrder' ) . get _screen _data ( 'previous-screen' ) ;
if ( previous ) {
this . set _current _screen ( previous ) ;
2012-07-09 15:53:51 +00:00
}
} ,
2014-06-13 14:19:05 +00:00
get _current _screen _param : function ( param ) {
var params = this . pos . get ( 'selectedOrder' ) . get _screen _data ( 'params' ) ;
return params ? params [ param ] : undefined ;
} ,
2012-06-11 14:04:41 +00:00
set _default _screen : function ( ) {
2014-06-13 14:19:05 +00:00
this . set _current _screen ( this . default _screen ) ;
2012-06-11 14:04:41 +00:00
} ,
2012-05-03 17:15:23 +00:00
} ) ;
2012-05-14 14:03:40 +00:00
module . ScreenWidget = module . PosBaseWidget . extend ( {
2012-06-11 14:04:41 +00:00
2012-07-03 11:47:41 +00:00
show _numpad : true ,
show _leftpane : true ,
2012-05-23 15:25:35 +00:00
init : function ( parent , options ) {
this . _super ( parent , options ) ;
this . hidden = false ;
} ,
2012-07-03 11:47:41 +00:00
help _button _action : function ( ) {
this . pos _widget . screen _selector . show _popup ( 'help' ) ;
} ,
2012-07-09 15:53:51 +00:00
barcode _product _screen : 'products' , //if defined, this screen will be loaded when a product is scanned
2012-07-03 11:47:41 +00:00
2013-12-04 17:21:22 +00:00
hotkeys _handlers : { } ,
2012-07-03 11:47:41 +00:00
// what happens when a product is scanned :
2014-06-13 14:19:05 +00:00
// it will add the product to the order and go to barcode_product_screen.
2013-09-23 14:51:39 +00:00
barcode _product _action : function ( code ) {
2012-08-09 17:21:13 +00:00
var self = this ;
2013-09-23 14:51:39 +00:00
if ( self . pos . scan _product ( code ) ) {
2012-08-13 16:00:26 +00:00
if ( self . barcode _product _screen ) {
self . pos _widget . screen _selector . set _current _screen ( self . barcode _product _screen ) ;
}
} else {
2014-06-13 14:19:05 +00:00
self . pos _widget . screen _selector . show _popup ( 'error-barcode' , code . code ) ;
2012-08-13 16:00:26 +00:00
}
2012-07-03 11:47:41 +00:00
} ,
2013-09-23 14:51:39 +00:00
2012-07-03 11:47:41 +00:00
// what happens when a cashier id barcode is scanned.
// the default behavior is the following :
// - if there's a user with a matching ean, put it as the active 'cashier', go to cashier mode, and return true
// - else : do nothing and return false. You probably want to extend this to show and appropriate error popup...
2013-09-23 14:51:39 +00:00
barcode _cashier _action : function ( code ) {
2013-12-16 14:35:59 +00:00
var users = this . pos . users ;
2012-07-03 11:47:41 +00:00
for ( var i = 0 , len = users . length ; i < len ; i ++ ) {
2013-09-23 14:51:39 +00:00
if ( users [ i ] . ean13 === code . code ) {
2013-12-16 14:35:59 +00:00
this . pos . cashier = users [ i ] ;
2012-07-03 11:47:41 +00:00
this . pos _widget . username . refresh ( ) ;
return true ;
}
}
2014-06-13 14:19:05 +00:00
this . pos _widget . screen _selector . show _popup ( 'error-barcode' , code . code ) ;
2012-07-03 11:47:41 +00:00
return false ;
} ,
// what happens when a client id barcode is scanned.
// the default behavior is the following :
// - if there's a user with a matching ean, put it as the active 'client' and return true
// - else : return false.
2013-09-23 14:51:39 +00:00
barcode _client _action : function ( code ) {
2014-06-13 14:19:05 +00:00
var partner = this . pos . db . get _partner _by _ean13 ( code . code ) ;
if ( partner ) {
this . pos . get ( 'selectedOrder' ) . set _client ( partner ) ;
this . pos _widget . username . refresh ( ) ;
return true ;
2012-07-03 11:47:41 +00:00
}
2014-06-13 14:19:05 +00:00
this . pos _widget . screen _selector . show _popup ( 'error-barcode' , code . code ) ;
2012-07-03 11:47:41 +00:00
return false ;
} ,
// what happens when a discount barcode is scanned : the default behavior
// is to set the discount on the last order.
2013-09-23 14:51:39 +00:00
barcode _discount _action : function ( code ) {
2012-07-03 11:47:41 +00:00
var last _orderline = this . pos . get ( 'selectedOrder' ) . getLastOrderline ( ) ;
if ( last _orderline ) {
2013-09-23 14:51:39 +00:00
last _orderline . set _discount ( code . value )
2012-07-03 11:47:41 +00:00
}
} ,
2014-06-13 14:19:05 +00:00
// What happens when an invalid barcode is scanned : shows an error popup.
barcode _error _action : function ( code ) {
this . pos _widget . screen _selector . show _popup ( 'error-barcode' , code . code ) ;
} ,
2012-07-19 15:25:19 +00:00
// shows an action bar on the screen. The actionbar is automatically shown when you add a button
// with add_action_button()
show _action _bar : function ( ) {
this . pos _widget . action _bar . show ( ) ;
} ,
// hides the action bar. The actionbar is automatically hidden when it is empty
hide _action _bar : function ( ) {
this . pos _widget . action _bar . hide ( ) ;
} ,
// adds a new button to the action bar. The button definition takes three parameters, all optional :
// - label: the text below the button
// - icon: a small icon that will be shown
// - click: a callback that will be executed when the button is clicked.
// the method returns a reference to the button widget, and automatically show the actionbar.
add _action _button : function ( button _def ) {
this . show _action _bar ( ) ;
return this . pos _widget . action _bar . add _new _button ( button _def ) ;
} ,
2012-07-03 11:47:41 +00:00
// this method shows the screen and sets up all the widget related to this screen. Extend this method
// if you want to alter the behavior of the screen.
2012-05-03 17:15:23 +00:00
show : function ( ) {
2012-07-30 15:41:43 +00:00
var self = this ;
2012-05-23 15:25:35 +00:00
this . hidden = false ;
2012-08-24 18:27:43 +00:00
if ( this . $el ) {
2013-09-17 10:14:20 +00:00
this . $el . removeClass ( 'oe_hidden' ) ;
2012-05-03 17:15:23 +00:00
}
2012-07-03 11:47:41 +00:00
2012-07-19 15:25:19 +00:00
if ( this . pos _widget . action _bar . get _button _count ( ) > 0 ) {
this . show _action _bar ( ) ;
} else {
this . hide _action _bar ( ) ;
}
2012-07-30 15:41:43 +00:00
2012-07-03 11:47:41 +00:00
var self = this ;
2014-06-13 14:19:05 +00:00
this . pos _widget . set _numpad _visible ( this . show _numpad ) ;
2012-07-03 11:47:41 +00:00
this . pos _widget . set _leftpane _visible ( this . show _leftpane ) ;
2012-07-19 15:25:19 +00:00
2012-07-03 11:47:41 +00:00
this . pos _widget . username . set _user _mode ( this . pos _widget . screen _selector . get _user _mode ( ) ) ;
this . pos . barcode _reader . set _action _callback ( {
2013-09-23 14:51:39 +00:00
'cashier' : self . barcode _cashier _action ? function ( code ) { self . barcode _cashier _action ( code ) ; } : undefined ,
'product' : self . barcode _product _action ? function ( code ) { self . barcode _product _action ( code ) ; } : undefined ,
'client' : self . barcode _client _action ? function ( code ) { self . barcode _client _action ( code ) ; } : undefined ,
'discount' : self . barcode _discount _action ? function ( code ) { self . barcode _discount _action ( code ) ; } : undefined ,
2014-06-13 14:19:05 +00:00
'error' : self . barcode _error _action ? function ( code ) { self . barcode _error _action ( code ) ; } : undefined ,
2012-07-03 11:47:41 +00:00
} ) ;
2012-05-03 17:15:23 +00:00
} ,
2012-07-03 11:47:41 +00:00
// this method is called when the screen is closed to make place for a new screen. this is a good place
// to put your cleanup stuff as it is guaranteed that for each show() there is one and only one close()
close : function ( ) {
2012-05-03 17:15:23 +00:00
if ( this . pos . barcode _reader ) {
this . pos . barcode _reader . reset _action _callbacks ( ) ;
}
2012-07-30 15:41:43 +00:00
this . pos _widget . action _bar . destroy _buttons ( ) ;
2012-05-03 17:15:23 +00:00
} ,
2012-07-03 11:47:41 +00:00
// this methods hides the screen. It's not a good place to put your cleanup stuff as it is called on the
// POS initialization.
hide : function ( ) {
this . hidden = true ;
2012-08-24 18:27:43 +00:00
if ( this . $el ) {
2013-09-17 10:14:20 +00:00
this . $el . addClass ( 'oe_hidden' ) ;
2012-07-03 11:47:41 +00:00
}
2012-06-11 14:04:41 +00:00
} ,
2012-07-03 11:47:41 +00:00
2012-05-23 15:25:35 +00:00
// we need this because some screens re-render themselves when they are hidden
// (due to some events, or magic, or both...) we must make sure they remain hidden.
// the good solution would probably be to make them not re-render themselves when they
// are hidden.
renderElement : function ( ) {
this . _super ( ) ;
if ( this . hidden ) {
2012-08-24 18:27:43 +00:00
if ( this . $el ) {
2013-09-17 10:14:20 +00:00
this . $el . addClass ( 'oe_hidden' ) ;
2012-05-23 15:25:35 +00:00
}
}
} ,
2012-05-03 17:15:23 +00:00
} ) ;
2012-06-11 14:04:41 +00:00
module . PopUpWidget = module . PosBaseWidget . extend ( {
show : function ( ) {
2012-08-24 18:27:43 +00:00
if ( this . $el ) {
2013-09-17 10:14:20 +00:00
this . $el . removeClass ( 'oe_hidden' ) ;
2012-06-11 14:04:41 +00:00
}
} ,
2012-09-11 10:18:30 +00:00
/* called before hide, when a popup is closed */
close : function ( ) {
} ,
/ * h i d e s t h e p o p u p . k e e p i n m i n d t h a t t h i s i s c a l l e d i n t h e i n i t i a l i z a t i o n p a s s o f t h e
* pos instantiation , so you don ' t want to do anything fancy in here * /
2012-05-07 12:39:32 +00:00
hide : function ( ) {
2012-08-24 18:27:43 +00:00
if ( this . $el ) {
2013-09-17 10:14:20 +00:00
this . $el . addClass ( 'oe_hidden' ) ;
2012-05-07 12:39:32 +00:00
}
} ,
} ) ;
module . ErrorPopupWidget = module . PopUpWidget . extend ( {
template : 'ErrorPopupWidget' ,
2014-09-02 15:35:01 +00:00
show : function ( options ) {
options = options || { } ;
2012-05-08 15:40:30 +00:00
var self = this ;
2012-05-07 12:39:32 +00:00
this . _super ( ) ;
2014-06-13 14:19:05 +00:00
2014-07-16 15:11:18 +00:00
$ ( 'body' ) . append ( '<audio src="/point_of_sale/static/src/sounds/error.wav" autoplay="true"></audio>' ) ;
2014-09-02 15:35:01 +00:00
this . message = options . message || _t ( 'Error' ) ;
this . comment = options . comment || '' ;
this . renderElement ( ) ;
2012-05-08 15:40:30 +00:00
this . pos . barcode _reader . save _callbacks ( ) ;
this . pos . barcode _reader . reset _action _callbacks ( ) ;
2014-09-02 15:35:01 +00:00
this . $ ( '.footer .button' ) . click ( function ( ) {
2012-09-18 13:37:32 +00:00
self . pos _widget . screen _selector . close _popup ( ) ;
2014-09-02 15:35:01 +00:00
if ( options . confirm ) {
options . confirm . call ( self ) ;
}
2014-09-03 12:28:17 +00:00
} ) ;
2012-05-06 21:34:39 +00:00
} ,
2012-06-11 14:04:41 +00:00
close : function ( ) {
2012-05-07 12:39:32 +00:00
this . _super ( ) ;
2012-05-08 15:40:30 +00:00
this . pos . barcode _reader . restore _callbacks ( ) ;
2012-05-06 21:34:39 +00:00
} ,
} ) ;
2014-07-22 12:33:32 +00:00
module . ErrorTracebackPopupWidget = module . ErrorPopupWidget . extend ( {
template : 'ErrorTracebackPopupWidget' ,
} ) ;
2012-05-14 14:31:21 +00:00
2014-06-13 14:19:05 +00:00
module . ErrorBarcodePopupWidget = module . ErrorPopupWidget . extend ( {
template : 'ErrorBarcodePopupWidget' ,
show : function ( barcode ) {
2014-09-02 15:35:01 +00:00
this . barcode = barcode ;
2014-06-13 14:19:05 +00:00
this . _super ( ) ;
} ,
} ) ;
module . ConfirmPopupWidget = module . PopUpWidget . extend ( {
template : 'ConfirmPopupWidget' ,
show : function ( options ) {
var self = this ;
this . _super ( ) ;
this . message = options . message || '' ;
this . comment = options . comment || '' ;
this . renderElement ( ) ;
this . $ ( '.button.cancel' ) . click ( function ( ) {
self . pos _widget . screen _selector . close _popup ( ) ;
if ( options . cancel ) {
options . cancel . call ( self ) ;
}
} ) ;
this . $ ( '.button.confirm' ) . click ( function ( ) {
self . pos _widget . screen _selector . close _popup ( ) ;
if ( options . confirm ) {
options . confirm . call ( self ) ;
}
} ) ;
} ,
2012-11-19 16:09:00 +00:00
} ) ;
2013-04-11 12:07:19 +00:00
module . ErrorNoClientPopupWidget = module . ErrorPopupWidget . extend ( {
template : 'ErrorNoClientPopupWidget' ,
} ) ;
module . ErrorInvoiceTransferPopupWidget = module . ErrorPopupWidget . extend ( {
template : 'ErrorInvoiceTransferPopupWidget' ,
} ) ;
2012-05-03 17:15:23 +00:00
2014-07-22 13:27:34 +00:00
module . UnsentOrdersPopupWidget = module . PopUpWidget . extend ( {
template : 'UnsentOrdersPopupWidget' ,
show : function ( options ) {
var self = this ;
this . _super ( options ) ;
this . renderElement ( ) ;
this . $ ( '.button.confirm' ) . click ( function ( ) {
self . pos _widget . screen _selector . close _popup ( ) ;
} ) ;
} ,
} ) ;
2012-07-03 11:47:41 +00:00
module . ScaleScreenWidget = module . ScreenWidget . extend ( {
template : 'ScaleScreenWidget' ,
2012-07-17 16:25:17 +00:00
next _screen : 'products' ,
previous _screen : 'products' ,
2014-06-13 14:19:05 +00:00
show _leftpane : false ,
2012-07-03 11:47:41 +00:00
show : function ( ) {
this . _super ( ) ;
var self = this ;
2013-09-11 15:41:55 +00:00
var queue = this . pos . proxy _queue ;
2012-07-09 15:53:51 +00:00
2013-09-11 15:41:55 +00:00
this . set _weight ( 0 ) ;
this . renderElement ( ) ;
2012-07-19 15:25:19 +00:00
2013-11-06 16:17:23 +00:00
this . hotkey _handler = function ( event ) {
if ( event . which === 13 ) {
self . order _product ( ) ;
self . pos _widget . screen _selector . set _current _screen ( self . next _screen ) ;
} else if ( event . which === 27 ) {
self . pos _widget . screen _selector . set _current _screen ( self . previous _screen ) ;
}
} ;
$ ( 'body' ) . on ( 'keyup' , this . hotkey _handler ) ;
2014-06-13 14:19:05 +00:00
this . $ ( '.back' ) . click ( function ( ) {
self . pos _widget . screen _selector . set _current _screen ( self . previous _screen ) ;
} ) ;
this . $ ( '.next,.buy-product' ) . click ( function ( ) {
self . order _product ( ) ;
self . pos _widget . screen _selector . set _current _screen ( self . next _screen ) ;
} ) ;
2012-07-09 15:53:51 +00:00
2013-09-11 15:41:55 +00:00
queue . schedule ( function ( ) {
2014-04-24 17:12:59 +00:00
return self . pos . proxy . scale _read ( ) . then ( function ( weight ) {
self . set _weight ( weight . weight ) ;
2013-09-11 15:41:55 +00:00
} ) ;
} , { duration : 50 , repeat : true } ) ;
2012-07-09 15:53:51 +00:00
} ,
get _product : function ( ) {
var ss = this . pos _widget . screen _selector ;
if ( ss ) {
return ss . get _current _screen _param ( 'product' ) ;
} else {
return undefined ;
}
} ,
order _product : function ( ) {
2013-09-17 12:34:46 +00:00
this . pos . get ( 'selectedOrder' ) . addProduct ( this . get _product ( ) , { quantity : this . weight } ) ;
2012-07-09 15:53:51 +00:00
} ,
get _product _name : function ( ) {
var product = this . get _product ( ) ;
2014-08-18 09:35:24 +00:00
return ( product ? product . display _name : undefined ) || 'Unnamed Product' ;
2012-07-09 15:53:51 +00:00
} ,
get _product _price : function ( ) {
var product = this . get _product ( ) ;
2013-12-16 14:35:59 +00:00
return ( product ? product . price : 0 ) || 0 ;
2012-07-09 15:53:51 +00:00
} ,
2013-09-11 15:41:55 +00:00
set _weight : function ( weight ) {
this . weight = weight ;
2014-06-13 14:19:05 +00:00
this . $ ( '.weight' ) . text ( this . get _product _weight _string ( ) ) ;
this . $ ( '.computed-price' ) . text ( this . get _computed _price _string ( ) ) ;
2013-09-11 15:41:55 +00:00
} ,
2013-09-06 15:34:48 +00:00
get _product _weight _string : function ( ) {
2014-04-28 15:33:26 +00:00
var product = this . get _product ( ) ;
var defaultstr = ( this . weight || 0 ) . toFixed ( 3 ) + ' Kg' ;
if ( ! product || ! this . pos ) {
return defaultstr ;
}
var unit _id = product . uos _id || product . uom _id ;
if ( ! unit _id ) {
return defaultstr ;
}
var unit = this . pos . units _by _id [ unit _id [ 0 ] ] ;
var weight = round _pr ( this . weight || 0 , unit . rounding ) ;
var weightstr = weight . toFixed ( Math . ceil ( Math . log ( 1.0 / unit . rounding ) / Math . log ( 10 ) ) ) ;
weightstr += ' Kg' ;
return weightstr ;
2012-07-03 11:47:41 +00:00
} ,
2014-06-13 14:19:05 +00:00
get _computed _price _string : function ( ) {
return this . format _currency ( this . get _product _price ( ) * this . weight ) ;
2013-12-16 15:21:13 +00:00
} ,
2012-07-03 11:47:41 +00:00
close : function ( ) {
2013-09-11 15:41:55 +00:00
var self = this ;
2012-07-03 11:47:41 +00:00
this . _super ( ) ;
2013-11-06 16:17:23 +00:00
$ ( 'body' ) . off ( 'keyup' , this . hotkey _handler ) ;
2013-09-11 15:41:55 +00:00
this . pos . proxy _queue . clear ( ) ;
2012-07-03 11:47:41 +00:00
} ,
} ) ;
2012-07-09 15:53:51 +00:00
module . ProductScreenWidget = module . ScreenWidget . extend ( {
template : 'ProductScreenWidget' ,
2012-06-11 15:20:22 +00:00
show _numpad : true ,
show _leftpane : true ,
2012-06-28 12:38:25 +00:00
start : function ( ) { //FIXME this should work as renderElement... but then the categories aren't properly set. explore why
2012-07-09 15:53:51 +00:00
var self = this ;
2012-05-03 17:15:23 +00:00
2012-07-09 15:53:51 +00:00
this . product _list _widget = new module . ProductListWidget ( this , {
click _product _action : function ( product ) {
2013-12-16 14:35:59 +00:00
if ( product . to _weight && self . pos . config . iface _electronic _scale ) {
2014-06-13 14:19:05 +00:00
self . pos _widget . screen _selector . set _current _screen ( 'scale' , { product : product } ) ;
2012-07-09 15:53:51 +00:00
} else {
self . pos . get ( 'selectedOrder' ) . addProduct ( product ) ;
}
} ,
2013-12-02 16:21:44 +00:00
product _list : this . pos . db . get _product _by _category ( 0 )
2012-07-09 15:53:51 +00:00
} ) ;
2014-07-01 13:01:28 +00:00
this . product _list _widget . replace ( this . $ ( '.placeholder-ProductListWidget' ) ) ;
2013-12-02 16:21:44 +00:00
this . product _categories _widget = new module . ProductCategoriesWidget ( this , {
product _list _widget : this . product _list _widget ,
} ) ;
2014-07-01 13:01:28 +00:00
this . product _categories _widget . replace ( this . $ ( '.placeholder-ProductCategoriesWidget' ) ) ;
2012-05-03 17:15:23 +00:00
} ,
2012-06-11 15:20:22 +00:00
2012-05-03 17:15:23 +00:00
show : function ( ) {
this . _super ( ) ;
var self = this ;
2012-05-04 10:27:37 +00:00
2012-05-11 16:02:23 +00:00
this . product _categories _widget . reset _category ( ) ;
2012-05-04 10:27:37 +00:00
2013-12-02 16:21:44 +00:00
this . pos _widget . order _widget . set _editable ( true ) ;
2012-05-03 17:15:23 +00:00
} ,
2012-06-11 15:20:22 +00:00
2012-06-11 14:04:41 +00:00
close : function ( ) {
2012-05-03 17:15:23 +00:00
this . _super ( ) ;
2013-12-02 16:21:44 +00:00
this . pos _widget . order _widget . set _editable ( false ) ;
2013-12-16 14:35:59 +00:00
if ( this . pos . config . iface _vkeyboard && this . pos _widget . onscreen _keyboard ) {
2013-09-12 15:13:34 +00:00
this . pos _widget . onscreen _keyboard . hide ( ) ;
}
2012-05-03 17:15:23 +00:00
} ,
} ) ;
2014-06-13 14:19:05 +00:00
module . ClientListScreenWidget = module . ScreenWidget . extend ( {
template : 'ClientListScreenWidget' ,
2014-07-17 15:51:27 +00:00
init : function ( parent , options ) {
this . _super ( parent , options ) ;
} ,
2014-06-13 14:19:05 +00:00
show _leftpane : false ,
auto _back : true ,
show : function ( ) {
var self = this ;
this . _super ( ) ;
this . renderElement ( ) ;
this . details _visible = false ;
this . old _client = this . pos . get ( 'selectedOrder' ) . get ( 'client' ) ;
this . new _client = this . old _client ;
this . $ ( '.back' ) . click ( function ( ) {
self . pos _widget . screen _selector . back ( ) ;
} ) ;
this . $ ( '.next' ) . click ( function ( ) {
self . save _changes ( ) ;
self . pos _widget . screen _selector . back ( ) ;
} ) ;
2014-09-17 14:01:53 +00:00
this . $ ( '.new-customer' ) . click ( function ( ) {
self . display _client _details ( 'edit' , {
'country_id' : self . pos . company . country _id ,
} ) ;
} ) ;
2014-08-22 10:07:19 +00:00
var partners = this . pos . db . get _partners _sorted ( 1000 ) ;
2014-06-13 14:19:05 +00:00
this . render _list ( partners ) ;
2014-08-22 10:07:19 +00:00
2014-09-17 14:01:53 +00:00
this . reload _partners ( ) ;
2014-06-13 14:19:05 +00:00
if ( this . old _client ) {
this . display _client _details ( 'show' , this . old _client , 0 ) ;
}
this . $ ( '.client-list-contents' ) . delegate ( '.client-line' , 'click' , function ( event ) {
self . line _select ( event , $ ( this ) , parseInt ( $ ( this ) . data ( 'id' ) ) ) ;
} ) ;
var search _timeout = null ;
2014-09-08 15:03:21 +00:00
if ( this . pos . config . iface _vkeyboard && this . pos _widget . onscreen _keyboard ) {
this . pos _widget . onscreen _keyboard . connect ( this . $ ( '.searchbox input' ) ) ;
}
2014-06-13 14:19:05 +00:00
this . $ ( '.searchbox input' ) . on ( 'keyup' , function ( event ) {
clearTimeout ( search _timeout ) ;
var query = this . value ;
search _timeout = setTimeout ( function ( ) {
self . perform _search ( query , event . which === 13 ) ;
} , 70 ) ;
} ) ;
this . $ ( '.searchbox .search-clear' ) . click ( function ( ) {
self . clear _search ( ) ;
} ) ;
} ,
2014-09-17 14:01:53 +00:00
barcode _client _action : function ( code ) {
if ( this . editing _client ) {
this . $ ( '.detail.barcode' ) . val ( code . code ) ;
} else if ( this . pos . db . get _partner _by _ean13 ( code . code ) ) {
this . display _client _details ( 'show' , this . pos . db . get _partner _by _ean13 ( code . code ) ) ;
}
} ,
2014-06-13 14:19:05 +00:00
perform _search : function ( query , associate _result ) {
if ( query ) {
var customers = this . pos . db . search _partner ( query ) ;
this . display _client _details ( 'hide' ) ;
if ( associate _result && customers . length === 1 ) {
this . new _client = customers [ 0 ] ;
this . save _changes ( ) ;
this . pos _widget . screen _selector . back ( ) ;
}
this . render _list ( customers ) ;
} else {
var customers = this . pos . db . get _partners _sorted ( ) ;
this . render _list ( customers ) ;
}
} ,
clear _search : function ( ) {
2014-08-22 10:07:19 +00:00
var customers = this . pos . db . get _partners _sorted ( 1000 ) ;
2014-06-13 14:19:05 +00:00
this . render _list ( customers ) ;
this . $ ( '.searchbox input' ) [ 0 ] . value = '' ;
this . $ ( '.searchbox input' ) . focus ( ) ;
} ,
render _list : function ( partners ) {
2014-07-17 15:51:27 +00:00
var contents = this . $el [ 0 ] . querySelector ( '.client-list-contents' ) ;
2014-08-04 09:09:47 +00:00
contents . innerHTML = "" ;
for ( var i = 0 , len = Math . min ( partners . length , 1000 ) ; i < len ; i ++ ) {
2014-07-17 15:51:27 +00:00
var partner = partners [ i ] ;
2014-08-22 10:07:19 +00:00
var clientline _html = QWeb . render ( 'ClientLine' , { widget : this , partner : partners [ i ] } ) ;
var clientline = document . createElement ( 'tbody' ) ;
clientline . innerHTML = clientline _html ;
clientline = clientline . childNodes [ 1 ] ;
2014-07-17 15:51:27 +00:00
if ( partners === this . new _client ) {
clientline . classList . add ( 'highlight' ) ;
} else {
clientline . classList . remove ( 'highlight' ) ;
2014-06-13 14:19:05 +00:00
}
2014-08-22 10:07:19 +00:00
2014-07-17 15:51:27 +00:00
contents . appendChild ( clientline ) ;
2014-06-13 14:19:05 +00:00
}
} ,
save _changes : function ( ) {
if ( this . has _client _changed ( ) ) {
this . pos . get ( 'selectedOrder' ) . set _client ( this . new _client ) ;
}
} ,
has _client _changed : function ( ) {
if ( this . old _client && this . new _client ) {
return this . old _client . id !== this . new _client . id ;
} else {
return ! ! this . old _client !== ! ! this . new _client ;
}
} ,
toggle _save _button : function ( ) {
var $button = this . $ ( '.button.next' ) ;
2014-09-17 14:01:53 +00:00
if ( this . editing _client ) {
$button . addClass ( 'oe_hidden' ) ;
return ;
} else if ( this . new _client ) {
2014-06-13 14:19:05 +00:00
if ( ! this . old _client ) {
$button . text ( _t ( 'Set Customer' ) ) ;
} else {
$button . text ( _t ( 'Change Customer' ) ) ;
}
} else {
$button . text ( _t ( 'Deselect Customer' ) ) ;
}
$button . toggleClass ( 'oe_hidden' , ! this . has _client _changed ( ) ) ;
} ,
line _select : function ( event , $line , id ) {
var partner = this . pos . db . get _partner _by _id ( id ) ;
this . $ ( '.client-list .lowlight' ) . removeClass ( 'lowlight' ) ;
if ( $line . hasClass ( 'highlight' ) ) {
$line . removeClass ( 'highlight' ) ;
$line . addClass ( 'lowlight' ) ;
this . display _client _details ( 'hide' , partner ) ;
this . new _client = null ;
this . toggle _save _button ( ) ;
} else {
this . $ ( '.client-list .highlight' ) . removeClass ( 'highlight' ) ;
$line . addClass ( 'highlight' ) ;
var y = event . pageY - $line . parent ( ) . offset ( ) . top
this . display _client _details ( 'show' , partner , y ) ;
this . new _client = partner ;
this . toggle _save _button ( ) ;
}
} ,
partner _icon _url : function ( id ) {
return '/web/binary/image?model=res.partner&id=' + id + '&field=image_small' ;
} ,
2014-09-17 14:01:53 +00:00
// ui handle for the 'edit selected customer' action
edit _client _details : function ( partner ) {
this . display _client _details ( 'edit' , partner ) ;
} ,
// ui handle for the 'cancel customer edit changes' action
undo _client _details : function ( partner ) {
if ( ! partner . id ) {
this . display _client _details ( 'hide' ) ;
} else {
this . display _client _details ( 'show' , partner ) ;
}
} ,
// what happens when we save the changes on the client edit form -> we fetch the fields, sanitize them,
// send them to the backend for update, and call saved_client_details() when the server tells us the
// save was successfull.
save _client _details : function ( partner ) {
var self = this ;
var fields = { }
this . $ ( '.client-details-contents .detail' ) . each ( function ( idx , el ) {
fields [ el . name ] = el . value ;
} ) ;
if ( ! fields . name ) {
this . pos _widget . screen _selector . show _popup ( 'error' , {
message : _t ( 'A Customer Name Is Required' ) ,
} ) ;
return ;
}
if ( this . uploaded _picture ) {
fields . image = this . uploaded _picture ;
}
fields . id = partner . id || false ;
fields . country _id = fields . country _id || false ;
fields . ean13 = fields . ean13 ? this . pos . barcode _reader . sanitize _ean ( fields . ean13 ) : false ;
new instance . web . Model ( 'res.partner' ) . call ( 'create_from_ui' , [ fields ] ) . then ( function ( partner _id ) {
self . saved _client _details ( partner _id ) ;
} ) ;
} ,
// what happens when we've just pushed modifications for a partner of id partner_id
saved _client _details : function ( partner _id ) {
var self = this ;
this . reload _partners ( ) . then ( function ( ) {
var partner = self . pos . db . get _partner _by _id ( partner _id ) ;
if ( partner ) {
self . new _client = partner ;
self . toggle _save _button ( ) ;
self . display _client _details ( 'show' , partner ) ;
} else {
// should never happen, because create_from_ui must return the id of the partner it
// has created, and reload_partner() must have loaded the newly created partner.
self . display _client _details ( 'hide' ) ;
}
} ) ;
} ,
// resizes an image, keeping the aspect ratio intact,
// the resize is useful to avoid sending 12Mpixels jpegs
// over a wireless connection.
resize _image _to _dataurl : function ( img , maxwidth , maxheight , callback ) {
img . onload = function ( ) {
var png = new Image ( ) ;
var canvas = document . createElement ( 'canvas' ) ;
var ctx = canvas . getContext ( '2d' ) ;
var ratio = 1 ;
if ( img . width > maxwidth ) {
ratio = maxwidth / img . width ;
}
if ( img . height * ratio > maxheight ) {
ratio = maxheight / img . height ;
}
var width = Math . floor ( img . width * ratio ) ;
var height = Math . floor ( img . height * ratio ) ;
canvas . width = width ;
canvas . height = height ;
ctx . drawImage ( img , 0 , 0 , width , height ) ;
var dataurl = canvas . toDataURL ( ) ;
callback ( dataurl ) ;
}
} ,
// Loads and resizes a File that contains an image.
// callback gets a dataurl in case of success.
load _image _file : function ( file , callback ) {
var self = this ;
if ( ! file . type . match ( /image.*/ ) ) {
this . pos _widget . screen _selector . show _popup ( 'error' , {
message : _t ( 'Unsupported File Format' ) ,
comment : _t ( 'Only web-compatible Image formats such as .png or .jpeg are supported' ) ,
} ) ;
return ;
}
var reader = new FileReader ( ) ;
reader . onload = function ( event ) {
var dataurl = event . target . result ;
var img = new Image ( ) ;
img . src = dataurl ;
self . resize _image _to _dataurl ( img , 800 , 600 , callback ) ;
}
reader . onerror = function ( ) {
self . pos _widget . screen _selector . show _popup ( 'error' , {
message : _t ( 'Could Not Read Image' ) ,
comment : _t ( 'The provided file could not be read due to an unknown error' ) ,
} ) ;
} ;
reader . readAsDataURL ( file ) ;
} ,
// This fetches partner changes on the server, and in case of changes,
// rerenders the affected views
reload _partners : function ( ) {
var self = this ;
return this . pos . load _new _partners ( ) . then ( function ( ) {
self . render _list ( self . pos . db . get _partners _sorted ( 1000 ) ) ;
// update the currently assigned client if it has been changed in db.
var curr _client = self . pos . get _order ( ) . get _client ( ) ;
if ( curr _client ) {
self . pos . get _order ( ) . set _client ( self . pos . db . get _partner _by _id ( curr _client . id ) ) ;
}
} ) ;
} ,
// Shows,hides or edit the customer details box :
// visibility: 'show', 'hide' or 'edit'
// partner: the partner object to show or edit
// clickpos: the height of the click on the list (in pixel), used
// to maintain consistent scroll.
2014-06-13 14:19:05 +00:00
display _client _details : function ( visibility , partner , clickpos ) {
2014-09-17 14:01:53 +00:00
var self = this ;
var contents = this . $ ( '.client-details-contents' ) ;
var parent = this . $ ( '.client-list' ) . parent ( ) ;
var scroll = parent . scrollTop ( ) ;
var height = contents . height ( ) ;
contents . off ( 'click' , '.button.edit' ) ;
contents . off ( 'click' , '.button.save' ) ;
contents . off ( 'click' , '.button.undo' ) ;
contents . on ( 'click' , '.button.edit' , function ( ) { self . edit _client _details ( partner ) ; } ) ;
contents . on ( 'click' , '.button.save' , function ( ) { self . save _client _details ( partner ) ; } ) ;
contents . on ( 'click' , '.button.undo' , function ( ) { self . undo _client _details ( partner ) ; } ) ;
this . editing _client = false ;
this . uploaded _picture = null ;
2014-06-13 14:19:05 +00:00
if ( visibility === 'show' ) {
contents . empty ( ) ;
2014-09-17 14:01:53 +00:00
contents . append ( $ ( QWeb . render ( 'ClientDetails' , { widget : this , partner : partner } ) ) ) ;
2014-06-13 14:19:05 +00:00
var new _height = contents . height ( ) ;
if ( ! this . details _visible ) {
2014-09-17 14:01:53 +00:00
if ( clickpos < scroll + new _height + 20 ) {
2014-06-13 14:19:05 +00:00
parent . scrollTop ( clickpos - 20 ) ;
} else {
parent . scrollTop ( parent . scrollTop ( ) + new _height ) ;
}
} else {
2014-09-17 14:01:53 +00:00
parent . scrollTop ( parent . scrollTop ( ) - height + new _height ) ;
2014-06-13 14:19:05 +00:00
}
this . details _visible = true ;
2014-09-17 14:01:53 +00:00
this . toggle _save _button ( ) ;
} else if ( visibility === 'edit' ) {
this . editing _client = true ;
contents . empty ( ) ;
contents . append ( $ ( QWeb . render ( 'ClientDetailsEdit' , { widget : this , partner : partner } ) ) ) ;
this . toggle _save _button ( ) ;
contents . find ( '.image-uploader' ) . on ( 'change' , function ( ) {
self . load _image _file ( event . target . files [ 0 ] , function ( res ) {
if ( res ) {
contents . find ( '.client-picture img, .client-picture .fa' ) . remove ( ) ;
contents . find ( '.client-picture' ) . append ( "<img src='" + res + "'>" ) ;
contents . find ( '.detail.picture' ) . remove ( ) ;
self . uploaded _picture = res ;
}
} ) ;
} ) ;
} else if ( visibility === 'hide' ) {
2014-06-13 14:19:05 +00:00
contents . empty ( ) ;
if ( height > scroll ) {
contents . css ( { height : height + 'px' } ) ;
contents . animate ( { height : 0 } , 400 , function ( ) {
contents . css ( { height : '' } ) ;
} ) ;
} else {
parent . scrollTop ( parent . scrollTop ( ) - height ) ;
}
this . details _visible = false ;
2014-09-17 14:01:53 +00:00
this . toggle _save _button ( ) ;
2014-06-13 14:19:05 +00:00
}
} ,
close : function ( ) {
this . _super ( ) ;
} ,
} ) ;
2012-07-03 11:47:41 +00:00
module . ReceiptScreenWidget = module . ScreenWidget . extend ( {
2012-05-03 17:15:23 +00:00
template : 'ReceiptScreenWidget' ,
2012-06-11 15:20:22 +00:00
show _numpad : true ,
show _leftpane : true ,
2012-05-04 10:27:37 +00:00
show : function ( ) {
this . _super ( ) ;
2012-05-06 21:34:39 +00:00
var self = this ;
2012-05-04 10:27:37 +00:00
2013-04-11 12:07:19 +00:00
var print _button = this . add _action _button ( {
2013-02-18 05:54:02 +00:00
label : _t ( 'Print' ) ,
2012-05-24 14:14:08 +00:00
icon : '/point_of_sale/static/src/img/icons/png48/printer.png' ,
click : function ( ) { self . print ( ) ; } ,
} ) ;
2013-04-11 12:07:19 +00:00
var finish _button = this . add _action _button ( {
2013-02-18 05:54:02 +00:00
label : _t ( 'Next Order' ) ,
2012-05-24 14:14:08 +00:00
icon : '/point_of_sale/static/src/img/icons/png48/go-next.png' ,
click : function ( ) { self . finishOrder ( ) ; } ,
} ) ;
2012-08-20 10:24:01 +00:00
2013-12-04 17:21:22 +00:00
this . refresh ( ) ;
2013-07-04 14:12:56 +00:00
this . print ( ) ;
2013-04-11 12:07:19 +00:00
//
// The problem is that in chrome the print() is asynchronous and doesn't
// execute until all rpc are finished. So it conflicts with the rpc used
// to send the orders to the backend, and the user is able to go to the next
// screen before the printing dialog is opened. The problem is that what's
// printed is whatever is in the page when the dialog is opened and not when it's called,
// and so you end up printing the product list instead of the receipt...
//
// Fixing this would need a re-architecturing
// of the code to postpone sending of orders after printing.
//
// But since the print dialog also blocks the other asynchronous calls, the
// button enabling in the setTimeout() is blocked until the printing dialog is
2013-04-11 12:22:50 +00:00
// closed. But the timeout has to be big enough or else it doesn't work
// 2 seconds is the same as the default timeout for sending orders and so the dialog
// should have appeared before the timeout... so yeah that's not ultra reliable.
2013-04-11 12:07:19 +00:00
finish _button . set _disabled ( true ) ;
setTimeout ( function ( ) {
finish _button . set _disabled ( false ) ;
} , 2000 ) ;
2012-05-04 10:27:37 +00:00
} ,
2012-05-03 17:15:23 +00:00
print : function ( ) {
window . print ( ) ;
} ,
finishOrder : function ( ) {
this . pos . get ( 'selectedOrder' ) . destroy ( ) ;
} ,
refresh : function ( ) {
2013-12-04 17:21:22 +00:00
var order = this . pos . get ( 'selectedOrder' ) ;
$ ( '.pos-receipt-container' , this . $el ) . html ( QWeb . render ( 'PosTicket' , {
widget : this ,
order : order ,
orderlines : order . get ( 'orderLines' ) . models ,
paymentlines : order . get ( 'paymentLines' ) . models ,
} ) ) ;
2012-05-03 17:15:23 +00:00
} ,
2013-11-27 14:44:27 +00:00
close : function ( ) {
this . _super ( ) ;
}
2012-05-03 17:15:23 +00:00
} ) ;
2014-06-13 14:19:05 +00:00
2012-07-03 11:47:41 +00:00
module . PaymentScreenWidget = module . ScreenWidget . extend ( {
2012-05-14 14:03:40 +00:00
template : 'PaymentScreenWidget' ,
2012-07-17 16:25:17 +00:00
back _screen : 'products' ,
next _screen : 'receipt' ,
2012-05-03 17:15:23 +00:00
init : function ( parent , options ) {
2013-12-04 17:21:22 +00:00
var self = this ;
2012-05-03 17:15:23 +00:00
this . _super ( parent , options ) ;
2013-12-04 17:21:22 +00:00
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 ) ;
}
} ;
2013-11-06 16:17:23 +00:00
this . hotkey _handler = function ( event ) {
if ( event . which === 13 ) {
2013-12-05 17:53:25 +00:00
self . validate _order ( ) ;
2013-11-06 16:17:23 +00:00
} else if ( event . which === 27 ) {
self . back ( ) ;
}
} ;
2013-12-05 17:53:25 +00:00
} ,
show : function ( ) {
this . _super ( ) ;
var self = this ;
this . enable _numpad ( ) ;
this . focus _selected _line ( ) ;
document . body . addEventListener ( 'keyup' , this . hotkey _handler ) ;
2013-12-02 16:21:44 +00:00
2013-04-11 12:22:50 +00:00
this . add _action _button ( {
2013-02-18 05:54:02 +00:00
label : _t ( 'Back' ) ,
2012-05-24 13:39:30 +00:00
icon : '/point_of_sale/static/src/img/icons/png48/go-previous.png' ,
click : function ( ) {
2013-11-06 16:17:23 +00:00
self . back ( ) ;
2012-05-24 13:39:30 +00:00
} ,
} ) ;
2013-07-11 08:45:47 +00:00
2013-04-11 12:22:50 +00:00
this . add _action _button ( {
2013-02-18 05:54:02 +00:00
label : _t ( 'Validate' ) ,
2012-08-31 14:31:59 +00:00
name : 'validation' ,
2012-05-24 13:39:30 +00:00
icon : '/point_of_sale/static/src/img/icons/png48/validate.png' ,
click : function ( ) {
2013-12-04 17:21:22 +00:00
self . validate _order ( ) ;
2012-05-24 13:39:30 +00:00
} ,
} ) ;
2013-04-11 12:22:50 +00:00
2013-12-16 14:35:59 +00:00
if ( this . pos . config . iface _invoicing ) {
2013-04-11 12:22:50 +00:00
this . add _action _button ( {
label : 'Invoice' ,
name : 'invoice' ,
icon : '/point_of_sale/static/src/img/icons/png48/invoice.png' ,
click : function ( ) {
2013-12-04 17:21:22 +00:00
self . validate _order ( { invoice : true } ) ;
2013-04-11 12:22:50 +00:00
} ,
} ) ;
}
2012-08-31 14:56:06 +00:00
2013-12-16 14:35:59 +00:00
if ( this . pos . config . iface _cashdrawer ) {
2013-11-12 15:10:41 +00:00
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 ( ) ;
} ,
} ) ;
}
2013-12-05 17:53:25 +00:00
this . update _payment _summary ( ) ;
2012-05-03 17:15:23 +00:00
} ,
2012-06-11 14:04:41 +00:00
close : function ( ) {
2012-05-03 17:15:23 +00:00
this . _super ( ) ;
2013-12-02 16:21:44 +00:00
this . disable _numpad ( ) ;
2013-12-05 17:53:25 +00:00
document . body . removeEventListener ( 'keyup' , this . hotkey _handler ) ;
2013-12-04 17:21:22 +00:00
} ,
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 ) ;
}
}
2012-05-03 17:15:23 +00:00
} ,
back : function ( ) {
2013-12-04 17:21:22 +00:00
this . remove _empty _lines ( ) ;
2013-11-06 16:17:23 +00:00
this . pos _widget . screen _selector . set _current _screen ( this . back _screen ) ;
2012-05-03 17:15:23 +00:00
} ,
2013-12-04 17:21:22 +00:00
bind _events : function ( ) {
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 ) ;
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 ( ) ;
}
}
} ,
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 ] ) ) ;
}
2013-12-05 17:53:25 +00:00
this . update _payment _summary ( ) ;
2013-12-04 17:21:22 +00:00
} ,
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' ) ;
2014-02-11 16:45:41 +00:00
return ( currentOrder . getTotalTaxIncluded ( ) < 0.000001
|| currentOrder . getPaidTotal ( ) + 0.000001 >= currentOrder . getTotalTaxIncluded ( ) ) ;
2013-12-04 17:21:22 +00:00
} ,
validate _order : function ( options ) {
2013-04-11 12:07:19 +00:00
var self = this ;
2013-03-27 15:40:54 +00:00
options = options || { } ;
2012-05-21 16:57:21 +00:00
var currentOrder = this . pos . get ( 'selectedOrder' ) ;
2013-12-02 16:21:44 +00:00
if ( ! this . is _paid ( ) ) {
2013-11-06 16:17:23 +00:00
return ;
2014-02-03 15:31:02 +00:00
}
2014-09-08 10:49:34 +00:00
// The exact amount must be paid if there is no cash payment method defined.
if ( Math . abs ( currentOrder . getTotalTaxIncluded ( ) - currentOrder . getPaidTotal ( ) ) > 0.00001 ) {
var cash = false ;
for ( var i = 0 ; i < this . pos . cashregisters . length ; i ++ ) {
cash = cash || ( this . pos . cashregisters [ i ] . journal . type === 'cash' ) ;
}
if ( ! cash ) {
this . pos _widget . screen _selector . show _popup ( 'error' , {
message : _t ( 'Cannot return change without a cash payment method' ) ,
comment : _t ( 'There is no cash payment method available in this point of sale to handle the change.\n\n Please pay the exact amount or add a cash payment method in the point of sale configuration' ) ,
} ) ;
return ;
}
}
2014-09-17 14:14:30 +00:00
if ( this . pos . config . iface _cashdrawer ) {
2014-02-03 15:31:02 +00:00
this . pos . proxy . open _cashbox ( ) ;
2013-11-06 16:17:23 +00:00
}
2013-04-11 12:07:19 +00:00
2013-03-27 15:40:54 +00:00
if ( options . invoice ) {
2013-04-11 12:07:19 +00:00
// 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 ) ;
invoiced . fail ( function ( error ) {
if ( error === 'error-no-client' ) {
2014-09-02 15:35:01 +00:00
self . pos _widget . screen _selector . show _popup ( 'error' , {
message : _t ( 'An anonymous order cannot be invoiced' ) ,
comment : _t ( 'Please select a client for this order. This can be done by clicking the order tab' ) ,
} ) ;
2013-04-11 12:07:19 +00:00
} else {
2014-09-02 15:35:01 +00:00
self . pos _widget . screen _selector . show _popup ( 'error' , {
message : _t ( 'The order could not be sent' ) ,
comment : _t ( 'Check your internet connection and try again.' ) ,
} ) ;
2013-04-11 12:07:19 +00:00
}
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 ( ) ;
} ) ;
2012-08-14 15:21:12 +00:00
} else {
2013-04-11 12:07:19 +00:00
this . pos . push _order ( currentOrder )
2013-12-16 14:35:59 +00:00
if ( this . pos . config . iface _print _via _proxy ) {
2014-03-20 16:45:30 +00:00
var receipt = currentOrder . export _for _printing ( ) ;
this . pos . proxy . print _receipt ( QWeb . render ( 'XmlReceipt' , {
2014-06-13 14:19:05 +00:00
receipt : receipt , widget : self ,
2014-03-20 16:45:30 +00:00
} ) ) ;
2013-04-11 12:07:19 +00:00
this . pos . get ( 'selectedOrder' ) . destroy ( ) ; //finish order and go back to scan screen
} else {
this . pos _widget . screen _selector . set _current _screen ( this . next _screen ) ;
}
2012-08-14 15:21:12 +00:00
}
2013-12-04 17:21:22 +00:00
2013-11-27 14:44:27 +00:00
// hide onscreen (iOS) keyboard
setTimeout ( function ( ) {
document . activeElement . blur ( ) ;
$ ( "input" ) . blur ( ) ;
} , 250 ) ;
2012-05-03 17:15:23 +00:00
} ,
2013-12-02 16:21:44 +00:00
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 ) ;
2012-08-31 14:56:06 +00:00
}
2012-05-03 17:15:23 +00:00
} ,
2013-12-02 16:21:44 +00:00
set _mode _back _to _payment : function ( ) {
this . numpad _state . set ( { mode : 'payment' } ) ;
2012-05-03 17:15:23 +00:00
} ,
2012-05-11 16:02:23 +00:00
set _value : function ( val ) {
2013-12-04 17:21:22 +00:00
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 ) ;
}
2012-05-03 17:15:23 +00:00
} ,
} ) ;
}