[WIP] point_of_sale: improved performance of the product screen & order widget +...
authorFrédéric van der Essen <fva@openerp.com>
Mon, 2 Dec 2013 16:21:44 +0000 (17:21 +0100)
committerFrédéric van der Essen <fva@openerp.com>
Mon, 2 Dec 2013 16:21:44 +0000 (17:21 +0100)
bzr revid: fva@openerp.com-20131202162144-6se3s0wjwz0jdfih

addons/point_of_sale/controllers/main.py
addons/point_of_sale/static/src/css/pos.css
addons/point_of_sale/static/src/css/pos_nohover.css [deleted file]
addons/point_of_sale/static/src/js/TODO.txt [deleted file]
addons/point_of_sale/static/src/js/models.js
addons/point_of_sale/static/src/js/screens.js
addons/point_of_sale/static/src/js/widget_scrollbar.js [deleted file]
addons/point_of_sale/static/src/js/widgets.js
addons/point_of_sale/static/src/xml/pos.xml

index 87244a9..705fd85 100644 (file)
@@ -56,7 +56,6 @@ class PosController(http.Controller):
     @http.route('/pos/web', type='http', auth='none')
     def a(self, debug=False, **k):
 
-        print '\nDEBUG',debug,'\n'
 
         js_list = manifest_list('js',db=request.db, debug=debug)
         css_list =   manifest_list('css',db=request.db, debug=debug)
@@ -64,8 +63,6 @@ class PosController(http.Controller):
         print css_list
         print js_list
 
-        js_list = [ js for js in js_list if 'select2' not in js ]
-
         js = "\n".join('<script type="text/javascript" src="%s"></script>' % i for i in js_list)
         css = "\n".join('<link rel="stylesheet" href="%s">' % i for i in css_list)
         r = html_template % {
@@ -73,15 +70,18 @@ class PosController(http.Controller):
             'css': css,
             'modules': simplejson.dumps(module_boot(request.db)),
             'init': """
-                     window.navigator.standalone = true;
+                     console.log('LOADED');
                      var wc = new s.web.WebClient();
-                     wc.appendTo($(document.body));
                      wc.show_application = function(){
+                         console.log('SHOW APPLICATION');
                          wc.action_manager.do_action("pos.ui");
                      };
                      wc.show_login = function(){
+                         console.log('SHOW LOGIN');
                          window.location.href = '/';
                      }
+                     wc.appendTo($(document.body));
+                     console.log('APPENDED');
                      """
         }
         return r
index 1d0cbb4..9d728f8 100644 (file)
     background: rgba(0,0,0,0.82);
     color: white;
     padding-bottom: 10px;
