[IMP] point-of-sale, customer selection, remove dead code

This commit is contained in:
Antony Lesuisse 2014-07-02 17:52:34 +02:00
commit 2851cbbf00
14 changed files with 1339 additions and 993 deletions

View File

@ -682,7 +682,7 @@ class Escpos:
try:
stylestack = StyleStack()
serializer = XmlSerializer(self)
root = ET.fromstring(xml)
root = ET.fromstring(xml.encode('utf-8'))
self._raw(stylestack.to_escpos())

View File

@ -60,6 +60,7 @@ Main Features
'wizard/pos_open_statement.xml',
'wizard/pos_payment.xml',
'wizard/pos_session_opening.xml',
'views/templates.xml',
'point_of_sale_report.xml',
'point_of_sale_view.xml',
'point_of_sale_data.xml',

View File

@ -12,55 +12,6 @@ from openerp.addons.web.controllers.main import module_boot, login_redirect
_logger = logging.getLogger(__name__)
html_template = """<!DOCTYPE html>
<html>
<head>
<title>OpenERP POS</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta http-equiv="content-type" content="text/html, charset=utf-8" />
<meta name="viewport" content=" width=1024, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<link rel="shortcut icon" sizes="196x196" href="/point_of_sale/static/src/img/touch-icon-196.png">
<link rel="shortcut icon" sizes="128x128" href="/point_of_sale/static/src/img/touch-icon-128.png">
<link rel="apple-touch-icon" href="/point_of_sale/static/src/img/touch-icon-iphone.png">
<link rel="apple-touch-icon" sizes="76x76" href="/point_of_sale/static/src/img/touch-icon-ipad.png">
<link rel="apple-touch-icon" sizes="120x120" href="/point_of_sale/static/src/img/touch-icon-iphone-retina.png">
<link rel="apple-touch-icon" sizes="152x152" href="/point_of_sale/static/src/img/touch-icon-ipad-retina.png">
<link rel="shortcut icon" href="/web/static/src/img/favicon.ico" type="image/x-icon"/>
<link rel="stylesheet" href="/point_of_sale/static/src/fonts/lato/stylesheet.css" />
<link rel="stylesheet" href="/point_of_sale/static/src/fonts/font-awesome-4.0.3/css/font-awesome.min.css" />
<link rel="stylesheet" href="/point_of_sale/static/src/css/pos.css" />
<link rel="stylesheet" href="/point_of_sale/static/src/css/keyboard.css" />
<script type="text/javascript" src="/web/js/web.assets_common"></script>
<script type="text/javascript" src="/web/js/web.assets_backend"></script>
<script type="text/javascript">
$(function() {
var s = new openerp.init(%(modules)s);
%(init)s
});
</script>
</head>
<body>
<div class="openerp openerp_webclient_container">
<table class="oe_webclient">
<tr>
<td class="oe_application"/>
</tr>
</table>
</div>
<!--[if lte IE 8]>
<script src="//ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js"></script>
<script>CFInstall.check({mode: "overlay"});</script>
<![endif]-->
</body>
</html>
"""
class PosController(http.Controller):
@ -70,15 +21,19 @@ class PosController(http.Controller):
if not request.session.uid:
return login_redirect()
r = html_template % {
'modules': simplejson.dumps(module_boot(request.db)),
'init': """
var wc = new s.web.WebClient();
wc.show_application = function(){
wc.action_manager.do_action("pos.ui");
};
wc.setElement($(document.body));
wc.start();
"""
}
return r
modules = simplejson.dumps(module_boot(request.db))
init = """
var wc = new s.web.WebClient();
wc.show_application = function(){
wc.action_manager.do_action("pos.ui");
};
wc.setElement($(document.body));
wc.start();
"""
html = request.registry.get('ir.ui.view').render(request.cr, request.session.uid,'point_of_sale.index',{
'modules': modules,
'init': init,
})
return html

View File

