[IMP] point_of_sale: refactored Orderline and Paymentline, new OrderWidget design

bzr revid: fva@openerp.com-20120703114741-3xixto34i2wqfy0v
This commit is contained in:
Frédéric van der Essen 2012-07-03 13:47:41 +02:00
parent c753d215dd
commit 0e255c2b90
9 changed files with 558 additions and 408 deletions

View File

@ -83,7 +83,7 @@ Main features :
'certificate' : '001156338024966477869',
# Web client
'js': [
'static/lib/backbone/backbone-0.5.3.js',
'static/lib/backbone/backbone-0.9.2.js',
'static/lib/mousewheel/jquery.mousewheel-3.0.6.js',
'static/src/js/pos_models.js',
'static/src/js/pos_basewidget.js',

View File

@ -12,6 +12,9 @@
height: 100%;
-webkit-user-select: none;
}
/* ********* The black loading screen ********* */
.point-of-sale .loader{
background-color: #222;
position:absolute;
@ -28,16 +31,8 @@
left:50%;
}
.point-of-sale table {
border-spacing: 0;
border-collapse: collapse;
}
.point-of-sale td {
border: 1px solid #e9eaec;
}
.point-of-sale input {
color: #555555;
}
/* ********* Generic element styling ********* */
.point-of-sale a {
text-decoration: none;
color: #555555;
@ -62,19 +57,16 @@
.point-of-sale li {
list-style-type: none;
}
.point-of-sale button img {
vertical-align: bottom;
}
.point-of-sale .pos-right-align {
text-align: right;
}
.point-of-sale .pos-right-align input {
text-align: right;
}
.point-of-sale #container {
width: 100%;
height: 100%;
}
/* ********* The black header bar ********* */
.point-of-sale #topheader {
position:absolute;
left:0;
@ -90,16 +82,9 @@
background: -moz-linear-gradient(#7b7979, #393939);
background: -webkit-gradient(linear, left top, left bottom, from(#7b7979), to(#393939));
}
.point-of-sale #topheader button {
color: black;
height:29px;
margin:2px;
margin-right:0px;
border: 1px solid black;
background: #7f82ac;
background: -moz-linear-gradient(#b2b3d7, #7f82ac);
background: -webkit-gradient(linear, left top, left bottom, from(#b2b3d7), to(#7f82ac));
}
/* a) The left part of the top-bar */
.point-of-sale #branding{
position: absolute;
display: table-cell;
@ -120,6 +105,17 @@
margin-left:5px;
vertical-align:middle;
}
.point-of-sale #branding .username{
float:right;
color:#DDD;
font-size:16px;
margin-right:32px;
margin-top:10px;
font-style:italic;
}
/* b) The right part of the top-bar */
.point-of-sale #rightheader {
position: absolute;
left:440px;
@ -127,23 +123,75 @@
top:0;
height:100%;
}
.point-of-sale #neworder-button {
.point-of-sale #rightheader button {
color: black;
height:29px;
margin:2px;
margin-right:0px;
border: 1px solid black;
background: #7f82ac;
background: -moz-linear-gradient(#b2b3d7, #7f82ac);
background: -webkit-gradient(linear, left top, left bottom, from(#b2b3d7), to(#7f82ac));
}
.point-of-sale #rightheader button.neworder-button {
width: 32px;
margin-left:5px;
margin-left:4px;
margin-right:4px;
}
.point-of-sale #loggedas {
float: right;
.point-of-sale div#order-selector {
display: inline;
}
.point-of-sale #loggedas p {
margin: 0 0 3px 0;
.point-of-sale ol#orders {
display: inline;
}
.point-of-sale li.order-selector-button {
display: inline;
}
.point-of-sale li.selected-order button {
font-weight: 900;
}
/* c) The notifications indicator */
.point-of-sale .oe_pos_synch-notification{
float:right;
color: rgba(255,255,255,0.4);
padding: 8px;
line-height:16px;
font-size:16px;
vertical-align:middle;
font-style: italic;
cursor:pointer;
}
.point-of-sale .oe_pos_synch-notification .oe_status_red{
display:inline-block;
cursor:pointer;
width:16px; height:16px;
background: url("../img/gtk-no.png") no-repeat ;
}
.point-of-sale .oe_pos_synch-notification .oe_status_green{
display:inline-block;
width:16px; height:16px;
background: url("../img/gtk-yes.png") no-repeat;
}
/* ********* Contains everything below the bar ********* */
.point-of-sale #content {
width: 100%;
position: absolute;
top: 35px;
bottom: 0;
background: #F0EEEE; //#E6E4E4; //yellow; //#F0EEEE;
background: #F0EEEE;
}
/* ********* The leftpane contains the order, numpad and paypad ********* */
.point-of-sale #leftpane {
-webkit-box-sizing:border-box;
position:absolute;
@ -151,9 +199,6 @@
width:440px;
top:0px;
bottom:105px;
/*height: 87%;
width: 440px;
position: relative;*/
border-right: solid 1px #CECBCB;
background-color: white;
}
@ -165,6 +210,9 @@
background: #F0EEEE;
white-space: nowrap;
}
/* ********* The paypad contains the payment buttons ********* */
.point-of-sale #paypad {
padding: 8px 4px 8px 8px;
display: inline-block;
@ -179,6 +227,7 @@
vertical-align: middle;
color: #555555;
border-top: 1px solid #efefef;
font-size: 14px;
}
.point-of-sale #paypad button:hover {
color: white;
@ -186,6 +235,9 @@
background: -moz-linear-gradient(#9d9fc5, #7f82ac);
background: -webkit-gradient(linear, left top, left bottom, from(#9d9fc5), to(#7f82ac));
}
/* ********* The Numpad ********* */
.point-of-sale #numpad {
padding: 8px 8px 8px 4px;
display: inline-block;
@ -212,15 +264,15 @@
background: -moz-linear-gradient(#9d9fc5, #7f82ac);
background: -webkit-gradient(linear, left top, left bottom, from(#9d9fc5), to(#7f82ac));
}
.point-of-sale .payment-button {
font-size: 14px;
}
.point-of-sale .input-button {
font-size: 24px;
}
.point-of-sale .mode-button, .point-of-sale #numpad-delete, .point-of-sale #numpad-minus {
font-size: 14px;
}
/* ********* The right pane contains the screens and headers ********* */
.point-of-sale #rightpane {
position: absolute;
top: 0;
@ -228,8 +280,8 @@
left: 440px;
right: 0;
vertical-align: top;
/*border-left: solid 1px #FFF;*/
}
.point-of-sale #rightpane header {
padding: 0;
height: 32px;
@ -238,9 +290,13 @@
background: -moz-linear-gradient(white, #d3d3d3);
background: -webkit-gradient(linear, left top, left bottom, from(white), to(#d3d3d3));
}
/* ********* The product list ********* */
.point-of-sale .product-list {
padding:10px;
}
.point-of-sale .product-list-scroller{
width:100%;
height:100%;
@ -252,8 +308,10 @@
bottom:0px;
left:0px;
right:0px;
//background:url('../img/bg_callout_gradient_scratched_stars.png');
}
/* a) the product list navigation bar */
.point-of-sale .breadcrumb li {
float: left;
line-height: 32px;
@ -279,6 +337,9 @@
height: 19px;
margin: 6px 0;
}
/* b) the search box */
.point-of-sale .searchbox {
position: absolute;
right: 2px;
@ -301,6 +362,9 @@
cursor: pointer;
display: none;
}
/* c) the categories list */
.point-of-sale #categories {
background:#f0f0f0;
border-bottom: 1px solid #cecbcb;
@ -320,6 +384,9 @@
margin: 4px 0;
font-size: 12px;
}
/* d) the product */
.point-of-sale .product {
position:relative;
vertical-align: top;
@ -337,6 +404,7 @@
-webkit-box-shadow: 0px 1px 8px rgba(0,0,0,0.2);
-box-shadow: 0px 1px 8px rgba(0,0,0,0.9);
}
.point-of-sale .product .product-img {
position: relative;
width: 120px;
@ -345,6 +413,7 @@
text-align: center;
-webkit-filter: blur(3px);
}
.point-of-sale .product .price-tag {
position: absolute;
top: 2px;
@ -357,6 +426,7 @@
-webkit-border-radius: 3px;
border-radius: 3px;
}
.point-of-sale .product .price-subtag {
position: absolute;
top: 24px;
@ -369,6 +439,7 @@
-webkit-border-radius: 3px;
border-radius: 3px;
}
.point-of-sale .product .product-name {
position: absolute;
-webkit-box-sizing: border-box;
@ -380,26 +451,9 @@
padding: 3px;
padding-top:15px;
}
.point-of-sale #login-form label, .point-of-sale #login-form input {
display: block;
}
.point-of-sale #login-form input {
margin: 4px 0 12px;
padding: 4px;
width: 96%;
}
.point-of-sale div#order-selector {
display: inline;
}
.point-of-sale ol#orders {
display: inline;
}
.point-of-sale li.order-selector-button {
display: inline;
}
.point-of-sale li.selected-order button {
font-weight: 900;
}
/* ********* The Screens ********* */
.point-of-sale .screen {
position:absolute;
@ -416,6 +470,8 @@
font-size: 18px;
}
/* a) Layout for the Product Screen */
.point-of-sale .screen .layout-table {
border:none;
width:100%;
@ -442,6 +498,8 @@
position:relative;
}
/* b) The payment screen */
.point-of-sale .pos-step-container {
display: inline-block;
font-size: 1.5em;
@ -473,6 +531,13 @@
font-size: 0.8em;
font-weight: bold;
}
/* c) The receipt screen */
.point-of-sale .pos-receipt-container {
font-size: 0.75em;
}
.point-of-sale .pos-sale-ticket {
text-align: left;
width: 300px;
@ -488,25 +553,6 @@
.point-of-sale .pos-sale-ticket table td {
border: 0;
}
.point-of-sale .pos-receipt-container {
font-size: 0.75em;
}
.point-of-sale .oe_pos_synch-notification-button {
color: white;
border: 1px solid black;
border-radius: 3px;
padding: 2px 3px 2px 3px;
background-color: #D92A2A;
}
.receipt-buttons {
white-space: nowrap;
}
.pos-payment-buttons {
white-space: nowrap;
}
@media print {
#oe_header, #oe_menu, .point-of-sale #topheader, .point-of-sale #leftpane {
@ -519,7 +565,7 @@
left: 0px;
background-color: white;
}
#receipt-screen header, .receipt-buttons {
#receipt-screen header {
display: none;
}
#receipt-screen {
@ -530,6 +576,8 @@
}
}
/* ********* The OrderWidget ********* */
.point-of-sale .order-container{
position: absolute;
top: 0px;
@ -537,11 +585,13 @@
width:100%;
background: #F0EEEE;
}
.point-of-sale .order-scroller{
width:100%;
height:100%;
overflow:hidden;
}
.point-of-sale .order{
background: #FFF;
background: -webkit-linear-gradient(0deg,rgba(245,245,245,1),rgba(255,255,255,1), rgba(245,245,245,1));
@ -555,6 +605,35 @@
-webkit-box-shadow: 0px 5px 16px rgba(0,0,0, 0.3);
}
.point-of-sale .order .empty{
text-align:center;
margin-top: 15px;
margin-bottom: 5px;
color:#999;
font-weight: normal;
}
.point-of-sale .order .summary{
width:100%;
text-align:right;
font-weight: bold;
margin-top:20px;
margin-bottom:10px;
}
.point-of-sale .order .summary .line{
margin-right:15px;
padding-top:5px;
border-top: solid 2px;
border-color:#777;
}
.point-of-sale .order .summary .line.empty{
border-color:#BBB;
color:#999;
}
/* ********* The OrderLineWidget ********* */
.point-of-sale .order .orderline{
width:100%;
margin:0px;
@ -566,14 +645,6 @@
-webkit-box-sizing: border-box;
-webkit-transition: background 250ms ease-in-out;
}
.point-of-sale .order .empty{
text-align:center;
margin-top: 15px;
margin-bottom: 5px;
color:#999;
font-weight: normal;
}
.point-of-sale .order .orderline:hover{
background: rgba(140,143,183,0.05);
-webkit-transition: background 50ms ease-in-out;
@ -605,25 +676,8 @@
font-weight: bold;
font-style:normal;
}
.point-of-sale .order .summary{
width:100%;
text-align:right;
font-weight: bold;
margin-top:20px;
margin-bottom:10px;
}
.point-of-sale .order .summary .line{
margin-right:15px;
padding-top:5px;
border-top: solid 2px;
border-color:#777;
}
.point-of-sale .order .summary .line.empty{
border-color:#BBB;
color:#999;
}
/* ----------------------- ACTION BAR ---------------------- */
/* ********* The ActionBarWidget ********* */
.point-of-sale .pos-actionbar{
position:absolute;
@ -704,7 +758,7 @@
float:right;
}
/* ----------------------- POP-UPS ---------------------- */
/* ********* The PopupWidgets ********* */
.point-of-sale .modal-dialog{
position: absolute;
@ -796,14 +850,14 @@
line-height:180px;
}
/* ----------------------- SCROLLBAR ---------------------- */
/* ********* The ScrollBarWidget ********* */
.point-of-sale .scrollbar{
position:absolute;
top:4px;
top:7px;
right:7px;
width:48px;
bottom:4px;
bottom:7px;
background: rgba(0,0,0,0.1);
}
@ -833,13 +887,6 @@
color:rgba(255,255,255,0.5);
-webkit-transition: all 250ms ease-in-out;
}
/*
.point-of-sale .scrollbar .button{
background: -webkit-linear-gradient(-90deg,#efefef,#d8d8d8);
border: 1px solid #ababab;
-webkit-box-shadow: 0px 1px 4px rgba(0,0,0,0.2);
}*/
.point-of-sale .scrollbar .down-button{
position:absolute;
bottom:0px;

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 B

View File

@ -34,6 +34,12 @@ function openerp_pos_basewidget(instance, module){ //module is instance.point_of
}
},
show: function(){
this.$element.show();
},
hide: function(){
this.$element.hide();
},
});
}