-    cursor:move;
+    cursor: move;
+    -webkit-transform: translate3d(0,0,0);
 }
 .pos .debug-widget .toggle{
     position: absolute;
diff --git a/addons/point_of_sale/static/src/css/pos_nohover.css b/addons/point_of_sale/static/src/css/pos_nohover.css
deleted file mode 100644 (file)
index 6f24abd..0000000
+++ /dev/null
@@ -1,1168 +0,0 @@
-@font-face{
-    font-family: 'Inconsolata';
-    src: url(../fonts/Inconsolata.otf);
-}
-
-.point-of-sale {
-    padding: 0;
-    margin: 0;
-    background-color: #f0eeee;
-    font-family: "Lucida Grande", Helvetica, Verdana, Arial;
-    color: #555555;
-    font-size: 12px;
-    position: absolute;
-    left: 0;
-    top: 0;
-    width: 100%;
-    height: 100%;
-    -webkit-user-select: none;
-       -moz-user-select: none;
-            user-select: none;
-}
-
-.point-of-sale .shadow-top{
-    position: absolute;
-    top:0;
-    left:0;
-    right:0;
-    height:10px;
-    background: -webkit-linear-gradient(top,rgba(0,0,0,0.09),rgba(0,0,0,0));
-    background:    -moz-linear-gradient(top,rgba(0,0,0,0.09),rgba(0,0,0,0));
-    background:     -ms-linear-gradient(top,rgba(0,0,0,0.09),rgba(0,0,0,0));
-    background:         linear-gradient(top,rgba(0,0,0,0.09),rgba(0,0,0,0));
-}
-
-.point-of-sale .darker-shadow-top{
-    position: absolute;
-    top:0;
-    left:0;
-    right:0;
-    height:10px;
-    background: -webkit-linear-gradient(top,rgba(0,0,0,0.15),rgba(0,0,0,0));
-    background:    -moz-linear-gradient(top,rgba(0,0,0,0.15),rgba(0,0,0,0));
-    background:     -ms-linear-gradient(top,rgba(0,0,0,0.15),rgba(0,0,0,0));
-    background:         linear-gradient(top,rgba(0,0,0,0.15),rgba(0,0,0,0));
-}
-
-/*  ********* The black loading screen ********* */
-
-.point-of-sale .loader{
-    background-color: #222;
-    position:absolute;
-    left:0px;
-    top:0px;
-    width:100%;
-    height:100%;
-    z-index: 999;
-    text-align: center;
-}
-.point-of-sale .loader img{
-    position:absolute;
-    top:50%;
-    left:50%;
-}
-
-/*  ********* Generic element styling  ********* */
-
-.point-of-sale a {
-    text-decoration: none;
-    color: #555555;
-}
-.point-of-sale button, .point-of-sale a.button {
-    display: inline-block;
-    cursor: pointer;
-    padding: 4px 10px;
-    font-size: 11px;
-    border: 1px solid #cacaca;
-    border-radius: 4px;
-    background: #e2e2e2;
-    background: -webkit-gradient(linear, left top, left bottom, from(#f0f0f0), to(#e2e2e2));
-    background: -moz-linear-gradient(#f0f0f0, #e2e2e2);
-    background:  -ms-linear-gradient(#f0f0f0, #e2e2e2);
-    background:      linear-gradient(#f0f0f0, #e2e2e2);
-}
-.point-of-sale ul, .point-of-sale ol {
-    padding: 0;
-    margin: 0;
-}
-.point-of-sale li {
-    list-style-type: none;
-}
-.point-of-sale .pos-right-align {
-    text-align: right;
-}
-.point-of-sale .pos-right-align input {
-    text-align: right;
-    border: 1px solid #cecbcb;
-    border-radius: 4px;
-}
-
-/*  ********* The black header bar ********* */
-
-
-.point-of-sale #topheader {
-    position:absolute;
-    left:0;
-    top:0;
-    width: 100%;
-    height: 33px;
-    margin:0;
-    padding:0;
-    color: gray;
-    border-top: solid 1px #d3d3d3;
-    border-bottom: solid 1px black;
-    background: #393939;
-    background: -moz-linear-gradient(#7b7979, #393939);
-    background: -webkit-gradient(linear, left top, left bottom, from(#7b7979), to(#393939));
-}
-
-/*  a) The left part of the top-bar */
-
-.point-of-sale #branding{
-    position: absolute;
-    display: table-cell;
-    left:0;
-    top:0;
-    width:439px;
-    height:100%;
-    margin:0;
-    padding:0;
-    border-right: 1px solid #373737;
-    text-align:left;
-    line-height:100%;
-    vertical-align: middle;
-}
-.point-of-sale #branding img {
-    height: 32px;
-    width: 116px;
-    margin-left:5px;
-    vertical-align:middle;
-} 
-.point-of-sale #branding .username{
-    float:right;
-    color:#DDD;
-    font-size:16px;
-    margin-right:32px;
-    margin-top:10px;
-    font-style:italic;
-}
-
-/*  b) The right part of the top-bar */
-
-.point-of-sale #rightheader {
-    position: absolute;
-    left:440px;
-    right:0;
-    top:0;
-    height:100%;
-}
-
-.point-of-sale #rightheader button {
-    color: #273072;
-    height:27px;
-    margin:3px;
-    margin-right:0px;
-    border: 1px solid #353A7E;
-    background: #7f82ac;
-    background: -webkit-gradient(linear, left top, left bottom, from(#b2b3d7), to(#7f82ac));
-    background: -moz-linear-gradient(#b2b3d7, #7f82ac);
-    background:  -ms-linear-gradient(#b2b3d7, #7f82ac);
-    background:      linear-gradient(#b2b3d7, #7f82ac);
-}
-
-.point-of-sale #rightheader button.neworder-button {
-    width: 32px;
-    margin-left:4px;
-    margin-right:4px;
-}
-
-.point-of-sale div#order-selector {
-    display: inline;
-}
-.point-of-sale ol#orders {
-    display: inline;
-}
-.point-of-sale li.order-selector-button {
-    display: inline;
-}
-.point-of-sale li.selected-order button {
-    font-weight: 900;
-}
-
-/*  c) The session buttons */
-
-.point-of-sale #rightheader .header-button{
-    float:right;
-    height:32px;
-    padding-left:10px;
-    padding-right:10px;
-    border-right:  1px solid #3a3a3a;
-    border-left:  1px solid #3a3a3a;
-    color:#DDD;
-    line-height:32px;
-    text-align:center;
-    cursor: pointer;
-
-    -webkit-transition-property: background;
-    -webkit-transition-duration: 0.2s;
-    -webkit-transition-timing-function: ease-out;
-}
-.point-of-sale #rightheader .header-button:last-child{
-    border-left:  1px solid #3a3a3a;
-}
-
-/*  c) The notifications indicator */
-
-.point-of-sale .oe_pos_synch-notification{
-    float:right; 
-    color: rgba(255,255,255,0.4);
-    padding: 8px;
-    line-height:16px;
-    font-size:16px;
-    vertical-align:middle;
-    font-style: italic;
-    cursor:pointer;
-}
-
-.point-of-sale .oe_pos_synch-notification .oe_status_red{
-    display:inline-block;
-    cursor:pointer;
-    width:16px; height:16px;
-    background: url("../img/gtk-no.png") no-repeat ;
-}
-
-.point-of-sale .oe_pos_synch-notification .oe_status_green{
-    display:inline-block;
-    width:16px; height:16px;
-    background: url("../img/gtk-yes.png") no-repeat;
-}
-
-/*  ********* Contains everything below the  bar ********* */
-
-.point-of-sale #content {
-    width: 100%;
-    position: absolute;
-    top: 35px;
-    bottom: 0;
-    background: #F0EEEE;
-}
-
-/*  ********* The leftpane contains the order, numpad and paypad ********* */
-
-.point-of-sale #leftpane {
-    -webkit-box-sizing: border-box;
-       -moz-box-sizing: border-box;
-        -ms-box-sizing: border-box;
-            box-sizing: border-box;
-    position:absolute;
-    left:0;
-    width:440px;
-    top:0px;
-    /*bottom:105px;*/
-    bottom:0;
-    border-right: solid 1px #CECBCB;
-    background-color: white;
-}
-.point-of-sale #leftpane footer {
-    position: absolute;
-    bottom: 0;
-    left: 0;
-    width: 100%;
-    background: #F0EEEE;
-    white-space: nowrap;
-}
-
-/*  ********* The paypad contains the payment buttons ********* */
-
-.point-of-sale #paypad {
-    padding: 8px 4px 8px 8px;
-    display: inline-block;
-    text-align: center;
-    vertical-align: top;
-}
-.point-of-sale #paypad button {
-    height: 54px;
-    width: 208px;
-    margin: 0 -3px;
-    font-weight: bold;
-    vertical-align: middle;
-    color: #555555;
-    border-top: 1px solid #efefef;
-    font-size: 14px;
-}
-
-/*  ********* The Numpad ********* */
-
-.point-of-sale #numpad {
-    padding: 8px 8px 8px 4px;
-    display: inline-block;
-    text-align: center;
-}
-.point-of-sale #numpad button {
-    height: 54px;
-    width: 54px;
-    margin: 0 -3px;
-    font-weight: bold;
-    vertical-align: middle;
-    color: #555555;
-    border-top: 1px solid #efefef;
-}
-.point-of-sale #numpad .selected-mode {
-    color: white;
-    background: #7f82ac;
-    background: -webkit-gradient(linear, left top, left bottom, from(#9d9fc5), to(#7f82ac));
-    background: -moz-linear-gradient(#9d9fc5, #7f82ac);
-    background:  -ms-linear-gradient(#9d9fc5, #7f82ac);
-    background:      linear-gradient(#9d9fc5, #7f82ac);
-}
-.point-of-sale .input-button {
-    font-size: 24px;
-}
-.point-of-sale .mode-button, .point-of-sale #numpad-delete, .point-of-sale #numpad-minus {
-    font-size: 14px;
-}
-
-/*  ********* The right pane contains the screens and headers ********* */
-
-.point-of-sale #rightpane {
-    position: absolute;
-    top: 0;
-    /*bottom: 105px;*/
-    bottom:0;
-    left: 440px;
-    right: 0;
-    vertical-align: top;
-}
-
-.point-of-sale #rightpane header {
-    padding: 0;
-    height: 32px;
-    border-bottom: 1px solid #cecbcb;
-    background: #d3d3d3;
-    background: -moz-linear-gradient(white, #d3d3d3);
-    background: -webkit-gradient(linear, left top, left bottom, from(white), to(#d3d3d3));
-}
-
-/*  ********* The product list  ********* */
-
-.point-of-sale .product-list {
-    padding:10px;
-}
-
-.point-of-sale .product-list-scroller{
-    -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 .product-list-container {
-    position:absolute;
-    top:0px;
-    bottom:0px;
-    left:0px;
-    right:0px;
-    background: #eeedff;
-}
-
-/*  a) the product list navigation bar */
-
-.point-of-sale .breadcrumb li {
-    float: left;
-    line-height: 32px;
-    height: 32px;
-}
-.point-of-sale .breadcrumb li:last-child {
-    padding-right: 3px;
-    border-right: 1px solid #c5c5c5;
-}
-.point-of-sale .breadcrumb a {
-    display: inline-block;
-    padding: 0 9px;
-    vertical-align: top;
-    text-shadow: #f7f7f7 0 1px 1px;
-    color: #555555;
-    font-weight: bold;
-}
-.point-of-sale .bc-arrow {
-    height: 33px;
-}
-.point-of-sale .homeimg {
-    width: 19px;
-    height: 19px;
-    margin: 6px 0;
-}
-
-/*  b) the search box */
-
-.point-of-sale .searchbox {
-    position: absolute;
-    right: 2px;
-}
-.point-of-sale .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 .search-clear {
-    position: absolute;
-    top: 11px;
-    right: 11px;
-    cursor: pointer;
-    display: none;
-}
-
-/*  c) the categories list */
-
-.point-of-sale #categories {
-    /*background:#f0f0f0;*/
-    position: relative;
-    background-image: url('../img/bg.png');
-    border-bottom: 1px solid #cecbcb;
-}
-.point-of-sale #categories .white-gradient{
-    position: absolute;
-    top:50%;
-    left:0;
-    right:0;
-    bottom:0;
-    background: -webkit-linear-gradient(bottom,rgba(255,255,255,0.5),rgba(255,255,255,0));
-    background:    -moz-linear-gradient(bottom,rgba(255,255,255,0.5),rgba(255,255,255,0));
-    background:     -ms-linear-gradient(bottom,rgba(255,255,255,0.5),rgba(255,255,255,0));
-    background:         linear-gradient(bottom,rgba(255,255,255,0.5),rgba(255,255,255,0));
-}
-.point-of-sale #categories h4 {
-    display: inline-block;
-    margin: 9px 5px;
-}
-
-.point-of-sale .category-list{
-    padding:10px;
-}
-/*  d) the category button */
-
-.point-of-sale .category-button {
-    position: relative;
-    vertical-align: top;
-    display: inline-block;
-    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(0,0,0,0.2);
-       -moz-box-shadow: 0px 1px 8px rgba(0,0,0,0.2);
-            box-shadow: 0px 1px 8px rgba(0,0,0,0.2);
-    cursor: pointer;
-}
-
-.point-of-sale .category-simple-button{
-    position: relative;
-    display: inline-block;
-    font-size: 14px;
-    margin-right:10px;
-    padding:5px;
-
-    cursor: pointer;
-    border: 1px solid #cacaca;
-    border-radius: 4px;
-
-    background: #e2e2e2;
-    background: -webkit-linear-gradient(#f0f0f0, #e2e2e2);
-    background:    -moz-linear-gradient(#f0f0f0, #e2e2e2);
-    background:     -ms-linear-gradient(#f0f0f0, #e2e2e2);
-    background:         linear-gradient(#f0f0f0, #e2e2e2);
-    -webkit-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
-       -moz-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
-            box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
-}
-
-.point-of-sale .category-button .category-img {
-    position: relative;
-    width: 120px;
-    height: 100px;
-    text-align: center;
-    cursor: pointer;
-}
-
-.point-of-sale .category-button .category-img img {
-    max-height: 100px;
-    max-width:  120px;
-}
-
-.point-of-sale .category-button .category-name {
-    position: absolute;
-    -webkit-box-sizing: border-box;
-       -moz-box-sizing: border-box;
-        -ms-box-sizing: border-box;
-            box-sizing: border-box;
-    top:auto;
-    left: 2px;
-    right: 2px;
-    bottom: 2px;
-    background: #8a89ba; /*-webkit-linear-gradient(-90deg,rgba(138,137,186,0),rgba(138,137,186,1), rgba(138,137,186,1));*/
-    padding: 3px;
-    /*color:#8a89ba;*/
-    color: #FFF;
-    cursor: pointer;
-    border-radius: 3px;
-}
-
-/*  e) the product */
-
-.point-of-sale .product {
-    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 .product .product-img {
-    position: relative;
-    width: 120px;
-    height: 100px;
-    background: white;
-    text-align: center;
-}
-
-.point-of-sale .product .product-img img {
-    max-height: 100px;
-    max-width:  120px;
-}
-
-.point-of-sale .product .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 .product .product-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;
-}
-
-
-/*  ********* The Screens  ********* */
-
-.point-of-sale .screen {
-    position:absolute;
-    text-align: center;
-    top:0px;
-    bottom:0px;
-    width:100%;
-}
-.point-of-sale .screen header h2 {
-    margin-top: 0px;
-    padding-top: 7px;
-}
-.point-of-sale .screen p{
-    font-size: 18px;
-}
-
-/* a) Layout for the Product Screen */
-
-.point-of-sale .screen .layout-table {
-    border:none;
-    width:100%;
-    height:100%;
-}
-
-.point-of-sale .screen .header-row {
-    border:none;
-    width:100%;
-    height:0px;
-}
-
-.point-of-sale .screen .header-cell{
-    border:none;
-    width:100%;
-    height:0px;
-}
-.point-of-sale .screen .content-row {
-    width:100%;
-    height:100%;
-}
-.point-of-sale .screen .content-cell{
-    width:100%;
-}
-.point-of-sale .screen .content-cell .content-container{
-    height:100%;
-    position:relative;
-}
-
-/* b) The payment screen */
-
-.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-payment-container {
-    text-align: left;
-    min-width: 500px;
-}
-.point-of-sale .pos-payment-container .left-block{
-    display: inline-block;
-    width:49%;
-    margin:0;
-    padding:0;
-    text-align:left;
-}
-.point-of-sale .pos-payment-container .header{
-    margin-top: 50px;
-    margin-bottom:20px;
-    font-weight: bold;
-}
-.point-of-sale .pos-payment-container .infoline{
-    margin-top:5px;
-    margin-bottom:5px;
-}
-.point-of-sale .pos-payment-container .right-block{
-    display: inline-block;
-    width:49%;
-    margin:0;
-    padding:0;
-    text-align:right;
-}
-.point-of-sale .pos-payment-container table {
-    width: 100%;
-    margin-bottom: 20px;
-}
-.point-of-sale .pos-payment-container td {
-    vertical-align: middle;
-}
-.point-of-sale .pos-payment-container .paymentline-type {
-    font-size: 1em;
-    font-weight: bold;
-    margin-right:10px;
-}
-
-/* c) The receipt screen */
-
-.point-of-sale .pos-receipt-container {
-    font-size: 0.75em;
-}
-
-.point-of-sale .pos-sale-ticket {
-    text-align: left;
-    width: 300px;
-    background-color: white;
-    margin: 20px;
-    padding: 15px;
-    padding-bottom:30px;
-    display: inline-block;
-    font-family: "Inconsolata";
-    -webkit-box-shadow: 0px 5px 16px rgba(0,0,0, 0.3);
-       -moz-box-shadow: 0px 5px 16px rgba(0,0,0, 0.3);
-            box-shadow: 0px 5px 16px rgba(0,0,0, 0.3);
-}
-.point-of-sale .pos-sale-ticket .emph{
-    font-size: 20px;
-    margin:5px;
-}
-.point-of-sale .pos-sale-ticket table {
-    width: 100%;
-    border: 0;
-}
-.point-of-sale .pos-sale-ticket table td {
-    border: 0;
-}
-
-@media print {
-    .point-of-sale #topheader, .point-of-sale #leftpane {
-        display: none !important;
-    }
-    .point-of-sale #content {
-        top: 0px !important;
-    }
-    .point-of-sale #rightpane {
-        left: 0px !important;
-        background-color: white;
-    }
-    #receipt-screen header {
-        display: none !important;
-    }
-    #receipt-screen {
-        text-align: left;
-    }
-    .pos-sale-ticket {
-        margin: 0;
-    }
-}
-
-/* d) The Scale screen */
-
-.point-of-sale .scale-screen .display{
-    position:relative;
-    width:600px;
-    height:190px;
-    margin-top: 100px;
-    margin-left: auto;
-    margin-right: auto;
-}
-
-.point-of-sale .scale-screen .product-picture {
-    -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;
-    -webkit-box-shadow: 0px 1px 8px rgba(0,0,0,0.2);
-       -moz-box-shadow: 0px 1px 8px rgba(0,0,0,0.2);
-            box-shadow: 0px 1px 8px rgba(0,0,0,0.2);
-}
-
-.point-of-sale .scale-screen .product-picture img{
-    vertical-align: middle;
-    cursor:pointer;
-}
-
-.point-of-sale .scale-screen .product-picture .product-price{
-    position: absolute;
-    top:8px;
-    right:8px;
-    width:auto;
-    height:auto;
-    line-height:1;
-    color:white;
-    background: #7f82ac;
-    padding: 2px 5px;
-    border-radius: 3px;
-    cursor:pointer;
-}
-
-.point-of-sale .scale-screen .product-name {
-    position: absolute;
-    left:0;
-    top:20px;
-    height:50px;
-    font-size:40px;
-    line-height:50px;
-    text-align:right;
-    right:225px;
-}
-.point-of-sale .scale-screen .weight{
-    position: absolute;
-    left:0;
-    height:90px;
-    bottom:15px;
-    right:220px;
-    padding:5px;
-}
-.point-of-sale .scale-screen .weight p{
-    display: inline-block;
-    text-align:right;
-    line-height: 90px;
-    font-size: 80px;
-    width:100%;
-    height:100%;
-    margin:0;
-    font-family: "Inconsolata";
-}
-
-/* e) The Welcome Screen */
-.point-of-sale .goodbye-message{
-    position: absolute;
-    left:50%;
-    top:30%;
-    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;
-    -webkit-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3);
-       -moz-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3);
-        -ms-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3);
-    z-index:1150;
-}
-
-/*  ********* The OrderWidget  ********* */
-
-.point-of-sale .order-container{
-    position: absolute;
-    top: 0px;
-    bottom: 232px;
-    width:100%;
-    background: #F0EEEE;
-}
-
-.point-of-sale .order-scroller{
-    width:100%;
-    height:100%;
-    overflow:hidden;
-}
-
-.point-of-sale .order{
-    background: #F00;
-    background: -webkit-linear-gradient(0deg,rgba(245,245,245,1),rgba(255,255,255,1), rgba(245,245,245,1));
-    background:    -moz-linear-gradient(0deg,rgba(245,245,245,1),rgba(255,255,255,1), rgba(245,245,245,1));
-    background:     -ms-linear-gradient(0deg,rgba(245,245,245,1),rgba(255,255,255,1), rgba(245,245,245,1));
-    background:         linear-gradient(0deg,rgba(245,245,245,1),rgba(255,255,255,1), rgba(245,245,245,1));
-    padding-bottom:15px;
-    padding-top:15px;
-    margin-left:16px;
-    margin-right:16px;
-    margin-top:16px;
-    margin-bottom:16px;
-    font-size:16px;
-    -webkit-box-shadow: 0px 5px 16px rgba(0,0,0, 0.3);
-       -moz-box-shadow: 0px 5px 16px rgba(0,0,0, 0.3);
-            box-shadow: 0px 5px 16px rgba(0,0,0, 0.3);
-
-}
-
-.point-of-sale .order .empty{
-    text-align:center;
-    margin-top: 15px;
-    margin-bottom: 5px;
-    color:#999;
-    font-weight: normal;
-}
-
-.point-of-sale .order .summary{
-    width:100%;
-    text-align:right;
-    font-weight: bold;
-    margin-top:20px;
-    margin-bottom:10px;
-}
-.point-of-sale .order .summary .line{
-    margin-right:15px;
-    padding-top:5px;
-    border-top: solid 2px;
-    border-color:#777;
-}
-.point-of-sale .order .summary .line.empty{
-    border-color:#BBB;
-    color:#999;
-}
-
-/*  ********* The OrderLineWidget  ********* */
-
-.point-of-sale .order .orderline{
-    width:100%;
-    margin:0px;
-    padding-top:3px;
-    padding-bottom:10px;
-    padding-left:15px;
-    padding-right:15px;
-    cursor: pointer;
-    -webkit-box-sizing: border-box;
-       -moz-box-sizing: border-box;
-        -ms-box-sizing: border-box;
-            box-sizing: border-box;
-    -webkit-transition: background 250ms ease-in-out;
-       -moz-transition: background 250ms ease-in-out;
-            transition: background 250ms ease-in-out;
-}
-
-.point-of-sale .order .orderline.selected{
-    background: rgba(140,143,183,0.2);
-    -webkit-transition: background 250ms ease-in-out;
-       -moz-transition: background 250ms ease-in-out;
-            transition: background 250ms ease-in-out;
-    cursor: default;
-}
-.point-of-sale .order .orderline .product-name{
-    padding:0;
-    display:inline-block;
-    font-weight: bold;
-    width:80%;
-    overflow:hidden;
-}
-.point-of-sale .order .orderline .price{
-    padding:0;
-    font-weight: bold;
-    float:right;
-}
-.point-of-sale .order .orderline .info-list{
-    color: #888;
-    margin-left:10px;
-}
-.point-of-sale .order .orderline .info-list em{
-    color: #777;
-    font-weight: bold;
-    font-style:normal;
-}
-
-/*  ********* The ActionBarWidget  ********* */
-
-.point-of-sale .pos-actionbar{
-    position:absolute;
-    left:    0;
-    bottom:  0px;
-    height: 105px;
-    width:  100%;
-    margin: 0;
-    background: #f5f5f5;    /*#ebebeb;*/
-    border-top: solid 1px #afafb6;
-    z-index:900;
-}
-
-.point-of-sale .pos-actionbar ul{
-    list-style:  none;
-}
-
-.point-of-sale .pos-actionbar-left-pane{
-    height: 100%;
-    width:  434px;
-    margin: 0px;
-    padding-left:3px;
-    padding-right:3px;
-    border-right: solid 1px #dfdfdf;
-    float:  left;
-}
-
-.point-of-sale .pos-actionbar-button-list{
-    height: 100%;
-    margin: 0px;
-    padding-left:3px;
-    padding-right:3px;
-    overflow:hidden;
-}
-
-.point-of-sale .pos-actionbar .button{
-    width: 90px;
-    height: 90px;
-    text-align:center;
-    margin:3px;
-    margin-top:6px;
-    float:left;
-
-    font-size:   14px;
-    font-weight: bold;
-
-    cursor: pointer;
-
-    border: 1px solid #cacaca;
-    border-radius: 4px;
-
-    background: #e2e2e2;
-    background: -webkit-linear-gradient(#f0f0f0, #e2e2e2);
-    background:    -moz-linear-gradient(#f0f0f0, #e2e2e2);
-    background:     -ms-linear-gradient(#f0f0f0, #e2e2e2);
-    background:         linear-gradient(#f0f0f0, #e2e2e2);
-    -webkit-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
-       -moz-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
-            box-shadow: 0px 2px 2px rgba(0,0,0, 0.1);
-}
-.point-of-sale .pos-actionbar .button .label{
-    margin-top: 37px;
-}
-.point-of-sale .pos-actionbar .button .icon{
-    margin-top: 10px;
-}
-
-.point-of-sale .pos-actionbar .button.disabled *{
-    opacity: 0.5;
-}
-
-.point-of-sale .pos-actionbar .button.rightalign{
-    float:right;
-}
-
-/*  ********* The PopupWidgets  ********* */
-
-.point-of-sale .modal-dialog{
-    position: absolute;
-    left: 0;
-    top: 0;
-    width: 100%;
-    height:100%;
-    background-color: rgba(0,0,0,0.5);
-    z-index:1000;
-}
-.point-of-sale .modal-dialog .popup{
-    position: absolute;
-    left:50%;
-    top:50%;
-    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;
-    -webkit-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3);
-       -moz-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3);
-        -ms-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3);
-    z-index:1200;
-}
-.point-of-sale .popup .footer{
-    position:absolute;
-    bottom:0;
-    left:0;
-    width:100%;
-    height:60px;
-    border-top: 1px solid #E0DDDD;
-}
-.point-of-sale .popup .button{
-    float:right;
-    width: 110px;
-    height: 40px;
-    line-height:40px;
-    text-align:center;
-    margin:3px;
-    margin-top:10px;
-    margin-right:10px;
-
-    font-size:   14px;
-    font-weight: bold;
-
-    cursor: pointer;
-
-    border: 1px solid #cacaca;
-    border-radius: 4px;
-
-    background: #e2e2e2;
-    background: -webkit-linear-gradient(#f0f0f0, #e2e2e2);
-    background:    -moz-linear-gradient(#f0f0f0, #e2e2e2);
-    background:     -ms-linear-gradient(#f0f0f0, #e2e2e2);
-    background:         linear-gradient(#f0f0f0, #e2e2e2);
-    -webkit-box-shadow: 0px 2px 2px rgba(0,0,0, 0.3);
-       -moz-box-shadow: 0px 2px 2px rgba(0,0,0, 0.3);
-            box-shadow: 0px 2px 2px rgba(0,0,0, 0.3);
-}
-
-.point-of-sale .popup .button.big-left{
-    position:absolute;
-    top: 120px;
-    left:40px;
-    width: 180px;
-    height: 180px;
-    line-height:180px;
-}
-
-.point-of-sale .popup .button.big-right{
-    position:absolute;
-    top: 120px;
-    right:40px;
-    width: 180px;
-    height: 180px;
-    line-height:180px;
-}
-
-/*  ********* The ScrollBarWidget  ********* */
-
-.point-of-sale .scrollbar{  
-    position:absolute;
-    top:7px;
-    right:7px;
-    width:48px;
-    bottom:7px;
-    background: rgba(0,0,0,0.1);
-
-}
-.point-of-sale .scrollbar .button{
-    width:100%;
-    height: 48px;
-    line-height: 38px;
-    text-align: center;
-    font-size:48px;
-    border-radius: 4px;
-    cursor: pointer;
-    -webkit-user-select: none;
-       -moz-user-select: none;
-        -ms-user-select: none;
-            user-select: none;
-    transition: all 250ms ease-in-out;
-}
-.point-of-sale .scrollbar .button{
-    color:white;
-    background: rgba(0,0,0,0.6);
-    -webkit-box-shadow: 0px 1px 4px rgba(0,0,0,0.01);
-       -moz-box-shadow: 0px 1px 4px rgba(0,0,0,0.01);
-            box-shadow: 0px 1px 4px rgba(0,0,0,0.01);
-    text-shadow: rgba(255,255,255,0.5) 0px 0px 10px;
-    -webkit-transition: all 250ms ease-in-out;
-       -moz-transition: all 250ms ease-in-out;
-            transition: all 250ms ease-in-out;
-}
-.point-of-sale .scrollbar .button.disabled{
-    background: rgba(0,0,0,0.3);
-    color:rgba(255,255,255,0.5);
-    -webkit-transition: all 250ms ease-in-out;
-       -moz-transition: all 250ms ease-in-out;
-            transition: all 250ms ease-in-out;
-}
-.point-of-sale .scrollbar .down-button{
-    position:absolute;
-    bottom:0px;
-}
-.point-of-sale .scrollbar .up-button{
-    position:absolute;
-    top:0px;
-}
-.point-of-sale .scrollbar .scroller{
-    position:absolute;
-    top:33%;
-    bottom:50%;
-    width:100%;
-    background: rgba(0,0,0,0.1);
-    border-radius: 4px;
-}
-
diff --git a/addons/point_of_sale/static/src/js/TODO.txt b/addons/point_of_sale/static/src/js/TODO.txt
deleted file mode 100644 (file)
index 1696f0b..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-v Affichage Catégories doivent respecter le poids
-v Affichage Catégories par poids doivent avoir une hiérarchie plate
-v Le Onscreen keyboard ne fonctionne plus
-v Scrolling dans la sélection produit et dans 
-  la liste de courses
-v Réductions
-v Redesign liste de courses
-v bugs scans produits par prix par poids
-v si pas photo photo par defaut
-v WebSQL
-v Redesign header, bouton retour aux backend en mode caissière
-v bouton exit en mode caissière doit retourner au backend.
-v posting orders
-v bug ajout ligne payment à zero + curseur dedans + suppression + design plus grand
-v si case print via proxy cochée, alors on skip l'ecran receipt
-v bouton exit self checkout
-v demarrage en mode self-checkout si self-checkout
-v produits poid code barre non reconnus
-v activer popup client aux écrans self-checkout
-v discount pas plus grand que 100
-v numpad dans l'écran payment pour pouvoir entrer le montant
-v différence de total. Methode d'arrondis
-
-- login alternatif
-- user preferences : désactiver la balance etc.
-- numpad state parfois sans state
-- numpad state en mode pesée ?? le cacher ? 
-- Redesign du receipt
-- retirer le  receipt screen
-- le numpad en mode paymenet marche pas
-- l'écran de payement est hyper laid
-- l'écran de pesée    est hyper laid
-x générer les données de printing
-x popups d'erreur certainement buggés
-
-TODO AUG 20
------------
-
-* GREEN STATUS
-    - make it green !
-
-* TRUNK
-    - Connection status tooltip
-    v Remove receipt screen
-    - Finish the receipt JSON generation
-        - Modifie le widget de liste course
-        v supprimer l'écran
-        v bloquer sur l'impression
-        v L'impression est foireuse
-        - description_sales
-
-* CLIENT
-    v create a new branch
-    - Self-checkout welcome screen
-        ~ removal of products for the root category
-    - Terminal de payement
-    - Code à barres
-    - Vidanges
-    - Design
-    - Backport
-    - CSS pour écran résistif
-    - Test sur matos
-
index 68acd4b..bd32e04 100644 (file)
@@ -43,7 +43,6 @@ 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(), 
                 'cashRegisters':    null, 
 
                 'bank_statements':  null,
@@ -444,11 +443,11 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
             }
 
             if(parsed_code.type === 'price'){
-                selectedOrder.addProduct(new module.Product(product), {price:parsed_code.value});
+                selectedOrder.addProduct(product, {price:parsed_code.value});
             }else if(parsed_code.type === 'weight'){
-                selectedOrder.addProduct(new module.Product(product), {quantity:parsed_code.value, merge:false});
+                selectedOrder.addProduct(product, {quantity:parsed_code.value, merge:false});
             }else{
-                selectedOrder.addProduct(new module.Product(product));
+                selectedOrder.addProduct(product);
             }
             return true;
         },
@@ -461,16 +460,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
         model: module.CashRegister,
     });
 
-    module.Product = Backbone.Model.extend({
-        get_image_url: function(){
-            return instance.session.url('/web/binary/image', {model: 'product.product', field: 'image_medium', id: this.get('id')});
-        },
-    });
-
-    module.ProductCollection = Backbone.Collection.extend({
-        model: module.Product,
-    });
-
     // 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.
@@ -479,7 +468,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
             this.pos = options.pos;
             this.order = options.order;
             this.product = options.product;
-            this.price   = options.product.get('price');
+            this.price   = options.product.price;
             this.quantity = 1;
             this.quantityStr = '1';
             this.discount = 0;
@@ -492,7 +481,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
             var disc = Math.min(Math.max(parseFloat(discount) || 0, 0),100);
             this.discount = disc;
             this.discountStr = '' + disc;
-            this.trigger('change');
+            this.trigger('change',this);
         },
         // returns the discount [0,100]%
         get_discount: function(){
@@ -522,7 +511,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
                     this.quantityStr = '' + this.quantity;
                 }
             }