@ -88,7 +88,13 @@ class pos_config(osv.osv):
'session_ids': fields.one2many('pos.session', 'config_id', 'Sessions'),
'group_by' : fields.boolean('Group Journal Items', help="Check this if you want to group the Journal Items by Product while closing a Session"),
'pricelist_id': fields.many2one('product.pricelist','Pricelist', required=True),
'company_id': fields.many2one('res.company', 'Company', required=True),
'company_id': fields.many2one('res.company', 'Company', required=True),
'barcode_product': fields.char('Product Barcodes', size=64, help='The pattern that identifies product barcodes'),
'barcode_cashier': fields.char('Cashier Barcodes', size=64, help='The pattern that identifies cashier login barcodes'),
'barcode_customer': fields.char('Customer Barcodes',size=64, help='The pattern that identifies customer\'s client card barcodes'),
'barcode_price': fields.char('Price Barcodes', size=64, help='The pattern that identifies a product with a barcode encoded price'),
'barcode_weight': fields.char('Weight Barcodes', size=64, help='The pattern that identifies a product with a barcode encoded weight'),
'barcode_discount': fields.char('Discount Barcodes', size=64, help='The pattern that identifies a product with a barcode encoded discount'),
}
def _check_cash_control(self, cr, uid, ids, context=None):
@ -156,6 +162,12 @@ class pos_config(osv.osv):
'iface_invoicing': True,
'stock_location_id': _get_default_location,
'company_id': _get_default_company,
'barcode_product': '*',
'barcode_cashier': '041*',
'barcode_customer':'042*',
'barcode_weight': '21xxxxxNNDDD',
'barcode_discount':'22xxxxxxxxNN',
'barcode_price': '23xxxxxNNNDD',
}
def onchange_picking_type_id(self, cr, uid, ids, picking_type_id, context=None):
@ -241,6 +253,8 @@ class pos_session(osv.osv):
'state' : fields.selection(POS_SESSION_STATE, 'Status',
required=True, readonly=True,
select=1),
'sequence_number': fields.integer('Order Sequence Number'),
'cash_control' : fields.function(_compute_cash_all,
multi='cash',
@ -303,6 +317,7 @@ class pos_session(osv.osv):
'name' : '/',
'user_id' : lambda obj, cr, uid, context: uid,
'state' : 'opening_control',
'sequence_number': 1,
}
_sql_constraints = [
@ -554,13 +569,17 @@ class pos_order(osv.osv):
for tmp_order in orders_to_save:
to_invoice = tmp_order['to_invoice']
order = tmp_order['data']
order_id = self.create(cr, uid, self._order_fields(cr, uid, order, context=context),context)
for payments in order['statement_ids']:
self.add_payment(cr, uid, order_id, self._payment_fields(cr, uid, payments[2], context=context), context=context)
session = self.pool.get('pos.session').browse(cr, uid, order['pos_session_id'], context=context)
if session.sequence_number <= order['sequence_number']:
session.write({'sequence_number': order['sequence_number'] + 1})
session.refresh()
if order['amount_return']:
session = self.pool.get('pos.session').browse(cr, uid, order['pos_session_id'], context=context)
cash_journal = session.cash_journal_id
if not cash_journal:
cash_journal_ids = filter(lambda st: st.journal_id.type=='cash', session.statement_ids)
@ -667,6 +686,7 @@ class pos_order(osv.osv):
'statement_ids': fields.one2many('account.bank.statement.line', 'pos_statement_id', 'Payments', states={'draft': [('readonly', False)]}, readonly=True),
'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)]}),
'sequence_number': fields.integer('Sequence Number', help='A session-unique sequence number for the order'),
'session_id' : fields.many2one('pos.session', 'Session',
#required=True,
@ -715,6 +735,7 @@ class pos_order(osv.osv):
'name': '/',
'date_order': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'nb_print': 0,
'sequence_number': 1,
'session_id': _default_session,
'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id,
'pricelist_id': _default_pricelist,

View File

@ -672,6 +672,14 @@
<field name="receipt_header" placeholder="A custom receipt header message"/>
<field name="receipt_footer" placeholder="A custom receipt header footage"/>
</group>
<group string="Barcode Types" col="4">
<field name="barcode_product" />
<field name="barcode_cashier" />
<field name="barcode_customer" />
<field name="barcode_weight" />
<field name="barcode_discount" />
<field name="barcode_price" />
</group>
</sheet>
</form>
@ -703,6 +711,7 @@
</field>
</record>
<!-- Sessions -->
<act_window
id="act_pos_config_sessions"

View File

@ -303,24 +303,65 @@ td {
}
.pos .order-button{
color: #273072;
height:27px;
margin:3px;
margin-right:0px;
border: 1px solid #353A7E;
background: #7f82ac;
text-shadow: none;
border-radius: 3px;
color: #f0f0f0;
display: inline-block;
box-sizing: border-box;
-moz-box-sizing: border-box;
height: 32px;
padding: 4px 8px;
margin: 3px;
margin-bottom: 0px;
margin-right: 2px;
padding-top: 0px;
background: #8b8b8b;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
vertical-align: top;
line-height: 26px;
text-align: center;
box-shadow: 0px -5px 10px -6px rgb(82,82,82) inset;
cursor: pointer;
min-width: 34px;
}
.pos .order-button.selected{
font-weight: 900;
background: #666997;
color: rgb(236, 237, 255);
background: #EEEEEE;
color: rgb(75,75,75);
height: 31px;
border-bottom: solid 1px rgb(196, 196, 196);
box-shadow: none;
}
.pos .order-button .order-sequence{
font-size: 16px;
font-weight: 800;
vertical-align: middle;
}
.pos .order-button.selected .order-sequence{
color: white;
background: black;
display: inline-block;
line-height: 24px;
min-width: 24px;
border-radius: 12px;
margin-right: 4px;
margin-left: -4px;
}
.pos .order-button.square{
width: 32px;
margin-left:4px;
width: 34px;
margin-left:1px;
background: #5c5c5c;
color: rgb(160,160,160);
}
.pos .order-button:not(.square) > .fa {
font-size: 16px;
vertical-align: middle;
margin-right: 4px;
}
.pos .order-button .order-sequence{
font-size: 16px;
font-weight: 800;
}
.pos .order-selector {
@ -420,6 +461,46 @@ td {
background: #F0EEEE;
white-space: nowrap;
}
/* ********* The control buttons ********* */
.pos .control-buttons {
padding-top: 8px;
}
.pos .control-button {
background: #e2e2e2;
border: solid 1px #BEBEBE;
display: inline-block;
line-height: 38px;
min-width: 38px;
text-align: center;
border-radius: 3px;
padding: 0px 10px;
font-size: 18px;
margin-left: 6px;
cursor: pointer;
}
.pos .control-button .fa{
margin-right: 4px;
}
.pos .control-button.highlight{
background: #6EC89B;
border: solid 1px #6EC89B;
color: white;
}
.pos .control-button:active {
background: #7F82AC;
border: solid 1px #7F82AC;
color: white;
}
.pos .control-button.disabled,
.pos .control-button.disabled:active{
background: #e2e2e2;
border: solid 1px #BEBEBE;
opacity: 0.5;
cursor: default;
color: inherit;
}
/* ********* The paypad contains the payment buttons ********* */
@ -782,6 +863,78 @@ td {
color: #5a5a5a;
}
/* a) Generic Screen Layout Constructs */
.screen .screen-content{
margin: 0px auto;
max-width: 1024px;
text-align: left;
height: 100%;
overflow: hidden;
position: relative;
}
.screen .top-content{
position: absolute;
left: 0px; top: 0px; right: 0px;
height: 64px;
border-bottom: dashed 1px rgb(215,215,215);
text-align: center;
}
.screen .top-content .button {
position: absolute;
top: 0px;
line-height: 32px;
padding: 3px 13px;
font-size: 20px;
background: rgb(230, 230, 230);
margin: 12px;
border-radius: 3px;
border: solid 1px rgb(209, 209, 209);
cursor: pointer;
}
.screen .top-content .button.highlight{
background: rgb(110,200,155);
color: white;
border: solid 1px rgb(110,200,155);
}
.screen .top-content .button.back {
left: 0px;
margin-left: 16px;
}
.screen .top-content .button.next{
right: 0px;
margin-right: 16px;
}
.screen .left-content{
position: absolute;
left:0px; top: 64px; bottom: 0px;
right:50%;
overflow-x: hidden;
overflow-y: auto;
border-right: dashed 1px rgb(215,215,215);
}
.screen .right-content{
position: absolute;
right:0px; top: 64px; bottom: 0px;
left:50%;
overflow-x: hidden;
overflow-y: auto;
}
.screen .centered-content{
position: absolute;
right:25%; top: 64px; bottom: 0px;
left:25%;
border-right: dashed 1px rgb(215,215,215);
border-left: dashed 1px rgb(215,215,215);
overflow-x: hidden;
overflow-y: auto;
}
.screen .full-content{
position: absolute;
right: 0%; top: 65px; bottom: 0px;
left: 0%;
}
/* a) Layout for the Product Screen */
.pos .screen .layout-table {
@ -995,117 +1148,145 @@ td {
/* d) The Scale screen */
.pos .scale-screen .display{
position:relative;
width: 550px;
height:190px;
margin-top: 100px;
margin-left: auto;
margin-right: auto;
}
.pos .scale-screen .product-picture {
position: relative;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
display: float;
float: right;
margin: 5px;
width: 180px;
height:180px;
line-height:180px;
cursor:pointer;
background:#fff;
border: 1px solid #fff;
border-radius: 3px;
box-shadow: 0px 2px rgb(228, 228, 228);
}
.pos .scale-screen .product-picture img{
max-width: 178px;
max-height: 178px;
vertical-align: middle;
cursor:pointer;
}
.pos .scale-screen .product-picture .product-price{
position: absolute;
top:8px;
right:8px;
width:auto;
height:auto;
line-height:1;
color:white;
background: #7f82ac;
padding: 4px 5px;
border-radius: 3px;
cursor:pointer;
}
.pos .scale-screen .product-name {
position: absolute;
left: 0px;
right: 210px;
top: 10px;
height: 50px;
font-size: 36px;
line-height: 50px;
text-align: right;
color: #8d8d8d;
overflow: hidden;
text-overflow: ellipsis;
}
.pos .scale-screen .weight{
position: absolute;
left: 0;
top: 80px;
height: 90px;
bottom: 55px;
right: 210px;
padding: 6px;
padding-right: 33px;
padding-top: 11px;
background: white;
border-radius: 3px;
box-shadow: 0px 2px rgb(228, 228, 228) inset;
}
.pos .scale-screen .weight p{
.pos .scale-screen .product-price{
font-size: 25px;
margin: 16px;
text-align: center;
display: inline-block;
text-align:right;
line-height: 90px;
font-size: 56px;
width: 100%;
height: 100%;
margin: 0;
margin-right: 18px;
font-family: "Inconsolata";
color: #6c6c6c;
width: 40%;
}
.pos .scale-screen .computed-price{
font-size: 25px;
display: inline-block;
text-align: right;
margin: 16px;
margin-top: 0px;
padding: 16px;
background: white;
width: 40%;
border-radius: 3px;
font-family: Inconsolata;
font-weight: bold;
text-shadow: 0px 2px 0px rgb(210,210,210);
box-shadow: 0px 2px 0px rgb(225,225,225) inset;
float: right;
}
.pos .scale-screen .buy-product{
text-align: center;
font-size: 32px;
background: rgb(110,200,155);
color: white;
border-radius: 3px;
padding: 16px;
margin: 16px;
cursor: pointer;
}
/* e) The Welcome Screen */
.pos .goodbye-message{
position: absolute;
left:50%;
top:40%;
width:500px;
height:400px;
margin-left: -250px;
margin-top: -200px;
padding:10px;
padding-top:20px;
text-align:center;
font-size:20px;
font-weight:bold;
background-color: #F0EEEE;
border: 1px solid #E0DDDD;
.pos .scale-screen .weight{
text-align: right;
margin: 16px;
background: white;
padding: 20px;
padding-right: 30px;
font-size: 56px;
border-radius: 3px;
box-shadow: 0px 10px 20px rgba(0,0,0,0.4);
z-index:1150;
font-family: Inconsolata;
text-shadow: 0px 2px 0px rgb(210, 210, 210);
box-shadow: 0px 2px 0px rgb(225,225,225) inset;
}
/* e) The Client List Screen */
.pos .clientlist-screen .client-list{
font-size: 16px;
width: 100%;
line-height: 40px;
}
.pos .clientlist-screen .client-list th,
.pos .clientlist-screen .client-list td {
padding: 0px 8px;
}
.pos .clientlist-screen .client-list tr{
transition: all 150ms linear;
background: rgb(230,230,230);
}
.pos .clientlist-screen .client-list thead > tr,
.pos .clientlist-screen .client-list tr:nth-child(even) {
background: rgb(247,247,247);
}
.pos .clientlist-screen .client-list tr.highlight{
transition: all 150ms linear;
background: rgb(110,200,155) !important;
color: white;
}
.pos .clientlist-screen .client-list tr.lowlight{
transition: all 150ms linear;
background: rgb(216, 238, 227);
}
.pos .clientlist-screen .client-list tr.lowlight:nth-child(even){
transition: all 150ms linear;
background: rgb(227, 246, 237);
}
.pos .clientlist-screen .client-details{
padding: 16px;
border-bottom: solid 5px rgb(110,200,155);
}
.pos .clientlist-screen .client-picture{
height: 64px;
width: 64px;
border-radius: 32px;
overflow: hidden;
text-align: center;
float: left;
margin-right: 16px;
background: white;
}
.pos .clientlist-screen .client-picture > img{
vertical-align: middle;
max-height: 64px;
}
.pos .clientlist-screen .client-name{
font-size: 32px;
line-height: 64px;
margin-bottom:16px;
}
.pos .clientlist-screen .client-details-box{
position: relative;
font-size: 16px;
}
.pos .clientlist-screen .client-details-left{
width: 50%;
float: left;
}
.pos .clientlist-screen .client-details-right{
width: 50%;
float: right;
}
.pos .clientlist-screen .client-detail{
line-height: 24px;
}
.pos .clientlist-screen .client-detail > .label{
font-weight: bold;
display: inline-block;
width: 64px;
text-align: right;
margin-right: 8px;
}
.pos .clientlist-screen .client-detail > .empty{
opacity: 0.3;
}
.pos .clientlist-screen .searchbox{
right: auto;
margin-left: -90px;
margin-top:8px;
left: 50%;
}
.pos .clientlist-screen .searchbox input{
width: 120px;
}
/* ********* The OrderWidget ********* */
.pos .order-container{
@ -1119,11 +1300,17 @@ td {
.pos .order-scroller{
width:100%;
height:100%;
overflow:hidden;
overflow: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.pos .scrollable-y{
overflow: hidden !important;
overflow-y: auto !important;
-webkit-overflow-scrolling: touch !important;
}
.pos .order{
background: white;
padding-bottom:15px;
@ -1135,6 +1322,8 @@ td {
font-size:16px;
border-radius: 3px;
border: solid 1px rgb(220,220,220);
text-align: left;
max-width: 500px;
-webkit-transform: translate3d(0,0,0);
}
@ -1172,6 +1361,25 @@ td {
color:#999;
}
.pos .order .summary .fidpoints{
position: absolute;
left: 20px;
padding: 10px;
color: #6EC89B;
background: rgba(110, 200, 155, 0.17);
border-radius: 3px;
}
.submit-kitchen-button {
float: left;
background: rgb(61, 235, 82);
color: white;
padding: 12px 20px;
margin: 0px 15px;
border-radius: 3px;
cursor: pointer;
}
/* ********* The OrderLineWidget ********* */
.pos .order .orderline{
@ -1231,6 +1439,50 @@ td {
font-style:normal;
}
/* ********* SplitBill ********* */
.splitbill-screen .order-info {
text-align: center;
margin-bottom:20px;
padding: 20px 0px;
font-size: 64px;
color: #43996E;
text-shadow: 0px 2px white, 0px 2px 2px rgba(0, 0, 0, 0.27);
border-bottom: dashed 1px rgb(215,215,215);
}
.splitbill-screen .order .orderline.selected{
background: rgb(110,200,155);
color: white;
-webkit-transition: background 250ms ease-in-out;
-moz-transition: background 250ms ease-in-out;
transition: background 250ms ease-in-out;
cursor: default;
}
.splitbill-screen .order .orderline.partially.selected{
background: rgb(136, 214, 176);
}
.splitbill-screen .order .orderline.selected .info-list {
color: white;
}
.splitbill-screen .order .orderline.selected .info-list em{
color: white;
font-size: 24px;
vertical-align: top;
}
.splitbill-screen .paymentmethods {
margin: 16px;
}
.splitbill-screen .paymentmethod {
background: rgb(221, 221, 221);
line-height: 40px;
margin-bottom: 4px;
border-radius: 3px;
font-size: 16px;
border: solid 1px rgb(202, 202, 202);
cursor: pointer;
text-align: center;
}
/* ********* The ActionBarWidget ********* */
.pos .pos-actionbar{
@ -1408,6 +1660,11 @@ td {
box-shadow: 0px 10px 20px rgba(0,0,0,0.4);
z-index:1200;
}
.pos .popup .comment{
font-weight: normal;
font-size: 18px;
margin: 0px 16px;
}
.pos .popup .footer{
position:absolute;
bottom:0;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@ -21,6 +21,11 @@ function openerp_pos_db(instance, module){
this.product_by_category_id = {};
this.product_by_reference = {};
this.partners_sorted = [];
this.partner_by_id = {};
this.partner_by_ean13 = {};
this.partner_search_string = "";
this.category_by_id = {};
this.root_category_id = 0;
this.category_products = {};
@ -196,6 +201,60 @@ function openerp_pos_db(instance, module){
}
}
},
_partner_search_string: function(partner){
var str = '' + partner.id + ':' + partner.name;
if(partner.ean13){
str += '|' + partner.ean13;
}
if(partner.address){
str += '|' + partner.address;
}
return str + '\n';
},
add_partners: function(partners){
for(var i = 0, len = partners.length; i < len; i++){
var partner = partners[i];
this.partner_by_id[partner.id] = partner;
if(partner.ean13){
this.partner_by_ean13[partner.ean13] = partner;
}
partner.address = (partner.street || '') +', '+
(partner.zip || '') +' '+
(partner.city || '') +', '+
(partner.country_id[1] || '');
this.partner_search_string += this._partner_search_string(partner);
this.partners_sorted.push(partner);
}
},
get_partner_by_id: function(id){
return this.partner_by_id[id];
},
get_partner_by_ean13: function(ean13){
return this.partner_by_ean13[ean13];
},
get_partners_sorted: function(){
return this.partners_sorted;
},
search_partner: function(query){
try {
query = query.replace(/[\[\]\(\)\+\*\?\.\-\!\&\^\$\|\~\_\{\}\:\,\\\/]/g,'.');
query = query.replace(' ','.+');
var re = RegExp("([0-9]+):.*?"+query,"gi");
}catch(e){
return [];
}
var results = [];
for(var i = 0; i < this.limit; i++){
r = re.exec(this.partner_search_string);
if(r){
var id = Number(r[1]);
results.push(this.get_partner_by_id(id));
}else{
break;
}
}
return results;
},
/* removes all the data from the database. TODO : being able to selectively remove data */
clear: function(stores){
for(var i = 0, len = arguments.length; i < len; i++){
@ -243,7 +302,13 @@ function openerp_pos_db(instance, module){
* - a name, package or ean13 containing the query (case insensitive)
*/
search_product_in_category: function(category_id, query){
var re = RegExp("([0-9]+):.*?"+query,"gi");
try {
query = query.replace(/[\[\]\(\)\+\*\?\.\-\!\&\^\$\|\~\_\{\}\:\,\\\/]/g,'.');
query = query.replace(' ','.+');
var re = RegExp("([0-9]+):.*?"+query,"gi");
}catch(e){
return [];
}
var results = [];
for(var i = 0; i < this.limit; i++){
r = re.exec(this.category_search_string[category_id]);

View File

@ -77,6 +77,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
};
// this object interfaces with the local proxy to communicate to the various hardware devices
// connected to the Point of Sale. As the communication only goes from the POS to the proxy,
// methods are used both to signal an event, and to fetch information.
@ -362,28 +363,6 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
this.notifications[name].push(callback);
},
//a product has been scanned and recognized with success
// ean is a parsed ean object
scan_item_success: function(ean){
return this.message('scan_item_success',{ean: ean});
},
// a product has been scanned but not recognized
// ean is a parsed ean object
scan_item_error_unrecognized: function(ean){
return this.message('scan_item_error_unrecognized',{ean: ean});
},
//the client is asking for help
help_needed: function(){
return this.message('help_needed');
},
//the client does not need help anymore
help_canceled: function(){
return this.message('help_canceled');
},
// returns the weight on the scale.
scale_read: function(){
var self = this;
@ -411,71 +390,6 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
this.debug_weight = 0;
},
// the pos asks the client to pay 'price' units
payment_request: function(price){
var ret = new $.Deferred();
this.paying = true;
this.custom_payment_status = this.default_payment_status;
return this.message('payment_request',{'price':price});
},
payment_status: function(){
if(this.bypass_proxy){
this.bypass_proxy = false;
return (new $.Deferred()).resolve(this.custom_payment_status);
}else{
return this.message('payment_status');
}
},
// override what the proxy says and accept the payment
debug_accept_payment: function(){
this.bypass_proxy = true;
this.custom_payment_status = {
status: 'paid',
message: 'Successfull Payment, have a nice day',
payment_method: 'AMEX',
receipt_client: '<xml>bla</xml>',
receipt_shop: '<xml>bla</xml>',
};
},
// override what the proxy says and reject the payment
debug_reject_payment: function(){
this.bypass_proxy = true;
this.custom_payment_status = {
status: 'error-rejected',
message: 'Sorry you don\'t have enough money :(',
};
},
// the client cancels his payment
payment_cancel: function(){
this.paying = false;
this.custom_payment_status = 'waiting_for_payment';
return this.message('payment_cancel');
},
// called when the client logs in or starts to scan product
transaction_start: function(){
return this.message('transaction_start');
},
// called when the clients has finished his interaction with the machine
transaction_end: function(){
return this.message('transaction_end');
},
// called when the POS turns to cashier mode
cashier_mode_activated: function(){
return this.message('cashier_mode_activated');
},
// called when the POS turns to client mode
cashier_mode_deactivated: function(){
return this.message('cashier_mode_deactivated');
},
// ask for the cashbox (the physical box where you store the cash) to be opened
open_cashbox: function(){
return this.message('open_cashbox');
@ -509,10 +423,6 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
return this.message('log',{'arguments': _.toArray(arguments)});
},
// asks the proxy to print an invoice in pdf form ( used to print invoices generated by the server )
print_pdf_invoice: function(pdfinvoice){
return this.message('print_pdf_invoice',{pdfinvoice: pdfinvoice});
},
});
// this module interfaces with the barcode reader. It assumes the barcode reader
@ -524,8 +434,8 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
'product',
'cashier',
'client',
'discount',
],
init: function(attributes){
this.pos = attributes.pos;
this.action_callback = {};
@ -535,11 +445,56 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
this.action_callback_stack = [];
this.weight_prefix_set = attributes.weight_prefix_set || {'21':''};
this.discount_prefix_set = attributes.discount_prefix_set || {'22':''};
this.price_prefix_set = attributes.price_prefix_set || {'23':''};
this.cashier_prefix_set = attributes.cashier_prefix_set || {'041':''};
this.client_prefix_set = attributes.client_prefix_set || {'042':''};
this.add_barcode_patterns(attributes.patterns || {
'product': ['xxxxxxxxxxxxx'],
'cashier': ['041xxxxxxxxxx'],
'client': ['042xxxxxxxxxx'],
'weight': ['21xxxxxNNDDDx'],
'discount': ['22xxxxxxxxNNx'],
'price': ['23xxxxxNNNDDx'],
});
},
add_barcode_patterns: function(patterns){
this.patterns = this.patterns || {};
for(type in patterns){
this.patterns[type] = this.patterns[type] || [];
var patternlist = patterns[type];
if( typeof patternlist === 'string'){
patternlist = patternlist.split(',');
}
for(var i = 0; i < patternlist.length; i++){
var pattern = patternlist[i].trim().substring(0,12);
if(!pattern.length){
continue;
}
pattern = pattern.replace(/[x\*]/gi,'x');
while(pattern.length < 12){
pattern += 'x';
}
this.patterns[type].push(pattern);
}
}
this.sorted_patterns = [];
for (var type in this.patterns){
var patterns = this.patterns[type];
for(var i = 0; i < patterns.length; i++){
var pattern = patterns[i];
var score = 0;
for(var j = 0; j < pattern.length; j++){
if(pattern[j] != 'x'){
score++;
}
}
this.sorted_patterns.push({type:type, pattern:pattern,score:score});
}
}
this.sorted_patterns.sort(function(a,b){
return b.score - a.score;
});
},
@ -625,67 +580,84 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
// most importantly :
// - code : the ean
// - type : the type of the ean:
// 'price' | 'weight' | 'unit' | 'cashier' | 'client' | 'discount' | 'error'
// 'price' | 'weight' | 'product' | 'cashier' | 'client' | 'discount' | 'error'
//
// - prefix : the prefix that has ben used to determine the type
// - id : the part of the ean that identifies something
// - value : if the id encodes a numerical value, it will be put there
// - unit : if the encoded value has a unit, it will be put there.
// not to be confused with the 'unit' type, which represent an unit of a
// unique product
// - base_code : the ean code with all the encoding parts set to zero; the one put on
// the product in the backend
parse_ean: function(ean){
var self = this;
var parse_result = {
encoding: 'ean13',
type:'unknown',
prefix:'',
type:'error',
code:ean,
base_code: ean,
id:'',
value: 0,
unit: 'none',
};
function match_prefix(prefix_set, type){
for(prefix in prefix_set){
if(ean.substring(0,prefix.length) === prefix){
parse_result.prefix = prefix;
parse_result.type = type;
return true;
}
}
return false;
if (!this.check_ean(ean)){
return parse_result;
}
if (!this.check_ean(ean)){
parse_result.type = 'error';
} else if( match_prefix(this.price_prefix_set,'price')){
parse_result.id = ean.substring(0,7);
parse_result.base_code = this.sanitize_ean(ean.substring(0,7));
parse_result.value = Number(ean.substring(7,12))/100.0;
parse_result.unit = 'euro';
} else if( match_prefix(this.weight_prefix_set,'weight')){
parse_result.id = ean.substring(0,7);
parse_result.value = Number(ean.substring(7,12))/1000.0;
parse_result.base_code = this.sanitize_ean(ean.substring(0,7));
parse_result.unit = 'Kg';
} else if( match_prefix(this.client_prefix_set,'client')){
parse_result.id = ean.substring(0,7);
parse_result.unit = 'Kg';
} else if( match_prefix(this.cashier_prefix_set,'cashier')){
parse_result.id = ean.substring(0,7);
} else if( match_prefix(this.discount_prefix_set,'discount')){
parse_result.id = ean.substring(0,7);
parse_result.base_code = this.sanitize_ean(ean.substring(0,7));
parse_result.value = Number(ean.substring(7,12))/100.0;
parse_result.unit = '%';
} else {
parse_result.type = 'unit';
parse_result.prefix = '';
parse_result.id = ean;
function is_number(char){
n = char.charCodeAt(0);
return n >= 48 && n <= 57;
}
function match_pattern(ean,pattern){
for(var i = 0; i < pattern.length; i++){
var p = pattern[i];
var e = ean[i];
if( is_number(p) && p !== e ){
return false;
}
}
return true;
}
function get_value(ean,pattern){
var value = 0;
var decimals = 0;
for(var i = 0; i < pattern.length; i++){
var p = pattern[i];
var v = parseInt(ean[i]);
if( p === 'N'){
value *= 10;
value += v;
}else if( p === 'D'){
decimals += 1;
value += v * Math.pow(10,-decimals);
}
}
return value;
}
function get_basecode(ean,pattern){
var base = '';
for(var i = 0; i < pattern.length; i++){
var p = pattern[i];
var v = ean[i];
if( p === 'x' || is_number(p)){
base += v;
}else{
base += '0';
}
}
return self.sanitize_ean(base);
}
var patterns = this.sorted_patterns;
for(var i = 0; i < patterns.length; i++){
if(match_pattern(ean,patterns[i].pattern)){
parse_result.type = patterns[i].type;
parse_result.value = get_value(ean,patterns[i].pattern);
parse_result.base_code = get_basecode(ean,patterns[i].pattern);
return parse_result;
}
}
return parse_result;
},
@ -702,21 +674,18 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
}else if(this.pos.db.get_product_by_reference(code)){
var parse_result = {
encoding: 'reference',
type: 'unit',
type: 'product',
code: code,
prefix: '',
};
}else{
var parse_result = {
encoding: 'error',
type: 'error',
code: code,
prefix: '',
};
return;
}
if(parse_result.type in {'unit':'', 'weight':'', 'price':''}){ //ean is associated to a product
if(parse_result.type in {'product':'', 'weight':'', 'price':'', 'discount':''}){ //ean is associated to a product
if(this.action_callback['product']){
this.action_callback['product'](parse_result);
}

View File

@ -24,7 +24,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
this.pos_widget = attributes.pos_widget;
this.proxy = new module.ProxyDevice(this); // used to communicate to the hardware devices via a local proxy
this.barcode_reader = new module.BarcodeReader({'pos': this, proxy:this.proxy}); // used to read barcodes
this.barcode_reader = new module.BarcodeReader({'pos': this, proxy:this.proxy, patterns: {}}); // used to read barcodes
this.proxy_queue = new module.JobQueue(); // used to prevent parallels communications to the proxy
this.db = new module.PosDB(); // a local database used to search trough products and categories & store pending orders
this.debug = jQuery.deparam(jQuery.param.querystring()).debug !== undefined; //debug mode
@ -34,6 +34,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
this.company_logo = null;
this.company_logo_base64 = '';
this.currency = null;
this.shop = null;
this.company = null;
this.user = null;
this.users = [];
@ -47,6 +48,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
this.units = [];
this.units_by_id = {};
this.pricelist = null;
this.order_sequence = 1;
window.posmodel = this;
// these dynamic attributes can be watched for change by other models or widgets
@ -118,6 +120,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
this.pos_widget.loading_message(_t('Loading')+' '+model,this._load_progress);
return new instance.web.Model(model).query(fields).filter(domain).context(ctx).all()
},
// loads all the needed data on the sever. returns a deferred indicating when all the data has loaded.
load_server_data: function(){
var self = this;
@ -148,6 +151,8 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
var units_by_id = {};
for(var i = 0, len = units.length; i < len; i++){
units_by_id[units[i].id] = units[i];
units[i].groupable = ( units[i].category_id[0] === 1 );
units[i].is_unit = ( units[i].id === 1 );
}
self.units_by_id = units_by_id;
@ -155,9 +160,10 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
}).then(function(users){
self.users = users;
return self.fetch('res.partner', ['name','ean13'], [['ean13', '!=', false]]);
return self.fetch('res.partner', ['name','street','city','country_id','phone','zip','mobile','email','ean13']);
}).then(function(partners){
self.partners = partners;
self.db.add_partners(partners);
return self.fetch('account.tax', ['name','amount', 'price_include', 'type']);
}).then(function(taxes){
@ -165,23 +171,13 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
return self.fetch(
'pos.session',
['id', 'journal_ids','name','user_id','config_id','start_at','stop_at'],
['id', 'journal_ids','name','user_id','config_id','start_at','stop_at','sequence_number'],
[['state', '=', 'opened'], ['user_id', '=', self.session.uid]]
);
}).then(function(pos_sessions){
self.pos_session = pos_sessions[0];
return self.fetch(
'pos.config',
['name','journal_ids','journal_id','pricelist_id',
'iface_self_checkout', 'iface_led', 'iface_cashdrawer',
'iface_payment_terminal', 'iface_electronic_scale', 'iface_barscan',
'iface_vkeyboard','iface_print_via_proxy','iface_scan_via_proxy',
'iface_cashdrawer','iface_invoicing','iface_big_scrollbars',
'receipt_header','receipt_footer','proxy_ip',
'state','sequence_id','session_ids'],
[['id','=', self.pos_session.config_id[0]]]
);
return self.fetch('pos.config',[],[['id','=', self.pos_session.config_id[0]]]);
}).then(function(configs){
self.config = configs[0];
self.config.use_proxy = self.config.iface_payment_terminal ||
@ -189,6 +185,18 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
self.config.iface_print_via_proxy ||
self.config.iface_scan_via_proxy ||
self.config.iface_cashdrawer;
self.barcode_reader.add_barcode_patterns({
'product': self.config.barcode_product,
'cashier': self.config.barcode_cashier,
'client': self.config.barcode_customer,
'weight': self.config.barcode_weight,
'discount': self.config.barcode_discount,
'price': self.config.barcode_price,
});
return self.fetch('stock.location',[],[['id','=', self.config.stock_location_id[0]]]);
}).then(function(shops){
self.shop = shops[0];
return self.fetch('product.pricelist',['currency_id'],[['id','=',self.config.pricelist_id[0]]]);
}).then(function(pricelists){
@ -198,12 +206,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
}).then(function(currencies){
self.currency = currencies[0];
/*
return (new instance.web.Model('decimal.precision')).call('get_precision',[['Account']]);
}).then(function(precision){
self.accounting_precision = precision;
console.log("PRECISION",precision);
*/
return self.fetch('product.packaging',['ean','product_tmpl_id']);
}).then(function(packagings){
self.db.add_packagings(packagings);
@ -292,7 +294,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
// this is called when an order is removed from the order collection. It ensures that there is always an existing
// order and a valid selected order
on_removed_order: function(removed_order,index,reason){
if(reason === 'abandon' && this.get('orders').size() > 0){
if( (reason === 'abandon' || removed_order.temporary) && this.get('orders').size() > 0){
// when we intentionally remove an unfinished order, and there is another existing one
this.set({'selectedOrder' : this.get('orders').at(index) || this.get('orders').last()});
}else{
@ -309,6 +311,10 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
this.set('selectedOrder', order);
},
get_order: function(){
return this.get('selectedOrder');
},
//removes the current order
delete_current_order: function(){
this.get('selectedOrder').destroy({'reason':'abandon'});
@ -497,6 +503,8 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
selectedOrder.addProduct(product, {price:parsed_code.value});
}else if(parsed_code.type === 'weight'){
selectedOrder.addProduct(product, {quantity:parsed_code.value, merge:false});
}else if(parsed_code.type === 'discount'){
selectedOrder.addProduct(product, {discount:parsed_code.value, merge:false});
}else{
selectedOrder.addProduct(product);
}
@ -504,6 +512,8 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
},
});
var orderline_id = 1;
// 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.
// An Order contains zero or more Orderlines.
@ -519,6 +529,21 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
this.discountStr = '0';
this.type = 'unit';
this.selected = false;
this.id = orderline_id++;
},
clone: function(){
var orderline = new module.Orderline({},{
pos: this.pos,
order: null,
product: this.product,
price: this.price,
});
orderline.quantity = this.quantity;
orderline.quantityStr = this.quantityStr;
orderline.discount = this.discount;
orderline.type = this.type;
orderline.selected = false;
return orderline;
},
// sets a discount [0,100]%
set_discount: function(discount){
@ -566,7 +591,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
},
get_quantity_str_with_unit: function(){
var unit = this.get_unit();
if(unit && unit.name !== 'Unit(s)'){
if(unit && !unit.is_unit){
return this.quantityStr + ' ' + unit.name;
}else{
return this.quantityStr;
@ -602,6 +627,8 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
can_be_merged_with: function(orderline){
if( this.get_product().id !== orderline.get_product().id){ //only orderline of the same product can be merged
return false;
}else if(!this.get_unit() || !this.get_unit().groupable){
return false;
}else if(this.get_product_type() !== orderline.get_product_type()){
return false;
}else if(this.get_discount() > 0){ // we don't merge discounted orderlines
@ -787,11 +814,24 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
this.selected_paymentline = undefined;
this.screen_data = {}; // see ScreenSelector
this.receipt_type = 'receipt'; // 'receipt' || 'invoice'
this.temporary = attributes.temporary || false;
this.sequence_number = this.pos.pos_session.sequence_number++;
return this;
},
is_empty: function(){
return (this.get('orderLines').models.length === 0);
},
generateUniqueId: function() {
return new Date().getTime();
},
addOrderline: function(line){
if(line.order){
order.removeOrderline(line);
}
line.order = this;
this.get('orderLines').add(line);
this.selectLine(this.getLastOrderline());
},
addProduct: function(product, options){
options = options || {};
var attr = JSON.parse(JSON.stringify(product));
@ -805,6 +845,9 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
if(options.price !== undefined){
line.set_unit_price(options.price);
}
if(options.discount !== undefined){
line.set_discount(options.discount);
}
var last_orderline = this.getLastOrderline();
if( last_orderline && last_orderline.can_be_merged_with(line) && options.merge !== false){
@ -818,6 +861,15 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
this.get('orderLines').remove(line);
this.selectLine(this.getLastOrderline());
},
getOrderline: function(id){
var orderlines = this.get('orderLines').models;
for(var i = 0; i < orderlines.length; i++){
if(orderlines[i].id === id){
return orderlines[i];
}
}
return null;
},
getLastOrderline: function(){
return this.get('orderLines').at(this.get('orderLines').length -1);
},
@ -950,6 +1002,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
var client = this.get('client');
var cashier = this.pos.cashier || this.pos.user;
var company = this.pos.company;
var shop = this.pos.shop;
var date = new Date();
return {
@ -994,6 +1047,9 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
phone: company.phone,
logo: this.pos.company_logo_base64,
},
shop:{
name: shop.name,
},
currency: this.pos.currency,
};
},
@ -1019,6 +1075,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
partner_id: this.get_client() ? this.get_client().id : false,
user_id: this.pos.cashier ? this.pos.cashier.id : this.pos.user.id,
uid: this.uid,
sequence_number: this.sequence_number,
};
},
getSelectedLine: function(){
@ -1102,7 +1159,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
}
},
switchSign: function() {
console.log('switchsing');
var oldBuffer;
oldBuffer = this.get('buffer');
this.set({

View File

@ -29,12 +29,11 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.popup_set = options.popup_set || {};
this.default_client_screen = options.default_client_screen;
this.default_cashier_screen = options.default_cashier_screen;
this.default_screen = options.default_screen;
this.current_popup = null;
this.current_mode = options.default_mode || 'client';
this.current_mode = options.default_mode || 'cashier';
this.current_screen = null;
@ -46,10 +45,8 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.popup_set[popup_name].hide();
}
this.selected_order = this.pos.get('selectedOrder');
this.selected_order.set_screen_data({
client_screen: this.default_client_screen,
cashier_screen: this.default_cashier_screen,
this.pos.get('selectedOrder').set_screen_data({
'screen': this.default_screen,
});
this.pos.bind('change:selectedOrder', this.load_saved_screen, this);
@ -59,12 +56,12 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.screen_set[screen_name] = screen;
return this;
},
show_popup: function(name){
show_popup: function(name,options){
if(this.current_popup){
this.close_popup();
}
this.current_popup = this.popup_set[name];
this.current_popup.show();
this.current_popup.show(options);
},
close_popup: function(){
if(this.current_popup){
@ -75,15 +72,10 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
},
load_saved_screen: function(){
this.close_popup();
var selectedOrder = this.pos.get('selectedOrder');
// this.set_current_screen(selectedOrder.get_screen_data('screen') || this.default_screen,null,'refresh');
this.set_current_screen(this.default_screen,null,'refresh');
if(this.current_mode === 'client'){
this.set_current_screen(selectedOrder.get_screen_data('client_screen') || this.default_client_screen,null,'refresh');
}else if(this.current_mode === 'cashier'){
this.set_current_screen(selectedOrder.get_screen_data('cashier_screen') || this.default_cashier_screen,null,'refresh');
}
this.selected_order = selectedOrder;
},
set_user_mode: function(user_mode){
if(user_mode !== this.current_mode){
@ -102,20 +94,21 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
}
this.close_popup();
var selectedOrder = this.pos.get('selectedOrder');
if(this.current_mode === 'client'){
selectedOrder.set_screen_data('client_screen',screen_name);
if(params){
selectedOrder.set_screen_data('client_screen_params',params);
}
}else{
selectedOrder.set_screen_data('cashier_screen',screen_name);
if(params){
selectedOrder.set_screen_data('cashier_screen_params',params);
}
var order = this.pos.get('selectedOrder');
var old_screen_name = order.get_screen_data('screen');
order.set_screen_data('screen',screen_name);
if(params){
order.set_screen_data('params',params);
}
if(screen && (refresh || screen !== this.current_screen)){
if( screen_name !== old_screen_name ){
order.set_screen_data('previous-screen',old_screen_name);
}
if ( refresh || screen !== this.current_screen){
if(this.current_screen){
this.current_screen.close();
this.current_screen.hide();
@ -124,21 +117,21 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.current_screen.show();
}
},
get_current_screen_param: function(param){
var selected_order = this.pos.get('selectedOrder');
if(this.current_mode === 'client'){
var params = selected_order.get_screen_data('client_screen_params');
}else{
var params = selected_order.get_screen_data('cashier_screen_params');
}
if(params){
return params[param];
}else{
return undefined;
get_current_screen: function(){
return this.pos.get('selectedOrder').get_screen_data('screen') || this.default_screen;
},
back: function(){
var previous = this.pos.get('selectedOrder').get_screen_data('previous-screen');
if(previous){
this.set_current_screen(previous);
}
},
get_current_screen_param: function(param){
var params = this.pos.get('selectedOrder').get_screen_data('params');
return params ? params[param] : undefined;
},
set_default_screen: function(){
this.set_current_screen(this.current_mode === 'client' ? this.default_client_screen : this.default_cashier_screen);
this.set_current_screen(this.default_screen);
},
});
@ -157,25 +150,19 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
},
barcode_product_screen: 'products', //if defined, this screen will be loaded when a product is scanned
barcode_product_error_popup: 'error-product', //if defined, this popup will be loaded when there's an error in the popup
hotkeys_handlers: {},
// 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.
// it will add the product to the order and go to barcode_product_screen.
barcode_product_action: function(code){
var self = this;
if(self.pos.scan_product(code)){
self.pos.proxy.scan_item_success(code);
if(self.barcode_product_screen){
self.pos_widget.screen_selector.set_current_screen(self.barcode_product_screen);
}
}else{
self.pos.proxy.scan_item_error_unrecognized(code);
if(self.barcode_product_error_popup && self.pos_widget.screen_selector.get_user_mode() !== 'cashier'){
self.pos_widget.screen_selector.show_popup(self.barcode_product_error_popup);
}
self.pos_widget.screen_selector.show_popup('error-barcode',code.code);
}
},
@ -189,12 +176,10 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
if(users[i].ean13 === code.code){
this.pos.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;
}
}
this.pos.proxy.scan_item_error_unrecognized(code);
this.pos_widget.screen_selector.show_popup('error-barcode',code.code);
return false;
},
@ -203,30 +188,28 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
// - 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(code){
var partners = this.pos.partners;
for(var i = 0, len = partners.length; i < len; i++){
if(partners[i].ean13 === code.code){
this.pos.get('selectedOrder').set_client(partners[i]);
this.pos_widget.username.refresh();
this.pos.proxy.scan_item_success(code);
return true;
}
var partner = this.pos.db.get_partner_by_ean13(code.code);
if(partner){
this.pos.get('selectedOrder').set_client(partner);
this.pos_widget.username.refresh();
return true;
}
this.pos.proxy.scan_item_error_unrecognized(code);
this.pos_widget.screen_selector.show_popup('error-barcode',code.code);
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(code){
this.pos.proxy.scan_item_success(code);
var last_orderline = this.pos.get('selectedOrder').getLastOrderline();
if(last_orderline){
last_orderline.set_discount(code.value)
}
},
// What happens when an invalid barcode is scanned : shows an error popup.
barcode_error_action: function(code){
this.pos_widget.screen_selector.show_popup('error-barcode',code.code);
},
// shows an action bar on the screen. The actionbar is automatically shown when you add a button
// with add_action_button()
show_action_bar: function(){
@ -264,33 +247,11 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.hide_action_bar();
}
// we add the help button by default. we do this because the buttons are cleared on each refresh so that
// the button stay local to each screen
this.pos_widget.left_action_bar.add_new_button({
label: _t('Help'),
icon: '/point_of_sale/static/src/img/icons/png48/help.png',
click: function(){ self.help_button_action(); },
});
var self = this;
this.cashier_mode = this.pos_widget.screen_selector.get_user_mode() === 'cashier';
this.pos_widget.set_numpad_visible(this.show_numpad && this.cashier_mode);
this.pos_widget.set_numpad_visible(this.show_numpad);
this.pos_widget.set_leftpane_visible(this.show_leftpane);
this.pos_widget.set_left_action_bar_visible(this.show_leftpane && !this.cashier_mode);
this.pos_widget.set_cashier_controls_visible(this.cashier_mode);
if(this.cashier_mode && this.pos.config.iface_self_checkout){
this.pos_widget.client_button.show();
}else{
this.pos_widget.client_button.hide();
}
if(this.cashier_mode){
this.pos_widget.close_button.show();
}else{
this.pos_widget.close_button.hide();
}
this.pos_widget.username.set_user_mode(this.pos_widget.screen_selector.get_user_mode());
this.pos.barcode_reader.set_action_callback({
@ -298,6 +259,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
'product': self.barcode_product_action ? function(code){ self.barcode_product_action(code); } : undefined ,
'client' : self.barcode_client_action ? function(code){ self.barcode_client_action(code); } : undefined ,
'discount': self.barcode_discount_action ? function(code){ self.barcode_discount_action(code); } : undefined,
'error' : self.barcode_error_action ? function(code){ self.barcode_error_action(code); } : undefined,
});
},
@ -308,7 +270,6 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.pos.barcode_reader.reset_action_callbacks();
}
this.pos_widget.action_bar.destroy_buttons();
this.pos_widget.left_action_bar.destroy_buttons();
},
// this methods hides the screen. It's not a good place to put your cleanup stuff as it is called on the
@ -352,21 +313,6 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
},
});
module.HelpPopupWidget = module.PopUpWidget.extend({
template:'HelpPopupWidget',
show: function(){
this._super();
this.pos.proxy.help_needed();
var self = this;
this.$el.find('.button').off('click').click(function(){
self.pos_widget.screen_selector.close_popup();
});
},
close:function(){
this.pos.proxy.help_canceled();
},
});
module.ChooseReceiptPopupWidget = module.PopUpWidget.extend({
template:'ChooseReceiptPopupWidget',
@ -398,42 +344,64 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
module.ErrorPopupWidget = module.PopUpWidget.extend({
template:'ErrorPopupWidget',
show: function(){
show: function(text){
var self = this;
this._super();
this.pos.proxy.help_needed();
this.pos.proxy.scan_item_error_unrecognized();
if( text && (text.message || text.comment) ){
this.$('.message').text(text.message);
this.$('.comment').text(text.comment);
}
this.pos.barcode_reader.save_callbacks();
this.pos.barcode_reader.reset_action_callbacks();
this.pos.barcode_reader.set_action_callback({
'cashier': function(code){
clearInterval(this.intervalID);
self.pos.proxy.cashier_mode_activated();
self.pos_widget.screen_selector.set_user_mode('cashier');
},
});
this.$('.footer .button').off('click').click(function(){
self.pos_widget.screen_selector.close_popup();
});
},
close:function(){
this._super();
this.pos.proxy.help_canceled();
this.pos.barcode_reader.restore_callbacks();
},
});
module.ProductErrorPopupWidget = module.ErrorPopupWidget.extend({
template:'ProductErrorPopupWidget',
});
module.ErrorSessionPopupWidget = module.ErrorPopupWidget.extend({
template:'ErrorSessionPopupWidget',
});
module.ErrorNegativePricePopupWidget = module.ErrorPopupWidget.extend({
template:'ErrorNegativePricePopupWidget',
module.ErrorBarcodePopupWidget = module.ErrorPopupWidget.extend({
template:'ErrorBarcodePopupWidget',
show: function(barcode){
this._super();
this.$('.barcode').text(barcode);
},
});
module.ConfirmPopupWidget = module.PopUpWidget.extend({
template: 'ConfirmPopupWidget',
show: function(options){
var self = this;
this._super();
this.message = options.message || '';
this.comment = options.comment || '';
this.renderElement();
this.$('.button.cancel').click(function(){
self.pos_widget.screen_selector.close_popup();
if( options.cancel ){
options.cancel.call(self);
}
});
this.$('.button.confirm').click(function(){
self.pos_widget.screen_selector.close_popup();
if( options.confirm ){
options.confirm.call(self);
}
});
},
});
module.ErrorNoClientPopupWidget = module.ErrorPopupWidget.extend({
@ -443,47 +411,6 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
module.ErrorInvoiceTransferPopupWidget = module.ErrorPopupWidget.extend({
template: 'ErrorInvoiceTransferPopupWidget',
});
module.ScaleInviteScreenWidget = module.ScreenWidget.extend({
template:'ScaleInviteScreenWidget',
next_screen:'scale',
previous_screen:'products',
show: function(){
this._super();
var self = this;
var queue = this.pos.proxy_queue;
queue.schedule(function(){
return self.pos.proxy.weighting_start();
},{ important: true });
queue.schedule(function(){
return self.pos.proxy.weighting_read_kg().then(function(weight){
if(weight > 0.001){
self.pos_widget.screen_selector.set_current_screen(self.next_screen);
}
});
},{duration: 100, repeat: true});
this.add_action_button({
label: _t('Back'),
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
click: function(){
self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
}
});
},
close: function(){
this._super();
var self = this;
this.pos.proxy_queue.clear();
this.pos.proxy_queue.schedule(function(){
return self.pos.proxy.weighting_end();
},{ important: true });
},
});
module.ScaleScreenWidget = module.ScreenWidget.extend({
template:'ScaleScreenWidget',
@ -491,6 +418,8 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
next_screen: 'products',
previous_screen: 'products',
show_leftpane: false,
show: function(){
this._super();
var self = this;
@ -510,23 +439,15 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
$('body').on('keyup',this.hotkey_handler);
this.add_action_button({
label: _t('Back'),
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
click: function(){
self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
}
});
this.$('.back').click(function(){
self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
});
this.$('.next,.buy-product').click(function(){
self.order_product();
self.pos_widget.screen_selector.set_current_screen(self.next_screen);
});
this.validate_button = this.add_action_button({
label: _t('Validate'),
icon: '/point_of_sale/static/src/img/icons/png48/validate.png',
click: function(){
self.order_product();
self.pos_widget.screen_selector.set_current_screen(self.next_screen);
},
});
queue.schedule(function(){
return self.pos.proxy.scale_read().then(function(weight){
self.set_weight(weight.weight);
@ -534,14 +455,6 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
},{duration:50, repeat: true});
},
renderElement: function(){
var self = this;
this._super();
this.$('.product-picture').click(function(){
self.order_product();
self.pos_widget.screen_selector.set_current_screen(self.next_screen);
});
},
get_product: function(){
var ss = this.pos_widget.screen_selector;
if(ss){
@ -563,7 +476,8 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
},
set_weight: function(weight){
this.weight = weight;
this.$('.js-weight').text(this.get_product_weight_string());
this.$('.weight').text(this.get_product_weight_string());
this.$('.computed-price').text(this.get_computed_price_string());
},
get_product_weight_string: function(){
var product = this.get_product();
@ -581,13 +495,8 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
weightstr += ' Kg';
return weightstr;
},
get_product_image_url: function(){
var product = this.get_product();
if(product){
return window.location.origin + '/web/binary/image?model=product.product&field=image_medium&id='+product.id;
}else{
return "";
}
get_computed_price_string: function(){
return this.format_currency(this.get_product_price() * this.weight);
},
close: function(){
var self = this;
@ -598,166 +507,9 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
},
});
module.ClientPaymentScreenWidget = module.ScreenWidget.extend({
template:'ClientPaymentScreenWidget',
next_screen: 'welcome',
previous_screen: 'products',
show: function(){
this._super();
var self = this;
this.queue = new module.JobQueue();
this.canceled = false;
this.paid = false;
// initiates the connection to the payment terminal and starts the update requests
this.start = function(){
var def = new $.Deferred();
self.pos.proxy.payment_request(self.pos.get('selectedOrder').getDueLeft())
.done(function(ack){
if(ack === 'ok'){
self.queue.schedule(self.update);
}else if(ack.indexOf('error') === 0){
console.error('cannot make payment. TODO');
}else{
console.error('unknown payment request return value:',ack);
}
def.resolve();
});
return def;
};
// gets updated status from the payment terminal and performs the appropriate consequences
this.update = function(){
var def = new $.Deferred();
if(self.canceled){
return def.resolve();
}
self.pos.proxy.payment_status()
.done(function(status){
if(status.status === 'paid'){
var currentOrder = self.pos.get('selectedOrder');
//we get the first cashregister marked as self-checkout
var selfCheckoutRegisters = [];
for(var i = 0; i < self.pos.cashregisters.length; i++){
var cashregister = self.pos.cashregisters[i];
if(cashregister.self_checkout_payment_method){
selfCheckoutRegisters.push(cashregister);
}
}
var cashregister = selfCheckoutRegisters[0] || self.pos.cashregisters[0];
currentOrder.addPaymentline(cashregister);
self.pos.push_order(currentOrder)
currentOrder.destroy();
self.pos.proxy.transaction_end();
self.pos_widget.screen_selector.set_current_screen(self.next_screen);
self.paid = true;
}else if(status.status.indexOf('error') === 0){
console.error('error in payment request. TODO');
}else if(status.status === 'waiting'){
self.queue.schedule(self.update,200);
}else{
console.error('unknown status value:',status.status);
}
def.resolve();
});
return def;
}
// cancels a payment.
this.cancel = function(){
if(!self.paid && !self.canceled){
self.canceled = true;
self.pos.proxy.payment_cancel();
self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
self.queue.clear();
}
return (new $.Deferred()).resolve();
}
if(this.pos.get('selectedOrder').getDueLeft() <= 0){
this.pos_widget.screen_selector.show_popup('error-negative-price');
}else{
this.queue.schedule(this.start);
}
this.add_action_button({
label: _t('Back'),
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
click: function(){
self.queue.schedule(self.cancel);
self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
}
});
},
close: function(){
if(this.queue){
this.queue.schedule(this.cancel);
}
//TODO CANCEL
this._super();
},
});
module.WelcomeScreenWidget = module.ScreenWidget.extend({
template:'WelcomeScreenWidget',
next_screen: 'products',
show_numpad: false,
show_leftpane: false,
start: function(){
this._super();
$('.goodbye-message').click(function(){
$(this).addClass('oe_hidden');
});
},
barcode_product_action: function(code){
this.pos.proxy.transaction_start();
this._super(code);
},
barcode_client_action: function(code){
this.pos.proxy.transaction_start();
this._super(code);
$('.goodbye-message').addClass('oe_hidden');
this.pos_widget.screen_selector.show_popup('choose-receipt');
},
show: function(){
this._super();
var self = this;
this.add_action_button({
label: _t('Help'),
icon: '/point_of_sale/static/src/img/icons/png48/help.png',
click: function(){
$('.goodbye-message').css({opacity:1}).addClass('oe_hidden');
self.help_button_action();
},
});
$('.goodbye-message').css({opacity:1}).removeClass('oe_hidden');
setTimeout(function(){
$('.goodbye-message').animate({opacity:0},500,'swing',function(){$('.goodbye-message').addClass('oe_hidden');});
},5000);
},
});
module.ProductScreenWidget = module.ScreenWidget.extend({
template:'ProductScreenWidget',
scale_screen: 'scale',
client_scale_screen : 'scale_invite',
client_next_screen: 'client_payment',
show_numpad: true,
show_leftpane: true,
@ -767,19 +519,19 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.product_list_widget = new module.ProductListWidget(this,{
click_product_action: function(product){
if(product.to_weight && self.pos.config.iface_electronic_scale){
self.pos_widget.screen_selector.set_current_screen( self.cashier_mode ? self.scale_screen : self.client_scale_screen, {product: product});
self.pos_widget.screen_selector.set_current_screen('scale',{product: product});
}else{
self.pos.get('selectedOrder').addProduct(product);
}
},
product_list: this.pos.db.get_product_by_category(0)
});
this.product_list_widget.replace($('.placeholder-ProductListWidget'));
this.product_list_widget.replace(this.$('.placeholder-ProductListWidget'));
this.product_categories_widget = new module.ProductCategoriesWidget(this,{
product_list_widget: this.product_list_widget,
});
this.product_categories_widget.replace($('.placeholder-ProductCategoriesWidget'));
this.product_categories_widget.replace(this.$('.placeholder-ProductCategoriesWidget'));
},
show: function(){
@ -789,16 +541,6 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.product_categories_widget.reset_category();
this.pos_widget.order_widget.set_editable(true);
if(this.pos_widget.screen_selector.current_mode === 'client'){
this.add_action_button({
label: _t('Pay'),
icon: '/point_of_sale/static/src/img/icons/png48/go-next.png',
click: function(){
self.pos_widget.screen_selector.set_current_screen(self.client_next_screen);
}
});
}
},
close: function(){
@ -812,6 +554,180 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
},
});
module.ClientListScreenWidget = module.ScreenWidget.extend({
template: 'ClientListScreenWidget',
show_leftpane: false,
auto_back: true,
show: function(){
var self = this;
this._super();
this.renderElement();
this.details_visible = false;
this.old_client = this.pos.get('selectedOrder').get('client');
this.new_client = this.old_client;
this.$('.back').click(function(){
self.pos_widget.screen_selector.back();
});
this.$('.next').click(function(){
self.save_changes();
self.pos_widget.screen_selector.back();
});
var partners = this.pos.db.get_partners_sorted();
this.render_list(partners);
if( this.old_client ){
this.display_client_details('show',this.old_client,0);
}
this.$('.client-list-contents').delegate('.client-line','click',function(event){
console.log('uh');
self.line_select(event,$(this),parseInt($(this).data('id')));
});
var search_timeout = null;
this.$('.searchbox input').on('keyup',function(event){
clearTimeout(search_timeout);
var query = this.value;
search_timeout = setTimeout(function(){
self.perform_search(query,event.which === 13);
},70);
});
this.$('.searchbox .search-clear').click(function(){
self.clear_search();
});
},
perform_search: function(query, associate_result){
if(query){
var customers = this.pos.db.search_partner(query);
this.display_client_details('hide');
if ( associate_result && customers.length === 1){
this.new_client = customers[0];
this.save_changes();
this.pos_widget.screen_selector.back();
}
this.render_list(customers);
}else{
var customers = this.pos.db.get_partners_sorted();
this.render_list(customers);
}
},
clear_search: function(){
var customers = this.pos.db.get_partners_sorted();
this.render_list(customers);
this.$('.searchbox input')[0].value = '';
this.$('.searchbox input').focus();
},
render_list: function(partners){
var contents = this.$('.client-list-contents');
contents.empty();
for(var i = 0, len = partners.length; i < len; i++){
var clientline = $(QWeb.render('ClientLine',{partner:partners[i]}));
if( partners[i] === this.new_client ){
clientline.addClass('highlight');
}
contents.append(clientline);
}
},
save_changes: function(){
if( this.has_client_changed() ){
this.pos.get('selectedOrder').set_client(this.new_client);
}
},
has_client_changed: function(){
if( this.old_client && this.new_client ){
return this.old_client.id !== this.new_client.id;
}else{
return !!this.old_client !== !!this.new_client;
}
},
toggle_save_button: function(){
var $button = this.$('.button.next');
if( this.new_client ){
if( !this.old_client){
$button.text(_t('Set Customer'));
}else{
$button.text(_t('Change Customer'));
}
}else{
$button.text(_t('Deselect Customer'));
}
$button.toggleClass('oe_hidden',!this.has_client_changed());
},
line_select: function(event,$line,id){
var partner = this.pos.db.get_partner_by_id(id);
this.$('.client-list .lowlight').removeClass('lowlight');
if ( $line.hasClass('highlight') ){
$line.removeClass('highlight');
$line.addClass('lowlight');
this.display_client_details('hide',partner);
this.new_client = null;
this.toggle_save_button();
}else{
this.$('.client-list .highlight').removeClass('highlight');
$line.addClass('highlight');
var y = event.pageY - $line.parent().offset().top
this.display_client_details('show',partner,y);
this.new_client = partner;
this.toggle_save_button();
}
},
partner_icon_url: function(id){
return '/web/binary/image?model=res.partner&id='+id+'&field=image_small';
},
display_client_details: function(visibility,partner,clickpos){
if(visibility === 'show'){
var contents = this.$('.client-details-contents');
var parent = this.$('.client-list').parent();
var old_scroll = parent.scrollTop();
var old_height = contents.height();
contents.empty();
contents.append($(QWeb.render('ClientDetails',{widget:this,partner:partner})));
var new_height = contents.height();
if(!this.details_visible){
if(clickpos < old_scroll + new_height + 20 ){
parent.scrollTop( clickpos - 20 );
}else{
parent.scrollTop(parent.scrollTop() + new_height);
}
}else{
parent.scrollTop(parent.scrollTop() - old_height + new_height);
}
this.details_visible = true;
}else if(visibility === 'hide'){
var contents = this.$('.client-details-contents');
var parent = this.$('.client-list').parent();
var scroll = parent.scrollTop();
var height = contents.height();
contents.empty();
if( height > scroll ){
contents.css({height:height+'px'});
contents.animate({height:0},400,function(){
contents.css({height:''});
});
//parent.scrollTop(0);
}else{
parent.scrollTop( parent.scrollTop() - height);
}
this.details_visible = false;
}
},
close: function(){
this._super();
},
});
module.ReceiptScreenWidget = module.ScreenWidget.extend({
template: 'ReceiptScreenWidget',
@ -879,6 +795,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
}
});
module.PaymentScreenWidget = module.ScreenWidget.extend({
template: 'PaymentScreenWidget',
back_screen: 'products',
@ -913,7 +830,6 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
if(node){
node.line.set_amount(this.value);
}
};
this.line_click_handler = function(event){
@ -943,8 +859,6 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.focus_selected_line();
document.body.addEventListener('keyup', this.hotkey_handler);
this.add_action_button({
label: _t('Back'),
@ -1182,7 +1096,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
if(this.pos.config.iface_print_via_proxy){
var receipt = currentOrder.export_for_printing();
this.pos.proxy.print_receipt(QWeb.render('XmlReceipt',{
receipt: receipt
receipt: receipt, widget: self,
}));
this.pos.get('selectedOrder').destroy(); //finish order and go back to scan screen
}else{

View File

@ -130,7 +130,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this._super(parent,options);
this.editable = false;
this.pos.bind('change:selectedOrder', this.change_selected_order, this);
this.bind_orderline_events();
this.line_click_handler = function(event){
if(!self.editable){
return;
@ -138,6 +137,10 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
self.pos.get('selectedOrder').selectLine(this.orderline);
self.pos_widget.numpad.state.reset();
};
this.client_change_handler = function(event){
self.update_summary();
}
this.bind_order_events();
},
enable_numpad: function(){
this.disable_numpad(); //ensure we don't register the callbacks twice
@ -177,11 +180,16 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
}
},
change_selected_order: function() {
this.bind_orderline_events();
this.bind_order_events();
this.renderElement();
},
bind_orderline_events: function() {
var lines = this.pos.get('selectedOrder').get('orderLines');
bind_order_events: function() {
var order = this.pos.get('selectedOrder');
order.unbind('change:client', this.client_change_handler);
order.bind('change:client', this.client_change_handler);
var lines = order.get('orderLines');
lines.unbind();
lines.bind('add', function(){
this.numpad_state.reset();
@ -233,10 +241,12 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
var orderlines = order.get('orderLines').models;
var el_str = openerp.qweb.render('OrderWidget',{widget:this, order:order, orderlines:orderlines});
var el_node = document.createElement('div');
el_node.innerHTML = _.str.trim(el_str);
el_node = el_node.childNodes[0];
var list_container = el_node.querySelector('.orderlines');
for(var i = 0, len = orderlines.length; i < len; i++){
var orderline = this.render_orderline(orderlines[i]);
@ -260,6 +270,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this.el.querySelector('.summary .total > .value').textContent = this.format_currency(total);
this.el.querySelector('.summary .total .subentry .value').textContent = this.format_currency(taxes);
},
});
@ -275,12 +286,22 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this.pos.bind('change:selectedOrder', this.renderElement,this );
},
renderElement:function(){
this.selected = ( this.pos.get('selectedOrder') === this.order )
this._super();
var self = this;
this.$el.click(function(){
self.selectOrder();
if( self.pos.get('selectedOrder') === self.order ){
var ss = self.pos.pos_widget.screen_selector;
if(ss.get_current_screen() === 'clientlist'){
ss.back();
}else{
ss.set_current_screen('clientlist');
}
}else{
self.selectOrder();
}
});
if( this.order === this.pos.get('selectedOrder') ){
if( this.selected){
this.$el.addClass('selected');
}
},
@ -687,14 +708,9 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
unknown_product: '9900000000004',
},
events:[
'scan_item_success',
'scan_item_error_unrecognized',
'payment_request',
'open_cashbox',
'print_receipt',
'print_pdf_invoice',
'scale_read',
'payment_status',
],
minimized: false,
init: function(parent,options){
@ -763,12 +779,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
}
self.minimized = !self.minimized;
});
this.$('.button.accept_payment').click(function(){
self.pos.proxy.debug_accept_payment();
});
this.$('.button.reject_payment').click(function(){
self.pos.proxy.debug_reject_payment();
});
this.$('.button.set_weight').click(function(){
var kg = Number(self.$('input.weight').val());
if(!isNaN(kg)){
@ -782,15 +792,15 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this.$('.button.custom_ean').click(function(){
var ean = self.pos.barcode_reader.sanitize_ean(self.$('input.ean').val() || '0');
self.$('input.ean').val(ean);
self.pos.barcode_reader.scan('ean13',ean);
self.pos.barcode_reader.scan(ean);
});
this.$('.button.reference').click(function(){
self.pos.barcode_reader.scan('reference',self.$('input.ean').val());
self.pos.barcode_reader.scan(self.$('input.ean').val());
});
_.each(this.eans, function(ean, name){
self.$('.button.'+name).click(function(){
self.$('input.ean').val(ean);
self.pos.barcode_reader.scan('ean13',ean);
self.pos.barcode_reader.scan(ean);
});
});
_.each(this.events, function(name){
@ -799,18 +809,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
self.$('.event.'+name).animate({'background-color':'#1E1E1E'},2000);
});
});
self.pos.proxy.add_notification('help_needed',function(){
self.$('.status.help_needed').addClass('on');
});
self.pos.proxy.add_notification('help_canceled',function(){
self.$('.status.help_needed').removeClass('on');
});
self.pos.proxy.add_notification('transaction_start',function(){
self.$('.status.transaction').addClass('on');
});
self.pos.proxy.add_notification('transaction_end',function(){
self.$('.status.transaction').removeClass('on');
});
},
});
@ -919,7 +917,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this.pos_widget = this; //So that pos_widget's childs have pos_widget set automatically
this.numpad_visible = true;
this.left_action_bar_visible = true;
this.leftpane_visible = true;
this.leftpane_width = '440px';
this.cashier_controls_visible = true;
@ -963,7 +960,17 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
});
self.$('.deleteorder-button').click(function(){
self.pos.delete_current_order();
if( !self.pos.get('selectedOrder').is_empty() ){
self.screen_selector.show_popup('confirm',{
message: _t('Destroy Current Order ?'),
comment: _t('You will lose any data associated with the current order'),
confirm: function(){
self.pos.delete_current_order();
},
});
}else{
self.pos.delete_current_order();
}
});
//when a new order is created, add an order button widget
@ -1048,28 +1055,20 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this.payment_screen = new module.PaymentScreenWidget(this, {});
this.payment_screen.appendTo(this.$('.screens'));
this.welcome_screen = new module.WelcomeScreenWidget(this,{});
this.welcome_screen.appendTo(this.$('.screens'));
this.client_payment_screen = new module.ClientPaymentScreenWidget(this, {});
this.client_payment_screen.appendTo(this.$('.screens'));
this.scale_invite_screen = new module.ScaleInviteScreenWidget(this, {});
this.scale_invite_screen.appendTo(this.$('.screens'));
this.clientlist_screen = new module.ClientListScreenWidget(this, {});
this.clientlist_screen.appendTo(this.$('.screens'));
this.scale_screen = new module.ScaleScreenWidget(this,{});
this.scale_screen.appendTo(this.$('.screens'));
// -------- Popups ---------
this.help_popup = new module.HelpPopupWidget(this, {});
this.help_popup.appendTo(this.$el);
// -------- Popups ---------
this.error_popup = new module.ErrorPopupWidget(this, {});
this.error_popup.appendTo(this.$el);
this.error_product_popup = new module.ProductErrorPopupWidget(this, {});
this.error_product_popup.appendTo(this.$el);
this.error_barcode_popup = new module.ErrorBarcodePopupWidget(this, {});
this.error_barcode_popup.appendTo(this.$el);
this.error_session_popup = new module.ErrorSessionPopupWidget(this, {});
this.error_session_popup.appendTo(this.$el);
@ -1077,15 +1076,15 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this.choose_receipt_popup = new module.ChooseReceiptPopupWidget(this, {});
this.choose_receipt_popup.appendTo(this.$el);
this.error_negative_price_popup = new module.ErrorNegativePricePopupWidget(this, {});
this.error_negative_price_popup.appendTo(this.$el);
this.error_no_client_popup = new module.ErrorNoClientPopupWidget(this, {});
this.error_no_client_popup.appendTo(this.$el);
this.error_invoice_transfer_popup = new module.ErrorInvoiceTransferPopupWidget(this, {});
this.error_invoice_transfer_popup.appendTo(this.$el);
this.confirm_popup = new module.ConfirmPopupWidget(this,{});
this.confirm_popup.appendTo(this.$el);
// -------- Misc ---------
this.close_button = new module.HeaderButtonWidget(this,{
@ -1108,9 +1107,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this.action_bar = new module.ActionBarWidget(this);
this.action_bar.replace(this.$(".placeholder-RightActionBar"));
this.left_action_bar = new module.ActionBarWidget(this);
this.left_action_bar.replace(this.$('.placeholder-LeftActionBar'));
this.paypad = new module.PaypadWidget(this, {});
this.paypad.replace(this.$('.placeholder-PaypadWidget'));
@ -1125,13 +1121,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
});
this.onscreen_keyboard.replace(this.$('.placeholder-OnscreenKeyboardWidget'));
this.client_button = new module.HeaderButtonWidget(this,{
label: _t('Self-Checkout'),
action: function(){ self.screen_selector.set_user_mode('client'); },
});
this.client_button.appendTo(this.$('.pos-rightheader'));
// -------- Screen Selector ---------
this.screen_selector = new module.ScreenSelector({
@ -1139,25 +1128,21 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
screen_set:{
'products': this.product_screen,
'payment' : this.payment_screen,
'client_payment' : this.client_payment_screen,
'scale_invite' : this.scale_invite_screen,
'scale': this.scale_screen,
'receipt' : this.receipt_screen,
'welcome' : this.welcome_screen,
'clientlist': this.clientlist_screen,
},
popup_set:{
'help': this.help_popup,
'error': this.error_popup,
'error-product': this.error_product_popup,
'error-barcode': this.error_barcode_popup,
'error-session': this.error_session_popup,
'error-negative-price': this.error_negative_price_popup,
'choose-receipt': this.choose_receipt_popup,
'error-no-client': this.error_no_client_popup,
'error-invoice-transfer': this.error_invoice_transfer_popup,
'confirm': this.confirm_popup,
},
default_client_screen: 'welcome',
default_cashier_screen: 'products',
default_mode: this.pos.config.iface_self_checkout ? 'client' : 'cashier',
default_screen: 'products',
default_mode: 'cashier',
});
if(this.pos.debug){
@ -1178,7 +1163,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
if(visible !== this.numpad_visible){
this.numpad_visible = visible;
if(visible){
this.set_left_action_bar_visible(false);
this.numpad.show();
this.paypad.show();
}else{
@ -1187,39 +1171,16 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
}
}
},
set_left_action_bar_visible: function(visible){
if(visible !== this.left_action_bar_visible){
this.left_action_bar_visible = visible;
if(visible){
this.set_numpad_visible(false);
this.left_action_bar.show();
}else{
this.left_action_bar.hide();
}
}
},
//shows or hide the leftpane (contains the list of orderlines, the numpad, the paypad, etc.)
set_leftpane_visible: function(visible){
if(visible !== this.leftpane_visible){
this.leftpane_visible = visible;
if(visible){
this.$('.pos-leftpane').removeClass('oe_hidden').animate({'width':this.leftpane_width},500,'swing');
this.$('.pos-rightpane').animate({'left':this.leftpane_width},500,'swing');
this.$('.pos-leftpane').removeClass('oe_hidden');
this.$('.rightpane').css({'left':this.leftpane_width});
}else{
var leftpane = this.$('.pos-leftpane');
leftpane.animate({'width':'0px'},500,'swing', function(){ leftpane.addClass('oe_hidden'); });
this.$('.pos-rightpane').animate({'left':'0px'},500,'swing');
}
}
},
//shows or hide the controls in the PosWidget that are specific to the cashier ( Orders, close button, etc. )
set_cashier_controls_visible: function(visible){
if(visible !== this.cashier_controls_visible){
this.cashier_controls_visible = visible;
if(visible){
this.$('.pos-rightheader').removeClass('oe_hidden');
}else{
this.$('.pos-rightheader').addClass('oe_hidden');
this.$('.pos-leftpane').addClass('oe_hidden');
this.$('.rightpane').css({'left':'0px'});
}
}
},

View File

@ -12,9 +12,9 @@
</div>
<div class="pos-rightheader">
<div class="order-selector">
<button class="order-button square neworder-button"><img src='/point_of_sale/static/src/img/plus.png' /></button>
<button class="order-button square deleteorder-button"><img src='/point_of_sale/static/src/img/minus.png' /></button>
<span class="orders"></span>
<span class="order-button square neworder-button"><i class='fa fa-plus' /></span>
<span class="order-button square deleteorder-button"><i class='fa fa-minus' /></span>
</div>
<!-- here goes header buttons -->
</div>
@ -35,6 +35,7 @@
<div class='subwindow collapsed'>
<div class='subwindow-container'>
<div class='subwindow-container-fix pads'>
<div class="control-buttons oe_hidden"></div>
<div class="placeholder-PaypadWidget"></div>
<div class="placeholder-NumpadWidget"></div>
</div>
@ -258,29 +259,132 @@
<t t-name="ScaleScreenWidget">
<div class="scale-screen screen">
<div class="display">
<span class="weight">
<p>
<span class='js-weight'>
<t t-esc="widget.get_product_weight_string()" />
</span>
</p>
</span>
<span class="product-name">
<t t-esc="widget.get_product_name()" />
</span>
<div class="product-picture">
<span class="product-price">
<div class="screen-content">
<div class="top-content">
<span class='button back'>
<i class='fa fa-angle-double-left'></i>
Back
</span>
<h1 class='product-name'><t t-esc="widget.get_product_name()" /></h1>
</div>
<div class="centered-content">
<div class='weight js-weight'>
<t t-esc="widget.get_product_weight_string()" />
</div>
<div class='product-price'>
<t t-esc="widget.format_currency(widget.get_product_price()) + '/Kg'" />
</span>
<t t-if="widget.get_product()">
<img t-att-src="widget.get_product_image_url()" />
</t>
</div>
<div class='computed-price'>
123.14 €
</div>
<div class='buy-product'>
Order
<i class='fa fa-angle-double-right'></i>
</div>
</div>
</div>
</div>
</t>
<t t-name="ClientLine">
<tr class='client-line' t-att-data-id='partner.id'>
<td><t t-esc='partner.name' /></td>
<td><t t-esc='partner.address' /></td>
</tr>
</t>
<t t-name="ClientDetails">
<section class='client-details'>
<div class='client-picture'>
<img t-att-src='widget.partner_icon_url(partner.id)' />
<!--<img src='http://static.fjcdn.com/gifs/Aku_c019d6_2548127.gif' />-->
</div>
<div class='client-name'><t t-esc='partner.name' /></div>
<div class='client-details-box clearfix'>
<div class='client-details-left'>
<div class='client-detail'>
<span class='label'>Address</span>
<span class='detail client-address'><t t-esc='partner.address' /></span>
</div>
<div class='client-detail'>
<span class='label'>email</span>
<span class='detail client-email'><t t-esc='partner.email' /></span>
</div>
<div class='client-detail'>
<span class='label'>phone</span>
<t t-if='partner.phone'>
<span class='detail client-phone'><t t-esc='partner.phone' /></span>
</t>
<t t-if='!partner.phone'>
<span class='detail client-phone empty'>N/A</span>
</t>
</div>
</div>
<div class='client-details-right'>
<div class='client-detail'>
<span class='label'>ID</span>
<t t-if='partner.ean13'>
<span class='detail client-id'><t t-esc='partner.ean13'/></span>
</t>
<t t-if='!partner.ean13'>
<span class='detail client-id empty'>N/A</span>
</t>
</div>
</div>
</div>
</section>
</t>
<t t-name="ClientListScreenWidget">
<div class="clientlist-screen screen">
<div class="screen-content">
<section class="top-content">
<span class='button back'>
<i class='fa fa-angle-double-left'></i>
Cancel
</span>
<span class='searchbox'>
<input placeholder='Search Customers' />
<span class='search-clear'></span>
</span>
<span class='searchbox'></span>
<span class='button next oe_hidden highlight'>
Select Customer
<i class='fa fa-angle-double-right'></i>
</span>
</section>
<section class="full-content">
<div class='window'>
<section class='subwindow collapsed'>
<div class='subwindow-container'>
<div class='subwindow-container-fix client-details-contents'>
</div>
</div>
</section>
<section class='subwindow'>
<div class='subwindow-container'>
<div class='subwindow-container-fix touch-scrollable scrollable-y'>
<table class='client-list'>
<thead>
<tr>
<th>Name</th>
<th>Address</th>
</tr>
</thead>
<tbody class='client-list-contents'>
</tbody>
</table>
</div>
</div>
</section>
</div>
</section>
</div>
</div>
</t>
<t t-name="PaymentScreenWidget">
<div class="payment-screen screen touch-scrollable">
<div class="pos-payment-container">
@ -330,6 +434,9 @@
<br/>
</t>
<div font='b'>
<t t-if='receipt.shop.name'>
<div><t t-esc='receipt.shop.name' /></div>
</t>
<t t-if='receipt.company.contact_address'>
<div><t t-esc='receipt.company.contact_address' /></div>
</t>
@ -373,7 +480,7 @@
</t>
<line indent='1'>
<left>
<value value-decimals='3' autoint='on'>
<value value-decimals='3' value-autoint='on'>
<t t-esc='line.quantity' />
</value>
<t t-if='line.unit_name !== "Unit(s)"'>
@ -465,61 +572,9 @@
</receipt>
</t>
<t t-name="WelcomeScreenWidget">
<div class="welcome-screen screen">
<header class='rightpane-header'><h2>Welcome</h2></header>
<div class="dialog">
<img src="/point_of_sale/static/src/img/scan.png" />
<p> Please scan an item or your member card </p>
</div>
</div>
<div class="goodbye-message">
<p>Thank you for shopping with us.</p>
</div>
</t>
<t t-name="ScanProductScreenWidget">
<div class="scan-product-screen screen touch-scrollable">
<header class='rightpane-header'><h2>Please scan an item</h2></header>
<img src="/point_of_sale/static/src/img/scan.png" />
</div>
</t>
<t t-name="ClientPaymentScreenWidget">
<div class="scan-product-screen screen">
<header class='rightpane-header'><h2>Payment</h2></header>
<div class="dialog">
<img src="/point_of_sale/static/src/img/bancontact.png" />
<p>Please insert your card in the reader and follow the instructions to complete
your purchase</p>
</div>
</div>
</t>
<t t-name="ScaleInviteScreenWidget">
<div class="scale-invite-screen screen">
<header class='rightpane-header'><h2>Please put your product on the scale</h2></header>
<img src="/point_of_sale/static/src/img/scale.png" />
</div>
</t>
<t t-name="HelpPopupWidget">
<div class="modal-dialog">
<div class="popup popup-help">
<p class="message">Please be patient, help is on the way</p>
<div class="footer">
<div class="button">
Cancel
</div>
</div>
</div>
</div>
</t>
<t t-name="ChooseReceiptPopupWidget">
<div class="modal-dialog">
<div class="popup popup-help">
<div class="popup popup-receipt">
<p class="message">Welcome <t t-esc="widget.get_client_name()" /><br /> Choose your type of receipt:</p>
<div class = "button big-left receipt">
Ticket
@ -531,38 +586,17 @@
</div>
</t>
<t t-name="ProductErrorPopupWidget">
<div class="modal-dialog">
<div class="popup popup-help">
<p class="message">The scanned product was not recognized<br /> Please wait, a cashier is on the way</p>
<div class="footer">
<div class="button">
Ok
</div>
</div>
</div>
</div>
</t>
<t t-name="ErrorSessionPopupWidget">
<div class="modal-dialog">
<div class="popup popup-help">
<div class="popup popup-nosession">
<p class="message">Sorry, we could not create a session for this user.</p>
</div>
</div>
</t>
<t t-name="ErrorNegativePricePopupWidget">
<div class="modal-dialog">
<div class="popup popup-help">
<p class="message">Your order has to be validated by a cashier.</p>
</div>
</div>
</t>
<t t-name="ErrorNoClientPopupWidget">
<div class="modal-dialog">
<div class="popup popup-help">
<div class="popup popup-noclient">
<p class="message">An anonymous order cannot be invoiced</p>
<div class="footer">
<div class="button">
@ -575,7 +609,7 @@
<t t-name="ErrorInvoiceTransferPopupWidget">
<div class="modal-dialog">
<div class="popup popup-help">
<div class="popup popup-invoice">
<p class="message">The Order could not be sent to the server for invoicing. Invoices cannot be generated
in offline mode. Please check your internet connection and try again.</p>
<div class="footer">
@ -589,9 +623,51 @@
<t t-name="ErrorPopupWidget">
<div class="modal-dialog">
<div class="popup popup-help">
<div class="popup popup-error">
<p class="message"><t t-esc=" widget.message || 'Error' " /></p>
<p class="comment"><t t-esc=" widget.comment || '' "/></p>
<div class="footer">
<div class="button">
Ok
</div>
</div>
</div>
</div>
</t>
<t t-name="ErrorBarcodePopupWidget">
<div class="modal-dialog">
<div class="popup popup-barcode">
<p class="message">Unknown Barcode
<br />
<span class='barcode'></span>
</p>
<p class="comment">
The Point of Sale could not find any product, client, employee
or action associated with the scanned barcode.
</p>
<div class="footer">
<div class="button">
Ok
</div>
</div>
</div>
</div>
</t>
<t t-name="ConfirmPopupWidget">
<div class="modal-dialog">
<div class="popup popup-confirm">
<p class="message"><t t-esc=" widget.message || 'Confirm ?' " /></p>
<p class="comment"><t t-esc=" widget.comment || '' "/></p>
<div class="footer">
<div class="button confirm">
Confirm
</div>
<div class="button cancel">
Cancel
</div>
</div>
</div>
</div>
</t>
@ -617,84 +693,6 @@
</span>
</t>
<t t-name="OrderWidget">
<div class="order-container">
<div class="order-scroller touch-scrollable">
<div class="order">
<ul class="orderlines">
<t t-if="orderlines.length === 0">
<li class="orderline empty">
Your shopping cart is empty
</li>
</t>
</ul>
<div class="summary clearfix">
<div t-attf-class="line #{orderlines.length === 0 ? 'empty' : ''}">
<div class='entry total'>
<span class="label">Total: </span> <span class="value">0.00 €</span>
<div class='subentry'>Taxes: <span class="value">0.00€</span></div>
</div>
</div>
<div class='clear'></div>
</div>
</div>
</div>
</div>
</t>
<t t-name="DebugWidget">
<div class="debug-widget">
<h1>Debug Window</h1>
<div class="toggle"></div>
<div class="content">
<p class="category">Payment Terminal</p>
<ul>
<li class="button accept_payment">Accept Payment</li>
<li class="button reject_payment">Reject Payment</li>
</ul>
<p class="category">Electronic Scale</p>
<ul>
<li><input type="text" class="weight"></input></li>
<li class="button set_weight">Set Weight</li>
<li class="button reset_weight">Reset</li>
</ul>
<p class="category">Barcode Scanner</p>
<ul>
<li><input type="text" class="ean"></input></li>
<li class="button custom_ean">Custom Ean13</li>
<li class="button admin_badge">Admin Badge</li>
<li class="button client_badge">Client Badge</li>
<li class="button soda_33cl">Soda 33cl</li>
<li class="button oranges_kg">3.141Kg Oranges</li>
<li class="button lemon_price">1.54€ Lemon</li>
<li class="button unknown_product">Unknown Product</li>
<li class="button invalid_ean">Invalid Ean</li>
<li class="button reference">Reference</li>
</ul>
<p class="category">Hardware Status</p>
<ul>
<li class="status help_needed">Help needed</li>
<li class="status weighting">Weighting</li>
<li class="status transaction">In Transaction</li>
</ul>
<p class="category">Hardware Events</p>
<ul>
<li class="event scan_item_success">Scan Item Success</li>
<li class="event scan_item_error_unrecognized">Scan Item Unrecognized</li>
<li class="event payment_request">Payment Request</li>
<li class="event payment_status">Payment Status</li>
<li class="event open_cashbox">Open Cashbox</li>
<li class="event print_receipt">Print Receipt</li>
<li class="event print_pdf_invoice">Print Invoice</li>
<li class="event scale_read">Read Weighting Scale</li>
</ul>
</div>
</div>
</t>
<t t-name="Orderline">
<li t-attf-class="orderline #{ line.selected ? 'selected' : '' }">
<span class="product-name">
@ -729,6 +727,73 @@
</li>
</t>
<t t-name="OrderWidget">
<div class="order-container">
<div class="order-scroller touch-scrollable">
<div class="order">
<ul class="orderlines">
<t t-if="orderlines.length === 0">
<li class="orderline empty">
Your shopping cart is empty
</li>
</t>
</ul>
<div class="summary clearfix">
<div t-attf-class="line #{orderlines.length === 0 ? 'empty' : ''}">
<div class='entry total'>
<span class="label">Total: </span> <span class="value">0.00 €</span>
<div class='subentry'>Taxes: <span class="value">0.00€</span></div>
</div>
</div>
</div>
</div>
</div>
</div>
</t>
<t t-name="DebugWidget">
<div class="debug-widget">
<h1>Debug Window</h1>
<div class="toggle"></div>
<div class="content">
<p class="category">Electronic Scale</p>
<ul>
<li><input type="text" class="weight"></input></li>
<li class="button set_weight">Set Weight</li>
<li class="button reset_weight">Reset</li>
</ul>
<p class="category">Barcode Scanner</p>
<ul>
<li><input type="text" class="ean"></input></li>
<li class="button custom_ean">Custom Ean13</li>
<li class="button admin_badge">Admin Badge</li>
<li class="button client_badge">Client Badge</li>
<li class="button soda_33cl">Soda 33cl</li>
<li class="button oranges_kg">3.141Kg Oranges</li>
<li class="button lemon_price">1.54€ Lemon</li>
<li class="button unknown_product">Unknown Product</li>
<li class="button invalid_ean">Invalid Ean</li>
<li class="button reference">Reference</li>
</ul>
<p class="category">Hardware Status</p>
<ul>
<li class="status weighting">Weighting</li>
</ul>
<p class="category">Hardware Events</p>
<ul>
<li class="event open_cashbox">Open Cashbox</li>
<li class="event print_receipt">Print Receipt</li>
<li class="event scale_read">Read Weighting Scale</li>
</ul>
</div>
</div>
</t>
<t t-name="Paymentline">
<div t-attf-class="paymentline #{line.selected ? 'selected' : ''}">
<div class='paymentline-name'>
@ -760,7 +825,22 @@
</t>
<t t-name="OrderButtonWidget">
<button class="order-button select-order"><t t-esc="(widget.order.get_client() ? widget.order.get_client_name()+' : ':'') + widget.order.get('creationDate').toString('t')"/></button>
<span class="order-button select-order">
<t t-if='widget.selected'>
<span class='order-sequence'>
<t t-esc='widget.order.sequence_number' />
</span>
<t t-if="widget.order.get_client()">
<i class='fa fa-user'/>
</t>
<t t-esc="(widget.order.get_client() ? widget.order.get_client_name()+' : ':'Unknown Customer: ') + widget.order.get('creationDate').toString('t')"/>
</t>
<t t-if='!widget.selected'>
<span class='order-sequence'>
<t t-esc='widget.order.sequence_number' />
</span>
</t>
</span>
</t>
<t t-name="UsernameWidget">
@ -778,6 +858,7 @@
<t t-esc="widget.pos.company.name"/><br />
Phone: <t t-esc="widget.pos.company.phone || ''"/><br />
User: <t t-esc="widget.pos.user.name"/><br />
Shop: <t t-esc="widget.pos.shop.name"/><br />
<br />
<t t-if="widget.pos.config.receipt_header">
<div style='text-align:center'>

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- vim:fdn=3:
-->
<openerp>
<data>
<template id="index" name="POS Index">&lt;!DOCTYPE html&gt;
<html>
<head>
<title>Odoo POS</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta http-equiv="content-type" content="text/html, charset=utf-8" />
<meta name="viewport" content=" width=1024, user-scalable=no"/>
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="mobile-web-app-capable" content="yes"/>
<link rel="shortcut icon" sizes="196x196" href="/point_of_sale/static/src/img/touch-icon-196.png"/>
<link rel="shortcut icon" sizes="128x128" href="/point_of_sale/static/src/img/touch-icon-128.png"/>
<link rel="apple-touch-icon" href="/point_of_sale/static/src/img/touch-icon-iphone.png"/>
<link rel="apple-touch-icon" sizes="76x76" href="/point_of_sale/static/src/img/touch-icon-ipad.png"/>
<link rel="apple-touch-icon" sizes="120x120" href="/point_of_sale/static/src/img/touch-icon-iphone-retina.png"/>
<link rel="apple-touch-icon" sizes="152x152" href="/point_of_sale/static/src/img/touch-icon-ipad-retina.png"/>
<link rel="shortcut icon" href="/web/static/src/img/favicon.ico" type="image/x-icon"/>
<link rel="stylesheet" href="/point_of_sale/static/src/fonts/lato/stylesheet.css" />
<link rel="stylesheet" href="/point_of_sale/static/src/fonts/font-awesome-4.0.3/css/font-awesome.min.css" />
<link rel="stylesheet" href="/point_of_sale/static/src/css/pos.css" id="pos-stylesheet"/>
<link rel="stylesheet" href="/point_of_sale/static/src/css/keyboard.css" />
<t t-call-assets="web.assets_common" t-css="false" />
<t t-call-assets="web.assets_backend" t-css="false" />
<script type="text/javascript" id="loading-script">
$(function() {
var s = new openerp.init(<t t-raw='modules' />);
<t t-raw='init' />
});
</script>
</head>
<body>
<div class='openerp openerp_webclient_container'>
<table class='oe_webclient'>
<tr>
<td class='oe_application' />
</tr>
</table>
</div>
</body>
</html>
</template>
</data>
</openerp>