View File

@ -94,7 +94,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
'user': null,
'user_list': null,
'cashier': null,
'customer': null,
'client': null,
'orders': new module.OrderCollection(),
//this is the product list as seen by the product list widgets, it will change based on the category filters
@ -126,6 +126,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
[['pos_categ_id','!=', false]]
).then(function(result){
self.set({'product_list': result});
console.log('PRODUCTS_DONE');
});
var uom_def = fetch( //unit of measure
@ -139,14 +140,16 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
units_by_id[result[i].id] = result[i];
}
self.set({'units_by_id':units_by_id});
console.log('UOM_DONE');
});
var user_def = fetch(
'res.users',
['name','ean13']
['name','ean13'],
[['ean13', '!=', false]]
).then(function(result){
self.set({'user_list':result});
console.log('USERS_DONE');
});
@ -166,11 +169,13 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
for(var i = 0; i < product_list.length; i++){
product_list[i].pos_category = cat_by_id[product_list[i].pos_categ_id[0]];
}
console.log('PROD_PROCESS_DONE');
});
var tax_def = fetch('account.tax', ['amount','price_include','type'])
.then(function(result){
self.set({'taxes': result});
console.log('TAX_DONE');
});
var session_def = fetch( // loading the PoS Session.
@ -204,6 +209,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
self.use_websql = result[0].iface_websql || false;
self.use_barcode_scanner = result[0].iface_barscan || false;
self.use_selfcheckout = result[0].iface_self_checkout || false;
console.log('POS_CONFIG_DONE');
});
var bank_def = fetch(
@ -212,6 +218,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
[['state','=','open'],['pos_session_id', '=', pos_session.id]]
).then(function(result){
self.set({'bank_statements':result});
console.log('BANK_DEF_DONE');
});
var journal_def = fetch(
@ -220,6 +227,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
[['user_id','=',pos_session.user_id[0]]]
).then(function(result){
self.set({'journals':result});
console.log('JOURNALS_DONE');
});
// associate the bank statements with their journals.
@ -235,6 +243,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
}
}
}
console.log('BANK_PROCESS_DONE');
});
session_data_def = $.when(pos_config_def,bank_def,journal_def,bank_process_def);
@ -251,9 +260,8 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
//self.build_tree();
self.build_categories();
self.set({'cashRegisters' : new module.CashRegisterCollection(self.get('bank_statements'))});
console.log('cashRegisters:',self.get('cashRegisters'));
self.ready.resolve();
self.log_loaded_data();
self.ready.resolve();
},function(){
//we failed to load some backend data, or the backend was badly configured.
//the error messages will be displayed in PosWidget
@ -277,10 +285,10 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
console.log('PosModel: company:',this.get('company'));
console.log('PosModel: currency:',this.get('currency'));
console.log('PosModel: user_list:',this.get('user_list'));
console.log('PosModel: user:',this.get('user'));
console.log('PosModel.session:',this.session);
console.log('PosModel.categories:',this.categories);
console.log('PosModel end of data log.');
},
// this is called when an order is removed from the order collection. It ensures that there is always an existing
@ -316,7 +324,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
push_order: function(record) {
var self = this;
console.log('push_order',record);
return this.dao.add_operation(record).pipe(function(){
return self.flush();
});
@ -334,7 +341,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
// it has been confirmed that they have been received.
flush: function() {
//this makes sure only one _int_flush is called at the same time
console.log('flush operations');
return this.flush_mutex.exec(_.bind(function() {
return this._int_flush();
}, this));
@ -347,13 +353,11 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
// they are saved to disk and then we attempt to send them to the backend so that they can
// be applied.
// since the network is not reliable we potentially have many 'pending operations' that have not been sent.
self.set( {'nbr_pending_operations':operations.length} );
if(operations.length === 0){
return $.when();
}
var order = operations[0];
console.log('Pushing Order:',order);
// we prevent the default error handler and assume errors
// are a normal use case, except we stop the current iteration
@ -439,8 +443,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
this.root_category = root_category;
console.log('categories:',categories);
//we add the products to the categories.
for(var i = 0, len = products.length; i < len; i++){
var product = products[i];
@ -509,56 +511,53 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
// An orderline represent one element of the content of a client's shopping cart.
// An orderline contains a product, its quantity, its price, discount. etc.
// Currently there is a limitation in that there can only be once orderline by type
// of product, but this will be subject to changes TODO
//
// An Order contains zero or more Orderlines.
module.Orderline = Backbone.Model.extend({
defaults: {
quantity: 1,
list_price: 0,
discount: 0,
weighted: false,
product_type: 'unit',
selected: false,
initialize: function(options){
this.pos = options.pos;
this.order = options.order;
this.product = options.product;
this.price = options.product.get('list_price');
this.quantity = 1;
this.discount = 0;
this.type = 'unit';
this.selected = false;
},
initialize: function(attributes) {
this.pos = attributes.pos;
Backbone.Model.prototype.initialize.apply(this, arguments);
if(attributes.weight){
this.setWeight(attributes.weight);
this.set({weighted: true});
this.set({product_type: 'weight'});
}
// sets a discount [0,100]%
set_discount: function(discount){
this.discount = Math.max(0,Math.min(100,discount));
this.trigger('change');
},
// we override the attributes set to prevent some out of range values
// we also round the quantity according to the unit of measure rounding methods
set: function(attributes, options){
var attributes = _.clone(attributes); //so we don't modify the argument
if(attributes.discount > 100){
attributes.discount = 100;
}else if(attributes.discount < 0){
attributes.discount = 0;
}
if(_.isNaN(attributes.quantity)){
console.log(this.get('order'));
this.get('order').removeOrderline(this);
return this;
}else if(attributes.quantity !== undefined){
attributes.quantity = Math.max(0,attributes.quantity);
// returns the discount [0,100]%
get_discount: function(){
return this.discount;
},
// FIXME
get_product_type: function(){
return this.type;
},
// sets the quantity of the product. The quantity will be rounded according to the
// product's unity of measure properties. Quantities greater than zero will not get
// rounded to zero
set_quantity: function(quantity){
if(_.isNaN(quantity)){
this.order.removeOrderline(this);
}else if(quantity !== undefined){
this.quantity = Math.max(0,quantity);
var unit = this.get_unit();
if(unit && attributes.quantity){
attributes.quantity = Math.max(unit.rounding, Math.round( attributes.quantity / unit.rounding) * unit.rounding);
if(unit && this.quantity > 0 ){
this.quantity = Math.max(unit.rounding, Math.round(quantity / unit.rounding) * unit.rounding);
}
}
Backbone.Model.prototype.set.call(this,attributes,options);
return this;
this.trigger('change');
},
// returns the unit of measure associated with the product if there is one, undefined otherwise
// return the quantity of product
get_quantity: function(){
return this.quantity;
},
// return the unit of measure of the product
get_unit: function(){
var unit_id = (this.get('uos_id') || this.get('uom_id'));
var unit_id = (this.product.get('uos_id') || this.product.get('uom_id'));
if(!unit_id){
return undefined;
}
@ -568,69 +567,77 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
}
return this.pos.get('units_by_id')[unit_id];
},
// return the product of this orderline
get_product: function(){
return this.product;
},
// return the base price of this product (for this orderline)
get_list_price: function(){
return this.price;
},
// changes the base price of the product for this orderline
set_list_price: function(price){
this.price = price;
this.trigger('change');
},
// selects or deselects this orderline
set_selected: function(selected){
this.selected = selected;
this.trigger('change');
},
// returns true if this orderline is selected
is_selected: function(){
return this.selected;
},
// when we add an new orderline we want to merge it with the last line to see reduce the number of items
// in the orderline. This returns true if it makes sense to merge the two
can_be_merged_with: function(orderline){
if( this.get('id') !== orderline.get('id')){ //only orderline of the same product can be merged
if( this.get_product().get('id') !== orderline.get_product().get('id')){ //only orderline of the same product can be merged
return false;
}else if(this.get('product_type') !== orderline.get('product_type')){
}else if(this.get_product_type() !== orderline.get_product_type()){
return false;
}else if(this.get('discount') > 0){ // we don't merge discounted orderlines
}else if(this.get_discount() > 0){ // we don't merge discounted orderlines
return false;
}else if(this.get('product_type') === 'unit'){
}else if(this.get_product_type() === 'unit'){
return true;
}else if(this.get('product_type') === 'weight'){
}else if(this.get_product_type() === 'weight'){
return true;
}else if(this.get('product_type') === 'price'){
return this.get('list_price') === orderline.get('list_price');
}else if(this.get_product_type() === 'price'){
return this.get_product().get('list_price') === orderline.get_product().get('list_price');
}else{
console.error('point_of_sale/pos_models.js/Orderline.can_be_merged_with() : unknown product type:',this.get('product_type'));
return false;
}
},
// Modifies this orderline so that it also contains the contents of another orderline.
// the two orderlines must be mergable ('can_be_merged_with()' === true)
merge: function(orderline){
this.set({quantity : this.get('quantity') + orderline.get('quantity') });
this.set_quantity(this.get_quantity() + orderline.get_quantity());
},
setWeight: function(weight){
return this.set({
quantity: weight,
});
export_as_JSON: function() {
return {
qty: this.get_quantity(),
price_unit: this.get_product().get('list_price'),
discount: this.get_discount(),
product_id: this.get_product().get('id')
};
},
incrementQuantity: function() {
return this.set({
quantity: (this.get('quantity')) + 1
});
get_price_without_tax: function(){
return this.get_all_prices().priceWithoutTax;
},
incrementWeight: function(weight){
return this.set({
quantity: (this.get('quantity')) + weight,
});
get_price_with_tax: function(){
return this.get_all_prices().priceWithTax;
},
set_discount: function(discount){
this.set({'discount': discount});
get_tax: function(){
return this.get_all_prices().tax;
},
getPriceWithoutTax: function() {
return this.getAllPrices().priceWithoutTax;
},
getPriceWithTax: function() {
return this.getAllPrices().priceWithTax;
},
getTax: function() {
return this.getAllPrices().tax;
},
getAllPrices: function() {
get_all_prices: function() {
var self = this;
var base = (this.get('quantity')) * (this.get('list_price')) * (1 - (this.get('discount')) / 100);
var base = this.get_quantity() * this.price * (1 - (this.get_discount() / 100));
var totalTax = base;
var totalNoTax = base;
var product_list = self.pos.get('product_list');
var product = _.detect(product_list, function(el) {return el.id === self.get('id');});
var product_list = this.pos.get('product_list');
var product = this.get_product();
var taxes_ids = product.taxes_id;
var taxes = self.pos.get('taxes');
var taxtotal = 0;
@ -641,7 +648,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
if (tax.type === "percent") {
tmp = base - (base / (1 + tax.amount));
} else if (tax.type === "fixed") {
tmp = tax.amount * self.get('quantity');
tmp = tax.amount * self.get_quantity();
} else {
throw "This type of tax is not supported by the point of sale: " + tax.type;
}
@ -652,7 +659,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
if (tax.type === "percent") {
tmp = tax.amount * base;
} else if (tax.type === "fixed") {
tmp = tax.amount * self.get('quantity');
tmp = tax.amount * self.get_quantity();
} else {
throw "This type of tax is not supported by the point of sale: " + tax.type;
}
@ -666,14 +673,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
"tax": taxtotal,
};
},
exportAsJSON: function() {
return {
qty: this.get('quantity'),
price_unit: this.get('list_price'),
discount: this.get('discount'),
product_id: this.get('id')
};
},
});
module.OrderlineCollection = Backbone.Collection.extend({
@ -682,22 +681,31 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
// Every PaymentLine has all the attributes of the corresponding CashRegister.
module.Paymentline = Backbone.Model.extend({
defaults: {
amount: 0,
initialize: function(cashRegister) {
this.amount = 0;
this.cashregister = cashRegister;
},
initialize: function(attributes) {
Backbone.Model.prototype.initialize.apply(this, arguments);
//sets the amount of money on this payment line
set_amount: function(value){
this.amount = value;
this.trigger('change');
},
getAmount: function(){
return this.get('amount');
// returns the amount of money on this paymentline
get_amount: function(){
return this.amount;
},
exportAsJSON: function(){
// returns the associated cashRegister
get_cashregister: function(){
return this.cashregister;
},
//exports as JSON for server communication
export_as_JSON: function(){
return {
name: instance.web.datetime_to_str(new Date()),
statement_id: this.get('id'),
account_id: (this.get('account_id'))[0],
journal_id: (this.get('journal_id'))[0],
amount: this.getAmount()
amount: this.get_amount()
};
},
});
@ -706,12 +714,11 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
model: module.Paymentline,
});
// An order more or less represents the content of a client's shopping cart (the OrderLines)
// plus the associated payment information (the PaymentLines)
// there is always an active ('selected') order in the Pos, a new one is created
// automaticaly once an order is completed and sent to the server.
module.Order = Backbone.Model.extend({
initialize: function(attributes){
Backbone.Model.prototype.initialize.apply(this, arguments);
@ -733,7 +740,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
var attr = product.toJSON();
attr.pos = this.pos;
attr.order = this;
var line = new module.Orderline(attr);
var line = new module.Orderline({pos: this.pos, order: this, product: product});
var self = this;
var last_orderline = this.getLastOrderline();
@ -753,14 +760,11 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
},
addPaymentLine: function(cashRegister) {
var newPaymentline;
console.log('addPaymentLine:',cashRegister);
newPaymentline = new module.Paymentline(cashRegister);
/* TODO: Should be 0 for cash-like accounts */
//FIXME the following 'set' call calls this method once again via callback
// events. Are we sure that it's what we want ???
newPaymentline.set({
amount: this.getDueLeft()
});
newPaymentline.set_amount( this.getDueLeft() );
this.get('paymentLines').add(newPaymentline);
},
getName: function() {
@ -768,22 +772,22 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
},
getTotal: function() {
return (this.get('orderLines')).reduce((function(sum, orderLine) {
return sum + orderLine.getPriceWithTax();
return sum + orderLine.get_price_with_tax();
}), 0);
},
getTotalTaxExcluded: function() {
return (this.get('orderLines')).reduce((function(sum, orderLine) {
return sum + orderLine.getPriceWithoutTax();
return sum + orderLine.get_price_without_tax();
}), 0);
},
getTax: function() {
return (this.get('orderLines')).reduce((function(sum, orderLine) {
return sum + orderLine.getTax();
return sum + orderLine.get_tax();
}), 0);
},
getPaidTotal: function() {
return (this.get('paymentLines')).reduce((function(sum, paymentLine) {
return sum + paymentLine.getAmount();
return sum + paymentLine.get_amount();
}), 0);
},
getChange: function() {
@ -796,11 +800,11 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
var orderLines, paymentLines;
orderLines = [];
(this.get('orderLines')).each(_.bind( function(item) {
return orderLines.push([0, 0, item.exportAsJSON()]);
return orderLines.push([0, 0, item.export_as_JSON()]);
}, this));
paymentLines = [];
(this.get('paymentLines')).each(_.bind( function(item) {
return paymentLines.push([0, 0, item.exportAsJSON()]);
return paymentLines.push([0, 0, item.export_as_JSON()]);
}, this));
return {
name: this.getName(),
@ -811,6 +815,8 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
lines: orderLines,
statement_ids: paymentLines,
pos_session_id: this.pos.get('pos_session').id,
partner_id: this.pos.get('client') ? this.pos.get('client').id : undefined,
user_id: this.pos.get('cashier') ? this.pos.get('cashier').id : this.pos.get('user').id,
};
},
getSelectedLine: function(){
@ -820,10 +826,10 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
if(line){
if(line !== this.selected_orderline){
if(this.selected_orderline){
this.selected_orderline.set({'selected':false});
this.selected_orderline.set_selected(false);
}
this.selected_orderline = line;
this.selected_orderline.set({'selected':true});
this.selected_orderline.set_selected(true);
}
}else{
this.selected_orderline = undefined;

View File

@ -20,7 +20,6 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
module.ScreenSelector = instance.web.Class.extend({
init: function(options){
console.log("ScreenSelector Initialized");
this.pos = options.pos;
this.screen_set = options.screen_set || {};
@ -120,21 +119,117 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
module.ScreenWidget = module.PosBaseWidget.extend({
show_numpad: true,
show_leftpane: true,
init: function(parent,options){
this._super(parent,options);
this.hidden = false;
},
help_button_action: function(){
this.pos_widget.screen_selector.show_popup('help');
},
logout_button_action: function(){
this.pos_widget.screen_selector.set_user_mode('client');
},
barcode_product_screen: 'scan', //if defined, this screen will be loaded when a product is scanned
barcode_product_error_popup: 'error', //if defined, this popup will be loaded when there's an error in the popup
// what happens when a product is scanned :
// it will add the product to the order and go to barcode_product_screen. Or show barcode_product_error_popup if
// there's an error.
barcode_product_action: function(ean){
if(this.pos_widget.scan_product(ean)){
this.pos.proxy.scan_item_success();
if(this.barcode_product_screen){
this.pos_widget.screen_selector.set_current_screen(this.barcode_product_screen);
}
}else{
if(this.barcode_product_error_popup){
this.pos_widget.screen_selector.show_popup(this.barcode_product_error_popup);
}
}
},
// 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...
barcode_cashier_action: function(ean){
var users = this.pos.get('user_list');
for(var i = 0, len = users.length; i < len; i++){
if(users[i].ean === ean.ean){
this.pos.set('cashier',users[i]);
this.pos_widget.username.refresh();
this.pos.proxy.cashier_mode_activated();
this.pos_widget.screen_selector.set_user_mode('cashier');
return true;
}
}
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.
barcode_client_action: function(ean){
var users = this.pos.get('user_list');
for(var i = 0, len = users.length; i < len; i++){
if(users[i].ean === ean.ean){
this.pos.set('client',users[i]);
this.pos_widget.username.refresh();
return true;
}
}
return false;
//TODO start the transaction
},
// what happens when a discount barcode is scanned : the default behavior
// is to set the discount on the last order.
barcode_discount_action: function(ean){
var last_orderline = this.pos.get('selectedOrder').getLastOrderline();
if(last_orderline){
last_orderline.set_discount(ean.value)
}
},
// 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.
show: function(){
this.hidden = false;
if(this.$element){
this.$element.show();
}
var self = this;
var cashier_mode = this.pos_widget.screen_selector.get_user_mode() === 'cashier';
this.pos_widget.set_numpad_visible(this.show_numpad && cashier_mode);
this.pos_widget.set_leftpane_visible(this.show_leftpane);
this.pos_widget.set_cashier_controls_visible(cashier_mode);
this.pos_widget.action_bar.set_element_visible('help-button', !cashier_mode, function(){ self.help_button_action(); });
this.pos_widget.action_bar.set_element_visible('logout-button', cashier_mode, function(){ self.logout_button_action(); });
this.pos_widget.action_bar.set_element_visible('close-button', cashier_mode);
this.pos_widget.username.set_user_mode(this.pos_widget.screen_selector.get_user_mode());
this.pos.barcode_reader.set_action_callback({
'cashier': self.barcode_cashier_action ? function(ean){ self.barcode_cashier_action(ean); } : undefined ,
'product': self.barcode_product_action ? function(ean){ self.barcode_product_action(ean); } : undefined ,
'client' : self.barcode_client_action ? function(ean){ self.barcode_client_action(ean); } : undefined ,
'discount': self.barcode_discount_action ? function(ean){ self.barcode_discount_action(ean); } : undefined,
});
},
hide: function(){
this.hidden = true;
if(this.$element){
this.$element.hide();
}
// 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(){
if(this.pos.barcode_reader){
this.pos.barcode_reader.reset_action_callbacks();
}
@ -142,8 +237,16 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.pos_widget.action_bar.destroy_buttons();
}
},
close: function(){
// 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;
if(this.$element){
this.$element.hide();
}
},
// 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
@ -234,75 +337,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
template:'ErrorNoSessionPopupWidget',
});
module.BaseScreenWidget = module.ScreenWidget.extend({
show_numpad: true,
show_leftpane: true,
help_button_action: function(){
this.pos_widget.screen_selector.show_popup('help');
},
logout_button_action: function(){
this.pos_widget.screen_selector.set_user_mode('client');
},
barcode_cashier_action: function(ean){
this.pos.proxy.cashier_mode_activated();
this.pos_widget.screen_selector.set_user_mode('cashier');
},
barcode_product_screen: 'scan',
barcode_product_error_popup: 'error',
barcode_product_action: function(ean){
if(this.pos_widget.scan_product(ean)){
this.pos.proxy.scan_item_success();
if(this.barcode_product_screen){
this.pos_widget.screen_selector.set_current_screen(this.barcode_product_screen);
}
}else{
if(this.barcode_product_error_popup){
this.pos_widget.screen_selector.show_popup(this.barcode_product_error_popup);
}
}
},
barcode_client_action: function(ean){
this.pos.proxy.transaction_start();
//TODO 'log the client'
this.pos_widget.screen_selector.show_popup('receipt');
},
barcode_discount_action: function(ean){
var currentOrder = this.pos.get('selectedOrder');
var last_orderline = currentOrder.last_orderline;
if(last_orderline){
last_orderline.set_discount(ean.value)
}
},
show: function(){
this._super();
var self = this;
var cashier_mode = this.pos_widget.screen_selector.get_user_mode() === 'cashier';
this.pos_widget.set_numpad_visible(this.show_numpad && cashier_mode);
this.pos_widget.set_leftpane_visible(this.show_leftpane);
this.pos_widget.set_cashier_controls_visible(cashier_mode);
this.pos_widget.action_bar.set_element_visible('help-button', !cashier_mode, function(){ self.help_button_action(); });
this.pos_widget.action_bar.set_element_visible('logout-button', cashier_mode, function(){ self.logout_button_action(); });
this.pos_widget.action_bar.set_element_visible('close-button', cashier_mode);
this.pos.barcode_reader.set_action_callback({
'cashier': self.barcode_cashier_action ? function(ean){ self.barcode_cashier_action(ean); } : undefined ,
'product': self.barcode_product_action ? function(ean){ self.barcode_product_action(ean); } : undefined ,
'client' : self.barcode_client_action ? function(ean){ self.barcode_client_action(ean); } : undefined ,
'discount': self.barcode_discount_action ? function(ean){ self.barcode_discount_action(ean); } : undefined,
});
},
});
module.ScaleInviteScreenWidget = module.BaseScreenWidget.extend({
module.ScaleInviteScreenWidget = module.ScreenWidget.extend({
template:'ScaleInviteScreenWidget',
show: function(){
@ -341,7 +376,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
},
});
module.ScaleProductScreenWidget = module.BaseScreenWidget.extend({
module.ScaleProductScreenWidget = module.ScreenWidget.extend({
template:'ScaleProductSelectionScreenWidget',
start: function(){
this.product_categories_widget = new module.ProductCategoriesWidget(this,{
@ -401,7 +436,29 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
},
});
module.ClientPaymentScreenWidget = module.BaseScreenWidget.extend({
module.ScaleScreenWidget = module.ScreenWidget.extend({
template:'ScaleScreenWidget',
show: function(){
this._super();
var self = this;
this.pos.proxy.weighting_start();
this.intervalID = setInterval(function(){
var weight = self.pos.proxy.weighting_read_kg();
if(weight != self.weight){
self.weight = weight;
self.renderElement();
}
},500);
},
close: function(){
this._super();
clearInterval(this.intervalID);
this.pos.proxy.weighting_end();
},
});
module.ClientPaymentScreenWidget = module.ScreenWidget.extend({
template:'ClientPaymentScreenWidget',
show: function(){
this._super();
@ -457,7 +514,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
},
});
module.WelcomeScreenWidget = module.BaseScreenWidget.extend({
module.WelcomeScreenWidget = module.ScreenWidget.extend({
template:'WelcomeScreenWidget',
show_numpad: false,
@ -479,7 +536,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
},
});
module.ScanProductScreenWidget = module.BaseScreenWidget.extend({
module.ScanProductScreenWidget = module.ScreenWidget.extend({
template:'ScanProductScreenWidget',
show_numpad: false,
@ -509,7 +566,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
},
});
module.SearchProductScreenWidget = module.BaseScreenWidget.extend({
module.SearchProductScreenWidget = module.ScreenWidget.extend({
template:'SearchProductScreenWidget',
show_numpad: true,
@ -550,7 +607,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
});
module.ReceiptScreenWidget = module.BaseScreenWidget.extend({
module.ReceiptScreenWidget = module.ScreenWidget.extend({
template: 'ReceiptScreenWidget',
show_numpad: true,
@ -609,7 +666,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
},
});
module.PaymentScreenWidget = module.BaseScreenWidget.extend({
module.PaymentScreenWidget = module.ScreenWidget.extend({
template: 'PaymentScreenWidget',
init: function(parent, options) {
this._super(parent,options);
@ -682,7 +739,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.renderElement();
},
addPaymentLine: function(newPaymentLine) {
console.log('addPaymentLine:',newPaymentLine);
console.log('NEW PAYMENT LINE WIDGET',newPaymentLine);
var x = new module.PaymentlineWidget(null, {
payment_line: newPaymentLine
});

View File

@ -1,7 +1,7 @@
function openerp_pos_widgets(instance, module){ //module is instance.point_of_sale
var QWeb = instance.web.qweb;
module.NumpadWidget = instance.web.Widget.extend({
module.NumpadWidget = module.PosBaseWidget.extend({
template:'NumpadWidget',
init: function(parent, options) {
this._super(parent);
@ -44,7 +44,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
renderElement: function() {
var self = this;
this._super();
console.log('PaypadWidget:',this);
this.pos.get('cashRegisters').each(function(cashRegister) {
var button = new module.PaypadButtonWidget(self,{
@ -97,7 +96,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
renderElement: function() {
this._super();
this.$element.click(_.bind(this.click_handler, this));
if(this.model.get('selected')){
if(this.model.is_selected()){
this.$element.addClass('selected');
}
},
@ -130,9 +129,14 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
set_value: function(val) {
var order = this.pos.get('selectedOrder');
if (order.get('orderLines').length !== 0) {
var param = {};
param[this.numpadState.get('mode')] = val;
order.getSelectedLine().set(param);
var mode = this.numpadState.get('mode');
if( mode === 'quantity'){
order.getSelectedLine().set_quantity(val);
}else if( mode === 'discount'){
order.getSelectedLine().set_discount(val);
}else if( mode === 'list_price'){
order.getSelectedLine().set_list_price(val);
}
} else {
this.pos.get('selectedOrder').destroy();
}
@ -202,9 +206,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
},
});
// ---------- Product Screen ----------
module.ProductWidget = module.PosBaseWidget.extend({
template: 'ProductWidget',
init: function(parent, options) {
@ -250,20 +251,18 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
newAmount = event.currentTarget.value;
if (newAmount && !isNaN(newAmount)) {
this.amount = parseFloat(newAmount);
this.payment_line.set({
amount: this.amount,
});
this.payment_line.set_amount(this.amount);
}
},
changedAmount: function() {
if (this.amount !== this.payment_line.get('amount'))
if (this.amount !== this.payment_line.get_amount())
this.renderElement();
},
renderElement: function() {
this.name = this.payment_line.get('journal_id')[1];
this.name = this.payment_line.get_cashregister().get('journal_id')[1];
this._super();
$('input', this.$element).keyup(_.bind(this.changeAmount, this));
$('.delete-payment-line', this.$element).click(this.on_delete);
this.$('input').keyup(_.bind(this.changeAmount, this));
this.$('.delete-payment-line').click(this.on_delete);
},
});
@ -385,7 +384,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
}else{
this.category = category;
}
console.log('setting categories:',this.category);
this.breadcrumb = [];
for(var i = 1; i < this.category.ancestors.length; i++){
@ -534,24 +532,60 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
},
});
module.UsernameWidget = module.PosBaseWidget.extend({
template: 'UsernameWidget',
init: function(parent, options){
var options = options || {};
this._super(parent,options);
this.mode = options.mode || 'cashier';
},
set_user_mode: function(mode){
this.mode = mode;
this.refresh();
},
refresh: function(){
this.renderElement();
},
get_name: function(){
var user;
if(this.mode === 'cashier'){
user = this.pos.get('cashier') || this.pos.get('user');
}else{
user = this.pos.get('client') || this.pos.get('user');
}
if(user){
return user.name;
}else{
return "";
}
},
});
// ---------- Main Point of Sale Widget ----------
// this is used to notify the user that data is being synchronized on the network
module.SynchNotificationWidget = instance.web.Widget.extend({
module.SynchNotificationWidget = module.PosBaseWidget.extend({
template: "SynchNotificationWidget",
init: function(parent) {
this._super(parent);
this.nbr_pending = 0;
init: function(parent,options) {
options = options || {};
this._super(parent,options);
},
renderElement: function() {
var self = this;
this._super();
$('.oe_pos_synch-notification-button', this.$element).click(this.on_synch);
this.$('.oe_pos_synch-notification-button').click(function(){
self.pos.flush();
});
},
on_change_nbr_pending: function(nbr_pending) {
this.nbr_pending = nbr_pending;
this.renderElement();
start: function(){
var self = this;
this.pos.bind('change:nbr_pending_operations', function(){
self.renderElement();
});
},
get_nbr_pending: function(){
return this.pos.get('nbr_pending_operations');
},
on_synch: function() {}
});
// The PosWidget is the main widget that contains all other widgets in the PointOfSale.
@ -567,7 +601,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
module.PosWidget = module.PosBaseWidget.extend({
template: 'PosWidget',
init: function() {
console.log('PosArguments:',arguments);
this._super(arguments[0],{});
this.pos = new module.PosModel(this.session);
@ -586,16 +619,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
return self.pos.ready.then(function() {
self.build_currency_template();
self.renderElement();
self.synch_notification = new module.SynchNotificationWidget(this);
self.synch_notification.replace($('.placeholder-SynchNotificationWidget', self.$element));
self.synch_notification.on_synch.add(_.bind(self.pos.flush, self.pos));
self.pos.bind('change:nbr_pending_operations', self.changed_pending_operations, self);
self.changed_pending_operations();
self.$element.find("#loggedas button").click(function() {
self.try_close();
});
self.$('button#neworder-button').click(_.bind(self.create_new_order, self));
@ -616,21 +639,16 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
instance.webclient.set_content_full_screen(true);
if (!self.pos.get('pos_session')) {
self.screen_selector.show_popup('error',
'Sorry, we could not create a user session');
//}else if (!self.pos.get('bank_statements') || self.pos.get('bank_statements').length === 0){
// self.screen_selector.show_popup('error',
// 'Sorry, we could not find any accounting journals in the configuration');
self.screen_selector.show_popup('error', 'Sorry, we could not create a user session');
}else if(!self.pos.get('pos_config')){
self.screen_selector.show_popup('error',
'Sorry, we could not find any PoS Configuration for this session');
self.screen_selector.show_popup('error', 'Sorry, we could not find any PoS Configuration for this session');
}
$('.loader').animate({opacity:0},3000,'swing',function(){$('.loader').hide();});
$('.loader img').hide();
self.$('.loader').animate({opacity:0},3000,'swing',function(){$('.loader').hide();});
self.$('.loader img').hide();
},function(){ // error when loading models data from the backend
$('.loader img').hide();
self.$('.loader img').hide();
return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_pos_session_opening']], ['res_id'])
.pipe( _.bind(function(res){
return instance.connection.rpc('/web/action/load', {'action_id': res[0]['res_id']})
@ -689,6 +707,12 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
// -------- Misc ---------
this.notification = new module.SynchNotificationWidget(this,{});
this.notification.replace(this.$('.placeholder-SynchNotificationWidget'));
this.username = new module.UsernameWidget(this,{});
this.username.replace(this.$('.placeholder-UsernameWidget'));
this.action_bar = new module.ActionBarWidget(this);
this.action_bar.appendTo($(".point-of-sale #content"));
@ -759,15 +783,12 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
if (parsed_ean.type === 'price') {
var itemCode = parsed_ean.id;
console.log('price! id:',itemCode);
var scannedPackaging = _.detect(allPackages, function(pack) {
return pack.ean && pack.ean.substring(0,7) === itemCode;
});
if (scannedPackaging) {
console.log('found matching package, finding matching product...');
scannedProductModel = _.detect(allProducts, function(pc) { return pc.id === scannedPackaging.product_id[0];});
}else{
console.log('matching package not found, finding matching product...');
scannedProductModel = _.detect(allProducts, function(pc) { return pc.ean13 && (pc.ean13.substring(0,7) === parsed_ean.id);});
}
if(scannedProductModel){
@ -780,10 +801,8 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
return pack.ean && pack.ean.substring(0,7) === itemCode;
});
if (scannedPackaging){
console.log('found matching package, finding matching product...');
scannedProductModel = _.detect(allProducts, function(pc) { return pc.id === scannedPackaging.product_id[0];});
}else{
console.log('matching package not found, finding matching product...');
scannedProductModel = _.detect(allProducts, function(pc) { return pc.ean13 && (pc.ean13.substring(0,7) === parsed_ean.id);});
}
if(scannedProductModel){
@ -811,13 +830,13 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
if(visible != this.numpad_visible){
this.numpad_visible = visible;
if(visible){
$('#numpad').show();
$('#paypad').show();
$('#current-order').css({'bottom':'271px'});
this.numpad.show();
this.paypad.show();
$('.point-of-sale .order-container').css({'bottom':'232px'});
}else{
$('#numpad').hide();
$('#paypad').hide();
$('#current-order').css({'bottom':'0px'});
this.numpad.hide();
this.paypad.hide();
$('.point-of-sale .order-container').css({'bottom':'0px'});
}
}
},

View File

@ -8,15 +8,14 @@
<div id="topheader">
<div id="branding">
<img src="/point_of_sale/static/src/img/logo.png" />
</div>
<div id="loggedas">
<span class="placeholder-SynchNotificationWidget"></span>
<span class="placeholder-UsernameWidget"></span>
</div>
<div id="rightheader">
<div id="order-selector">
<button id="neworder-button">+</button>
<button class="neworder-button">+</button>
<ol id="orders"></ol>
</div>
<span class="placeholder-SynchNotificationWidget"></span>
</div>
</div>
<div id="content">
@ -39,11 +38,11 @@
<t t-name="SynchNotificationWidget">
<span>
<a t-if="widget.nbr_pending &gt; 0" href="javascript:void(0)" class="oe_pos_synch-notification-button">
<t t-esc="widget.nbr_pending"/> pending orders
</a>
</span>
<div class="oe_pos_synch-notification">
<t t-if="widget.get_nbr_pending() > 0" t-esc="widget.get_nbr_pending()"/>
<div t-if="widget.get_nbr_pending() > 0" class="oe_status_red"></div>
<div t-if="widget.get_nbr_pending() === 0" class="oe_status_green"></div>
</div>
</t>
<t t-name="PosCloseWarning">
@ -146,6 +145,16 @@
</table>
</div>
</t>
<t t-name="ScaleScreenWidget">
<div class="scale-screen screen">
<header><h2>Product Weighting</h2></header>
<div class="display">
<span class="weight">0.000Kg</span>
<span class="product-name"></span>
</div>
</div>
</t>
<t t-name="PaymentScreenWidget">
<div id="payment-screen" class="screen">
@ -358,29 +367,29 @@
<t t-name="OrderlineWidget">
<li class="orderline">
<span class="product-name">
<t t-esc="widget.model.get('name')"/>
<t t-esc="widget.model.get_product().get('name')"/>
</span>
<span class="price">
<t t-esc="widget.format_currency(widget.model.getPriceWithTax())"/>
<t t-esc="widget.format_currency(widget.model.get_price_with_tax())"/>
</span>
<ul class="info-list">
<t t-if="widget.model.get('quantity') !== 1.0">
<t t-if="widget.model.get_quantity() !== 1.0">
<li class="info">
<em>
<t t-esc="widget.model.get('quantity')" />
<t t-esc="widget.model.get_quantity()" />
</em>
<t t-esc="widget.model.get_unit().name" />
at
<t t-esc="widget.format_currency(widget.model.get('list_price'))" />
<t t-esc="widget.format_currency(widget.model.get_list_price())" />
/
<t t-esc="widget.model.get_unit().name" />
</li>
</t>
<t t-if="widget.model.get('discount') > 0">
<t t-if="widget.model.get_discount() > 0">
<li class="info">
With a
<em>
<t t-esc="widget.model.get('discount')" />%
<t t-esc="widget.model.get_discount()" />%
</em>
discount
</li>
@ -395,14 +404,14 @@
<t t-esc="widget.name"/>
</td>
<td class="paymentline-amount pos-right-align">
<input type="text" t-att-value="widget.payment_line.get('amount').toFixed(2)" />
<input type="text" t-att-value="widget.payment_line.get_amount().toFixed(2)" />
<a href='javascript:void(0)' class='delete-payment-line'><img src="/point_of_sale/static/src/img/search_reset.gif" /></a>
</td>
</tr>
</t>
<t t-name="PaypadButtonWidget">
<button class="payment-button" t-att-cash-register-id="widget.cashRegister.get('id')">
<button class="paypad-button" t-att-cash-register-id="widget.cashRegister.get('id')">
<t t-esc="widget.cashRegister.get('journal').name"/>
</button>
<br />
@ -414,6 +423,12 @@
</li>
</t>
<t t-name="UsernameWidget">
<span class="username">
<t t-esc="widget.get_name()" />
</span>
</t>
<t t-name="PosTicket">
<div class="pos-sale-ticket">
<div class="pos-right-align"><t t-esc="new Date().toString(Date.CultureInfo.formatPatterns.shortDate + ' ' +
@ -427,13 +442,13 @@
<table>
<tr t-foreach="widget.currentOrderLines.toArray()" t-as="order">
<td>
<t t-esc="order.get('name')"/>
<t t-esc="order.get_product().get('name')"/>
</td>
<td class="pos-right-align">
<t t-esc="order.get('quantity').toFixed(0)"/>
<t t-esc="order.get_quantity().toFixed(0)"/>
</td>
<td class="pos-right-align">
<t t-esc="widget.format_currency((order.get('list_price') * (1 - order.get('discount')/100) * order.get('quantity')).toFixed(2))"/>
<t t-esc="widget.format_currency(order.get_list_price() * (1 - order.get_discount()/100) * order.get_quantity().toFixed(2))"/>
</td>
</tr>
</table>
@ -453,7 +468,7 @@
<t t-esc="pline.get('journal_id')[1]"/>
</td>
<td class="pos-right-align">
<t t-esc="widget.format_currency((pline.getAmount()).toFixed(2))"/>
<t t-esc="widget.format_currency((pline.get_amount()).toFixed(2))"/>
</td>
</tr>
</table>