-            this.trigger('change');
+            this.trigger('change',this);
         },
         // return the quantity of product
         get_quantity: function(){
@@ -541,7 +530,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
         },
         // return the unit of measure of the product
         get_unit: function(){
-            var unit_id = (this.product.get('uos_id') || this.product.get('uom_id'));
+            var unit_id = (this.product.uos_id || this.product.uom_id);
             if(!unit_id){
                 return undefined;
             }
@@ -558,7 +547,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
         // selects or deselects this orderline
         set_selected: function(selected){
             this.selected = selected;
-            this.trigger('change');
+            this.trigger('change',this);
         },
         // returns true if this orderline is selected
         is_selected: function(){
@@ -567,7 +556,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
         // when we add an new orderline we want to merge it with the last line to see reduce the number of items
         // in the orderline. This returns true if it makes sense to merge the two
         can_be_merged_with: function(orderline){
-            if( this.get_product().get('id') !== orderline.get_product().get('id')){    //only orderline of the same product can be merged
+            if( this.get_product().id !== orderline.get_product().id){    //only orderline of the same product can be merged
                 return false;
             }else if(this.get_product_type() !== orderline.get_product_type()){
                 return false;
@@ -587,7 +576,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
                 qty: this.get_quantity(),
                 price_unit: this.get_unit_price(),
                 discount: this.get_discount(),
-                product_id: this.get_product().get('id'),
+                product_id: this.get_product().id,
             };
         },
         //used to create a json of the ticket, to be sent to the printer
@@ -597,19 +586,19 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
                 unit_name:          this.get_unit().name,
                 price:              this.get_unit_price(),
                 discount:           this.get_discount(),
-                product_name:       this.get_product().get('name'),
+                product_name:       this.get_product().name,
                 price_display :     this.get_display_price(),
                 price_with_tax :    this.get_price_with_tax(),
                 price_without_tax:  this.get_price_without_tax(),
                 tax:                this.get_tax(),
-                product_description:      this.get_product().get('description'),
-                product_description_sale: this.get_product().get('description_sale'),
+                product_description:      this.get_product().description,
+                product_description_sale: this.get_product().description_sale,
             };
         },
         // changes the base price of the product for this orderline
         set_unit_price: function(price){
             this.price = round_di(parseFloat(price) || 0, 2);
-            this.trigger('change');
+            this.trigger('change',this);
         },
         get_unit_price: function(){
             var rounding = this.pos.get('currency').rounding;
@@ -637,8 +626,8 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
             
             var product_list = this.pos.get('product_list');
             var product =  this.get_product(); 
-            var taxes_ids = product.get('taxes_id');;
-            var taxes =  self.pos.get('taxes');
+            var taxes_ids = product.taxes_id;
+            var taxes =  self.pos.taxes;
             var taxtotal = 0;
             _.each(taxes_ids, function(el) {
                 var tax = _.detect(taxes, function(t) {return t.id === el;});
@@ -749,7 +738,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
         },
         addProduct: function(product, options){
             options = options || {};
-            var attr = product.toJSON();
+            var attr = JSON.parse(JSON.stringify(product));
             attr.pos = this.pos;
             attr.order = this;
             var line = new module.Orderline({}, {pos: this.pos, order: this, product: product});
@@ -951,6 +940,12 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
                 this.selected_orderline = undefined;
             }
         },
+        deselectLine: function(){
+            if(this.selected_orderline){
+                this.selected_orderline.set_selected(false);
+                this.selected_orderline = undefined;
+            }
+        },
     });
 
     module.OrderCollection = Backbone.Collection.extend({
index 88a83fc..965451e 100644 (file)
@@ -634,7 +634,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
                             }
 
                             var cashregister = selfCheckoutRegisters[0] || self.pos.get('cashRegisters').models[0];
-                            currentOrder.addPaymentLine(cashregister);
+                            currentOrder.add_payment_line(cashregister);
                             self.pos.push_order(currentOrder)
                             currentOrder.destroy();
                             self.pos.proxy.transaction_end();
@@ -745,19 +745,23 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
 
         start: function(){ //FIXME this should work as renderElement... but then the categories aren't properly set. explore why
             var self = this;
-            this.product_categories_widget = new module.ProductCategoriesWidget(this,{});
-            this.product_categories_widget.replace($('.placeholder-ProductCategoriesWidget'));
 
             this.product_list_widget = new module.ProductListWidget(this,{
                 click_product_action: function(product){
-                    if(product.get('to_weight') && self.pos.iface_electronic_scale){
+                    if(product.to_weight && self.pos.iface_electronic_scale){
                         self.pos_widget.screen_selector.set_current_screen( self.cashier_mode ? self.scale_screen : self.client_scale_screen, {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_categories_widget = new module.ProductCategoriesWidget(this,{
+                product_list_widget: this.product_list_widget,
+            });
+            this.product_categories_widget.replace($('.placeholder-ProductCategoriesWidget'));
         },
 
         show: function(){
@@ -766,7 +770,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
 
             this.product_categories_widget.reset_category();
 
-            this.pos_widget.order_widget.set_numpad_state(this.pos_widget.numpad.state);
+            this.pos_widget.order_widget.set_editable(true);
 
             if(this.pos_widget.screen_selector.current_mode === 'client'){ 
                 this.add_action_button({
@@ -781,13 +785,13 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
 
         close: function(){
             this._super();
-            this.pos_widget.order_widget.set_numpad_state(null);
-            this.pos_widget.payment_screen.set_numpad_state(null);
+
+            this.pos_widget.order_widget.set_editable(false);
+
             if(this.pos.iface_vkeyboard && this.pos_widget.onscreen_keyboard){
                 this.pos_widget.onscreen_keyboard.hide();
             }
         },
-
     });
 
     module.ReceiptScreenWidget = module.ScreenWidget.extend({
@@ -812,8 +816,6 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
             this._super();
             var self = this;
 
-            this.pos_widget.set_numpad_visible(false);
-
             var print_button = this.add_action_button({
                     label: _t('Print'),
                     icon: '/point_of_sale/static/src/img/icons/png48/printer.png',
@@ -876,7 +878,6 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
         },
         close: function(){
             this._super();
-            this.pos_widget.set_numpad_visible(true);
         }
     });
 
@@ -888,7 +889,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
             this._super(parent,options);
             this.model = options.model;
             this.pos.bind('change:selectedOrder', this.change_selected_order, this);
-            this.bindPaymentLineEvents();
+            this.bind_payment_line_events();
             this.bind_orderline_events();
             this.paymentlinewidgets = [];
             this.focusedLine = null;
@@ -899,7 +900,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
 
             this.hotkey_handler = function(event){
                 if(event.which === 13){
-                    self.validateCurrentOrder();
+                    self.validate();
                 }else if(event.which === 27){
                     self.back();
                 }
@@ -912,8 +913,8 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
                     this.pos.proxy.open_cashbox();
             }
 
-            this.set_numpad_state(this.pos_widget.numpad.state);
-            
+            this.enable_numpad();
+
             this.add_action_button({
                     label: _t('Back'),
                     icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
@@ -927,7 +928,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
                     name: 'validation',
                     icon: '/point_of_sale/static/src/img/icons/png48/validate.png',
                     click: function(){
-                        self.validateCurrentOrder();
+                        self.validate();
                     },
                 });
            
@@ -937,7 +938,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
                         name: 'invoice',
                         icon: '/point_of_sale/static/src/img/icons/png48/invoice.png',
                         click: function(){
-                            self.validateCurrentOrder({invoice: true});
+                            self.validate({invoice: true});
                         },
                     });
             }
@@ -953,13 +954,12 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
                     });
             }
 
-            this.updatePaymentSummary();
+            this.update_payment_summary();
             this.line_refocus();
         },
         close: function(){
             this._super();
-            this.pos_widget.order_widget.set_numpad_state(null);
-            this.pos_widget.payment_screen.set_numpad_state(null);
+            this.disable_numpad();
             $('body').off('keyup',this.hotkey_handler);
         },
         back: function() {
@@ -970,13 +970,13 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
             });
             this.pos_widget.screen_selector.set_current_screen(this.back_screen);
         },
-        validateCurrentOrder: function(options) {
+        validate: function(options) {
             var self = this;
             options = options || {};
 
             var currentOrder = this.pos.get('selectedOrder');
 
-            if(!this.isPaymentPaid()){
+            if(!this.is_paid()){
                 return;
             }
 
@@ -1018,19 +1018,19 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
                 $("input").blur();
             },250);
         },
-        bindPaymentLineEvents: function() {
+        bind_payment_line_events: function() {
             this.currentPaymentLines = (this.pos.get('selectedOrder')).get('paymentLines');
-            this.currentPaymentLines.bind('add', this.addPaymentLine, this);
+            this.currentPaymentLines.bind('add', this.add_payment_line, this);
             this.currentPaymentLines.bind('remove', this.renderElement, this);
-            this.currentPaymentLines.bind('all', this.updatePaymentSummary, this);
+            this.currentPaymentLines.bind('all', this.update_payment_summary, this);
         },
         bind_orderline_events: function() {
             this.currentOrderLines = (this.pos.get('selectedOrder')).get('orderLines');
-            this.currentOrderLines.bind('all', this.updatePaymentSummary, this);
+            this.currentOrderLines.bind('all', this.update_payment_summary, this);
         },
         change_selected_order: function() {
             this.currentPaymentLines.unbind();
-            this.bindPaymentLineEvents();
+            this.bind_payment_line_events();
             this.currentOrderLines.unbind();
             this.bind_orderline_events();
             this.renderElement();
@@ -1045,7 +1045,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
                 this.focusedLine.focus();
             }
         },
-        addPaymentLine: function(newPaymentLine) {
+        add_payment_line: function(newPaymentLine) {
             var self = this;
             var l = new module.PaymentlineWidget(this, {
                     payment_line: newPaymentLine,
@@ -1069,21 +1069,21 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
             this.paymentlinewidgets = [];
             
             this.currentPaymentLines.each(_.bind( function(paymentLine) {
-                this.addPaymentLine(paymentLine);
+                this.add_payment_line(paymentLine);
             }, this));
-            this.updatePaymentSummary();
+            this.update_payment_summary();
         },
         deleteLine: function(lineWidget) {
                this.currentPaymentLines.remove([lineWidget.payment_line]);
             lineWidget.destroy();
         },
-        isPaymentPaid: function(){
+        is_paid: function(){
             var currentOrder = this.pos.get('selectedOrder');
             return (currentOrder.getTotalTaxIncluded() >= 0.000001 
                    && currentOrder.getPaidTotal() + 0.000001 >= currentOrder.getTotalTaxIncluded());
 
         },
-        updatePaymentSummary: function() {
+        update_payment_summary: function() {
             var currentOrder = this.pos.get('selectedOrder');
             var paidTotal = currentOrder.getPaidTotal();
             var dueTotal = currentOrder.getTotalTaxIncluded();
@@ -1099,25 +1099,29 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
             }
                 
             if(this.pos_widget.action_bar){
-                this.pos_widget.action_bar.set_button_disabled('validation', !this.isPaymentPaid());
-                this.pos_widget.action_bar.set_button_disabled('invoice', !this.isPaymentPaid());
+                this.pos_widget.action_bar.set_button_disabled('validation', !this.is_paid());
+                this.pos_widget.action_bar.set_button_disabled('invoice', !this.is_paid());
+            }
+        },
+        enable_numpad: function(){
+            this.disable_numpad();  //ensure we don't register the callbacks twice
+            this.numpad_state = this.pos_widget.numpad.state;
+            if(this.numpad_state){
+                this.numpad_state.reset();
+                this.numpad_state.changeMode('payment');
+                this.numpad_state.bind('set_value',   this.set_value, this);
+                this.numpad_state.bind('change:mode', this.set_mode_back_to_payment, this);
+            }
+                    
+        },
+        disable_numpad: function(){
+            if(this.numpad_state){
+                this.numpad_state.unbind('set_value',  this.set_value);
+                this.numpad_state.unbind('change:mode',this.set_mode_back_to_payment);
             }
         },
-        set_numpad_state: function(numpadState) {
-               if (this.numpadState) {
-                       this.numpadState.unbind('set_value', this.set_value);
-                       this.numpadState.unbind('change:mode', this.setNumpadMode);
-               }
-               this.numpadState = numpadState;
-               if (this.numpadState) {
-                       this.numpadState.bind('set_value', this.set_value, this);
-                       this.numpadState.bind('change:mode', this.setNumpadMode, this);
-                       this.numpadState.reset();
-                       this.setNumpadMode();
-               }
-        },
-       setNumpadMode: function() {
-               this.numpadState.set({mode: 'payment'});
+       set_mode_back_to_payment: function() {
+               this.numpad_state.set({mode: 'payment'});
        },
         set_value: function(val) {
                this.currentPaymentLines.last().set_amount(val);
diff --git a/addons/point_of_sale/static/src/js/widget_scrollbar.js b/addons/point_of_sale/static/src/js/widget_scrollbar.js
deleted file mode 100644 (file)
index 001e96d..0000000
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * This Widget provides a javascript scrollbar that is suitable to use with resistive 
- * tactile screens. 
- *
- * Options:
- * target_widget : the widget that will be scrolled. 
- * target_selector : if you don't want to scroll the root element of the the widget, you can provide a
- *   jquery selector string that will match on the widget's dom element. If there is no widget provided,
- *   it will match on the document
- * step: on each click, the target will be scrolled by it's deplayed size multiplied by this value. 
- * duration: this is the duration of the scrolling animation
- * wheel_step: the target will be scrolled by wheel_step pixels on each mouse scroll.
- * track_bottom: the target will be kept on bottom when it's on the bottom and the size has changed
- * on_show: this function will be called with the scrollbar as sole argument when the scrollbar is shown
- * on_hide: this function will be called with the scrollbar as sole argument when the scrollbar is hidden
- */
-function openerp_pos_scrollbar(instance, module){ //module is instance.point_of_sale
-
-    module.ScrollbarWidget = instance.web.Widget.extend({
-        template:'ScrollbarWidget',
-
-        init: function(parent,options){
-            var self = this;
-            options = options || {};
-            this._super(parent,options);
-            this.target_widget = options.target_widget;
-            this.target_selector = options.target_selector;
-            this.scroll_target = this.target().scrollTop();
-            this.scroll_step   = options.step || 0.8;
-            this.scroll_duration  = options.duration || 250;
-            this.wheel_step = options.wheel_step || 80;
-            this.name = options.name || 'unnamed';
-            this.bottom = false;  // true if the scroller cannot be scrolled further 
-            this.track_bottom = options.track_bottom || false;
-            this.on_show = options.on_show || function(){};
-            this.on_hide = options.on_hide || function(){};
-
-            // these handlers are declared once for the object's lifetime so that we can bind and unbind them.
-            this.resize_handler = function(){
-                setTimeout(function(){
-                    if(self.bottom && self.track_bottom){
-                        self.set_position(Number.MAX_VALUE);
-                    }
-                    self.update_scroller_dimensions();
-                    self.update_button_status();
-                    self.auto_hide(false);
-                },0);
-            };
-            this.target_mousewheel_handler = function(event,delta){
-                self.scroll(delta*self.wheel_step);
-            }
-        },
-
-        renderElement: function(){
-            this._super();
-            var self = this;
-            this.$('.up-button').off('click').click(function(){
-                self.page_up();
-            });
-            this.$('.down-button').off('click').click(function(){
-                self.page_down();
-            });
-            this.update_scroller_dimensions(false);
-            this.update_button_status();
-            this.auto_hide(false);
-            this.$el.bind('mousewheel',function(event,delta){
-                self.scroll(delta*self.wheel_step);
-                return false;
-            });
-            this.$el.bind('click',function(event){
-                var vpos = event.pageY - self.$el.offset().top;
-                var spos = self.scroller_dimensions();
-                if(vpos > spos.bar_pos && vpos < spos.pos){
-                    self.page_up();
-                }else if(   (vpos < spos.bar_pos + spos.bar_height) && 
-                            (vpos > spos.pos + spos.height) ){
-                    self.page_down();
-                }
-            });
-            // FIXME: use the event bus to handle window resize events
-            $(window).unbind('resize',this.resize_handler);
-            $(window).bind('resize',this.resize_handler);
-
-            this.target().unbind('mousewheel',this.target_mousewheel_handler);
-            this.target().bind('mousewheel',this.target_mousewheel_handler);
-            
-            // because the rendering is asynchronous we must wait for the next javascript update
-            // for good dimensions values
-            setTimeout(function(){
-                self.update_scroller_dimensions(false);
-                self.update_button_status();
-                self.auto_hide(false);
-            },0);
-        },
-
-        destroy: function(){
-            $(window).unbind('resize',this.resize_handler);
-            this.target().unbind('mousewheel',this.target_mousewheel_handler);
-            this._super();
-        },
-
-        // shows the scrollbar. if animated is true, it will do it in an animated fashion
-        show: function(animated){   //FIXME: animated show and hide don't work ... ? 
-            if(animated){
-                this.$el.removeClass('oe_hidden').animate({'width':'48px'}, 500, 'swing');
-            }else{
-                this.$el.removeClass('oe_hidden').css('width','48px');
-            }
-            this.on_show(this);
-        },
-
-        // hides the scrollbar. if animated is true, it will do it in a animated fashion
-        hide: function(animated){
-            var self = this;
-            if(animated){
-                this.$el.animate({'width':'0px'}, 500, 'swing', function(){ self.$el.addClass('oe_hidden');});
-            }else{
-                this.$el.addClass('oe_hidden').css('width','0px');
-            }
-            this.on_hide(this);
-        },
-
-        // returns the scroller position and other information as a dictionnary with the following fields:
-        // pos: the position in pixels of the top of the scroller starting from the top of the scrollbar
-        // height: the height of the scroller in pixels
-        // bar_pos: the position of the top of the scrollbar's inner region, starting from the top
-        // bar_height: the height of the scrollbar's inner region
-        scroller_dimensions: function(){
-            var target = this.target()[0];
-            var scroller_height = target.clientHeight / target.scrollHeight || 0;
-            var scroller_pos    = this.scroll_target / target.scrollHeight || 0;
-            var button_up_height = this.$('.up-button')[0].offsetHeight || 48;
-            var button_down_height = this.$('.down-button')[0].offsetHeight || 48;
-            
-            var bar_height = this.$el[0].offsetHeight || 96;
-            var scrollbar_height = bar_height - button_up_height - button_down_height;
-            
-            scroller_pos = scroller_pos * scrollbar_height + button_up_height;
-            scroller_height = scroller_height * scrollbar_height;
-
-            return { pos: Math.round(scroller_pos), 
-                     height: Math.round(scroller_height),
-                     bar_pos: button_up_height,
-                     bar_height: scrollbar_height };
-        },
-
-
-        //checks if it should show or hide the scrollbar based on the target content and then show or hide it
-        // if animated is true, then the scrollbar will be shown or hidden with an animation
-        auto_hide: function(animated){
-            var target = this.target()[0];
-            if(target.clientHeight && (target.clientHeight === target.scrollHeight)){
-                this.hide(animated);
-            }else{
-                this.show(animated);
-            }
-        },
-
-        //returns the pageup/down scrolling distance in pixels
-        get_scroll_step: function(){
-            var target = this.target()[0];
-            var step = target.clientHeight * this.scroll_step;
-            var c    = target.scrollHeight / step;
-            var c    = Math.max(1,Math.ceil(c));
-            return target.scrollHeight / c;
-        },
-
-        //sets the scroller to the correct size and position based on the target scrolling status
-        //if animated is true, the scroller will move smoothly to its destination
-        update_scroller_dimensions: function(animated){
-            var dim = this.scroller_dimensions();
-            var target = this.target()[0];
-            if(animated){
-                this.$('.scroller').animate({'top':dim.pos+'px', 'height': dim.height+'px'},this.scroll_duration);
-            }else{
-                this.$('.scroller').css({'top':dim.pos+'px', 'height': dim.height+'px'});
-            }
-            if(this.scroll_target + target.clientHeight >= target.scrollHeight){
-                this.bottom = true;
-            }else{
-                this.bottom = false;
-            }
-        },
-
-        //disable or enable the up/down buttons according to the scrolled position
-        update_button_status: function(){
-            var target = this.target()[0];
-            this.$('.up-button').removeClass('disabled');
-            this.$('.down-button').removeClass('disabled');
-            if(this.scroll_target === 0){
-                this.$('.up-button').addClass('disabled');
-            }
-            if(this.scroll_target + target.clientHeight >= target.scrollHeight){
-                this.$('.down-button').addClass('disabled');
-            }
-        },
-
-        //returns the jquery object of the scrolling target
-        target: function(){
-            if(this.target_widget){
-                if(this.target_selector){
-                    return this.target_widget.$(this.target_selector);
-                }else{
-                    return this.target_widget.$el;
-                }
-            }else if(this.target_selector){
-                return $(this.target_selector);
-            }else{
-                return undefined;
-            }
-        },
-
-        //scroll one page up
-        page_up: function(){
-            return this.set_position(this.scroll_target - this.get_scroll_step(), true);
-        },
-
-        //scroll one page down
-        page_down: function(){
-            return this.set_position(this.scroll_target + this.get_scroll_step(), true);
-        },
-
-        //scrolls up or down by pixels
-        scroll: function(pixels){
-            return this.set_position(this.scroll_target - pixels, false);
-        },
-
-        //scroll to a specific position (in pixels). 
-        //if animated is true, it will do this in an animated fashion with a duration equal to scroll_duration 
-        set_position: function(position,animated){
-            var self = this;
-            var target = this.target()[0];
-            var bottom = target.scrollHeight-target.clientHeight;
-            this.scroll_target = Math.max(0,Math.min(bottom,position));
-            if(this.scroll_target === 0){
-                this.position = 'top';
-            }else if(this.scroll_target === 'bottom'){
-                this.position = 'bottom';
-            }else{
-                this.position = 'center';
-            }
-            if(animated){
-                this.target().animate({'scrollTop':this.scroll_target},this.scroll_duration);
-                this.update_button_status();
-                this.update_scroller_dimensions(true);
-            }else{
-                this.target().scrollTop(this.scroll_target);
-                this.update_scroller_dimensions(false);
-                this.update_button_status();
-            }
-            return this.scroll_target;
-        },
-        
-        //returns the current position of the scrollbar
-        get_position: function(){
-            return this.scroll_target;
-        },
-        
-        //returns true if it cannot be scrolled further down
-        is_at_bottom: function(){
-            return this.bottom;
-        }
-            
-    });
-}
index de571ff..438dbc4 100644 (file)
@@ -58,6 +58,11 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
         init: function(parent, options) {
             this._super(parent);
             this.state = new module.NumpadState();
+            window.numpadstate = this.state;
+            var self = this;
+            this.state.bind('change:buffer',function(){
+                console.log('BUFFER:',self.state.get('buffer'));
+            })
         },
         start: function() {
             this.state.bind('change:mode', this.changedMode, this);
@@ -129,61 +134,50 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
         },
     });
 
-    module.OrderlineWidget = module.PosBaseWidget.extend({
-        template: 'OrderlineWidget',
-        init: function(parent, options) {
-            this._super(parent,options);
-
-            this.model = options.model;
-            this.order = options.order;
-
-            this.model.bind('change', this.refresh, this);
-        },
-        renderElement: function() {
-            var self = this;
-            this._super();
-            this.$el.click(function(){
-                self.order.selectLine(self.model);
-                self.trigger('order_line_selected');
-            });
-            if(this.model.is_selected()){
-                this.$el.addClass('selected');
-            }
-        },
-        refresh: function(){
-            this.renderElement();
-            this.trigger('order_line_refreshed');
-        },
-        destroy: function(){
-            this.model.unbind('change',this.refresh,this);
-            this._super();
-        },
-    });
-    
     module.OrderWidget = module.PosBaseWidget.extend({
         template:'OrderWidget',
         init: function(parent, options) {
+            var self = this;
             this._super(parent,options);
-            this.display_mode = options.display_mode || 'numpad';   // 'maximized' | 'actionbar' | 'numpad'
-            this.set_numpad_state(options.numpadState);
+            this.editable = false;
             this.pos.bind('change:selectedOrder', this.change_selected_order, this);
             this.bind_orderline_events();
-            this.orderlinewidgets = [];
+            this.line_click_handler = function(event){
+                if(!self.editable){
+                    return;
+                }
+                self.pos.get('selectedOrder').selectLine(this.orderline);
+                self.pos_widget.numpad.state.reset();
+            };
+        },
+        enable_numpad: function(){
+            this.disable_numpad();  //ensure we don't register the callbacks twice
+            this.numpad_state = this.pos_widget.numpad.state;
+            if(this.numpad_state){
+                this.numpad_state.reset();
+                this.numpad_state.bind('set_value',   this.set_value, this);
+            }
+                    
         },
-        set_numpad_state: function(numpadState) {
-               if (this.numpadState) {
-                       this.numpadState.unbind('set_value', this.set_value);
-               }
-               this.numpadState = numpadState;
-               if (this.numpadState) {
-                       this.numpadState.bind('set_value', this.set_value, this);
-                       this.numpadState.reset();
-               }
+        disable_numpad: function(){
+            if(this.numpad_state){
+                this.numpad_state.unbind('set_value',  this.set_value);
+                this.numpad_state.reset();
+            }
+        },
+        set_editable: function(editable){
+            this.editable = editable;
+            if(editable){
+                this.enable_numpad();
+            }else{
+                this.disable_numpad();
+                this.pos.get('selectedOrder').deselectLine();
+            }
         },
         set_value: function(val) {
                var order = this.pos.get('selectedOrder');
-               if (order.get('orderLines').length !== 0) {
-                var mode = this.numpadState.get('mode');
+               if (this.editable && order.getSelectedLine()) {
+                var mode = this.numpad_state.get('mode');
                 if( mode === 'quantity'){
                     order.getSelectedLine().set_quantity(val);
                 }else if( mode === 'discount'){
@@ -194,81 +188,96 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
                }
         },
         change_selected_order: function() {
-            this.currentOrderLines.unbind();
             this.bind_orderline_events();
             this.renderElement();
         },
         bind_orderline_events: function() {
-            this.currentOrderLines = (this.pos.get('selectedOrder')).get('orderLines');
-            this.currentOrderLines.bind('add', function(){ this.renderElement(true);}, this);
-            this.currentOrderLines.bind('remove', this.renderElement, this);
+            var lines = this.pos.get('selectedOrder').get('orderLines');
+                lines.unbind();
+                lines.bind('add', function(){ 
+                        this.numpad_state.reset();
+                        this.renderElement(true);
+                    },this);
+                lines.bind('remove', function(line){
+                        this.remove_orderline(line);
+                        this.numpad_state.reset();
+                        this.update_summary();
+                    },this);
+                lines.bind('change', function(line){
+                        this.rerender_orderline(line);
+                        this.update_summary();
+                    },this);
         },
         update_numpad: function() {
             this.selected_line = this.pos.get('selectedOrder').getSelectedLine();
             if (this.numpadState)
                 this.numpadState.reset();
         },
-        renderElement: function(goto_bottom) {
-            var self = this;
-            var scroller = this.$('.order-scroller')[0];
-            var scrollbottom = true;
-            var scrollTop = 0;
-            if(scroller){
-                var overflow_bottom = scroller.scrollHeight - scroller.clientHeight;
-                scrollTop = scroller.scrollTop;
-                if( !goto_bottom && scrollTop < 0.9 * overflow_bottom){
-                    scrollbottom = false;
-                }
+        render_orderline: function(orderline){
+            var el_str  = openerp.qweb.render('Orderline',{widget:this, line:orderline}); 
+            var el_node = document.createElement('div');
+                el_node.innerHTML = _.str.trim(el_str);
+                el_node = el_node.childNodes[0];
+                el_node.orderline = orderline;
+                el_node.addEventListener('click',this.line_click_handler);
+
+            orderline.node = el_node;
+            return el_node;
+        },
+        remove_orderline: function(order_line){
+            if(this.pos.get('selectedOrder').get('orderLines').length === 0){
+                this.renderElement();
+            }else{
+                order_line.node.parentNode.removeChild(order_line.node);
             }
-            this._super();
+        },
+        rerender_orderline: function(order_line){
+            var node = order_line.node;
+            var replacement_line = this.render_orderline(order_line);
+            node.parentNode.replaceChild(replacement_line,node);
+        },
+        replace: function($target){
+            this.renderElement();
+            var target = $target[0];
+            target.parentNode.replaceChild(this.el,target);
+        },
+        renderElement: function(scrollbottom){
+            this.pos_widget.numpad.state.reset();
 
-            // freeing subwidgets
-            
-            for(var i = 0, len = this.orderlinewidgets.length; i < len; i++){
-                this.orderlinewidgets[i].destroy();
+            var order  = this.pos.get('selectedOrder');
+            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]);
+                list_container.appendChild(orderline);
             }
-            this.orderlinewidgets = [];
 
-            var $content = this.$('.orderlines');
-            this.currentOrderLines.each(_.bind( function(orderLine) {
-                var line = new module.OrderlineWidget(this, {
-                        model: orderLine,
-                        order: this.pos.get('selectedOrder'),
-                });
-               line.on('order_line_selected', self, self.update_numpad);
-                line.on('order_line_refreshed', self, self.update_summary);
-                line.appendTo($content);
-                self.orderlinewidgets.push(line);
-            }, this));
-            this.update_numpad();
+            if(this.el && this.el.parentNode){
+                this.el.parentNode.replaceChild(el_node,this.el);
+            }
+            this.el = el_node;
             this.update_summary();
 
-            scroller = this.$('.order-scroller')[0];
-
-            if(scroller){
-                if(scrollbottom){
-                    scroller.scrollTop = scroller.scrollHeight - scroller.clientHeight;
-                }else{
-                    scroller.scrollTop = scrollTop;
-                }
+            if(scrollbottom){
+                this.el.querySelector('.order-scroller').scrollTop = 100 * orderlines.length;
             }
         },
         update_summary: function(){
             var order = this.pos.get('selectedOrder');
             var total     = order ? order.getTotalTaxIncluded() : 0;
             var taxes     = order ? total - order.getTotalTaxExcluded() : 0;
-            this.$('.summary .total > .value').html(this.format_currency(total));
-            this.$('.summary .total .subentry .value').html(this.format_currency(taxes));
-        },
-        set_display_mode: function(mode){
-            if(this.display_mode !== mode){
-                this.display_mode = mode;
-                this.renderElement();
-            }
+
+            this.el.querySelector('.summary .total > .value').innerText = this.format_currency(total);
+            this.el.querySelector('.summary .total .subentry .value').innerText = this.format_currency(taxes);
         },
     });
 
-
     module.PaymentlineWidget = module.PosBaseWidget.extend({
         template: 'PaymentlineWidget',
         init: function(parent, options) {
@@ -439,8 +448,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
         },
     });
 
-    module.CategoryButton = module.PosBaseWidget.extend({
-    });
     module.ProductCategoriesWidget = module.PosBaseWidget.extend({
         template: 'ProductCategoriesWidget',
         init: function(parent, options){
@@ -451,7 +458,13 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
             this.category = this.pos.root_category;
             this.breadcrumb = [];
             this.subcategories = [];
+            this.product_list_widget = options.product_list_widget || null;
+            this.category_cache = {};
             this.set_category();
+            this.switch_category_handler = function(event){
+                self.set_category(self.pos.db.get_category_by_id(Number(this.dataset['categoryId'])));
+                self.renderElement();
+            };
         },
 
         // changes the category. if undefined, sets to root category
@@ -474,46 +487,77 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
         },
 
         get_image_url: function(category){
-            return instance.session.url('/web/binary/image', {model: 'pos.category', field: 'image_medium', id: category.id});
+            return window.location.origin + '/web/binary/image?model=pos.category&field=image_medium&id='+category.id;
+        },
+
+        render_category: function( category, with_image ){
+            if(!this.category_cache[category.id]){
+
+                if(with_image){
+                    var image_url = this.get_image_url(category);
+                    var category_html = QWeb.render('CategoryButton',{ 
+                            widget:  this, 
+                            category: category, 
+                        });
+                        category_html = _.str.trim(category_html);
+                    var category_node = document.createElement('div');
+                        category_node.innerHTML = category_html;
+                        category_node = category_node.childNodes[0];
+                    var img = category_node.querySelector('img');
+                        img.parentNode.replaceChild(this.pos_widget.image_cache.get_image(image_url),img);
+                }else{
+                    var category_html = QWeb.render('CategorySimpleButton',{ 
+                            widget:  this, 
+                            category: category, 
+                        });
+                        category_html = _.str.trim(category_html);
+                    var category_node = document.createElement('div');
+                        category_node.innerHTML = category_html;
+                        category_node = category_node.childNodes[0];
+                }
+                this.category_cache[category.id] = category_node;
+            }
+            return this.category_cache[category.id];
+        },
+
+        replace: function($target){
+            this.renderElement();
+            var target = $target[0];
+            target.parentNode.replaceChild(this.el,target);
         },
 
         renderElement: function(){
             var self = this;
-            this._super();
+
+            var el_str  = openerp.qweb.render(this.template, {widget: this});
+            var el_node = document.createElement('div');
+                el_node.innerHTML = el_str;
+                el_node = el_node.childNodes[1];
+
+            if(this.el && this.el.parentNode){
+                this.el.parentNode.replaceChild(el_node,this.el);
+            }
+
+            this.el = el_node;
 
             var hasimages = false;  //if none of the subcategories have images, we don't display buttons with icons
-            _.each(this.subcategories, function(category){
-                if(category.image){
+            for(var i = 0; i < this.subcategories.length; i++){
+                if(this.subcategories[i].image){
                     hasimages = true;
+                    break;
                 }
-            });
+            }
 
-            _.each(this.subcategories, function(category){
-                if(hasimages){
-                    var button = QWeb.render('CategoryButton',{category:category});
-                    var button = _.str.trim(button);
-                    var button = $(button);
-                    button.find('img').replaceWith(self.pos_widget.image_cache.get_image(self.get_image_url(category)));
-                }else{
-                    var button = QWeb.render('CategorySimpleButton',{category:category});
-                    button = _.str.trim(button);    // we remove whitespace between buttons to fix spacing
-                    var button = $(button);
-                }
+            var list_container = el_node.querySelector('.category-list');
+            for(var i = 0, len = this.subcategories.length; i < len; i++){
+                list_container.appendChild(this.render_category(this.subcategories[i],hasimages));
+            };
+
+            var buttons = el_node.querySelectorAll('.js-category-switch');
+            for(var i = 0; i < buttons.length; i++){
+                buttons[i].addEventListener('click',this.switch_category_handler);
+            }
 
-                button.appendTo(this.$('.category-list')).click(function(event){
-                    var id = category.id;
-                    var cat = self.pos.db.get_category_by_id(id);
-                    self.set_category(cat);
-                    self.renderElement();
-                });
-            });
-            // breadcrumb click actions
-            this.$(".oe-pos-categories-list a").click(function(event){
-                var id = $(event.target).data("category-id");
-                var category = self.pos.db.get_category_by_id(id);
-                self.set_category(category);
-                self.renderElement();
-            });
 
             this.search_and_categories();
 
@@ -522,11 +566,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
             }
         },
         
-        set_product_type: function(type){       // 'all' | 'weightable'
-            this.product_type = type;
-            this.reset_category();
-        },
-
         // resets the current category to the root category
         reset_category: function(){
             this.set_category();
@@ -536,7 +575,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
         // empties the content of the search box
         clear_search: function(){
             var products = this.pos.db.get_product_by_category(this.category.id);
-            this.pos.get('products').reset(products);
+            this.product_list_widget.set_product_list(products);
             this.$('.searchbox input').val('').focus();
             this.$('.search-clear').fadeOut();
         },
@@ -547,9 +586,10 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
 
             // find all products belonging to the current category
             var products = this.pos.db.get_product_by_category(this.category.id);
-            self.pos.get('products').reset(products);
+            this.product_list_widget.set_product_list(products);
 
 
+            /*
             var searchtimeout = null;
             // filter the products according to the search string
             this.$('.searchbox input').keyup(function(event){
@@ -562,17 +602,17 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
                     if(query){
                         if(event.which === 13){
                             if( self.pos.get('products').size() === 1 ){
-                                self.pos.get('selectedOrder').addProduct(self.pos.get('products').at(0));
+                                self.pos.get('selectedOrder').addProduct(self.pos.get('products').at(0)); // FIXME
                                 self.clear_search();
                             }
                         }else{
                             var products = self.pos.db.search_product_in_category(self.category.id, query);
-                            self.pos.get('products').reset(products);
+                            this.product_list_widget.set_product_list(products);
                             self.$('.search-clear').fadeIn();
                         }
                     }else{
                         var products = self.pos.db.get_product_by_category(self.category.id);
-                        self.pos.get('products').reset(products);
+                        this.product_list_widget.set_product_list(products);
                         self.$('.search-clear').fadeOut();
                     }
                 },200);
@@ -581,11 +621,11 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
             //reset the search when clicking on reset
             this.$('.search-clear').click(function(){
                 self.clear_search();
-            });
+            });*/
         },
     });
 
-    module.ProductListWidget = module.ScreenWidget.extend({
+    module.ProductListWidget = module.PosBaseWidget.extend({
         template:'ProductListWidget',
         init: function(parent, options) {
             var self = this;
@@ -595,38 +635,65 @@ 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.click_product_action = options.click_product_action;
 
-            this.pos.get('products').bind('reset', function(){
-                self.renderElement();
-            });
-        },
-        renderElement: function() {
-            var self = this;
-            this._super();
+            this.click_product_handler = function(event){
+                var product = self.pos.db.get_product_by_id(this.dataset['productId']);
+                options.click_product_action(product);
+            };
 
-            var products = this.pos.get('products').models || [];
+            this.product_list = options.product_list || [];
+            this.product_cache = {};
+        },
+        set_product_list: function(product_list){
+            this.product_list = product_list;
+            this.renderElement();
+        },
+        get_product_image_url: function(product){
+            return window.location.origin + '/web/binary/image?model=product.product&field=image_medium&id='+product.id;
+        },
+        replace: function($target){
+            this.renderElement();
+            var target = $target[0];
+            target.parentNode.replaceChild(this.el,target);
+        },
 
-            _.each(products,function(product,i){
-                var $product = $(QWeb.render('Product',{ widget:self, product: products[i] }));
-                $product.find('img').replaceWith(self.pos_widget.image_cache.get_image(products[i].get_image_url()));
-                $product.appendTo(self.$('.product-list'));
-            });
+        render_product: function(product){
+            if(!this.product_cache[product.id]){
+                var image_url = this.get_product_image_url(product);
+                var product_html = QWeb.render('Product',{ 
+                        widget:  this, 
+                        product: product, 
+                    });
+                var product_node = document.createElement('div');
+                product_node.innerHTML = product_html;
+                product_node = product_node.childNodes[1];
+                var img = product_node.querySelector('img');
+                img.parentNode.replaceChild(this.pos_widget.image_cache.get_image(image_url),img);
+                this.product_cache[product.id] = product_node;
+            }
+            return this.product_cache[product.id];
+        },
 
-            this.$el.delegate('.product','touchstart',function TOUCHSTART(){ 
-                console.log('Touchstart',(new Date()).getTime());
-            });
+        renderElement: function() {
+            var self = this;
 
-            this.$el.delegate('.product','touchend',function TOUCHEND(){ 
-                console.log('Touchend',(new Date()).getTime());
-            });
+            // this._super()
+            var el_str  = openerp.qweb.render(this.template, {widget: this});
+            var el_node = document.createElement('div');
+                el_node.innerHTML = el_str;
+                el_node = el_node.childNodes[1];
 
-            this.$el.delegate('.product','click',function TOUCHCLICK(){ 
-                console.log('ClickStart',(new Date()).getTime());
-                self.click_product_action(new module.Product(self.pos.db.get_product_by_id(+$(this).data('product-id')))); 
-                console.log('ClickEnd',(new Date()).getTime());
-            });
+            if(this.el && this.el.parentNode){
+                this.el.parentNode.replaceChild(el_node,this.el);
+            }
+            this.el = el_node;
 
+            var list_container = el_node.querySelector('.product-list');
+            for(var i = 0, len = this.product_list.length; i < len; i++){
+                var product_node = this.render_product(this.product_list[i]);
+                product_node.addEventListener('click',this.click_product_handler);
+                list_container.appendChild(product_node);
+            };
         },
     });
 
@@ -1046,13 +1113,9 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
                     this.set_left_action_bar_visible(false);
                     this.numpad.show();
                     this.paypad.show();
-                    this.order_widget.set_display_mode('numpad');
                 }else{
                     this.numpad.hide();
                     this.paypad.hide();
-                    if(this.order_widget.display_mode === 'numpad'){
-                        this.order_widget.set_display_mode('maximized');
-                    }
                 }
             }
         },
@@ -1062,20 +1125,11 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
                 if(visible){
                     this.set_numpad_visible(false);
                     this.left_action_bar.show();
-                    this.order_widget.set_display_mode('actionbar');
                 }else{
                     this.left_action_bar.hide();
-                    if(this.order_widget.display_mode === 'actionbar'){
-                        this.order_widget.set_display_mode('maximized');
-                    }
                 }
             }
         },
-        set_fullscreen: function(){
-            if(this.el.webkitRequestFullscreen){
-                this.el.webkitRequestFullscreen();
-            }
-        },
         //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){
index d8ce116..941fdc8 100644 (file)
     </t>
 
     <t t-name="CategoryButton">
-        <li class='category-button'>
+        <li class='category-button js-category-switch' t-att-data-category-id="category.id" >
             <div class="category-img">
                 <img src="" />
             </div>
     </t>
 
     <t t-name="CategorySimpleButton">
-        <li class='category-simple-button'>
+        <li class='category-simple-button js-category-switch' t-att-data-category-id="category.id" >
             <t t-esc="category.name"/>
         </li>
     </t>
 
     <t t-name="ProductCategoriesWidget">
+        <div>
         <header>
             <ol class="breadcrumb">
                 <li class="oe-pos-categories-list">
-                    <a href="javascript:void(0)">
+                    <a href="javascript:void(0)" class="js-category-switch">
                         <img src="/point_of_sale/static/src/img/home.png" class="homeimg" />
                     </a>
                 </li>
                 <t t-foreach="widget.breadcrumb" t-as="category">
                     <li class="oe-pos-categories-list">
                         <img src="/point_of_sale/static/src/img/bc-arrow.png" class="bc-arrow" />
-                        <a href="javascript:void(0)" t-att-data-category-id="category.id">
+                        <a href="javascript:void(0)" class="js-category-switch" t-att-data-category-id="category.id">
                             <t t-esc="category.name"/>
                         </a>
                     </li>
                 </ol>
             </div>
         </t>
+        </div>
     </t>
 
     <t t-name="ProductListWidget">
         <li class='product' t-att-data-product-id="product.id">
             <div class="product-img">
                 <img src='' /> <!-- the product thumbnail -->
-                <t t-if="!product.get('to_weight')">
+                <t t-if="!product.to_weight">
                     <span class="price-tag">
-                        <t t-esc="widget.format_currency(product.get('price'))"/>
+                        <t t-esc="widget.format_currency(product.price)"/>
                     </span>
                 </t>
-                <t t-if="product.get('to_weight')">
+                <t t-if="product.to_weight">
                     <span class="price-tag">
-                        <t t-esc="widget.format_currency(product.get('price'))+'/Kg'"/>
+                        <t t-esc="widget.format_currency(product.price)+'/Kg'"/>
                     </span>
                 </t>
             </div>
             <div class="product-name">
-                <t t-esc="product.get('name')"/>
+                <t t-esc="product.name"/>
             </div>
         </li>
     </t>
 
-    <t t-name="ScrollbarWidget">
-        <div class='scrollbar'>
-            <div class='up-button button oe_e'>
-                &amp;atilde;
-            </div>
-
-            <div class='scroller'>
-            </div>
-
-            <div class='down-button button oe_e'>
-                &amp;auml;
-            </div>
-        </div>
-    </t>
-
     <t t-name="OrderWidget">
         <div class="order-container">
             <div class="order-scroller">
                 <div class="order">
                     <ul class="orderlines">
-                        <t t-if="widget.pos.get('selectedOrder').get('orderLines').length === 0">
+                        <t t-if="orderlines.length === 0">
                             <li class="orderline empty">
                                 Your shopping cart is empty
                             </li>
                         </t>
-
                     </ul>
                     <div class="summary">
-                        <div t-attf-class="line #{widget.pos.get('selectedOrder').get('orderLines').length === 0 ? 'empty' : ''}">
+                        <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 class="shadow-top"></div>
-            <span class="placeholder-ScrollbarWidget"></span>
         </div>
     </t>
 
         </div>
     </t>
 
-    <t t-name="OrderlineWidget">
-        <li class="orderline">
+    <t t-name="Orderline">
+        <li t-attf-class="orderline #{ line.selected ? 'selected' : '' }">
             <span class="product-name">
-                <t t-esc="widget.model.get_product().get('name')"/>
+                <t t-esc="line.get_product().name"/>
             </span>
             <span class="price">
-                <t t-esc="widget.format_currency(widget.model.get_display_price())"/>
+                <t t-esc="widget.format_currency(line.get_display_price())"/>
             </span>
             <ul class="info-list">
-                <t t-if="widget.model.get_quantity_str() !== '1'">
+                <t t-if="line.get_quantity_str() !== '1' || line.selected ">
                     <li class="info">
                         <em>
-                            <t t-esc="widget.model.get_quantity_str()" />
+                            <t t-esc="line.get_quantity_str()" />
                         </em>
-                        <t t-esc="widget.model.get_unit().name" />
+                        <t t-esc="line.get_unit().name" />
                         at
-                        <t t-esc="widget.format_currency(widget.model.get_unit_price())" />
+                        <t t-esc="widget.format_currency(line.get_unit_price())" />
                         /
-                        <t t-esc="widget.model.get_unit().name" />
+                        <t t-esc="line.get_unit().name" />
                     </li>
                 </t>
-                <t t-if="widget.model.get_discount_str() !== '0'">
+                <t t-if="line.get_discount_str() !== '0'">
                     <li class="info">
                         With a 
                         <em>
-                            <t t-esc="widget.model.get_discount_str()" />%
+                            <t t-esc="line.get_discount_str()" />%
                         </em>
                         discount
                     </li>
                 </colgroup>
                 <tr t-foreach="widget.currentOrderLines.toArray()" t-as="orderline">
                     <td>
-                        <t t-esc="orderline.get_product().get('name')"/>
+                        <t t-esc="orderline.get_product().name"/>
                          <t t-if="orderline.get_discount() > 0">
                             <div class="pos-disc-font">
                                 With a <t t-esc="orderline.get_discount()"/>% discount