Modification : gère point_of_sale.patch dans ce module.
[burette/pos_membership.git] / point_of_sale.patch
diff --git a/point_of_sale.patch b/point_of_sale.patch
deleted file mode 100644 (file)
index d8be7a1..0000000
+++ /dev/null
@@ -1,1258 +0,0 @@
-diff --git a/controllers/main.py b/controllers/main.py
-index b189c8f..d3743ac 100644
---- a/controllers/main.py
-+++ b/controllers/main.py
-@@ -29,7 +29,6 @@ class PointOfSaleController(openerp.addons.web.http.Controller):
-     def manifest(self, req, **kwargs):
-         """ This generates a HTML5 cache manifest files that preloads the categories and products thumbnails 
-             and other ressources necessary for the point of sale to work offline """
--
-         ml = ["CACHE MANIFEST"]
-         # loading all the images in the static/src/img/* directories
-@@ -56,10 +55,17 @@ class PointOfSaleController(openerp.addons.web.http.Controller):
-             category_id = c['id']
-             url = "/web/binary/image?session_id=%s&model=pos.category&field=image&id=%s" % (req.session_id, category_id)
-             ml.append(url)
-+        
-+        partners = req.session.model('res.partner')
-+        for c in partners.search_read([],['name']):
-+            partner_id = c['id']
-+            url = "/web/binary/image?session_id=%s&model=res.partner&field=image&id=%s" % (req.session_id, partner_id)
-+            ml.append(url)
-         ml += ["NETWORK:","*"]
-         m = "\n".join(ml)
-+        print ("DEV: [point_of_sale] [controllers] [main] return=%s" % m)
-         return m
-     @openerp.addons.web.http.jsonrequest
-diff --git a/point_of_sale.py b/point_of_sale.py
-index 7e8487b..371a07b 100644
---- a/point_of_sale.py
-+++ b/point_of_sale.py
-@@ -479,8 +479,22 @@ class pos_order(osv.osv):
-     _description = "Point of Sale"
-     _order = "id desc"
-+    def create_partner_from_ui(self, cr, uid, partners, context=None):
-+        print ("DEV: [point_of_sale] [create_partner_from_ui] partners=%s" % str(partners))
-+        partner_ids = []
-+        partner_obj = self.pool.get('res.partner')
-+        for tmp_partner in partners:
-+            partner = tmp_partner['data']
-+            partner_id = partner_obj.create(cr, uid, {
-+                'name': partner['name'],
-+            }, context)
-+            partner_ids.append(partner_id)
-+            #self.signal_paid(cr, uid, [partner_id])
-+        return partner_ids
-+
-     def create_from_ui(self, cr, uid, orders, context=None):
-         #_logger.info("orders: %r", orders)
-+        print ("DEV: [point_of_sale] [create_from_ui] order=", str(orders))
-         order_ids = []
-         for tmp_order in orders:
-             order = tmp_order['data']
-@@ -489,7 +503,8 @@ class pos_order(osv.osv):
-                 'user_id': order['user_id'] or False,
-                 'session_id': order['pos_session_id'],
-                 'lines': order['lines'],
--                'pos_reference':order['name']
-+                'pos_reference':order['name'],
-+                'partner_id':order['partner_id'],
-             }, context)
-             for payments in order['statement_ids']:
-diff --git a/static/src/css/pos.css b/static/src/css/pos.css
-index d9a4d6d..9357d5d 100644
---- a/static/src/css/pos.css
-+++ b/static/src/css/pos.css
-@@ -619,6 +619,200 @@
-     padding-top:15px;
- }
-+/*  ********* The partner list  ********* */
-+
-+.point-of-sale .partner-list {
-+    padding:10px !important;
-+}
-+
-+.point-of-sale .partner-list-scroller{
-+    -webkit-box-sizing: border-box;
-+       -moz-box-sizing: border-box;
-+        -ms-box-sizing: border-box;
-+            box-sizing: border-box;
-+    width:100%;
-+    height:100%;
-+    overflow: hidden;
-+}
-+.point-of-sale .partner-list-container {
-+    position:absolute;
-+    top:0px;
-+    bottom:0px;
-+    left:0px;
-+    right:0px;
-+}
-+
-+/*  a) the search box */
-+
-+.point-of-sale #partners-screen .searchbox {
-+    position: absolute;
-+    right: 2px;
-+}
-+.point-of-sale #partners-screen .searchbox input {
-+    width: 130px;
-+    border-radius: 11px;
-+    border: 1px solid #cecbcb;
-+    padding: 3px 19px;
-+    margin: 6px;
-+    background: url("../img/search.png") no-repeat 5px;
-+    background-color: white;
-+}
-+.point-of-sale #partners-screen .search-clear {
-+    position: absolute;
-+    top: 11px;
-+    right: 11px;
-+    cursor: pointer;
-+    display: none;
-+}
-+
-+.point-of-sale #partners-screen .search-create {
-+    position: absolute;
-+    top: 6px;
-+    right: 22px;
-+    cursor: pointer;
-+    display: none;
-+}
-+
-+
-+/*  b) the partner */
-+
-+.point-of-sale .partner {
-+    position:relative;
-+    vertical-align: top;
-+    display: inline-block;
-+    line-height: 100px;
-+    font-size: 11px;
-+    margin: 5px !important;
-+    width: 120px;
-+    height:120px;
-+    background:#fff;
-+    border: 1px solid #fff;
-+    border-radius: 3px;
-+    -webkit-box-shadow: 0px 2px 0px #dad8e4, 0px 1px 8px #636480; 
-+       -moz-box-shadow: 0px 2px 0px #dad8e4, 0px 1px 8px #636480; 
-+            box-shadow: 0px 2px 0px #dad8e4, 0px 1px 8px #636480; 
-+}
-+
-+.point-of-sale .partner .partner-img {
-+    position: relative;
-+    width: 120px;
-+    height: 100px;
-+    background: white;
-+    text-align: center;
-+}
-+
-+.point-of-sale .partner .partner-img img {
-+    max-height: 100px;
-+    max-width:  120px;
-+}
-+
-+.point-of-sale .partner .price-tag {
-+    position: absolute;
-+    top: 2px;
-+    right: 2px;
-+    vertical-align: top;
-+    color: white;
-+    line-height: 13px;
-+    background: #7f82ac;
-+    padding: 2px 5px;
-+    border-radius: 3px;
-+    box-shadow: 0px 1px 0px #9A9CC5, 0px 3px 0px #7E86AC, 0px 3px 3px rgba(12, 14, 68, 0.67);
-+}
-+
-+.point-of-sale .partner .partner-name {
-+    position: absolute;
-+    -webkit-box-sizing: border-box;
-+       -moz-box-sizing: border-box;
-+        -ms-box-sizing: border-box;
-+            box-sizing: border-box;
-+    bottom:0;
-+    top:auto;
-+    line-height: 14px;
-+    width:100%;
-+    background: -webkit-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1));
-+    background:    -moz-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1));
-+    background:     -ms-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1));
-+    /* troublesome in latest webkit
-+    background:         linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1));
-+    */
-+    /*background:#FFF;*/
-+    padding: 3px;
-+    padding-top:15px;
-+}
-+
-+/*  c) the partner creation */
-+
-+.point-of-sale .pos-step-container {
-+    display: inline-block;
-+    font-size: 1.5em;
-+}
-+.point-of-sale .greyed-out{
-+    color: #AAA;
-+}
-+.point-of-sale .pos-step-container input{
-+    font-size: 1em;
-+    outline: none;
-+    border: none;
-+    padding: 0px 8px;
-+    padding-top: 8px;
-+    margin-left: 16px;
-+    border-radius: 5px;
-+    background: white;
-+    box-shadow: 0px -1px 0px #E2E2E2 inset,0px 1px 0px white inset, 0px 4px 0px #DDD inset, 0px 4px 8px rgba(0, 0, 0, 0.55) inset;
-+    color: #4c4c4c; 
-+    -webkit-animation: all 250ms linear;
-+}
-+
-+.point-of-sale .pos-step-container input:focus{
-+    box-shadow: 0px -1px 0px #C9CFFD inset,0px 1px 0px #B8C8FC inset, 0px 4px 0px #9FD5FF inset, 0px 4px 9px rgba(0, 31, 255, 0.55) inset;
-+    color: #5d7ad6;
-+    -webkit-animation: all 250ms linear;
-+}
-+
-+.point-of-sale .pos-partner-create-container {
-+    text-align: left;
-+    min-width: 500px;
-+    margin-top: 50px;
-+    padding: 40px;
-+    background-color: #f8f8f8;
-+    border-radius: 4px;
-+    box-shadow: 0px 1px 0px white,0px -1px 0px white, 0px 4px 0px #DFDFDF, 0px 10px 30px rgba(0, 0, 0, 0.21);
-+}
-+.point-of-sale .pos-partner-create-container .left-block{
-+    display: inline-block;
-+    width:49%;
-+    margin:0;
-+    padding:0;
-+    text-align:left;
-+}
-+.point-of-sale .pos-partner-create-container .header{
-+    margin-top: 0px;
-+    margin-bottom:20px;
-+    font-weight: bold;
-+}
-+.point-of-sale .pos-partner-create-container .infoline{
-+    margin-top:5px;
-+    margin-bottom:5px;
-+}
-+.point-of-sale .pos-partner-create-container .right-block{
-+    display: inline-block;
-+    width:49%;
-+    margin:0;
-+    padding:0;
-+    text-align:right;
-+}
-+.point-of-sale .pos-partner-create-container table {
-+    width: 100%;
-+    margin-bottom: 20px;
-+}
-+.point-of-sale .pos-partner-create-container td {
-+    vertical-align: middle;
-+}
-+.point-of-sale .pos-partner-create-container .partner-createline-type {
-+    font-size: 1em;
-+    font-weight: bold;
-+    margin-right:10px;
-+}
- /*  ********* The Screens  ********* */
-diff --git a/static/src/css/pos_nohover.css b/static/src/css/pos_nohover.css
-index 6f24abd..7dcb4f0 100644
---- a/static/src/css/pos_nohover.css
-+++ b/static/src/css/pos_nohover.css
-@@ -579,6 +579,175 @@
-     padding-top:15px;
- }
-+/*  ********* The partner list  ********* */
-+
-+.point-of-sale .partner-list {
-+    padding:10px;
-+}
-+
-+.point-of-sale .partner-list-scroller{
-+    -webkit-box-sizing: border-box;
-+       -moz-box-sizing: border-box;
-+        -ms-box-sizing: border-box;
-+            box-sizing: border-box;
-+    width:100%;
-+    height:100%;
-+    overflow: hidden;
-+}
-+.point-of-sale .partner-list-container {
-+    position:absolute;
-+    top:0px;
-+    bottom:0px;
-+    left:0px;
-+    right:0px;
-+    background: #eeedff;
-+}
-+
-+/*  a) the search box */
-+
-+.point-of-sale #partners-screen .searchbox {
-+    position: absolute;
-+    right: 2px;
-+}
-+.point-of-sale #partners-screen .searchbox input {
-+    width: 130px;
-+    border-radius: 11px;
-+    border: 1px solid #cecbcb;
-+    padding: 3px 19px;
-+    margin: 6px;
-+    background: url("../img/search.png") no-repeat 5px;
-+    background-color: white;
-+}
-+.point-of-sale #partners-screen .search-clear {
-+    position: absolute;
-+    top: 11px;
-+    right: 11px;
-+    cursor: pointer;
-+    display: none;
-+}
-+.point-of-sale #partners-screen .search-create {
-+    position: absolute;
-+    top: 6px;
-+    right: 22px;
-+    cursor: pointer;
-+    display: none;
-+}
-+
-+/*  b) the partner */
-+
-+.point-of-sale .partner {
-+    position:relative;
-+    vertical-align: top;
-+    display: inline-block;
-+    line-height: 100px;
-+    font-size: 11px;
-+    margin: 5px;
-+    width: 120px;
-+    height:120px;
-+    background:#fff;
-+    border: 1px solid #fff;
-+    border-radius: 3px;
-+    -webkit-box-shadow: 0px 1px 8px rgba(127,130,172,0.4);
-+       -moz-box-shadow: 0px 1px 8px rgba(127,130,172,0.4);
-+            box-shadow: 0px 1px 8px rgba(127,130,172,0.4);
-+}
-+
-+.point-of-sale .partner .partner-img {
-+    position: relative;
-+    width: 120px;
-+    height: 100px;
-+    background: white;
-+    text-align: center;
-+}
-+
-+.point-of-sale .partner .partner-img img {
-+    max-height: 100px;
-+    max-width:  120px;
-+}
-+
-+.point-of-sale .partner .price-tag {
-+    position: absolute;
-+    top: 2px;
-+    right: 2px;
-+    vertical-align: top;
-+    color: white;
-+    line-height: 14px;
-+    background: #7f82ac;
-+    padding: 2px 5px;
-+    border-radius: 3px;
-+}
-+
-+.point-of-sale .partner .partner-name {
-+    position: absolute;
-+    -webkit-box-sizing: border-box;
-+       -moz-box-sizing: border-box;
-+        -ms-box-sizing: border-box;
-+            box-sizing: border-box;
-+    bottom:0;
-+    top:auto;
-+    line-height: 14px;
-+    width:100%;
-+    background: -webkit-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1));
-+    background:    -moz-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1));
-+    background:     -ms-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1));
-+    background:         linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1));
-+    /*background:#FFF;*/
-+    padding: 3px;
-+    padding-top:15px;
-+}
-+
-+/*  c) the partner creation */
-+
-+.point-of-sale .pos-step-container {
-+    display: inline-block;
-+    font-size: 1.5em;
-+}
-+.point-of-sale .greyed-out{
-+    color: #AAA;
-+}
-+.point-of-sale .pos-step-container input{
-+    font-size: 1em;
-+}
-+
-+.point-of-sale .pos-partner-create-container {
-+    text-align: left;
-+    min-width: 500px;
-+}
-+.point-of-sale .pos-partner-create-container .left-block{
-+    display: inline-block;
-+    width:49%;
-+    margin:0;
-+    padding:0;
-+    text-align:left;
-+}
-+.point-of-sale .pos-partner-create-container .header{
-+    margin-top: 50px;
-+    margin-bottom:20px;
-+    font-weight: bold;
-+}
-+.point-of-sale .pos-partner-create-container .infoline{
-+    margin-top:5px;
-+    margin-bottom:5px;
-+}
-+.point-of-sale .pos-partner-create-container .right-block{
-+    display: inline-block;
-+    width:49%;
-+    margin:0;
-+    padding:0;
-+    text-align:right;
-+}
-+.point-of-sale .pos-partner-create-container table {
-+    width: 100%;
-+    margin-bottom: 20px;
-+}
-+.point-of-sale .pos-partner-create-container td {
-+    vertical-align: middle;
-+}
-+.point-of-sale .pos-partner-create-container .partner-createline-type {
-+    font-size: 1em;
-+    font-weight: bold;
-+    margin-right:10px;
-+}
- /*  ********* The Screens  ********* */
-diff --git a/static/src/js/db.js b/static/src/js/db.js
-index a79b111..93c71e9 100644
---- a/static/src/js/db.js
-+++ b/static/src/js/db.js
-@@ -40,6 +40,9 @@ function openerp_pos_db(instance, module){
-             //cache the data in memory to avoid roundtrips to the localstorage
-             this.cache = {};
-+            this.partner_search_string = '';
-+            this.partner_by_id = {};
-+            this.partner_list = [];
-             this.product_by_id = {};
-             this.product_by_ean13 = {};
-             this.product_by_category_id = {};
-@@ -196,6 +199,23 @@ function openerp_pos_db(instance, module){
-                 }
-             }
-         },
-+        _partner_search_string: function(partner){
-+            var str = '' + partner.id + ':' + partner.name;
-+            return str + '\n';
-+        },
-+        add_partners: function(partners){
-+            if(!partners instanceof Array){
-+                partners = [partners];
-+            }
-+            for(var i = 0, len = partners.length; i < len; i++){
-+                var partner = partners[i];
-+                //console.log("[db] [add_partners] partner.name:",partner.name);
-+                this.partner_by_id[partner.id] = partner;
-+                this.partner_list.push(partner);
-+                this.partner_search_string += this._partner_search_string(partner);
-+            }
-+                                              //console.log("[db] [add_partners] partner_search_string:", this.partner_search_string);
-+        },
-         add_packagings: function(packagings){
-             for(var i = 0, len = packagings.length; i < len; i++){
-                 var pack = packagings[i];
-@@ -225,6 +245,9 @@ function openerp_pos_db(instance, module){
-             }
-             return count;
-         },
-+        get_partner_by_id: function(id){
-+            return this.partner_by_id[id];
-+        },
-         get_product_by_id: function(id){
-             return this.product_by_id[id];
-         },
-@@ -266,13 +289,48 @@ function openerp_pos_db(instance, module){
-             }
-             return results;
-         },
-+        search_partner: function(query){
-+            var re = RegExp("([0-9]+):.*?"+query,"gi");
-+            var results = [];
-+            //console.log("[db] [search_partner] query:",query);
-+            for(var i = 0; i < this.limit; i++){
-+                r = re.exec(this.partner_search_string);
-+                if(r){
-+                  var id = Number(r[1]);
-+                  //console.log("[db] [search_partner] id:",id);
-+                  results.push(this.get_partner_by_id(id));
-+                }else{
-+                    break;
-+                }
-+            }
-+            return results;
-+        },
-+        get_partner_list: function(){
-+            return this.partner_list;
-+        },
-+        add_partner: function(partner){
-+            console.log("[PosLS] [add_partner] partner=", partner.name);
-+            var last_id = this.load('last_partner_id',0);
-+            var partners  = this.load('partners',[]);
-+            partners.push({id: last_id + 1, data: partner});
-+            this.save('last_partner_id',last_id+1);
-+            this.save('partners',partners);
-+        },
-         add_order: function(order){
-+            console.log("[PosLS] [add_order] order=", order);
-             var last_id = this.load('last_order_id',0);
-             var orders  = this.load('orders',[]);
-             orders.push({id: last_id + 1, data: order});
-             this.save('last_order_id',last_id+1);
-             this.save('orders',orders);
-         },
-+        remove_partner: function(partner_id){
-+            var partners = this.load('partners',[]);
-+            partners = _.filter(partners, function(partner){
-+                return partner.id !== partner_id;
-+            });
-+            this.save('partners',partners);
-+        },
-         remove_order: function(order_id){
-             var orders = this.load('orders',[]);
-             orders = _.filter(orders, function(order){
-@@ -280,6 +338,9 @@ function openerp_pos_db(instance, module){
-             });
-             this.save('orders',orders);
-         },
-+        get_partners: function(){
-+            return this.load('partners',[]);
-+        },
-         get_orders: function(){
-             return this.load('orders',[]);
-         },
-diff --git a/static/src/js/models.js b/static/src/js/models.js
-index d2cf15b..409f193 100644
---- a/static/src/js/models.js
-+++ b/static/src/js/models.js
-@@ -41,7 +41,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
-             this.barcode_reader = new module.BarcodeReader({'pos': this});  // used to read barcodes
-             this.proxy = new module.ProxyDevice();              // used to communicate to the hardware devices via a local proxy
-             this.db = new module.PosLS();                       // a database used to store the products and categories
--            this.db.clear('products','categories');
-+            this.db.clear('products','categories','partners');
-             this.debug = jQuery.deparam(jQuery.param.querystring()).debug !== undefined;    //debug mode 
-             // default attributes values. If null, it will be loaded below.
-@@ -59,6 +59,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
-                 'orders':           new module.OrderCollection(),
-                 //this is the product list as seen by the product list widgets, it will change based on the category filters
-                 'products':         new module.ProductCollection(), 
-+                'partners':         new module.PartnerCollection(),
-                 'cashRegisters':    null, 
-                 'bank_statements':  null,
-@@ -69,6 +70,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
-                 'units_by_id':      null,
-                 'selectedOrder':    null,
-+                'selectedPartner':  null,
-             });
-             this.get('orders').bind('remove', function(){ self.on_removed_order(); });
-@@ -144,6 +146,10 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
-                 }).then(function(partners){
-                     self.set('partner_list',partners);
-+                    return self.fetch('res.partner', ['name']);
-+                }).then(function(partners){
-+                    self.db.add_partners(partners);
-+
-                     return self.fetch('account.tax', ['amount', 'price_include', 'type']);
-                 }).then(function(taxes){
-                     self.set('taxes', taxes);
-@@ -256,12 +262,23 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
-             }
-         },
-+        push_partner: function(partner) {
-+            console.log("[PosModel] [push_partner] partner=", partner.name);
-+            this.db.add_partner(partner);
-+            this.flush_partner();
-+        },
-         // saves the order locally and try to send it to the backend. 'record' is a bizzarely defined JSON version of the Order
-         push_order: function(record) {
-             this.db.add_order(record);
-             this.flush();
-         },
-+        add_new_partner: function(attr){
-+            var partner = new module.PartnerCreate({pos:this, name:attr.name});
-+            console.log("[PosModel] [add_new_partner] partner=", partner.get('name'));
-+            this.get('partners').add(partner);
-+            this.set('selectedPartner', partner);
-+        },
-         //creates a new empty order and sets it as the current order
-         add_new_order: function(){
-             var order = new module.Order({pos:this});
-@@ -269,6 +286,16 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
-             this.set('selectedOrder', order);
-         },
-+        flush_partner: function() {
-+            //TODO make the mutex work 
-+            //this makes sure only one _int_flush is called at the same time
-+            /*
-+            return this.flush_mutex.exec(_.bind(function() {
-+                return this._flush_partner(0);
-+            }, this));
-+            */
-+            this._flush_partner(0);
-+        },
-         // attemps to send all pending orders ( stored in the pos_db ) to the server,
-         // and remove the successfully sent ones from the db once
-         // it has been confirmed that they have been sent correctly.
-@@ -282,6 +309,40 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
-             */
-             this._flush(0);
-         },
-+        _flush_partner: function(index){
-+            var self = this;
-+            var partners = this.db.get_partners();
-+            self.set('nbr_pending_operations',partners.length);
-+
-+            var partner  = partners[index];
-+            console.log("[PosModel] [_flush_partner] index=", index);
-+            if(!partner){
-+                return;
-+            }
-+            console.log("[PosModel] [_flush_partner] partner=", partner.data.name);
-+            //try to push a partner to the server
-+            return (new instance.web.Model('pos.order')).get_func('create_partner_from_ui')([partner])
-+                .fail(function(unused, event){
-+                    //don't show error popup if it fails
-+                    event.preventDefault();
-+                    console.error('Failed to send partner:',partner);
-+                    self._flush_partner(index+1);
-+                })
-+                .done(function(args){
-+                    //remove from db if success
-+                    console.log("[PosModel] [_flush_partner] [.done] args=", args);
-+                    self.db.remove_partner(partner.id);
-+                    var name = partner.data.name;
-+                    p = {name:name, id:args[0]};
-+                    console.log("[PosModel] [_flush_partner] [.done] partner=", p);
-+                    self.db.add_partners([p]);
-+                    //self.set('selectedPartner', p);
-+                    //console.log("[PosModel] [_flush_partner] [.done] selectedPartner=", self.get('selectedPartner').name);
-+                    //console.log("[PosModel] [_flush_partner] [.done] selectedPartner.id=", self.get('selectedPartner').id);
-+                    self.get('selectedOrder').addPartner(p); // NOTE: set .id asynchronously..
-+                    self._flush_partner(index);
-+                });
-+        },
-         // attempts to send an order of index 'index' in the list of order to send. The index
-         // is used to skip orders that failed. do not call this method outside the mutex provided
-         // by flush() 
-@@ -295,6 +356,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
-                 return;
-             }
-             //try to push an order to the server
-+            console.log("[PosModel] [_flush] order=", order);
-             (new instance.web.Model('pos.order')).get_func('create_from_ui')([order])
-                 .fail(function(unused, event){
-                     //don't show error popup if it fails 
-@@ -341,10 +403,22 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
-             return instance.session.url('/web/binary/image', {model: 'product.product', field: 'image', id: this.get('id')});
-         },
-     });
-+    module.Partner = Backbone.Model.extend({
-+        initialize: function(attr, options) {
-+          this.name = attr.name;
-+          this.id   = attr.id;
-+        },
-+        get_image_url: function(){
-+            return instance.session.url('/web/binary/image', {model: 'res.partner', field: 'image', id: this.get('id')});
-+        },
-+    });
-     module.ProductCollection = Backbone.Collection.extend({
-         model: module.Product,
-     });
-+    module.PartnerCollection = Backbone.Collection.extend({
-+        model: module.Partner,
-+    });
-     // 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. 
-@@ -598,6 +672,21 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
-     });
-     
-+    module.PartnerCreate = Backbone.Model.extend({
-+        initialize: function(attr){
-+            Backbone.Model.prototype.initialize.apply(this, arguments);
-+            this.pos = attr.pos;
-+            this.name = attr.name;
-+            return this;
-+        },
-+        exportAsJSON: function() {
-+            console.log("[PartnerCreate] [exportAsJSON] name=",this.name);
-+            return {
-+                name: this.name,
-+            };
-+        },
-+    });
-+
-     // An order more or less represents the content of a client's shopping cart (the OrderLines) 
-     // plus the associated payment information (the PaymentLines) 
-     // there is always an active ('selected') order in the Pos, a new one is created
-@@ -643,6 +732,11 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
-             }
-             this.selectLine(this.getLastOrderline());
-         },
-+        addPartner: function(partner, options){
-+            options = options || {};
-+            console.log("[Order] [addPartner] partner=", partner.name, " partner_id=", partner.id);
-+            this.set_client(partner);
-+        },
-         removeOrderline: function( line ){
-             this.get('orderLines').remove(line);
-             this.selectLine(this.getLastOrderline());
-@@ -747,6 +841,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
-             var company = this.pos.get('company');
-             var shop    = this.pos.get('shop');
-             var date = new Date();
-+            console.log("[Order] [export_for_printing] this.get('client')=",this.get('client'));
-             return {
-                 orderlines: orderlines,
-@@ -804,7 +899,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
-                 lines: orderLines,
-                 statement_ids: paymentLines,
-                 pos_session_id: this.pos.get('pos_session').id,
--                partner_id: this.pos.get('client') ? this.pos.get('client').id : undefined,
-+                partner_id: this.get('client') ? this.get('client').id : undefined,
-                 user_id: this.pos.get('cashier') ? this.pos.get('cashier').id : this.pos.get('user').id,
-             };
-         },
-diff --git a/static/src/js/screens.js b/static/src/js/screens.js
-index eca4a3c..eb1986a 100644
---- a/static/src/js/screens.js
-+++ b/static/src/js/screens.js
-@@ -729,10 +729,54 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
-         },
-     });
-     
-+    module.PartnerScreenWidget = module.ScreenWidget.extend({
-+        template:'PartnerScreenWidget',
-+
-+        next_screen: 'products',
-+
-+        show_numpad:     false,
-+        show_leftpane:   false,
-+        init: function(parent, options) {
-+            this._super(parent,options);
-+            this.model = options.model;
-+            // TODO: this.pos.bind('change:selectedOrder', this.change_selected_order, this);
-+        },
-+
-+        start: function(){
-+            var self = this;
-+
-+            this.partner_search_widget = new module.PartnerSearchWidget(this,{});
-+            this.partner_search_widget.replace($('.placeholder-PartnerSearchWidget'));
-+
-+            this.partner_list_widget = new module.PartnerListWidget(this,{
-+                click_partner_action: function(partner){
-+                    self.pos.get('selectedOrder').addPartner(partner);
-+                    self.pos_widget.screen_selector.set_current_screen(self.next_screen);
-+                },
-+            });
-+            this.partner_list_widget.replace($('.placeholder-PartnerListWidget'));
-+        },
-+
-+        show: function(){
-+            this._super();
-+            var self = this;
-+
-+            this.partner_search_widget.reset_filter();
-+        },
-+
-+        close: function(){
-+            this._super();
-+            this.pos_widget.order_widget.set_numpad_state(null);
-+            this.pos_widget.payment_screen.set_numpad_state(null);
-+        },
-+
-+    });
-+    
-     module.ProductScreenWidget = module.ScreenWidget.extend({
-         template:'ProductScreenWidget',
-         scale_screen: 'scale_invite',
-+        back_screen: 'partner',
-         client_next_screen:  'client_payment',
-         show_numpad:     true,
-@@ -775,6 +819,16 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
-                         }
-                     });
-             }
-+
-+            this.back_button = this.add_action_button({
-+                    label: 'Back',
-+                    icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
-+                    click: function(){
-+                        self.pos.get('selectedOrder').get('orderLines').reset([]);
-+                        self.pos_widget.numpad.state.trigger('set_value','remove');
-+                        self.pos_widget.screen_selector.set_current_screen(self.back_screen);
-+                    },
-+                });
-         },
-         close: function(){
-@@ -846,6 +900,63 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
-         },
-     });
-+    module.PartnerCreateScreenWidget = module.ScreenWidget.extend({
-+        template: 'PartnerCreateScreenWidget',
-+        show_numpad: false,
-+        show_leftpane: false,
-+        back_screen: 'partner',
-+        next_screen: 'products',
-+        init: function(parent, options) {
-+            this._super(parent,options);
-+            this.model = options.model;
-+        },
-+        show: function(){
-+            this._super();
-+            var self = this;
-+
-+            this.back_button = this.add_action_button({
-+                    label: 'Back',
-+                    icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
-+                    click: function(){
-+                        self.pos_widget.screen_selector.set_current_screen(self.back_screen);
-+                    },
-+                });
-+            this.validate_button = this.add_action_button({
-+                    label: 'Validate',
-+                    name: 'validation',
-+                    icon: '/point_of_sale/static/src/img/icons/png48/validate.png',
-+                    click: function(){
-+                        self.validatePartner();
-+                    },
-+                });
-+            this.updatePartnerCreateSummary();
-+        },
-+        close: function(){
-+            this._super();
-+        },
-+        back: function() {
-+            this.pos_widget.screen_selector.set_current_screen(self.back_screen);
-+        },
-+        validatePartner: function() {
-+            var partner = this.pos.get('selectedPartner');
-+
-+            this.pos.push_partner(partner.exportAsJSON());
-+            this.pos.get('selectedOrder').addPartner({name:partner.name}); // NOTE: .id set asynchronously in .done()
-+            this.pos_widget.screen_selector.set_current_screen(this.next_screen);
-+        },
-+        renderElement: function() {
-+            this._super();
-+            this.updatePartnerCreateSummary();
-+        },
-+        updatePartnerCreateSummary: function() {
-+          var partner = this.pos.get('selectedPartner');
-+          if(partner){
-+            console.log("[PartnerCreateScreenWidget] [updatePartnerCreateSummary] partner=",partner.get('name'));
-+            this.$('#partner-create-name').html(partner.get('name'));
-+           }
-+        },
-+    });
-+
-     module.PaymentScreenWidget = module.ScreenWidget.extend({
-         template: 'PaymentScreenWidget',
-         back_screen: 'products',
-diff --git a/static/src/js/widgets.js b/static/src/js/widgets.js
-index 874c387..9b6c120 100644
---- a/static/src/js/widgets.js
-+++ b/static/src/js/widgets.js
-@@ -282,6 +282,27 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
-         },
-     });
-+    module.PartnerWidget = module.PosBaseWidget.extend({
-+        template: 'PartnerWidget',
-+        init: function(parent, options) {
-+            this._super(parent,options);
-+            this.model = options.model;
-+            this.next_screen = options.next_screen; //when a partner is clicked, this screen is set
-+            this.click_partner_action = options.click_partner_action; 
-+        },
-+        // returns the url of the partner thumbnail
-+        renderElement: function() {
-+            this._super();
-+            this.$('img').replaceWith(this.pos_widget.image_cache.get_image(this.model.get_image_url()));
-+            var self = this;
-+            $("a", this.$el).click(function(e){
-+                if(self.click_partner_action){
-+                    self.click_partner_action(self.model);
-+                }
-+            });
-+        },
-+    });
-+
-     module.ProductWidget = module.PosBaseWidget.extend({
-         template: 'ProductWidget',
-         init: function(parent, options) {
-@@ -289,6 +310,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
-             this.model = options.model;
-             this.model.attributes.weight = options.weight;
-             this.next_screen = options.next_screen; //when a product is clicked, this screen is set
-+            //this.back_screen = options.back_screen;
-             this.click_product_action = options.click_product_action; 
-         },
-         // returns the url of the product thumbnail
-@@ -465,6 +487,79 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
-     module.CategoryButton = module.PosBaseWidget.extend({
-     });
-+
-+    module.PartnerSearchWidget = module.PosBaseWidget.extend({
-+        template: 'PartnerSearchWidget',
-+        init: function(parent, options){
-+            var self = this;
-+            this._super(parent,options);
-+        },
-+
-+        get_image_url: function(partner){
-+            return instance.session.url('/web/binary/image', {model: 'res.partner', field: 'image', id: partner.id});
-+        },
-+
-+        renderElement: function(){
-+            var self = this;
-+            this._super();
-+            this.filter();
-+        },
-+        reset_filter: function(){
-+            this.renderElement();
-+            this.filter();
-+        },
-+        filter: function(category){
-+            var self = this;
-+
-+            var partners = self.pos.db.get_partner_list();
-+            self.pos.get('partners').reset(partners);
-+
-+            // filter the partners according to the search string
-+            this.$('.searchbox input').keyup(function(){
-+                query = $(this).val();
-+                if(query){
-+                    console.log("[PartnerSearchWidget] [filter]: query:", query);
-+                    var partners = self.pos.db.search_partner(query);
-+                    self.pos.get('partners').reset(partners);
-+                    //if(partners.length == 0) {
-+                      console.log("[PartnerSearchWidget] [filter]: no result");
-+                      self.query = query;
-+                      self.$('.search-create').fadeIn();
-+                    //}else{
-+                    //  self.$('.search-create').fadeOut();
-+                    //}
-+                    self.$('.search-clear').fadeIn();
-+                }else{
-+                    var partners = self.pos.db.get_partner_list();
-+                    self.pos.get('partners').reset(partners);
-+                    self.$('.search-clear').fadeOut();
-+                    self.$('.search-create').fadeOut();
-+                }
-+            });
-+
-+            //this.$('.searchbox input').click(function(){}); //Why ???
-+
-+            //reset the search when clicking on reset
-+            this.$('.search-clear').click(function(){
-+                var partners = self.pos.db.get_partner_list();
-+                self.pos.get('partners').reset(partners);
-+                self.$('.searchbox input').val('').focus();
-+                self.$('.search-clear').fadeOut();
-+                self.$('.search-create').fadeOut();
-+            });
-+            this.$('.search-create').click(function(){
-+                var partners = self.pos.db.get_partner_list();
-+                self.pos.get('partners').reset(partners);
-+                self.$('.searchbox input').val('').focus();
-+                self.$('.search-clear').fadeOut();
-+                self.$('.search-create').fadeOut();
-+                console.log("[PartnerSearchWidget] [filter] [search-create] query=",self.query);
-+                self.pos.add_new_partner({name:self.query});
-+                self.pos_widget.screen_selector.set_current_screen('partner_create');
-+            });
-+        },
-+    });
-+    
-     module.ProductCategoriesWidget = module.PosBaseWidget.extend({
-         template: 'ProductCategoriesWidget',
-         init: function(parent, options){
-@@ -588,7 +683,61 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
-             });
-         },
-     });
-+    
-+    module.PartnerListWidget = module.ScreenWidget.extend({
-+        template:'PartnerListWidget',
-+        init: function(parent, options) {
-+            var self = this;
-+            this._super(parent,options);
-+            this.model = options.model;
-+            this.partnerwidgets = [];
-+            //this.weight = options.weight || 0;
-+            this.show_scale = options.show_scale || false;
-+            this.next_screen = options.next_screen || false;
-+            this.click_partner_action = options.click_partner_action;
-+            this.pos.get('partners').bind('reset', function(){
-+                self.renderElement();
-+            });
-+        },
-+        renderElement: function() {
-+            var self = this;
-+            this._super();
-+
-+            // free subwidgets  memory from previous renders
-+            for(var i = 0, len = this.partnerwidgets.length; i < len; i++){
-+                this.partnerwidgets[i].destroy();
-+            }
-+            this.partnerwidgets = []; 
-+            if(this.scrollbar){
-+                this.scrollbar.destroy();
-+            }
-+            var partners = this.pos.get('partners').models || [];
-+            for(var i = 0, len = partners.length; i < len; i++){
-+                                                              //console.log("[PartnerListWidget] [renderElement]: partners[i].get('name')=",partners[i].get('name'));
-+                var partner = new module.PartnerWidget(self, {
-+                    model: partners[i],
-+                    click_partner_action: this.click_partner_action,
-+                });
-+                this.partnerwidgets.push(partner);
-+                partner.appendTo(this.$('.partner-list'));
-+            }
-+            this.scrollbar = new module.ScrollbarWidget(this,{
-+                target_widget:   this,
-+                target_selector: '.partner-list-scroller',
-+                on_show: function(){
-+                    self.$('.partner-list-scroller').css({'padding-right':'62px'},100);
-+                },
-+                on_hide: function(){
-+                    self.$('.partner-list-scroller').css({'padding-right':'0px'},100);
-+                },
-+            });
-+
-+            this.scrollbar.replace(this.$('.placeholder-ScrollbarWidget'));
-+
-+        },
-+    });
-+    
-     module.ProductListWidget = module.ScreenWidget.extend({
-         template:'ProductListWidget',
-         init: function(parent, options) {
-@@ -599,6 +748,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
-             this.weight = options.weight || 0;
-             this.show_scale = options.show_scale || false;
-             this.next_screen = options.next_screen || false;
-+            //this.back_screen = options.back_screen || false;
-             this.click_product_action = options.click_product_action;
-             this.pos.get('products').bind('reset', function(){
-@@ -899,6 +1049,9 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
-             // --------  Screens ---------
-+            this.partner_screen = new module.PartnerScreenWidget(this,{});
-+            this.partner_screen.appendTo($('#rightpane'));
-+
-             this.product_screen = new module.ProductScreenWidget(this,{});
-             this.product_screen.appendTo($('#rightpane'));
-@@ -908,6 +1061,9 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
-             this.payment_screen = new module.PaymentScreenWidget(this, {});
-             this.payment_screen.appendTo($('#rightpane'));
-+            this.partner_create_screen = new module.PartnerCreateScreenWidget(this, {});
-+            this.partner_create_screen.appendTo($('#rightpane'));
-+
-             this.welcome_screen = new module.WelcomeScreenWidget(this,{});
-             this.welcome_screen.appendTo($('#rightpane'));
-@@ -993,6 +1149,8 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
-                     'scale':    this.scale_screen,
-                     'receipt' : this.receipt_screen,
-                     'welcome' : this.welcome_screen,
-+                    'partner' : this.partner_screen,
-+                    'partner_create' : this.partner_create_screen,
-                 },
-                 popup_set:{
-                     'help': this.help_popup,
-@@ -1003,7 +1161,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
-                     'choose-receipt': this.choose_receipt_popup,
-                 },
-                 default_client_screen: 'welcome',
--                default_cashier_screen: 'products',
-+                default_cashier_screen: 'partner',
-                 default_mode: this.pos.iface_self_checkout ?  'client' : 'cashier',
-             });
-diff --git a/static/src/xml/pos.xml b/static/src/xml/pos.xml
-index 3da76ec..6aabe35 100644
---- a/static/src/xml/pos.xml
-+++ b/static/src/xml/pos.xml
-@@ -185,6 +185,49 @@
-         </div>
-     </t>
-+    <t t-name="PartnerSearchWidget">
-+        <header>
-+            <div class="searchbox">
-+                <input placeholder="Search Products" />
-+                <img class="search-create" src="/point_of_sale/static/src/img/validate-icon.png" />
-+                <img class="search-clear" src="/point_of_sale/static/src/img/search_reset.gif" />
-+            </div>
-+        </header>
-+    </t>
-+
-+    <t t-name="PartnerListWidget">
-+        <div class='partner-list-container'>
-+            <div class="partner-list-scroller">
-+                <ol id="partners-screen-ol" class="partner-list">
-+                </ol>
-+            </div>
-+            <div class="shadow-top"></div>
-+            <span class="placeholder-ScrollbarWidget" />
-+        </div>
-+    </t>
-+
-+    <t t-name="PartnerScreenWidget">
-+        <div id="partners-screen" class="screen">
-+            <table class="layout-table">
-+
-+                <tr class="header-row">
-+                    <td class="header-cell">
-+                        <span class="placeholder-PartnerSearchWidget" />
-+                    </td>
-+                </tr>
-+
-+                <tr class="content-row">
-+                    <td class="content-cell">
-+                        <div class="content-container">
-+                            <span class="placeholder-PartnerListWidget" />
-+                        </div>
-+                    </td>
-+                </tr>
-+
-+            </table>
-+        </div>
-+    </t>
-+
-     <t t-name="ScaleScreenWidget">
-         <div class="scale-screen screen">
-             <header><h2>Product Weighting</h2></header>
-@@ -210,6 +253,41 @@
-         </div>
-     </t>
-+    <t t-name="PartnerCreateScreenWidget">
-+        <div id="partner-create-screen" class="screen">
-+            <header><h2>Partner Create</h2></header>
-+            <div class="pos-step-container">
-+                <div class="pos-partner-create-container">
-+                    <br />
-+                    <div class="header">
-+                        <span class="left-block">
-+                            Name:
-+                        </span>
-+                        <span class='right-block' id="partner-create-name"></span>
-+                                                                                              <!--
-+                        <span class="left-block">
-+                            Name2:
-+                        </span>
-+                        <span class='right-block'>
-+                          <t t-if="widget.model.get('name')"/>
-+                        </span>
-+                                                                                              -->
-+                    </div>
-+                    <!--<table id="partner-createlines">
-+                    </table>-->
-+                    <div class="footer">
-+                        <div class="infoline">
-+                            <span class='left-block'>
-+                                Address:
-+                            </span>
-+                            <span class='right-block' id="partner-create-address"></span>
-+                        </div>
-+                    </div>
-+                </div>
-+            </div>
-+        </div>
-+    </t>
-+
-     <t t-name="PaymentScreenWidget">
-         <div id="payment-screen" class="screen">
-             <header><h2>Payment</h2></header>
-@@ -370,6 +448,30 @@
-         </div>
-     </t>
-+    <t t-name="PartnerWidget">
-+        <li class='partner'>
-+            <a href="#">
-+                <div class="partner-img">
-+                    <img src='' /> <!-- the partner thumbnail -->
-+                    <!--<t t-if="!widget.model.get('to_weight')">
-+                        <span class="price-tag">
-+                            <t t-esc="widget.format_currency(widget.model.get('price'))"/>
-+                        </span>
-+                    </t>
-+                    <t t-if="widget.model.get('to_weight')">
-+                        <span class="price-tag">
-+                            <t t-esc="widget.format_currency(widget.model.get('price'))+'/Kg'"/>
-+                        </span>
-+                    </t>-->
-+                </div>
-+                <div class="partner-name">
-+                    <t t-esc="widget.model.get('name')"/>
-+                    <!--(<t t-esc="widget.model.get('id')"/>)-->
-+                </div>
-+            </a>
-+        </li>
-+    </t>
-+
-     <t t-name="ProductWidget">
-         <li class='product'>
-             <a href="#">