[MERGE] lp:~openerp-dev/openobject-addons/trunk-pos-fva
bzr revid: stw@openerp.com-20120430092650-3oozjjx9pgbqh73j
This commit is contained in:
@@ -235,8 +235,27 @@ class account_cash_statement(osv.osv):
|
||||
print "line: %r" % (line,)
|
||||
if line and len(line)==3 and line[2]:
|
||||
amount_total+= line[2]['pieces'] * line[2]['number_opening']
|
||||
|
||||
vals.update(balance_start= amount_total)
|
||||
vals.update(balance_end_real=self._compute_balance_end_real(cr, uid, vals['journal_id'], context=context))
|
||||
|
||||
details = vals.get('details_ids')
|
||||
if not details:
|
||||
result = self.onchange_journal_id(cr, uid, None, vals['journal_id'], context=context)
|
||||
vals['details_ids'] = []
|
||||
|
||||
for value in (result['value']['details_ids'] or []):
|
||||
print "value: %r" % (value,)
|
||||
nested_values = {
|
||||
'number_closing' : False,
|
||||
'number_opening' : False,
|
||||
'pieces' : value['pieces'],
|
||||
'subtotal_closing' : False,
|
||||
'subtotal_opening' : False,
|
||||
}
|
||||
|
||||
vals['details_ids'].append([0, False, nested_values])
|
||||
|
||||
return super(account_cash_statement, self).create(cr, uid, vals, context=context)
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
@@ -285,8 +304,6 @@ class account_cash_statement(osv.osv):
|
||||
for line in journal.cashbox_line_ids:
|
||||
values['value']['details_ids'].append({'pieces' : line.pieces})
|
||||
|
||||
print "values: %r" % (values,)
|
||||
|
||||
return values
|
||||
else:
|
||||
return {
|
||||
|
||||
@@ -44,4 +44,5 @@ class res_currency_account(osv.osv):
|
||||
|
||||
res_currency_account()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
||||
@@ -82,7 +82,13 @@ Main features :
|
||||
'application': True,
|
||||
'certificate' : '001156338024966477869',
|
||||
# Web client
|
||||
'js': ['static/lib/backbone/backbone-0.5.3.js', 'static/src/js/pos.js'], #, 'static/src/js/pos2.js'],
|
||||
'js': [
|
||||
'static/lib/backbone/backbone-0.5.3.js',
|
||||
'static/src/js/pos_models.js',
|
||||
'static/src/js/pos_widgets.js',
|
||||
'static/src/js/pos_devices.js',
|
||||
'static/src/js/pos_main.js'
|
||||
],
|
||||
'css': ['static/src/css/pos.css'],
|
||||
'qweb': ['static/src/xml/pos.xml'],
|
||||
'auto_install': True,
|
||||
|
||||
@@ -180,7 +180,7 @@ class pos_session(osv.osv):
|
||||
|
||||
def _create_cash_register(self, cr, uid, pos_config, name, context=None):
|
||||
import pdb
|
||||
pdb.set_trace()
|
||||
#pdb.set_trace()
|
||||
|
||||
if not pos_config:
|
||||
return False
|
||||
@@ -200,7 +200,9 @@ class pos_session(osv.osv):
|
||||
'name' : name,
|
||||
'journal_id' : journal_id,
|
||||
}
|
||||
return proxy.create(cr, uid, values, context=context)
|
||||
cash_register_id = proxy.create(cr, uid, values, context=context)
|
||||
|
||||
return cash_register_id
|
||||
|
||||
def create(self, cr, uid, values, context=None):
|
||||
|
||||
@@ -348,7 +350,7 @@ class pos_order(osv.osv):
|
||||
'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', required=True, states={'draft': [('readonly', False)]}, readonly=True),
|
||||
'partner_id': fields.many2one('res.partner', 'Customer', change_default=True, select=1, states={'draft': [('readonly', False)], 'paid': [('readonly', False)]}),
|
||||
|
||||
#'session_id' : fields.many2one('pos.session', 'Session', required=True, select=1),
|
||||
'session_id' : fields.many2one('pos.session', 'Session', required=True, select=1),
|
||||
|
||||
'state': fields.selection([('draft', 'New'),
|
||||
('cancel', 'Cancelled'),
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -861,12 +861,22 @@
|
||||
<notebook colspan="4">
|
||||
<page string="Cash">
|
||||
<field name="cash_register_id" invisible="0" />
|
||||
<field name="details_ids" colspan="4" nolabel="1" />
|
||||
<field name="details_ids" colspan="4" nolabel="1">
|
||||
<tree string="" editable="bottom">
|
||||
<field name="pieces" readonly="1" />
|
||||
<field name="number_opening" string="Opening Unit Numbers" on_change="on_change_sub_opening(pieces, number_opening, parent.balance_end)" />
|
||||
<field name="subtotal_opening" string="Opening Subtotal"/>
|
||||
<field name="number_closing" string="Closing Unit Numbers" on_change="on_change_sub_closing(pieces, number_closing, parent.balance_end)" />
|
||||
<field name="subtotal_closing" string="Closing Subtotal"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<!--
|
||||
<page string="Others">
|
||||
</page>
|
||||
<page string="Nothing">
|
||||
</page>
|
||||
-->
|
||||
</notebook>
|
||||
|
||||
<group colspan="4" col="5">
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
|
||||
body{
|
||||
overflow: hidden;
|
||||
}
|
||||
.point-of-sale {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
@@ -116,9 +118,15 @@
|
||||
bottom: 0;
|
||||
}
|
||||
.point-of-sale #leftpane {
|
||||
height: 100%;
|
||||
-webkit-box-sizing:border-box;
|
||||
position:absolute;
|
||||
left:0;
|
||||
width:440px;
|
||||
top:0px;
|
||||
bottom:105px;
|
||||
/*height: 87%;
|
||||
width: 440px;
|
||||
position: relative;
|
||||
position: relative;*/
|
||||
border-right: solid 1px #afafb6;
|
||||
background-color: white;
|
||||
}
|
||||
@@ -249,10 +257,10 @@
|
||||
.point-of-sale #rightpane {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 441px;
|
||||
bottom: 105px;
|
||||
left: 440px;
|
||||
//height:100%;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
vertical-align: top;
|
||||
}
|
||||
.point-of-sale #rightpane header {
|
||||
@@ -487,3 +495,229 @@
|
||||
}
|
||||
}
|
||||
|
||||
.point-of-sale .pos-actionbar{
|
||||
position:absolute;
|
||||
left: 0;
|
||||
bottom: 0px;
|
||||
height: 105px;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
background: #f5f5f5; /*#ebebeb;*/
|
||||
border-top: solid 1px #afafb6;
|
||||
}
|
||||
|
||||
.point-of-sale .pos-actionbar ul{
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.point-of-sale .pos-actionbar-left-region{
|
||||
height: 100%;
|
||||
width: 434px;
|
||||
margin: 0px;
|
||||
padding-left:3px;
|
||||
padding-right:3px;
|
||||
border-right: solid 1px #dfdfdf;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.point-of-sale .pos-actionbar-right-region{
|
||||
height: 100%;
|
||||
margin: 0px;
|
||||
padding-left:3px;
|
||||
padding-right:3px;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
.point-of-sale .pos-actionbar .button{
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
text-align:center;
|
||||
margin:3px;
|
||||
margin-top:6px;
|
||||
//line-height: 90px;
|
||||
//vertical-align:center;
|
||||
float:left;
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
border: 1px solid #cacaca;
|
||||
-moz-border-radius: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
|
||||
background: #e2e2e2;
|
||||
background: -moz-linear-gradient(#f0f0f0, #e2e2e2);
|
||||
background: -webkit-gradient(linear, left top, left bottom, from(#f0f0f0), to(#e2e2e2));
|
||||
-webkit-box-shadow: 0px 2px 2px rgba(0,0,0, 0.3);
|
||||
}
|
||||
.point-of-sale .pos-actionbar .button .label{
|
||||
margin-top: 37px;
|
||||
}
|
||||
.point-of-sale .pos-actionbar .button .icon{
|
||||
margin-top: 10px;
|
||||
}
|
||||
.point-of-sale .pos-actionbar .button:hover {
|
||||
color: white;
|
||||
background: #7f82ac;
|
||||
border: 1px solid #7f82ac;
|
||||
background: -moz-linear-gradient(#9d9fc5, #7f82ac);
|
||||
background: -webkit-gradient(linear, left top, left bottom, from(#9d9fc5), to(#7f82ac));
|
||||
|
||||
-webkit-transition-property: background, border;
|
||||
-webkit-transition-duration: 0.2s;
|
||||
-webkit-transition-timing-function: ease-out;
|
||||
}
|
||||
|
||||
.point-of-sale .pos-actionbar .button.rightalign{
|
||||
float:right;
|
||||
}
|
||||
|
||||
/* Onscreen Keyboard http://net.tutsplus.com/tutorials/javascript-ajax/creating-a-keyboard-with-css-and-jquery/ */
|
||||
|
||||
.point-of-sale .keyboard_frame{
|
||||
display: none;
|
||||
position:absolute;
|
||||
left: 0;
|
||||
bottom: 0px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-top: 15px;
|
||||
width: 100%;
|
||||
height: 0px; /* 235px, animated via jquery */
|
||||
background-color: #BBB;
|
||||
overflow:hidden;
|
||||
-webkit-box-shadow: 0px 0px 10px rgba(0,0,0, 0.3);
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
}
|
||||
.point-of-sale .keyboard_frame .close_button{
|
||||
height:40px;
|
||||
width:60px;
|
||||
text-align:center;
|
||||
background-color: #DDD;
|
||||
font-size: 12px;
|
||||
line-height:40px;
|
||||
border: 1px solid #CCC;
|
||||
-webkit-border-radius: 5px;
|
||||
-webkit-box-shadow: 0px 2px 5px rgba(0,0,0, 0.2);
|
||||
position:absolute;
|
||||
top:0;
|
||||
right:15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.point-of-sale .keyboard li {
|
||||
float: left;
|
||||
text-align: center;
|
||||
background-color: #fff;
|
||||
border: 1px solid #f0f0f0;
|
||||
top:0;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
-webkit-box-shadow: 0px 2px 5px rgba(0,0,0, 0.2);
|
||||
-webkit-transition-property: top, background-color;
|
||||
-webkit-transition-duration: 0.2s;
|
||||
-webkit-transition-timing-function: linear;
|
||||
}
|
||||
.point-of-sale .keyboard li:hover {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
left: 0px;
|
||||
border-color: #ddd;
|
||||
background-color:#e5e5e5;
|
||||
cursor: pointer;
|
||||
-webkit-transition-property: top, background-color;
|
||||
-webkit-transition-duration: 0.1s;
|
||||
-webkit-transition-timing-function: ease-out;
|
||||
}
|
||||
.point-of-sale .uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.point-of-sale .on {
|
||||
display: none;
|
||||
}
|
||||
.point-of-sale .firstitem{
|
||||
clear: left;
|
||||
}
|
||||
.point-of-sale .keyboard .lastitem {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
/* ---- full sized keyboard ---- */
|
||||
|
||||
.point-of-sale .full_keyboard {
|
||||
list-style: none;
|
||||
font-size: 14px;
|
||||
width: 680px;
|
||||
height: 100%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.point-of-sale .full_keyboard li{
|
||||
margin: 0 5px 5px 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
.point-of-sale .full_keyboard .tab, .point-of-sale .full_keyboard .delete {
|
||||
width: 70px;
|
||||
}
|
||||
.point-of-sale .full_keyboard .capslock {
|
||||
width: 80px;
|
||||
}
|
||||
.point-of-sale .full_keyboard .return {
|
||||
width: 77px;
|
||||
}
|
||||
.point-of-sale .full_keyboard .left-shift {
|
||||
width: 95px;
|
||||
}
|
||||
.point-of-sale .full_keyboard .right-shift {
|
||||
width: 109px;
|
||||
}
|
||||
.point-of-sale .full_keyboard .space {
|
||||
clear: left;
|
||||
width: 673px;
|
||||
}
|
||||
|
||||
/* ---- simplified keyboard ---- */
|
||||
|
||||
.point-of-sale .simple_keyboard {
|
||||
list-style: none;
|
||||
font-size: 16px;
|
||||
width: 545px;
|
||||
height: 220px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.point-of-sale .simple_keyboard li{
|
||||
margin: 0 5px 5px 0;
|
||||
width: 49px;
|
||||
height: 49px;
|
||||
line-height: 49px;
|
||||
}
|
||||
.point-of-sale .simple_keyboard .firstitem.row_asdf{
|
||||
margin-left:25px;
|
||||
}
|
||||
.point-of-sale .simple_keyboard .firstitem.row_zxcv{
|
||||
margin-left:55px;
|
||||
}
|
||||
.point-of-sale .simple_keyboard .delete{
|
||||
width: 103px;
|
||||
}
|
||||
.point-of-sale .simple_keyboard .return{
|
||||
width: 103px;
|
||||
}
|
||||
.point-of-sale .simple_keyboard .space{
|
||||
width:268px;
|
||||
}
|
||||
.point-of-sale .simple_keyboard .numlock{
|
||||
width:103px;
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,149 @@
|
||||
|
||||
function openerp_pos_devices(module, instance){ //module is instance.point_of_sale
|
||||
module.BarcodeReader = instance.web.Class.extend({
|
||||
|
||||
init: function(attributes){
|
||||
this.pos = attributes.pos;
|
||||
this.connect();
|
||||
},
|
||||
|
||||
// returns true if the code is a valid EAN codebar number by checking the control digit.
|
||||
checkEan: function(code){
|
||||
var st1 = code.slice();
|
||||
var st2 = st1.slice(0,st1.length-1).reverse();
|
||||
// some EAN13 barcodes have a length of 12, as they start by 0
|
||||
while (st2.length < 12) {
|
||||
st2.push(0);
|
||||
}
|
||||
var countSt3 = 1;
|
||||
var st3 = 0;
|
||||
$.each(st2, function() {
|
||||
if (countSt3%2 === 1) {
|
||||
st3 += this;
|
||||
}
|
||||
countSt3 ++;
|
||||
});
|
||||
st3 *= 3;
|
||||
var st4 = 0;
|
||||
var countSt4 = 1;
|
||||
$.each(st2, function() {
|
||||
if (countSt4%2 === 0) {
|
||||
st4 += this;
|
||||
}
|
||||
countSt4 ++;
|
||||
});
|
||||
var st5 = st3 + st4;
|
||||
var cd = (10 - (st5%10)) % 10;
|
||||
return code[code.length-1] === cd;
|
||||
},
|
||||
|
||||
// returns a product that has a packaging with an EAN matching to provided ean string.
|
||||
// returns undefined if no such product is found.
|
||||
getProductByEAN: function(ean) {
|
||||
var allProducts = this.pos.get('product_list');
|
||||
var allPackages = this.pos.get('product.packaging');
|
||||
var prefix = ean.substring(0,2);
|
||||
var scannedProductModel = undefined;
|
||||
|
||||
if (prefix in {'02':'', '22':'', '24':'', '26':'', '28':''}) {
|
||||
|
||||
// PRICE barcode
|
||||
var itemCode = ean.substring(0,7);
|
||||
var scannedPackaging = _.detect(allPackages, function(pack) { return pack.ean !== undefined && pack.ean.substring(0,7) === itemCode;});
|
||||
if (scannedPackaging !== undefined) {
|
||||
scannedProductModel = _.detect(allProducts, function(pc) { return pc.id === scannedPackaging.product_id[0];});
|
||||
scannedProductModel.list_price = Number(ean.substring(7,12))/100;
|
||||
}
|
||||
} else if (prefix in {'21':'','23':'','27':'','29':'','25':''}) {
|
||||
// WEIGHT barcode
|
||||
var weight = Number(barcode.substring(7,12))/1000;
|
||||
var itemCode = ean.substring(0,7);
|
||||
var scannedPackaging = _.detect(allPackages, function(pack) { return pack.ean !== undefined && pack.ean.substring(0,7) === itemCode;});
|
||||
if (scannedPackaging !== undefined) {
|
||||
scannedProductModel = _.detect(allProducts, function(pc) { return pc.id === scannedPackaging.product_id[0];});
|
||||
scannedProductModel.list_price *= weight;
|
||||
scannedProductModel.name += ' - ' + weight + ' Kg.';
|
||||
}
|
||||
} else {
|
||||
// UNIT barcode
|
||||
scannedProductModel = _.detect(allProducts, function(pc) { return pc.ean13 === ean;}); //TODO DOES NOT SCALE
|
||||
}
|
||||
return scannedProductModel;
|
||||
},
|
||||
|
||||
// starts catching keyboard events and tries to interpret codebar
|
||||
connect: function(){
|
||||
var self = this;
|
||||
var codeNumbers = [];
|
||||
var timeStamp = 0;
|
||||
var lastTimeStamp = 0;
|
||||
|
||||
// The barcode readers acts as a keyboard, we catch all keyup events and try to find a
|
||||
// barcode sequence in the typed keys, then act accordingly.
|
||||
$('body').delegate('','keyup', function (e){
|
||||
|
||||
//We only care about numbers
|
||||
if (!isNaN(Number(String.fromCharCode(e.keyCode)))) {
|
||||
|
||||
// The barcode reader sends keystrokes with a specific interval.
|
||||
// We look if the typed keys fit in the interval.
|
||||
if (codeNumbers.length==0) {
|
||||
timeStamp = new Date().getTime();
|
||||
} else {
|
||||
if (lastTimeStamp + 30 < new Date().getTime()) {
|
||||
// not a barcode reader
|
||||
codeNumbers = [];
|
||||
timeStamp = new Date().getTime();
|
||||
}
|
||||
}
|
||||
codeNumbers.push(e.keyCode - 48);
|
||||
lastTimeStamp = new Date().getTime();
|
||||
if (codeNumbers.length == 13) {
|
||||
//console.log('found code:', codeNumbers.join(''));
|
||||
|
||||
// a barcode reader
|
||||
if (!self.checkEan(codeNumbers)) {
|
||||
// barcode read error, raise warning
|
||||
$(QWeb.render('pos-scan-warning')).dialog({
|
||||
resizable: false,
|
||||
height:220,
|
||||
modal: true,
|
||||
title: "Warning",
|
||||
});
|
||||
}
|
||||
var selectedOrder = self.pos.get('selectedOrder');
|
||||
var scannedProductModel = self.getProductByEAN(codeNumbers.join(''),allPackages,allProducts);
|
||||
if (scannedProductModel === undefined) {
|
||||
// product not recognized, raise warning
|
||||
$(QWeb.render('pos-scan-warning')).dialog({
|
||||
resizable: false,
|
||||
height:220,
|
||||
modal: true,
|
||||
title: "Warning",
|
||||
/*
|
||||
buttons: {
|
||||
"OK": function() {
|
||||
$( this ).dialog( "close" );
|
||||
return;
|
||||
},
|
||||
}*/
|
||||
});
|
||||
} else {
|
||||
selectedOrder.addProduct(new module.Product(scannedProductModel));
|
||||
}
|
||||
|
||||
codeNumbers = [];
|
||||
}
|
||||
} else {
|
||||
// NaN
|
||||
codeNumbers = [];
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// stops catching keyboard events
|
||||
disconnect: function(){
|
||||
$('body').undelegate('', 'keyup')
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
openerp.point_of_sale = function(instance) {
|
||||
|
||||
instance.point_of_sale = {};
|
||||
|
||||
var module = instance.point_of_sale;
|
||||
|
||||
openerp_pos_models(module,instance); // import pos_models.js
|
||||
|
||||
openerp_pos_widgets(module,instance); // import pos_widgets.js
|
||||
|
||||
openerp_pos_devices(module,instance); // import pos_devices.js
|
||||
|
||||
instance.web.client_actions.add('pos.ui', 'instance.point_of_sale.PosWidget');
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,550 @@
|
||||
function openerp_pos_models(module, instance){ //module is instance.point_of_sale
|
||||
var QWeb = instance.web.qweb;
|
||||
|
||||
module.LocalStorageDAO = instance.web.Class.extend({
|
||||
add_operation: function(operation) {
|
||||
var self = this;
|
||||
return $.async_when().pipe(function() {
|
||||
var tmp = self._get('oe_pos_operations', []);
|
||||
var last_id = self._get('oe_pos_operations_sequence', 1);
|
||||
tmp.push({'id': last_id, 'data': operation});
|
||||
self._set('oe_pos_operations', tmp);
|
||||
self._set('oe_pos_operations_sequence', last_id + 1);
|
||||
});
|
||||
},
|
||||
remove_operation: function(id) {
|
||||
var self = this;
|
||||
return $.async_when().pipe(function() {
|
||||
var tmp = self._get('oe_pos_operations', []);
|
||||
tmp = _.filter(tmp, function(el) {
|
||||
return el.id !== id;
|
||||
});
|
||||
self._set('oe_pos_operations', tmp);
|
||||
});
|
||||
},
|
||||
get_operations: function() {
|
||||
var self = this;
|
||||
return $.async_when().pipe(function() {
|
||||
return self._get('oe_pos_operations', []);
|
||||
});
|
||||
},
|
||||
_get: function(key, default_) {
|
||||
var txt = localStorage[key];
|
||||
if (! txt)
|
||||
return default_;
|
||||
return JSON.parse(txt);
|
||||
},
|
||||
_set: function(key, value) {
|
||||
localStorage[key] = JSON.stringify(value);
|
||||
},
|
||||
});
|
||||
|
||||
var fetch = function(osvModel, fields, domain){
|
||||
var dataSetSearch = new instance.web.DataSetSearch(null, osvModel, {}, domain);
|
||||
return dataSetSearch.read_slice(fields, 0);
|
||||
};
|
||||
|
||||
/*
|
||||
Gets all the necessary data from the OpenERP web client (instance, shop data etc.)
|
||||
*/
|
||||
module.PosModel = Backbone.Model.extend({
|
||||
initialize: function(session, attributes) {
|
||||
Backbone.Model.prototype.initialize.call(this, attributes);
|
||||
var self = this;
|
||||
this.dao = new module.LocalStorageDAO();
|
||||
this.ready = $.Deferred();
|
||||
this.flush_mutex = new $.Mutex();
|
||||
this.build_tree = _.bind(this.build_tree, this);
|
||||
this.session = session;
|
||||
this.categories = {};
|
||||
this.set({
|
||||
'nbr_pending_operations': 0,
|
||||
'currency': {symbol: '$', position: 'after'},
|
||||
'shop': {},
|
||||
'company': {},
|
||||
'user': {},
|
||||
'orders': new module.OrderCollection(),
|
||||
'products': new module.ProductCollection(),
|
||||
//'cashRegisters': [], // new module.CashRegisterCollection(this.pos.get('bank_statements')),
|
||||
'selectedOrder': undefined,
|
||||
});
|
||||
|
||||
var cat_def = fetch('pos.category', ['name', 'parent_id', 'child_id'])
|
||||
.pipe(function(result){
|
||||
return self.set({'categories': result});
|
||||
});
|
||||
|
||||
var prod_def = fetch(
|
||||
'product.product',
|
||||
['name', 'list_price', 'pos_categ_id', 'taxes_id','product_image_small'],
|
||||
[['pos_categ_id','!=', 'false']]
|
||||
).then(function(result){
|
||||
return self.set({'product_list': result});
|
||||
});
|
||||
|
||||
var bank_def = fetch(
|
||||
'account.bank.statement',
|
||||
['account_id', 'currency', 'journal_id', 'state', 'name'],
|
||||
[['state','=','open'], ['user_id', '=', this.session.uid]]
|
||||
).then(function(result){
|
||||
return self.set({'bank_statements': result});
|
||||
});
|
||||
|
||||
var tax_def = fetch('account.tax', ['amount','price_include','type'])
|
||||
.then(function(result){
|
||||
return self.set({'taxes': result});
|
||||
});
|
||||
|
||||
$.when(cat_def,prod_def,bank_def,tax_def,this.get_app_data(), this.flush())
|
||||
.pipe(_.bind(this.build_tree, this))
|
||||
.pipe(function(){
|
||||
self.set({'cashRegisters': new module.CashRegisterCollection(self.get('bank_statements')) });
|
||||
self.ready.resolve();
|
||||
});
|
||||
|
||||
return (this.get('orders')).bind('remove', _.bind( function(removedOrder) {
|
||||
if ((this.get('orders')).isEmpty()) {
|
||||
this.addAndSelectOrder(new module.Order({pos: self}));
|
||||
}
|
||||
if ((this.get('selectedOrder')) === removedOrder) {
|
||||
return this.set({
|
||||
selectedOrder: (this.get('orders')).last()
|
||||
});
|
||||
}
|
||||
}, this));
|
||||
|
||||
},
|
||||
|
||||
get_app_data: function() {
|
||||
var self = this;
|
||||
return $.when(new instance.web.Model("sale.shop").get_func("search_read")([]).pipe(function(result) {
|
||||
self.set({'shop': result[0]});
|
||||
var company_id = result[0]['company_id'][0];
|
||||
return new instance.web.Model("res.company").get_func("read")(company_id, ['currency_id', 'name', 'phone']).pipe(function(result) {
|
||||
self.set({'company': result});
|
||||
var currency_id = result['currency_id'][0]
|
||||
return new instance.web.Model("res.currency").get_func("read")([currency_id],
|
||||
['symbol', 'position']).pipe(function(result) {
|
||||
self.set({'currency': result[0]});
|
||||
|
||||
});
|
||||
});
|
||||
}), new instance.web.Model("res.users").get_func("read")(this.session.uid, ['name']).pipe(function(result) {
|
||||
self.set({'user': result});
|
||||
}));
|
||||
},
|
||||
push_order: function(record) {
|
||||
var self = this;
|
||||
return this.dao.add_operation(record).pipe(function(){
|
||||
return self.flush();
|
||||
});
|
||||
},
|
||||
addAndSelectOrder: function(newOrder) {
|
||||
(this.get('orders')).add(newOrder);
|
||||
return this.set({
|
||||
selectedOrder: newOrder
|
||||
});
|
||||
},
|
||||
flush: function() {
|
||||
return this.flush_mutex.exec(_.bind(function() {
|
||||
return this._int_flush();
|
||||
}, this));
|
||||
},
|
||||
_int_flush : function() {
|
||||
var self = this;
|
||||
|
||||
this.dao.get_operations().pipe(function(operations) {
|
||||
self.set( {'nbr_pending_operations':operations.length} );
|
||||
if(operations.length === 0){
|
||||
return $.when();
|
||||
}
|
||||
var op = operations[0];
|
||||
|
||||
// we prevent the default error handler and assume errors
|
||||
// are a normal use case, except we stop the current iteration
|
||||
|
||||
return new instance.web.Model('pos.order').get_func('create_from_ui')([op])
|
||||
.fail(function(unused, event){
|
||||
event.preventDefault();
|
||||
})
|
||||
.pipe(function(){
|
||||
console.debug('saved 1 record'); //TODO Debug this
|
||||
self.dao.remove_operation(operations[0].id).pipe(function(){
|
||||
return self._int_flush();
|
||||
});
|
||||
}, function(){
|
||||
return $.when();
|
||||
});
|
||||
});
|
||||
},
|
||||
build_tree: function() {
|
||||
var c, id, _i, _len, _ref, _ref2;
|
||||
_ref = this.get('categories');
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
c = _ref[_i];
|
||||
this.categories[c.id] = {
|
||||
id: c.id,
|
||||
name: c.name,
|
||||
children: c.child_id,
|
||||
parent: c.parent_id[0],
|
||||
ancestors: [c.id],
|
||||
subtree: [c.id]
|
||||
};
|
||||
}
|
||||
_ref2 = this.categories;
|
||||
for (id in _ref2) {
|
||||
c = _ref2[id];
|
||||
this.current_category = c;
|
||||
this.build_ancestors(c.parent);
|
||||
this.build_subtree(c);
|
||||
}
|
||||
this.categories[0] = {
|
||||
ancestors: [],
|
||||
children: (function() {
|
||||
var _j, _len2, _ref3, _results;
|
||||
_ref3 = this.get('categories');
|
||||
_results = [];
|
||||
for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) {
|
||||
c = _ref3[_j];
|
||||
if (!(c.parent_id[0] != null)) {
|
||||
_results.push(c.id);
|
||||
}
|
||||
}
|
||||
return _results;
|
||||
}).call(this),
|
||||
subtree: (function() {
|
||||
var _j, _len2, _ref3, _results;
|
||||
_ref3 = this.get('categories');
|
||||
_results = [];
|
||||
for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) {
|
||||
c = _ref3[_j];
|
||||
_results.push(c.id);
|
||||
}
|
||||
return _results;
|
||||
}).call(this)
|
||||
};
|
||||
},
|
||||
build_ancestors: function(parent) {
|
||||
if (parent != null) {
|
||||
this.current_category.ancestors.unshift(parent);
|
||||
return this.build_ancestors(this.categories[parent].parent);
|
||||
}
|
||||
},
|
||||
build_subtree: function(category) {
|
||||
var c, _i, _len, _ref, _results;
|
||||
_ref = category.children;
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
c = _ref[_i];
|
||||
this.current_category.subtree.push(c);
|
||||
_results.push(this.build_subtree(this.categories[c]));
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
});
|
||||
|
||||
module.CashRegister = Backbone.Model.extend({
|
||||
});
|
||||
|
||||
module.CashRegisterCollection = Backbone.Collection.extend({
|
||||
model: module.CashRegister,
|
||||
});
|
||||
|
||||
module.Product = Backbone.Model.extend({
|
||||
});
|
||||
|
||||
module.ProductCollection = Backbone.Collection.extend({
|
||||
model: module.Product,
|
||||
});
|
||||
|
||||
/*
|
||||
Each Order contains zero or more Orderlines (i.e. the content of the "shopping cart".)
|
||||
There should only ever be one Orderline per distinct product in an Order.
|
||||
To add more of the same product, just update the quantity accordingly.
|
||||
The Order also contains payment information.
|
||||
*/
|
||||
module.Orderline = Backbone.Model.extend({
|
||||
defaults: {
|
||||
quantity: 1,
|
||||
list_price: 0,
|
||||
discount: 0
|
||||
},
|
||||
initialize: function(attributes) {
|
||||
this.pos = attributes.pos;
|
||||
Backbone.Model.prototype.initialize.apply(this, arguments);
|
||||
this.bind('change:quantity', function(unused, qty) {
|
||||
if (qty == 0)
|
||||
this.trigger('killme');
|
||||
}, this);
|
||||
},
|
||||
incrementQuantity: function() {
|
||||
return this.set({
|
||||
quantity: (this.get('quantity')) + 1
|
||||
});
|
||||
},
|
||||
getPriceWithoutTax: function() {
|
||||
return this.getAllPrices().priceWithoutTax;
|
||||
},
|
||||
getPriceWithTax: function() {
|
||||
return this.getAllPrices().priceWithTax;
|
||||
},
|
||||
getTax: function() {
|
||||
return this.getAllPrices().tax;
|
||||
},
|
||||
getAllPrices: function() {
|
||||
var self = this;
|
||||
var base = (this.get('quantity')) * (this.get('list_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 taxes_ids = product.taxes_id;
|
||||
var taxes = self.pos.get('taxes');
|
||||
var taxtotal = 0;
|
||||
_.each(taxes_ids, function(el) {
|
||||
var tax = _.detect(taxes, function(t) {return t.id === el;});
|
||||
if (tax.price_include) {
|
||||
var tmp;
|
||||
if (tax.type === "percent") {
|
||||
tmp = base - (base / (1 + tax.amount));
|
||||
} else if (tax.type === "fixed") {
|
||||
tmp = tax.amount * self.get('quantity');
|
||||
} else {
|
||||
throw "This type of tax is not supported by the point of sale: " + tax.type;
|
||||
}
|
||||
taxtotal += tmp;
|
||||
totalNoTax -= tmp;
|
||||
} else {
|
||||
var tmp;
|
||||
if (tax.type === "percent") {
|
||||
tmp = tax.amount * base;
|
||||
} else if (tax.type === "fixed") {
|
||||
tmp = tax.amount * self.get('quantity');
|
||||
} else {
|
||||
throw "This type of tax is not supported by the point of sale: " + tax.type;
|
||||
}
|
||||
taxtotal += tmp;
|
||||
totalTax += tmp;
|
||||
}
|
||||
});
|
||||
return {
|
||||
"priceWithTax": totalTax,
|
||||
"priceWithoutTax": totalNoTax,
|
||||
"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({
|
||||
model: module.Orderline,
|
||||
});
|
||||
|
||||
// Every PaymentLine has all the attributes of the corresponding CashRegister.
|
||||
module.Paymentline = Backbone.Model.extend({
|
||||
defaults: {
|
||||
amount: 0,
|
||||
},
|
||||
initialize: function(attributes) {
|
||||
Backbone.Model.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
getAmount: function(){
|
||||
return this.get('amount');
|
||||
},
|
||||
exportAsJSON: 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()
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
module.PaymentlineCollection = Backbone.Collection.extend({
|
||||
model: module.Paymentline,
|
||||
});
|
||||
|
||||
module.Order = Backbone.Model.extend({
|
||||
defaults:{
|
||||
validated: false,
|
||||
step: 'products',
|
||||
},
|
||||
initialize: function(attributes){
|
||||
Backbone.Model.prototype.initialize.apply(this, arguments);
|
||||
this.set({
|
||||
creationDate: new Date(),
|
||||
orderLines: new module.OrderlineCollection(),
|
||||
paymentLines: new module.PaymentlineCollection(),
|
||||
name: "Order " + this.generateUniqueId(),
|
||||
});
|
||||
this.pos = attributes.pos; //TODO put that in set and remember to use 'get' to read it ...
|
||||
this.bind('change:validated', this.validatedChanged);
|
||||
return this;
|
||||
},
|
||||
events: {
|
||||
'change:validated': 'validatedChanged'
|
||||
},
|
||||
validatedChanged: function() {
|
||||
if (this.get("validated") && !this.previous("validated")) {
|
||||
this.set({'step': 'receipt'});
|
||||
}
|
||||
},
|
||||
generateUniqueId: function() {
|
||||
return new Date().getTime();
|
||||
},
|
||||
addProduct: function(product) {
|
||||
var existing;
|
||||
existing = (this.get('orderLines')).get(product.id);
|
||||
if (existing != null) {
|
||||
existing.incrementQuantity();
|
||||
} else {
|
||||
var attr = product.toJSON();
|
||||
attr.pos = this.pos;
|
||||
var line = new module.Orderline(attr);
|
||||
this.get('orderLines').add(line);
|
||||
line.bind('killme', function() {
|
||||
this.get('orderLines').remove(line);
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
addPaymentLine: function(cashRegister) {
|
||||
var newPaymentline;
|
||||
newPaymentline = new module.Paymentline(cashRegister);
|
||||
/* TODO: Should be 0 for cash-like accounts */
|
||||
newPaymentline.set({
|
||||
amount: this.getDueLeft()
|
||||
});
|
||||
return (this.get('paymentLines')).add(newPaymentline);
|
||||
},
|
||||
getName: function() {
|
||||
return this.get('name');
|
||||
},
|
||||
getTotal: function() {
|
||||
return (this.get('orderLines')).reduce((function(sum, orderLine) {
|
||||
return sum + orderLine.getPriceWithTax();
|
||||
}), 0);
|
||||
},
|
||||
getTotalTaxExcluded: function() {
|
||||
return (this.get('orderLines')).reduce((function(sum, orderLine) {
|
||||
return sum + orderLine.getPriceWithoutTax();
|
||||
}), 0);
|
||||
},
|
||||
getTax: function() {
|
||||
return (this.get('orderLines')).reduce((function(sum, orderLine) {
|
||||
return sum + orderLine.getTax();
|
||||
}), 0);
|
||||
},
|
||||
getPaidTotal: function() {
|
||||
return (this.get('paymentLines')).reduce((function(sum, paymentLine) {
|
||||
return sum + paymentLine.getAmount();
|
||||
}), 0);
|
||||
},
|
||||
getChange: function() {
|
||||
return this.getPaidTotal() - this.getTotal();
|
||||
},
|
||||
getDueLeft: function() {
|
||||
return this.getTotal() - this.getPaidTotal();
|
||||
},
|
||||
exportAsJSON: function() {
|
||||
var orderLines, paymentLines;
|
||||
orderLines = [];
|
||||
(this.get('orderLines')).each(_.bind( function(item) {
|
||||
return orderLines.push([0, 0, item.exportAsJSON()]);
|
||||
}, this));
|
||||
paymentLines = [];
|
||||
(this.get('paymentLines')).each(_.bind( function(item) {
|
||||
return paymentLines.push([0, 0, item.exportAsJSON()]);
|
||||
}, this));
|
||||
return {
|
||||
name: this.getName(),
|
||||
amount_paid: this.getPaidTotal(),
|
||||
amount_total: this.getTotal(),
|
||||
amount_tax: this.getTax(),
|
||||
amount_return: this.getChange(),
|
||||
lines: orderLines,
|
||||
statement_ids: paymentLines
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
module.OrderCollection = Backbone.Collection.extend({
|
||||
model: module.Order,
|
||||
});
|
||||
|
||||
/*
|
||||
The numpad handles both the choice of the property currently being modified
|
||||
(quantity, price or discount) and the edition of the corresponding numeric value.
|
||||
*/
|
||||
module.NumpadState = Backbone.Model.extend({
|
||||
defaults: {
|
||||
buffer: "0",
|
||||
mode: "quantity"
|
||||
},
|
||||
appendNewChar: function(newChar) {
|
||||
var oldBuffer;
|
||||
oldBuffer = this.get('buffer');
|
||||
if (oldBuffer === '0') {
|
||||
this.set({
|
||||
buffer: newChar
|
||||
});
|
||||
} else if (oldBuffer === '-0') {
|
||||
this.set({
|
||||
buffer: "-" + newChar
|
||||
});
|
||||
} else {
|
||||
this.set({
|
||||
buffer: (this.get('buffer')) + newChar
|
||||
});
|
||||
}
|
||||
this.updateTarget();
|
||||
},
|
||||
deleteLastChar: function() {
|
||||
var tempNewBuffer;
|
||||
tempNewBuffer = (this.get('buffer')).slice(0, -1) || "0";
|
||||
if (isNaN(tempNewBuffer)) {
|
||||
tempNewBuffer = "0";
|
||||
}
|
||||
this.set({
|
||||
buffer: tempNewBuffer
|
||||
});
|
||||
this.updateTarget();
|
||||
},
|
||||
switchSign: function() {
|
||||
var oldBuffer;
|
||||
oldBuffer = this.get('buffer');
|
||||
this.set({
|
||||
buffer: oldBuffer[0] === '-' ? oldBuffer.substr(1) : "-" + oldBuffer
|
||||
});
|
||||
this.updateTarget();
|
||||
},
|
||||
changeMode: function(newMode) {
|
||||
this.set({
|
||||
buffer: "0",
|
||||
mode: newMode
|
||||
});
|
||||
},
|
||||
reset: function() {
|
||||
this.set({
|
||||
buffer: "0",
|
||||
mode: "quantity"
|
||||
});
|
||||
},
|
||||
updateTarget: function() {
|
||||
var bufferContent, params;
|
||||
bufferContent = this.get('buffer');
|
||||
if (bufferContent && !isNaN(bufferContent)) {
|
||||
this.trigger('setValue', parseFloat(bufferContent));
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
<!-- vim:fdl=1:
|
||||
-->
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="PointOfSale">
|
||||
<t t-name="POSWidget">
|
||||
<div class="point-of-sale">
|
||||
<div id="topheader">
|
||||
<div id="branding">
|
||||
@@ -141,6 +141,9 @@
|
||||
</a>
|
||||
</span>
|
||||
</t>
|
||||
<t t-name="pos-scan-warning">
|
||||
<div>The product could not be recognized. Please contact an employee.</div>
|
||||
</t>
|
||||
<t t-name="pos-close-warning">
|
||||
<div>There are pending operations that could not be saved into the database, are you sure you want to exit?</div>
|
||||
</t>
|
||||
@@ -291,4 +294,138 @@
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="pos-actionbar">
|
||||
<div class="pos-actionbar">
|
||||
<ul class="pos-actionbar-left-region">
|
||||
<!-- <li class="button">yoyo</li>
|
||||
<li class="button">foo</li>
|
||||
<li class="button rightalign">bar</li> -->
|
||||
</ul>
|
||||
|
||||
<ul class="pos-actionbar-right-region">
|
||||
<!-- <li class="button">BUTTOOON</li>
|
||||
<li class="button">JEEENKIINS</li>
|
||||
<li class="button rightalign">ARGH</li> -->
|
||||
</ul>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="pos-action-button">
|
||||
<li t-att-class=" 'button '+ (widget.rightalign ? 'rightalign ' : '')">
|
||||
<div class='label'>
|
||||
<t t-esc="widget.label" />
|
||||
</div>
|
||||
</li>
|
||||
</t>
|
||||
<t t-name="pos-action-button-with-icon">
|
||||
<li t-att-class=" 'button '+ (widget.rightalign ? 'rightalign ' : '')">
|
||||
<div class='icon'>
|
||||
<img t-att-src="widget.icon" />
|
||||
<div class='iconlabel'><t t-esc="widget.label" /></div>
|
||||
</div>
|
||||
</li>
|
||||
</t>
|
||||
<!-- Onscreen Keyboard :
|
||||
http://net.tutsplus.com/tutorials/javascript-ajax/creating-a-keyboard-with-css-and-jquery/ -->
|
||||
<t t-name="pos-onscreen-keyboard-full-template">
|
||||
<div class="keyboard_frame">
|
||||
<ul class="keyboard full_keyboard">
|
||||
<li class="symbol"><span class="off">`</span><span class="on">~</span></li>
|
||||
<li class="symbol"><span class="off">1</span><span class="on">!</span></li>
|
||||
<li class="symbol"><span class="off">2</span><span class="on">@</span></li>
|
||||
<li class="symbol"><span class="off">3</span><span class="on">#</span></li>
|
||||
<li class="symbol"><span class="off">4</span><span class="on">$</span></li>
|
||||
<li class="symbol"><span class="off">5</span><span class="on">%</span></li>
|
||||
<li class="symbol"><span class="off">6</span><span class="on">^</span></li>
|
||||
<li class="symbol"><span class="off">7</span><span class="on">&</span></li>
|
||||
<li class="symbol"><span class="off">8</span><span class="on">*</span></li>
|
||||
<li class="symbol"><span class="off">9</span><span class="on">(</span></li>
|
||||
<li class="symbol"><span class="off">0</span><span class="on">)</span></li>
|
||||
<li class="symbol"><span class="off">-</span><span class="on">_</span></li>
|
||||
<li class="symbol"><span class="off">=</span><span class="on">+</span></li>
|
||||
<li class="delete lastitem">delete</li>
|
||||
<li class="tab firstitem">tab</li>
|
||||
<li class="letter">q</li>
|
||||
<li class="letter">w</li>
|
||||
<li class="letter">e</li>
|
||||
<li class="letter">r</li>
|
||||
<li class="letter">t</li>
|
||||
<li class="letter">y</li>
|
||||
<li class="letter">u</li>
|
||||
<li class="letter">i</li>
|
||||
<li class="letter">o</li>
|
||||
<li class="letter">p</li>
|
||||
<li class="symbol"><span class="off">[</span><span class="on">{</span></li>
|
||||
<li class="symbol"><span class="off">]</span><span class="on">}</span></li>
|
||||
<li class="symbol lastitem"><span class="off">\</span><span class="on">|</span></li>
|
||||
<li class="capslock firstitem">caps lock</li>
|
||||
<li class="letter">a</li>
|
||||
<li class="letter">s</li>
|
||||
<li class="letter">d</li>
|
||||
<li class="letter">f</li>
|
||||
<li class="letter">g</li>
|
||||
<li class="letter">h</li>
|
||||
<li class="letter">j</li>
|
||||
<li class="letter">k</li>
|
||||
<li class="letter">l</li>
|
||||
<li class="symbol"><span class="off">;</span><span class="on">:</span></li>
|
||||
<li class="symbol"><span class="off">'</span><span class="on">"</span></li>
|
||||
<li class="return lastitem">return</li>
|
||||
<li class="left-shift firstitem">shift</li>
|
||||
<li class="letter">z</li>
|
||||
<li class="letter">x</li>
|
||||
<li class="letter">c</li>
|
||||
<li class="letter">v</li>
|
||||
<li class="letter">b</li>
|
||||
<li class="letter">n</li>
|
||||
<li class="letter">m</li>
|
||||
<li class="symbol"><span class="off">,</span><span class="on"><</span></li>
|
||||
<li class="symbol"><span class="off">.</span><span class="on">></span></li>
|
||||
<li class="symbol"><span class="off">/</span><span class="on">?</span></li>
|
||||
<li class="right-shift lastitem">shift</li>
|
||||
<li class="space firstitem lastitem">&nbsp;</li>
|
||||
</ul>
|
||||
<p class="close_button">close</p>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="pos-onscreen-keyboard-simple-template">
|
||||
<div class="keyboard_frame">
|
||||
<ul class="keyboard simple_keyboard">
|
||||
<li class="symbol firstitem row_qwerty"><span class="off">q</span><span class="on">1</span></li>
|
||||
<li class="symbol"><span class="off">w</span><span class="on">2</span></li>
|
||||
<li class="symbol"><span class="off">e</span><span class="on">3</span></li>
|
||||
<li class="symbol"><span class="off">r</span><span class="on">4</span></li>
|
||||
<li class="symbol"><span class="off">t</span><span class="on">5</span></li>
|
||||
<li class="symbol"><span class="off">y</span><span class="on">6</span></li>
|
||||
<li class="symbol"><span class="off">u</span><span class="on">7</span></li>
|
||||
<li class="symbol"><span class="off">i</span><span class="on">8</span></li>
|
||||
<li class="symbol"><span class="off">o</span><span class="on">9</span></li>
|
||||
<li class="symbol lastitem"><span class="off">p</span><span class="on">0</span></li>
|
||||
|
||||
<li class="symbol firstitem row_asdf"><span class="off">a</span><span class="on">@</span></li>
|
||||
<li class="symbol"><span class="off">s</span><span class="on">#</span></li>
|
||||
<li class="symbol"><span class="off">d</span><span class="on">%</span></li>
|
||||
<li class="symbol"><span class="off">f</span><span class="on">*</span></li>
|
||||
<li class="symbol"><span class="off">g</span><span class="on">/</span></li>
|
||||
<li class="symbol"><span class="off">h</span><span class="on">-</span></li>
|
||||
<li class="symbol"><span class="off">j</span><span class="on">+</span></li>
|
||||
<li class="symbol"><span class="off">k</span><span class="on">(</span></li>
|
||||
<li class="symbol lastitem"><span class="off">l</span><span class="on">)</span></li>
|
||||
|
||||
<li class="symbol firstitem row_zxcv"><span class="off">z</span><span class="on">?</span></li>
|
||||
<li class="symbol"><span class="off">x</span><span class="on">!</span></li>
|
||||
<li class="symbol"><span class="off">c</span><span class="on">"</span></li>
|
||||
<li class="symbol"><span class="off">v</span><span class="on">'</span></li>
|
||||
<li class="symbol"><span class="off">b</span><span class="on">:</span></li>
|
||||
<li class="symbol"><span class="off">n</span><span class="on">;</span></li>
|
||||
<li class="symbol"><span class="off">m</span><span class="on">,</span></li>
|
||||
<li class="delete lastitem">delete</li>
|
||||
|
||||
<li class="numlock firstitem row_space"><span class="off">123</span><span class="on">ABC</span></li>
|
||||
<li class="space">&nbsp;</li>
|
||||
<li class="symbol"><span class="off">.</span><span class="on">.</span></li>
|
||||
<li class="return lastitem">return</li>
|
||||
</ul>
|
||||
<p class="close_button">close</p>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
|
||||
@@ -36,6 +36,14 @@
|
||||
key2="client_action_multi"
|
||||
id="action_box_entries" />
|
||||
|
||||
<act_window name="Put Money In"
|
||||
res_model="pos.box.entries"
|
||||
src_model="pos.session"
|
||||
view_mode="form"
|
||||
target="new"
|
||||
key2="client_action_multi"
|
||||
id="action_pos_session_box_entries" />
|
||||
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -36,5 +36,13 @@
|
||||
key2="client_action_multi"
|
||||
id="action_box_out" />
|
||||
|
||||
<act_window name="Take Money Out"
|
||||
res_model="pos.box.out"
|
||||
src_model="account.bank.statement"
|
||||
view_mode="form"
|
||||
target="new"
|
||||
key2="client_action_multi"
|
||||
id="action_pos_session_box_out" />
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -518,7 +518,7 @@ class product_product(osv.osv):
|
||||
'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the product without removing it."),
|
||||
'variants': fields.char('Variants', size=64),
|
||||
'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True, ondelete="cascade"),
|
||||
'ean13': fields.char('EAN13', size=13),
|
||||
'ean13': fields.char('EAN13', size=13, help='Barcode'),
|
||||
'packaging' : fields.one2many('product.packaging', 'product_id', 'Logistical Units', help="Gives the different ways to package the same product. This has no impact on the picking order and is mainly used if you use the EDI module."),
|
||||
'price_extra': fields.float('Variant Price Extra', digits_compute=dp.get_precision('Sale Price')),
|
||||
'price_margin': fields.float('Variant Price Margin', digits_compute=dp.get_precision('Sale Price')),
|
||||
|
||||
Reference in New Issue
Block a user