diff options
| author | Pierre Masereel <pim@odoo.com> | 2020-01-31 13:08:42 +0000 |
|---|---|---|
| committer | Pierre Masereel <pim@odoo.com> | 2020-06-10 17:54:54 +0000 |
| commit | 09cfde14bd086bd190a8c34a157557fa363d0438 (patch) | |
| tree | 98ea6bbc36839a8b4c97a0c47f9892db97bfcbd5 /addons | |
| parent | 152b3464c0342d9b59279ca0a454b84715c6c2b8 (diff) | |
[IMP] point_of_sale: responsive UI
The point of sale user interface has been adapted to work on mobile
devices.
We modified the design of payment lines to look like payment methods and
stack them together in the mobile view.
When you had a cash method, the amount was not preffilled during
payment, now we always prefill the amount to keep it more simple.
closes odoo/odoo#51891
Task-id: 1887609
Related: odoo/upgrade#1334
Signed-off-by: pimodoo <pimodoo@users.noreply.github.com>
Diffstat (limited to 'addons')
33 files changed, 783 insertions, 465 deletions
diff --git a/addons/point_of_sale/__manifest__.py b/addons/point_of_sale/__manifest__.py index 047bccb3665..798c8bd49e5 100644 --- a/addons/point_of_sale/__manifest__.py +++ b/addons/point_of_sale/__manifest__.py @@ -59,6 +59,7 @@ 'static/src/xml/Screens/PaymentScreen/PaymentScreenNumpad.xml', 'static/src/xml/Screens/PaymentScreen/PaymentScreenElectronicPayment.xml', 'static/src/xml/Screens/PaymentScreen/PaymentScreenPaymentLines.xml', + 'static/src/xml/Screens/PaymentScreen/PaymentScreenStatus.xml', 'static/src/xml/Screens/PaymentScreen/PaymentMethodButton.xml', 'static/src/xml/Screens/PaymentScreen/PaymentScreen.xml', 'static/src/xml/Screens/ProductScreen/Orderline.xml', @@ -103,6 +104,7 @@ 'static/src/xml/Screens/ProductScreen/ControlButtons/SetPricelistButton.xml', 'static/src/xml/Screens/ProductScreen/ControlButtons/SetFiscalPositionButton.xml', 'static/src/xml/ChromeWidgets/ClientScreenButton.xml', + 'static/src/xml/Misc/MobileOrderWidget.xml', ], 'website': 'https://www.odoo.com/page/point-of-sale-shop', } diff --git a/addons/point_of_sale/models/pos_config.py b/addons/point_of_sale/models/pos_config.py index b882ef5ff4c..f85107f33f5 100644 --- a/addons/point_of_sale/models/pos_config.py +++ b/addons/point_of_sale/models/pos_config.py @@ -133,8 +133,6 @@ class PosConfig(models.Model): help='The receipt will automatically be printed at the end of each order.') iface_print_skip_screen = fields.Boolean(string='Skip Preview Screen', default=True, help='The receipt screen will be skipped if the receipt can be printed automatically.') - iface_precompute_cash = fields.Boolean(string='Prefill Cash Payment', - help='The payment input will behave similarily to bank payment input, and will be prefilled with the exact due amount.') iface_tax_included = fields.Selection([('subtotal', 'Tax-Excluded Price'), ('total', 'Tax-Included Price')], string="Tax Display", default='subtotal', required=True) iface_start_categ_id = fields.Many2one('pos.category', string='Initial Category', help='The point of sale will display this product category by default. If no category is specified, all available products will be shown.') diff --git a/addons/point_of_sale/static/src/css/pos.css b/addons/point_of_sale/static/src/css/pos.css index af5c7b57175..9211354963e 100644 --- a/addons/point_of_sale/static/src/css/pos.css +++ b/addons/point_of_sale/static/src/css/pos.css @@ -209,7 +209,7 @@ td { } /* firefox seems to ignore the relative positionning of the subwindow-container * putting this inside subwindow-container fixes it. - */ + */ .pos .subwindow .subwindow-container-fix{ height: 100%; position: relative; @@ -313,16 +313,14 @@ td { padding:0; color: gray; background: #393939; + display: flex; } /* a) The left part of the top-bar */ .pos .pos-branding{ - position: absolute; - display: table-cell; - left:0; - top:0; - width:439px; + max-width: 459px; + flex-grow: 1; height:100%; margin:0; padding:0; @@ -336,7 +334,7 @@ td { margin-left: 10px; margin-top: 5px; vertical-align:middle; -} +} .pos .pos-branding .username{ float:right; color:#DDD; @@ -350,16 +348,11 @@ td { /* b) The right part of the top-bar */ .pos .pos-rightheader { - position: absolute; - left:440px; - right:0; - top:0; + flex-grow: 1; height:100%; - display: -webkit-flex; display: flex; overflow : hidden; overflow-x: auto; - -webkit-overflow-scrolling: touch; } .pos .pos-rightheader > * { border-right: 1px solid #292929; @@ -483,7 +476,7 @@ td { /* c) The notifications indicator */ .pos .oe_status{ - float:right; + float:right; color: rgba(255,255,255,0.4); padding: 14px; line-height: 20px; @@ -528,23 +521,61 @@ td { /* ********* The leftpane contains the order, numpad and paypad ********* */ +.pos .switchpane { + height: 100px; + flex-shrink: 0; + display: flex; +} + +.pos .switchpane .order-info { + flex-grow: 1; + border-top: 1px solid #ebebeb; + padding: 8px 16px; + background-color: #FFFFFF; + color: #6ec89b; +} +.pos .switchpane .order-info h2 { + padding: 0px; + margin: 3px 0px; + font-weight: bold; +} + +.pos .switchpane .btn-switchpane { + background-color: #6ec89b; + border-radius: 0px; + color: #FFFFFF; + font-size: 15px; + font-weight: bold; + flex-grow: 1; + flex-basis: 50%; +} + +.pos .switchpane .btn-switchpane.secondary { + background-color: #FFFFFF; + color: #6ec89b; +} + + + .pos .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:0; - border-right: solid 1px #CECBCB; border-right: solid 3px #787878; background: #e2e2e2; + height: 100%; + display: flex; + flex-direction: column; + flex-grow: 1; + max-width: 500px; } .pos .leftpane .pads { border-top: solid 3px rgb(110, 200, 155); + display: flex; + flex-direction: column; +} + +.pos .leftpane .pads .subpads { + display: flex; + flex-direction: row; } /* ********* The control buttons ********* */ @@ -604,7 +635,7 @@ td { .pos .control-button.highlight, .pos .button.highlight { background: #6EC89B !important; - border: solid 1px #64AF8A !important; + border: solid 1px #64AF8A !important; color: white !important; } .pos .control-button.altlight, @@ -625,15 +656,12 @@ td { /* ********* The actionpad (payment, set customer) ********* */ .pos .actionpad { - display: inline-block; - float: left; padding: 0; margin: 16px; margin-top: 8px; margin-right: 0; text-align: center; vertical-align: top; - width: 186px; border: none; border-radius: 0; border-top: 1px solid; @@ -641,6 +669,7 @@ td { border-color: #bfbfbf; border-top-left-radius: 4px; border-bottom-left-radius: 4px; + flex-grow: 1; } .pos .actionpad .button { position: relative; @@ -716,8 +745,6 @@ td { /* ********* The Numpad ********* */ .pos .numpad { - display: inline-block; - float: right; text-align: center; width: 216px; margin: 16px; @@ -782,13 +809,24 @@ td { /* ********* The right pane contains the screens and headers ********* */ .pos .rightpane { - position: absolute; - top: 0; - bottom:0; - left: 440px; - right: 0; - vertical-align: top; - -webkit-transform: translate3d(0,0,0); + height: 100%; + display: flex; + flex-direction: column; + flex-basis: 25%; + flex-grow: 1; +} + +.pos .products-widget { + display: flex; + flex-direction: column; + flex-grow: 1; + overflow: hidden; +} + +.pos .product-list-container { + overflow: hidden; + overflow-y: scroll; + flex-grow: 1; } .pos .rightpane-header { @@ -797,6 +835,7 @@ td { border-bottom: 1px solid #c7c7c7; background: #d3d3d3; text-align: center; + display: flex; } /* ********* The product list ********* */ @@ -805,6 +844,8 @@ td { padding: 10px; text-align: left; -webkit-transform: translate3d(0,0,0); + display: flex; + flex-wrap: wrap; } .pos .product-list-scroller{ @@ -820,13 +861,6 @@ td { -webkit-transform: translate3d(0,0,0); } -.pos .product-list-container { - position:absolute; - top:0px; - bottom:0px; - left:0px; - right:0px; -} /* a) the product list navigation bar */ @@ -870,11 +904,18 @@ td { margin: 12px 6px; } +@media screen and (max-width: 768px) { + .pos .breadcrumb-button.breadcrumb-home { + width: auto; + } +} + /* b) the search box */ .pos .searchbox { position: absolute; right: 2px; + flex-grow: 1; } .pos .searchbox input { width: 100px; @@ -915,6 +956,26 @@ td { cursor: pointer; } +@media screen and (max-width: 768px) { + .search-clear.left { + position: relative; + top: -40px; + left: 5%; + } + + .search-clear.right { + position: relative; + top: -70px; + left: 85%; + } + .pos .searchbox input { + width: 70%; + } + .pos .searchbox { + position: relative; + } +} + /* c) the categories list */ .pos .categories { @@ -1022,7 +1083,7 @@ td { line-height: 14px; width: 100%; /* for some reason the -90deg orientation doesn't match the -webkit-linear-gradient. It should be 180deg here. - * webkit also insists on rendering *both* gradients instead of only the native one. So it doesn't looks right. ugh. + * webkit also insists on rendering *both* gradients instead of only the native one. So it doesn't looks right. ugh. background: linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1)); */ /*background:#FFF;*/ padding: 3px; @@ -1063,6 +1124,34 @@ td { vertical-align: middle; } +@media screen and (max-width: 768px) { + .pos .product-list { + padding: 0px; + } + .pos .product { + width: 32%; + height: auto; + margin: 0px !important; + } + .pos .product:after { + content: ""; + display: block; + padding-bottom: 100%; + } + .pos .product .product-img { + position: absolute; + width: 100%; + height: 100%; + } + .pos .product .product-img img { + max-height: none; + max-width: none; + } + +} + + + .pos .product .price-tag { position: absolute; top: 2px; @@ -1133,12 +1222,22 @@ td { /* a) Generic Screen Layout Constructs */ .screen .screen-content{ + position: relative; margin: 0px auto; max-width: 1024px; text-align: left; height: 100%; overflow: hidden; - position: relative; + display: flex; + flex-direction: column; +} + +.screen .screen-full-width{ + height: 100%; + width: 100%; + display: flex; + flex-wrap: wrap; + flex-direction: row; } @media screen and (min-width: 1024px) { @@ -1149,20 +1248,22 @@ td { } .screen .top-content{ - position: absolute; - left: 0px; top: 0px; right: 0px; height: 64px; border-bottom: dashed 1px rgb(215,215,215); text-align: center; + display: flex; + padding-right: 10px; + padding-left: 10px; } .screen .top-content .button { - position: absolute; - top: 0px; line-height: 32px; padding: 3px 13px; font-size: 20px; background: rgb(230, 230, 230); - margin: 12px; + margin-top: 12px; + margin-bottom: 12px; + margin-left: 6px; + margin-right: 6px; border-radius: 3px; border: solid 1px rgb(209, 209, 209); cursor: pointer; @@ -1184,43 +1285,50 @@ td { .screen .top-content .button.highlight:hover { background: rgb(120,210,165); } -.screen .top-content .button.back { - left: 0px; - margin-left: 16px; + +.screen .top-content .top-content-center { + flex-grow: 1; } -.screen .top-content .button.next{ - right: 0px; - margin-right: 16px; + +.screen .main-content{ + display: flex; + flex-grow: 1; + flex-wrap: wrap; + overflow-y: scroll; } .screen .left-content{ - position: absolute; - left:0px; top: 64px; bottom: 0px; - right:50%; overflow-x: hidden; overflow-y: auto; border-right: dashed 1px rgb(215,215,215); + flex-grow: 1; + min-width: 200px; } -.screen .left-content.pc40{ - right: 66%; -} + .screen .right-content{ - position: absolute; - right:0px; top: 64px; bottom: 0px; - left:50%; overflow-x: hidden; overflow-y: auto; + flex-grow: 1; + display: flex; + flex-direction: column; } -.screen .right-content.pc60{ - left:34%; + +.pos .btn-switch-payment { + background-color: #6ec89b; + border-radius: 0px; + color: #FFFFFF; + font-size: 15px; + font-weight: bold; + height: 100px; + width: 100%; } .screen .centered-content{ - position: absolute; - right:25%; top: 64px; bottom: 0px; - left:25%; border-right: dashed 1px rgb(215,215,215); border-left: dashed 1px rgb(215,215,215); overflow-x: hidden; overflow-y: auto; + max-width: 512px; + margin-left: auto; + margin-right: auto; } .screen .full-content{ position: absolute; @@ -1233,7 +1341,8 @@ td { .pos .screen .layout-table { border:none; width:100%; - height:100%; + display: flex; + flex-grow: 1; } .pos .screen .header-row { @@ -1262,23 +1371,26 @@ td { /* b) The payment screen */ +.pos .payment-buttons-container { + display: flex; + flex-direction: row; + flex-wrap: wrap; +} + .pos .payment-numpad { - display: inline-block; - width: 50%; box-sizing: border-box; - padding: 16px; + margin: 16px; text-align: center; - float: left; + flex-grow: 1; } .pos .payment-numpad .numpad { - float: none; border-radius: 4px; border-top: 1px solid; border-left: 1px solid; border-color: #cacaca; width: 296px; - overflow: hidden; - margin: 0; + height: 100%; + margin: auto; } .pos .payment-numpad .numpad button { width: 74px; @@ -1299,61 +1411,61 @@ td { } .pos .paymentlines { - width: 100%; border-spacing: 0px 10px; border-collapse: inherit; + margin: 16px; } -.pos .paymentlines .controls { - width: 40px; -} -.pos .paymentlines .body{ - background: white; - border-radius: 3px; -} -.pos .paymentline{ - display: grid; - grid-template-columns: 4fr 3fr 1fr; - font-size: 22px; - border-bottom: 10px solid #f0eeee; + +.paymentlines .paymentline:first-child { + border-top-width: 1px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; } -.pos .paymentline.electronic_payment { - display: grid; - grid-template-columns: 7fr 1fr; +.paymentlines .paymentline:last-child { + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; } -.pos .paymentline.heading { + +.pos .paymentline { + background: #e2e2e2; + line-height: 74px; font-size: 16px; - font-weight: bold; + border: solid 1px rgb(202, 202, 202); + border-top-width: 0px; + cursor: pointer; + display: flex; + padding-left: 30px; + padding-right: 30px; + flex-grow: 1; + flex-basis: 100%; } -.pos .paymentline.selected { - font-size: 22px; - background: #6EC89B; +.paymentline:active { + background: black; + border-color: black; color: white; } -.pos .paymentline.selected.payment-terminal { - border-bottom: 0px; -} -.pos .paymentline > *{ - padding: 8px 12px; +.paymentline .payment-name { + flex-grow: 1; + margin-left: 10px; + margin-right: 10px; } -.pos .paymentline .col-tendered { - min-width: 90px; +.paymentline .payment-amount { + margin-left: 10px; + margin-right: 10px; } -.pos .paymentline .col-name { - display: flex; - align-items: center; - font-size: 16px; +.paymentline .delete-button { + margin-left: 10px; + margin-right: 10px; } -.pos .paymentline .delete-button{ - cursor: pointer; - text-align: center; +.paymentline.selected{ + background: white; } + .pos .payment-buttons { - display: inline-block; - width: 50%; box-sizing: border-box; - padding: 16px; + margin: 16px; padding-left: 0; - float: right; + flex-grow: 5; } .payment-screen .payment-buttons .button { background: #e2e2e2; @@ -1457,6 +1569,28 @@ td { padding-right: 7px; } +@media screen and (max-width: 768px) { + .pos .paymentlines-container { + min-height: 0px; + } + .payment-status-container { + font-size: 22px; + } + .payment-screen .payment-buttons { + display: flex; + flex-wrap: wrap; + } + .payment-screen .payment-buttons .button { + flex-basis: 30%; + flex-grow: 1; + border: 1px solid rgba(0,0,0,0.2); + border-radius: 0px; + } + .payment-screen .payment-buttons .button .fa { + display: none; + } +} + /* c) The receipt screen */ .pos .receipt-screen .centered-content .button { @@ -1509,8 +1643,8 @@ td { .o_notification_manager, .oe_leftbar, .oe_loading, - .pos .pos-topheader, - .pos .pos-leftpane, + .pos .pos-topheader, + .pos .pos-leftpane, .pos .keyboard_frame, .pos .receipt-screen header, .pos .receipt-screen .top-content, @@ -1521,9 +1655,9 @@ td { .pos, .pos .pos-content, .pos .rightpane, - .pos .screen, - .pos .window, - .pos .window .subwindow, + .pos .screen, + .pos .window, + .pos .window .subwindow, .pos .subwindow .subwindow-container{ display: block; position: static; @@ -1628,6 +1762,10 @@ td { /* e) The Client List Screen */ +.pos .clientlist-screen .full-content{ + overflow: auto; +} + .pos .clientlist-screen .client-list{ font-size: 16px; width: 100%; @@ -1758,52 +1896,69 @@ td { .pos .clientlist-screen .client-detail > .empty{ opacity: 0.3; } -.pos .clientlist-screen .searchbox{ - right: auto; - margin-left: -90px; - margin-top:8px; - left: 50%; + +.pos .clientlist-screen .button.new-customer { + min-width: 30px; } -.pos .clientlist-screen .searchbox input{ + +.pos .searchbox-client { + padding: 3px 13px; + margin-top: 12px; + margin-bottom : 12px; +} + +.pos .searchbox-client input { width: 120px; + border: 1px solid #cecbcb; + padding: 10px 20px; + padding-left: 38px; + padding-right: 33px; + background-color: white; + border-radius: 20px; + font-family: "Lato","Lucida Grande", Helvetica, Verdana, Arial; + font-size: 13px; } -.pos .clientlist-screen .button.new-customer { - left: 50%; - margin-left: 120px; +.pos .searchbox-client input:focus { + outline: none; + box-shadow: 0px 0px 0px 2px rgb(153, 153, 255) inset; + color: rgb(153, 153, 255); } -/* These css declarations is to make sure that the list of clients - * always occupies the remaining space regardless when the client - * details is displayed or not. - */ - .clientlist-screen .client-window { +.pos .search-clear-client { + position: absolute; + top: 9px; + width: 30px; + height: 30px; display: flex; - flex-flow: column; - height: 100%; + align-items: center; + justify-content: center; } -.clientlist-screen .client-window section.list { - flex: 1; - overflow: auto; +.search-clear-client.left { + left: 11px; + color: #808080; } +.search-clear-client.right { + left: 145px; + color: #808080; + cursor: pointer; +} + + + + /* ********* The OrderWidget ********* */ .pos .order-container{ - position: absolute; top: 0px; width:100%; height: 100%; background: white; -} - -.pos .order-scroller{ - width:100%; - height:100%; + flex-grow: 1; overflow: hidden; overflow-y: auto; - -webkit-overflow-scrolling: touch; } .pos .scrollable-y{ @@ -1988,6 +2143,8 @@ td { } .paymentmethods { margin: 16px; + display: flex; + flex-wrap: wrap; } .paymentmethods .button { background: #e2e2e2; @@ -1996,7 +2153,6 @@ td { border: solid 1px rgb(202, 202, 202); border-top-width: 0px; cursor: pointer; - text-align: center; } .paymentmethods .button:first-child { border-top-width: 1px; @@ -2017,8 +2173,39 @@ td { color: white; border-color: #6EC89B; } +.paymentmethod { + display: flex; + padding-left: 30px; + padding-right: 30px; + flex-grow: 1; + flex-basis: 100%; +} +.paymentmethod .payment-name { + flex-grow: 1; + margin-left: 10px; + margin-right: 10px; +} +.paymentmethod .payment-amount { + margin-left: 10px; + margin-right: 10px; +} +.paymentmethod .delete-button { + margin-left: 10px; + margin-right: 10px; +} +.paymentmethod.selected{ + background: white; +} - +@media screen and (max-width: 768px) { + .paymentmethods .button { + border: solid 1px rgb(202, 202, 202); + border-radius: 3px; + } + .paymentmethod { + flex-basis: 30%; + } +} /* ********* The ActionBarWidget ********* */ @@ -2098,7 +2285,7 @@ td { top: 10px; width: 200px; font-size: 10px; - + background: rgba(0,0,0,0.82); color: white; padding-bottom: 10px; @@ -2188,7 +2375,8 @@ td { .pos .modal-dialog .popup{ position: absolute; margin: auto; - width:500px; + max-width:500px; + width: 100%; height:400px; text-align:center; font-size:20px; @@ -2350,7 +2538,7 @@ td { line-height: 50px; margin-top: -1px; border-top: solid 3px rgba(60,60,60,0.1); - + } .pos .popup.popup-selection .selection-item { width: auto; diff --git a/addons/point_of_sale/static/src/js/Misc/MobileOrderWidget.js b/addons/point_of_sale/static/src/js/Misc/MobileOrderWidget.js new file mode 100644 index 00000000000..024a77b3882 --- /dev/null +++ b/addons/point_of_sale/static/src/js/Misc/MobileOrderWidget.js @@ -0,0 +1,39 @@ +odoo.define('point_of_sale.MobileOrderWidget', function(require) { + 'use strict'; + + const PosComponent = require('point_of_sale.PosComponent'); + const Registries = require('point_of_sale.Registries'); + + class MobileOrderWidget extends PosComponent { + constructor() { + super(...arguments); + this.pane = this.props.pane; + this.update(); + } + get order() { + return this.env.pos.get_order(); + } + mounted() { + this.order.on('change', () => { + this.update(); + this.render(); + }); + this.order.orderlines.on('change', () => { + this.update(); + this.render(); + }); + } + update() { + const total = this.order ? this.order.get_total_with_tax() : 0; + const tax = this.order ? total - this.order.get_total_without_tax() : 0; + this.total = this.env.pos.format_currency(total); + this.items_number = this.order ? this.order.orderlines.reduce((items_number,line) => items_number + line.quantity, 0) : 0; + } + } + + MobileOrderWidget.template = 'MobileOrderWidget'; + + Registries.Component.add(MobileOrderWidget); + + return MobileOrderWidget; +}); diff --git a/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreenPaymentLines.js b/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreenPaymentLines.js index 14bed3f700f..8f2311468f3 100644 --- a/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreenPaymentLines.js +++ b/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreenPaymentLines.js @@ -8,22 +8,6 @@ odoo.define('point_of_sale.PaymentScreenPaymentLines', function(require) { formatLineAmount(paymentline) { return this.env.pos.format_currency_no_symbol(paymentline.get_amount()); } - get changeText() { - return this.env.pos.format_currency(this.currentOrder.get_change()); - } - get totalDueText() { - return this.env.pos.format_currency( - this.currentOrder.get_total_with_tax() + this.currentOrder.get_rounding_applied() - ); - } - get remainingText() { - return this.env.pos.format_currency( - this.currentOrder.get_due() > 0 ? this.currentOrder.get_due() : 0 - ); - } - get currentOrder() { - return this.env.pos.get_order(); - } selectedLineClass(line) { return { 'payment-terminal': line.get_payment_status() }; } diff --git a/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreenStatus.js b/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreenStatus.js new file mode 100644 index 00000000000..12ccaa841f7 --- /dev/null +++ b/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreenStatus.js @@ -0,0 +1,30 @@ +odoo.define('point_of_sale.PaymentScreenStatus', function(require) { + 'use strict'; + + const PosComponent = require('point_of_sale.PosComponent'); + const Registries = require('point_of_sale.Registries'); + + class PaymentScreenStatus extends PosComponent { + get changeText() { + return this.env.pos.format_currency(this.currentOrder.get_change()); + } + get totalDueText() { + return this.env.pos.format_currency( + this.currentOrder.get_total_with_tax() + this.currentOrder.get_rounding_applied() + ); + } + get remainingText() { + return this.env.pos.format_currency( + this.currentOrder.get_due() > 0 ? this.currentOrder.get_due() : 0 + ); + } + get currentOrder() { + return this.env.pos.get_order(); + } + } + PaymentScreenStatus.template = 'PaymentScreenStatus'; + + Registries.Component.add(PaymentScreenStatus); + + return PaymentScreenStatus; +}); diff --git a/addons/point_of_sale/static/src/js/Screens/ProductScreen/HomeCategoryBreadcrumb.js b/addons/point_of_sale/static/src/js/Screens/ProductScreen/HomeCategoryBreadcrumb.js index 4c170777a1b..28641236176 100644 --- a/addons/point_of_sale/static/src/js/Screens/ProductScreen/HomeCategoryBreadcrumb.js +++ b/addons/point_of_sale/static/src/js/Screens/ProductScreen/HomeCategoryBreadcrumb.js @@ -3,8 +3,42 @@ odoo.define('point_of_sale.HomeCategoryBreadcrumb', function(require) { const PosComponent = require('point_of_sale.PosComponent'); const Registries = require('point_of_sale.Registries'); + const { useListener } = require('web.custom_hooks'); - class HomeCategoryBreadcrumb extends PosComponent {} + class HomeCategoryBreadcrumb extends PosComponent { + constructor() { + super(...arguments); + useListener('categ-popup', this._categPopup); + } + get selectedCategoryId() { + return this.env.pos.get('selectedCategoryId'); + } + async _categPopup() { + let selectionList = [{ + id: 0, + label:'All Items', + isSelected: 0 === this.env.pos.get('selectedCategoryId'), + item: {id:0,name:'All Items'}, + }]; + let subs = this.props.subcategories.map(category => ({ + id: category.id, + label: category.name, + isSelected: category.id === this.env.pos.get('selectedCategoryId'), + item: category, + })); + selectionList = selectionList.concat(subs); + const { confirmed, payload: selectedCategory } = await this.showPopup( + 'SelectionPopup', + { + title: this.env._t('Select the category'), + list: selectionList, + } + ); + if (confirmed) { + this.trigger('switch-category', selectedCategory.id); + } + } + } HomeCategoryBreadcrumb.template = 'HomeCategoryBreadcrumb'; Registries.Component.add(HomeCategoryBreadcrumb); diff --git a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ProductScreen.js b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ProductScreen.js index 074ea00fa03..5658b5b1493 100644 --- a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ProductScreen.js +++ b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ProductScreen.js @@ -31,6 +31,7 @@ odoo.define('point_of_sale.ProductScreen', function(require) { useWithBarcode: true, }); this.numpadMode = 'quantity'; + this.mobile_pane = this.props.mobile_pane || 'right'; } mounted() { this.env.pos.on('change:selectedClient', this.render, this); @@ -267,6 +268,16 @@ odoo.define('point_of_sale.ProductScreen', function(require) { _onClickPay() { this.showScreen('PaymentScreen'); } + switchPane() { + if (this.mobile_pane === "left") { + this.mobile_pane = "right"; + this.render(); + } + else { + this.mobile_pane = "left"; + this.render(); + } + } } ProductScreen.template = 'ProductScreen'; diff --git a/addons/point_of_sale/static/src/js/main.js b/addons/point_of_sale/static/src/js/main.js index e174717723a..3258481198f 100644 --- a/addons/point_of_sale/static/src/js/main.js +++ b/addons/point_of_sale/static/src/js/main.js @@ -10,6 +10,20 @@ odoo.define('web.web_client', function (require) { owl.config.mode = env.isDebug() ? 'dev' : 'prod'; owl.Component.env = env; + function setupResponsivePlugin(env) { + const isMobile = () => window.innerWidth <= 768; + env.isMobile = isMobile(); + const updateEnv = owl.utils.debounce(() => { + if (env.isMobile !== isMobile()) { + env.isMobile = !env.isMobile; + env.qweb.forceUpdate(); + } + }, 15); + window.addEventListener("resize", updateEnv); + } + + setupResponsivePlugin(owl.Component.env); + async function startPosApp(webClient) { Registries.Component.freeze(); await env.session.is_bound; diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 1a8d5fad652..459ac9138d5 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -2845,9 +2845,7 @@ exports.Order = Backbone.Model.extend({ add_paymentline: function(payment_method) { this.assert_editable(); var newPaymentline = new exports.Paymentline({},{order: this, payment_method:payment_method, pos: this.pos}); - if(!payment_method.is_cash_count || this.pos.config.iface_precompute_cash){ - newPaymentline.set_amount(this.get_due() ); - }; + newPaymentline.set_amount(this.get_due()); this.paymentlines.add(newPaymentline); this.select_paymentline(newPaymentline); return newPaymentline; diff --git a/addons/point_of_sale/static/src/xml/Chrome.xml b/addons/point_of_sale/static/src/xml/Chrome.xml index bf3d483fd56..f99905f8024 100644 --- a/addons/point_of_sale/static/src/xml/Chrome.xml +++ b/addons/point_of_sale/static/src/xml/Chrome.xml @@ -8,12 +8,14 @@ <div class="pos-topheader"> <div t-if="tempScreen.isShown" class="block-top-header" /> <div class="pos-branding"> - <img class="pos-logo" t-on-click="trigger('toggle-debug-widget')" + <img t-if= "!env.isMobile" class="pos-logo" t-on-click="trigger('toggle-debug-widget')" src="/point_of_sale/static/src/img/logo.png" alt="Logo" /> </div> <div class="pos-rightheader"> <OrderSelector /> - <CashierName /> + <t t-if="!env.isMobile"> + <CashierName /> + </t> <SaleDetailsButton t-if="env.pos.proxy.printer" /> <ProxyStatus t-if="env.pos.config.use_proxy" /> <ClientScreenButton t-if="clientScreenButtonIsShown" /> diff --git a/addons/point_of_sale/static/src/xml/Misc/MobileOrderWidget.xml b/addons/point_of_sale/static/src/xml/Misc/MobileOrderWidget.xml new file mode 100644 index 00000000000..883631a2a8a --- /dev/null +++ b/addons/point_of_sale/static/src/xml/Misc/MobileOrderWidget.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates id="template" xml:space="preserve"> + + <t t-name="MobileOrderWidget" owl="1"> + <div class="switchpane"> + <t t-if="pane === 'right'"> + <button class="btn-switchpane" t-on-click="trigger('click-pay')"> + <h1>Pay</h1> + <span><t t-esc="total" /></span> + </button> + <button class="btn-switchpane secondary" t-on-click="trigger('switchpane')"> + <h1>Review</h1> + <span><t t-esc="items_number"/> items</span> + </button> + </t> + <t t-if="pane === 'left'"> + <button class="btn-switchpane" t-on-click="trigger('switchpane')"><h1>Back</h1></button> + </t> + </div> + </t> + +</templates> diff --git a/addons/point_of_sale/static/src/xml/Screens/ClientListScreen/ClientLine.xml b/addons/point_of_sale/static/src/xml/Screens/ClientListScreen/ClientLine.xml index c5aecab131a..ffa45fbaefa 100644 --- a/addons/point_of_sale/static/src/xml/Screens/ClientListScreen/ClientLine.xml +++ b/addons/point_of_sale/static/src/xml/Screens/ClientListScreen/ClientLine.xml @@ -10,7 +10,7 @@ <td> <t t-esc="props.partner.address" /> </td> - <td> + <td t-if="!env.isMobile"> <t t-esc="props.partner.phone or props.partner.mobile or ''" /> </td> </tr> diff --git a/addons/point_of_sale/static/src/xml/Screens/ClientListScreen/ClientListScreen.xml b/addons/point_of_sale/static/src/xml/Screens/ClientListScreen/ClientListScreen.xml index a83c4db173b..5ac85f47bae 100644 --- a/addons/point_of_sale/static/src/xml/Screens/ClientListScreen/ClientListScreen.xml +++ b/addons/point_of_sale/static/src/xml/Screens/ClientListScreen/ClientListScreen.xml @@ -4,27 +4,32 @@ <t t-name="ClientListScreen" owl="1"> <div class="clientlist-screen screen" t-on-activate-edit-mode="activateEditMode"> <div class="screen-content"> - <section class="top-content"> - <span class="button back" t-on-click="back"> + <div class="top-content"> + <div class="button back" t-on-click="back"> <i class="fa fa-angle-double-left"></i> - Cancel - </span> - <span class="searchbox"> + <t t-if="!env.isMobile"> Cancel</t> + </div> + <div class="searchbox-client top-content-center"> <input placeholder="Search Customers" t-on-keyup="updateClientList" /> - <span class="search-clear"></span> - </span> - <span class="searchbox"></span> - <span class="button new-customer" role="img" aria-label="Add a customer" + <span class="search-clear-client"></span> + </div> + <div class="button new-customer" role="img" aria-label="Add a customer" t-on-click="trigger('activate-edit-mode', { isNewClient: true })" title="Add a customer"> <i class="fa fa-user"></i> <i class="fa fa-plus"></i> - </span> - <span t-if="isNextButtonVisible" t-on-click="clickNext" + </div> + <div t-if="isNextButtonVisible" t-on-click="clickNext" class="button next highlight"> - <t t-esc="nextButton.text" /> - </span> - </section> + <t t-if="!env.isMobile"> + <t t-esc="nextButton.text" /> + </t> + <t t-else=""> + <i t-if="nextButton.command === 'deselect'" class="fa fa-trash"></i> + <i t-if="nextButton.command === 'set'" class="fa fa-check"></i> + </t> + </div> + </div> <section class="full-content"> <div class="client-window"> <section class="subwindow collapsed"> @@ -49,7 +54,7 @@ <tr> <th>Name</th> <th>Address</th> - <th>Phone</th> + <th t-if="!env.isMobile">Phone</th> </tr> </thead> <tbody class="client-list-contents"> diff --git a/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentMethodButton.xml b/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentMethodButton.xml index 373a7578f3f..dacf7e96185 100644 --- a/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentMethodButton.xml +++ b/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentMethodButton.xml @@ -4,7 +4,9 @@ <t t-name="PaymentMethodButton" owl="1"> <div class="button paymentmethod" t-on-click="trigger('new-payment-line', props.paymentMethod)"> - <t t-esc="props.paymentMethod.name" /> + <div class="payment-name"> + <t t-esc="props.paymentMethod.name" /> + </div> </div> </t> diff --git a/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentScreen.xml b/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentScreen.xml index 6d3e0f0f9cf..e2382e2f3f3 100644 --- a/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentScreen.xml +++ b/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentScreen.xml @@ -4,72 +4,95 @@ <t t-name="PaymentScreen" owl="1"> <div class="payment-screen screen" t-att-class="{ oe_hidden: !props.isShown }"> <div class="screen-content"> - <div class="top-content"> - <span class="button back" - t-on-click="showScreen('ProductScreen')"> - <i class="fa fa-angle-double-left"></i> - Back - </span> - <h1>Payment</h1> - <span class="button next" t-att-class="{ highlight: currentOrder.is_paid() }" - t-on-click="validateOrder(false)"> - Validate - <i class="fa fa-angle-double-right"></i> - </span> - </div> - <div class="left-content pc40 touch-scrollable scrollable-y"> - <div class="paymentmethods-container"> - <div class="paymentmethods"> - <t t-foreach="env.pos.payment_methods" t-as="paymentMethod" - t-key="paymentMethod.id"> - <PaymentMethodButton paymentMethod="paymentMethod" /> - </t> + <t t-if="!env.isMobile"> + <div class="top-content"> + <div class="button back" + t-on-click="showScreen('ProductScreen')"> + <i class="fa fa-angle-double-left"></i> + Back + </div> + <div class="top-content-center"><h1>Payment</h1></div> + <div class="button next" t-att-class="{ highlight: currentOrder.is_paid() }" + t-on-click="validateOrder(false)"> + Validate + <i class="fa fa-angle-double-right"></i> </div> </div> - </div> - <div class="right-content pc60 touch-scrollable scrollable-y"> - - <section class="paymentlines-container"> - <PaymentScreenPaymentLines paymentLines="paymentLines" /> - </section> - - <section class="payment-numpad"> - <PaymentScreenNumpad /> - </section> - - <div class="payment-buttons"> - <div class="button" t-on-click="selectClient"> - <i class="fa fa-user" role="img" aria-label="Customer" - title="Customer" /> - <span class="js_customer_name"> - <t t-if="env.pos.get_client()"> - <t t-esc="env.pos.get_client().name" /> - </t> - <t t-if="!env.pos.get_client()"> - Customer + </t> + <div class="main-content"> + <div class="left-content"> + <t t-if="env.isMobile"> + <section class="paymentlines-container"> + <PaymentScreenStatus paymentLines="paymentLines" /> + </section> + </t> + <div class="paymentmethods-container"> + <PaymentScreenPaymentLines paymentLines="paymentLines" /> + <div class="paymentmethods"> + <t t-foreach="env.pos.payment_methods" t-as="paymentMethod" + t-key="paymentMethod.id"> + <PaymentMethodButton paymentMethod="paymentMethod" /> </t> - </span> + </div> </div> - <div t-if="env.pos.config.module_account" class="button js_invoice" - t-att-class="{ highlight: currentOrder.is_to_invoice() }" - t-on-click="toggleIsToInvoice"> - <i class="fa fa-file-text-o" /> Invoice - </div> - <div class="button js_email" - t-att-class="{ highlight: currentOrder.is_to_email() }" - t-on-click="toggleIsToEmail"> - <i class="fa fa-inbox" /> Email - </div> - <div t-if="env.pos.config.tip_product_id" class="button js_tip" - t-on-click="addTip"> - <i class="fa fa-heart" /> Tip - </div> - <div t-if="env.pos.config.iface_cashdrawer" class="button js_cashdrawer" - t-on-click="openCashbox"> - <i class="fa fa-archive" /> Open Cashbox + </div> + <div class="right-content"> + <t t-if="!env.isMobile"> + <section class="paymentlines-container"> + <PaymentScreenStatus paymentLines="paymentLines" /> + </section> + </t> + + <div class="payment-buttons-container"> + <section class="payment-numpad"> + <PaymentScreenNumpad /> + </section> + + <div class="payment-buttons"> + <div class="button" t-on-click="selectClient"> + <i class="fa fa-user" role="img" aria-label="Customer" + title="Customer" /> + <span class="js_customer_name"> + <t t-if="env.pos.get_client()"> + <t t-esc="env.pos.get_client().name" /> + </t> + <t t-if="!env.pos.get_client()"> + Customer + </t> + </span> + </div> + <div t-if="env.pos.config.module_account" class="button js_invoice" + t-att-class="{ highlight: currentOrder.is_to_invoice() }" + t-on-click="toggleIsToInvoice"> + <i class="fa fa-file-text-o" /> Invoice + </div> + <div class="button js_email" + t-att-class="{ highlight: currentOrder.is_to_email() }" + t-on-click="toggleIsToEmail"> + <i class="fa fa-inbox" /> Email + </div> + <div t-if="env.pos.config.tip_product_id" class="button js_tip" + t-on-click="addTip"> + <i class="fa fa-heart" /> Tip + </div> + <div t-if="env.pos.config.iface_cashdrawer" class="button js_cashdrawer" + t-on-click="openCashbox"> + <i class="fa fa-archive" /> Open Cashbox + </div> + </div> </div> </div> </div> + <t t-if="env.isMobile"> + <div class="switchpane"> + <button class="btn-switchpane" t-att-class="{ secondary: !currentOrder.is_paid() }" t-on-click="validateOrder(false)"> + <h1>Validate</h1> + </button> + <button class="btn-switchpane secondary" t-on-click="showScreen('ProductScreen', {mobile_pane: 'left'})"> + <h1>Review</h1> + </button> + </div> + </t> </div> </div> </t> diff --git a/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentScreenPaymentLines.xml b/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentScreenPaymentLines.xml index 14aca829264..43a3e955cdb 100644 --- a/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentScreenPaymentLines.xml +++ b/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentScreenPaymentLines.xml @@ -2,114 +2,57 @@ <templates id="template" xml:space="preserve"> <t t-name="PaymentScreenPaymentLines" owl="1"> - <div t-if="props.paymentLines.length === 0" class="paymentlines-empty"> - <div class="total"> - <t t-esc="totalDueText" /> - </div> - <div class="message"> - Please select a payment method. - </div> - </div> - - <div t-else=""> - <div class="payment-status-container"> - <div> - <div class="payment-status-remaining"> - <span class="label">Remaining</span> - <span class="amount" - t-att-class="{ highlight: currentOrder.get_due() > 0 }"> - <t t-esc="remainingText" /> - </span> - </div> - <div class="payment-status-total-due"> - <span class="label">Total Due</span> - <span> - <t t-esc="totalDueText" /> - </span> - </div> - </div> - <div> - <div class="payment-status-change"> - <span class="label">Change</span> - <span class="amount" - t-att-class="{ highlight: currentOrder.get_change() > 0 }"> - <t t-esc="changeText" /> - </span> - </div> - </div> - </div> - <hr /> <div class="paymentlines"> - <div class="paymentline heading"> - <div>Tendered</div> - <div>Method</div> - <div></div> - </div> - <div class="body"> - <t t-foreach="props.paymentLines" t-as="line" t-key="line.cid"> - <t t-if="line.selected"> - <div class="paymentline selected" - t-att-class="selectedLineClass(line)" - t-on-click="trigger('select-payment-line', { cid: line.cid })"> - <t - t-if="line and line.payment_status and ['done', 'waitingCard', 'waiting', 'reversing', 'reversed'].includes(line.payment_status)"> - <div class="col-tendered"> - <t - t-esc="env.pos.format_currency_no_symbol(line.get_amount())" /> - </div> + <t t-foreach="props.paymentLines" t-as="line" t-key="line.cid"> + <t t-if="line.selected"> + <div class="paymentline selected" + t-att-class="selectedLineClass(line)" + t-on-click="trigger('select-payment-line', { cid: line.cid })"> + <div class="payment-name"> + <t t-esc="line.payment_method.name" /> + </div> + <div class="payment-amount"> + <t t-if="line and line.payment_status and ['done', 'waitingCard', 'waiting', 'reversing', 'reversed'].includes(line.payment_status)"> + <t t-esc="env.pos.format_currency_no_symbol(line.get_amount())" /> </t> <t t-else=""> - <div class="col-tendered edit"> <t t-esc="formatLineAmount(line)" /> - </div> - </t> - <div class="col-name"> - <t t-esc="line.payment_method.name" /> - </div> - <t - t-if="line and line.payment_status and ['done', 'reversed', 'reversing'].includes(line.payment_status)"> - <div /> - </t> - <t t-else=""> - <div class="delete-button" - t-on-click="trigger('delete-payment-line', { cid: line.cid })" - aria-label="Delete" title="Delete"> - <i class="fa fa-times-circle" /> - </div> </t> </div> - <t t-if="line and line.payment_status"> - <PaymentScreenElectronicPayment line="line" /> - </t> + <div class="delete-button" + t-on-click="trigger('delete-payment-line', { cid: line.cid })" + aria-label="Delete" title="Delete"> + <i class="fa fa-times-circle" /> + </div> + </div> + <t t-if="line and line.payment_status"> + <PaymentScreenElectronicPayment line="line" /> </t> - <t t-else=""> - <div class="paymentline" - t-att-class="unselectedLineClass(line)" - t-on-click="trigger('select-payment-line', { cid: line.cid })"> - <div class="col-tendered"> - <t - t-esc="env.pos.format_currency_no_symbol(line.get_amount())" /> - </div> - <div class="col-name"> - <t t-esc="line.payment_method.name" /> - </div> - <t - t-if="line and line.payment_status and ['done', 'reversed', 'reversing'].includes(line.payment_status)"> - <div /> + </t> + <t t-else=""> + <div class="paymentline" + t-att-class="unselectedLineClass(line)" + t-on-click="trigger('select-payment-line', { cid: line.cid })"> + <div class="payment-name"> + <t t-esc="line.payment_method.name" /> + </div> + <div class="payment-amount"> + <t t-if="line and line.payment_status and ['done', 'waitingCard', 'waiting', 'reversing', 'reversed'].includes(line.payment_status)"> + <t t-esc="env.pos.format_currency_no_symbol(line.get_amount())" /> </t> <t t-else=""> - <div class="delete-button" - t-on-click="trigger('delete-payment-line', { cid: line.cid })" - aria-label="Delete" title="Delete"> - <i class="fa fa-times-circle" /> - </div> + <t t-esc="formatLineAmount(line)" /> </t> </div> - </t> + <div class="delete-button" + t-on-click="trigger('delete-payment-line', { cid: line.cid })" + aria-label="Delete" title="Delete"> + <i class="fa fa-times-circle" /> + </div> + </div> </t> - </div> + </t> </div> - </div> </t> </templates> diff --git a/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentScreenStatus.xml b/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentScreenStatus.xml new file mode 100644 index 00000000000..7c90c8a5a4e --- /dev/null +++ b/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentScreenStatus.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates id="template" xml:space="preserve"> + +<t t-name="PaymentScreenStatus" owl="1"> + <div t-if="props.paymentLines.length === 0" class="paymentlines-empty"> + <div class="total"> + <t t-esc="totalDueText" /> + </div> + <div class="message"> + Please select a payment method. + </div> + </div> + + <div t-else=""> + <div class="payment-status-container"> + <div> + <div class="payment-status-remaining"> + <span class="label">Remaining</span> + <span class="amount" + t-att-class="{ highlight: currentOrder.get_due() > 0 }"> + <t t-esc="remainingText" /> + </span> + </div> + <div class="payment-status-total-due"> + <span class="label">Total Due</span> + <span> + <t t-esc="totalDueText" /> + </span> + </div> + </div> + <div> + <div class="payment-status-change"> + <span class="label">Change</span> + <span class="amount" + t-att-class="{ highlight: currentOrder.get_change() > 0 }"> + <t t-esc="changeText" /> + </span> + </div> + </div> + </div> + </div> +</t> +</templates> diff --git a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/HomeCategoryBreadcrumb.xml b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/HomeCategoryBreadcrumb.xml index 2fa2ac159bf..2bfa426e4b6 100644 --- a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/HomeCategoryBreadcrumb.xml +++ b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/HomeCategoryBreadcrumb.xml @@ -3,10 +3,19 @@ <t t-name="HomeCategoryBreadcrumb" owl="1"> <span class="breadcrumb"> - <span class="breadcrumb-button breadcrumb-home" + <span t-if="!env.isMobile" class="breadcrumb-button breadcrumb-home" t-on-click="trigger('switch-category', 0)"> <i class="fa fa-home" role="img" aria-label="Home" title="Home"></i> </span> + <span t-if="env.isMobile" class="breadcrumb-button breadcrumb-home" + t-on-click="trigger('categ-popup', props.subcategories)"> + <t t-if="env.pos.get('selectedCategoryId') === 0"> + All + </t> + <t t-else=""> + <t t-esc="props.currentCat.name"/> + </t> + </span> </span> </t> diff --git a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/OrderWidget.xml b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/OrderWidget.xml index 792291d2cb7..316453726fc 100644 --- a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/OrderWidget.xml +++ b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/OrderWidget.xml @@ -2,25 +2,23 @@ <templates id="template" xml:space="preserve"> <t t-name="OrderWidget" owl="1"> - <div class="order-container"> - <div t-ref="scrollable" class="order-scroller touch-scrollable"> - <div class="order"> - <t t-if="orderlinesArray.length === 0"> - <div class="order-empty"> - <i class="fa fa-shopping-cart" role="img" aria-label="Shopping cart" - title="Shopping cart" /> - <h1>Your shopping cart is empty</h1> - </div> - </t> - <t t-else=""> - <ul class="orderlines"> - <t t-foreach="orderlinesArray" t-as="orderline" t-key="orderline.id"> - <Orderline line="orderline" /> - </t> - </ul> - <OrderSummary total="state.total" tax="state.tax" /> - </t> - </div> + <div class="order-container" t-ref="scrollable"> + <div class="order"> + <t t-if="orderlinesArray.length === 0" > + <div class='order-empty'> + <i class='fa fa-shopping-cart' role="img" aria-label="Shopping cart" + title="Shopping cart"/> + <h1>Your shopping cart is empty</h1> + </div> + </t> + <t t-else=""> + <ul class="orderlines"> + <t t-foreach="orderlinesArray" t-as="orderline" t-key="orderline.id"> + <Orderline line="orderline" /> + </t> + </ul> + <OrderSummary total="state.total" tax="state.tax" /> + </t> </div> </div> </t> diff --git a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductList.xml b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductList.xml index 715da883bb6..9e87ca95f7c 100644 --- a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductList.xml +++ b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductList.xml @@ -3,25 +3,23 @@ <t t-name="ProductList" owl="1"> <div class="product-list-container"> - <div class="product-list-scroller touch-scrollable"> - <div t-if="props.products.length != 0" class="product-list"> - <t t-foreach="props.products" t-as="product" t-key="product.id"> - <ProductItem product="product" /> + <div t-if="props.products.length != 0" class="product-list"> + <t t-foreach="props.products" t-as="product" t-key="product.id"> + <ProductItem product="product" /> + </t> + </div> + <div t-else="" class="product-list-empty"> + <div class="product-list-empty"> + <t t-if="props.searchWord !== ''"> + <p> + No results found for " + <b t-esc="props.searchWord"></b> + ". + </p> + </t> + <t t-else=""> + <p>There are no products in this category.</p> </t> - </div> - <div t-else="" class="product-list-empty"> - <div class="product-list-empty"> - <t t-if="props.searchWord !== ''"> - <p> - No results found for " - <b t-esc="props.searchWord"></b> - ". - </p> - </t> - <t t-else=""> - <p>There are no products in this category.</p> - </t> - </div> </div> </div> </div> diff --git a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductScreen.xml b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductScreen.xml index 7d3a6ad3499..619659ef11e 100644 --- a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductScreen.xml +++ b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductScreen.xml @@ -3,34 +3,30 @@ <t t-name="ProductScreen" owl="1"> <div class="product-screen screen" t-att-class="{ oe_hidden: !props.isShown }"> - <div class="leftpane"> - <div class="window"> - <div class="subwindow"> - <div class="subwindow-container"> - <div class="subwindow-container-fix"> - <OrderWidget /> - </div> + <div class="screen-full-width"> + <div class="leftpane" t-if="!env.isMobile || mobile_pane === 'left'"> + <OrderWidget/> + <div class="pads"> + <div class="control-buttons"> + <t t-foreach="controlButtons" t-as="cb" t-key="cb.name"> + <t t-component="cb.component" t-key="cb.name" /> + </t> </div> - </div> - - <div class="subwindow collapsed"> - <div class="subwindow-container"> - <div class="subwindow-container-fix pads"> - <div class="control-buttons"> - <t t-foreach="controlButtons" t-as="cb" t-key="cb.name"> - <t t-component="cb.component" t-key="cb.name" /> - </t> - </div> - <ActionpadWidget client="client" /> - <NumpadWidget /> - </div> + <div class="subpads"> + <ActionpadWidget client="client"/> + <NumpadWidget /> </div> </div> - + <t t-if="env.isMobile"> + <MobileOrderWidget pane="mobile_pane" t-on-switchpane="switchPane"/> + </t> + </div> + <div class="rightpane" t-if="!env.isMobile || mobile_pane === 'right'"> + <ProductsWidget/> + <t t-if="env.isMobile"> + <MobileOrderWidget pane="mobile_pane" t-on-switchpane="switchPane"/> + </t> </div> - </div> - <div class="rightpane"> - <ProductsWidget /> </div> </div> </t> diff --git a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidget.xml b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidget.xml index aca096590f0..c0a49a555b2 100644 --- a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidget.xml +++ b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidget.xml @@ -2,24 +2,12 @@ <templates id="template" xml:space="preserve"> <t t-name="ProductsWidget" owl="1"> - <table class="layout-table"> - <tbody> - <tr class="header-row"> - <td class="header-cell"> - <ProductsWidgetControlPanel breadcrumbs="breadcrumbs" - subcategories="subcategories" /> - </td> - </tr> + <div class="products-widget"> + <ProductsWidgetControlPanel breadcrumbs="breadcrumbs" + subcategories="subcategories" /> - <tr class="content-row"> - <td class="content-cell"> - <div class="content-container"> - <ProductList products="productsToDisplay" searchWord="searchWord" /> - </div> - </td> - </tr> - </tbody> - </table> + <ProductList products="productsToDisplay" searchWord="searchWord" /> + </div> </t> </templates> diff --git a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidgetControlPanel.xml b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidgetControlPanel.xml index 92db04835b1..e3e9836d447 100644 --- a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidgetControlPanel.xml +++ b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidgetControlPanel.xml @@ -2,13 +2,15 @@ <templates id="template" xml:space="preserve"> <t t-name="ProductsWidgetControlPanel" owl="1"> - <div> - <header class="rightpane-header"> + <div class="products-widget-control"> + <div class="rightpane-header"> <!-- Breadcrumbs --> <div class="breadcrumbs"> - <HomeCategoryBreadcrumb /> - <t t-foreach="props.breadcrumbs" t-as="category" t-key="category.id"> - <CategoryBreadcrumb category="category" /> + <HomeCategoryBreadcrumb subcategories="props.subcategories" currentCat="props.breadcrumbs[props.breadcrumbs.length - 1]"/> + <t t-if="!env.isMobile"> + <t t-foreach="props.breadcrumbs" t-as="category" t-key="category.id"> + <CategoryBreadcrumb category="category" /> + </t> </t> </div> <!-- Searchbox --> @@ -22,9 +24,9 @@ <i class="fa fa-remove" /> </span> </div> - </header> + </div> <!-- Subcategories --> - <t t-if="props.subcategories.length > 0"> + <t t-if="props.subcategories.length > 0 && !env.isMobile"> <div class="categories"> <div class="category-list-scroller touch-scrollable"> <div class="category-list" @@ -44,4 +46,4 @@ </div> </t> -</templates>
\ No newline at end of file +</templates> diff --git a/addons/point_of_sale/static/src/xml/Screens/ReceiptScreen/ReceiptScreen.xml b/addons/point_of_sale/static/src/xml/Screens/ReceiptScreen/ReceiptScreen.xml index 9baf8b6b504..659f2980bd5 100644 --- a/addons/point_of_sale/static/src/xml/Screens/ReceiptScreen/ReceiptScreen.xml +++ b/addons/point_of_sale/static/src/xml/Screens/ReceiptScreen/ReceiptScreen.xml @@ -5,14 +5,16 @@ <div class="receipt-screen screen"> <div class="screen-content"> <div class="top-content"> - <h1> - Change: <span class="change-value"> - <t t-esc="change" /></span> - </h1> - <span class="button next" t-att-class="{ highlight: !locked }" + <div class="top-content-center"> + <h1 t-if="!env.isMobile"> + Change: <span class="change-value"> + <t t-esc="change" /></span> + </h1> + </div> + <div class="button next" t-att-class="{ highlight: !locked }" t-on-click="orderDone"> Next Order <i class="fa fa-angle-double-right"></i> - </span> + </div> </div> <div class="centered-content touch-scrollable"> <div t-if="state.printInvoiceIsShown" class="button print_invoice" diff --git a/addons/point_of_sale/static/tests/tours/PaymentScreen.tour.js b/addons/point_of_sale/static/tests/tours/PaymentScreen.tour.js index 0253361323b..e9d88ccb7e5 100644 --- a/addons/point_of_sale/static/tests/tours/PaymentScreen.tour.js +++ b/addons/point_of_sale/static/tests/tours/PaymentScreen.tour.js @@ -14,7 +14,6 @@ odoo.define('point_of_sale.tour.PaymentScreen', function (require) { // Pay with cash, created line should have zero amount PaymentScreen.do.clickPaymentMethod('Cash'); - PaymentScreen.check.selectedPaymentlineHas('Cash', '0.00'); PaymentScreen.do.pressNumpad('1 1'); PaymentScreen.check.selectedPaymentlineHas('Cash', '11.00'); PaymentScreen.check.remainingIs('41.8'); diff --git a/addons/point_of_sale/static/tests/tours/helpers/PaymentScreenTourMethods.js b/addons/point_of_sale/static/tests/tours/helpers/PaymentScreenTourMethods.js index 07a4be28a7a..dedcaced98c 100644 --- a/addons/point_of_sale/static/tests/tours/helpers/PaymentScreenTourMethods.js +++ b/addons/point_of_sale/static/tests/tours/helpers/PaymentScreenTourMethods.js @@ -22,7 +22,7 @@ odoo.define('point_of_sale.tour.PaymentScreenTourMethods', function (require) { return [ { content: `delete ${name} paymentline with ${amount} amount`, - trigger: `.paymentlines .paymentline .col-name:contains("${name}") ~ .delete-button`, + trigger: `.paymentlines .paymentline .payment-name:contains("${name}") ~ .delete-button`, }, ]; } @@ -175,12 +175,12 @@ odoo.define('point_of_sale.tour.PaymentScreenTourMethods', function (require) { return [ { content: `line paid via '${paymentMethodName}' is selected`, - trigger: `.paymentlines .paymentline.selected .col-name:contains("${paymentMethodName}")`, + trigger: `.paymentlines .paymentline.selected .payment-name:contains("${paymentMethodName}")`, run: () => {}, }, { content: `amount tendered in the line is '${amount}'`, - trigger: `.paymentlines .paymentline.selected .col-tendered:contains("${amount}")`, + trigger: `.paymentlines .paymentline.selected .payment-amount:contains("${amount}")`, run: () => {}, }, ]; diff --git a/addons/point_of_sale/static/tests/unit/test_PaymentScreen.js b/addons/point_of_sale/static/tests/unit/test_PaymentScreen.js index 754ade22d70..8f44e82cc84 100644 --- a/addons/point_of_sale/static/tests/unit/test_PaymentScreen.js +++ b/addons/point_of_sale/static/tests/unit/test_PaymentScreen.js @@ -115,7 +115,7 @@ odoo.define('point_of_sale.tests.PaymentScreen', function (require) { }); QUnit.test('PaymentScreenPaymentLines', async function (assert) { - assert.expect(15); + assert.expect(12); class Parent extends PosComponent { constructor() { @@ -156,15 +156,11 @@ odoo.define('point_of_sale.tests.PaymentScreen', function (require) { const cashPM = { id: 0, name: 'Cash', is_cash_count: true, use_payment_terminal: false }; const bankPM = { id: 0, name: 'Bank', is_cash_count: false, use_payment_terminal: false }; - assert.ok(parent.el.querySelector('.paymentlines-empty'), 'empty payment lines'); - let paymentline1 = order.add_paymentline(cashPM); await testUtils.nextTick(); - assert.notOk(parent.el.querySelector('.paymentlines-empty'), 'not empty anymore'); let statusContainer = parent.el.querySelector('.payment-status-container'); let linesEl = parent.el.querySelector('.paymentlines'); - assert.ok(statusContainer, 'payment status is shown'); assert.ok(linesEl, 'payment lines are shown'); let newLine = linesEl.querySelector('.selected'); assert.ok(newLine, 'the new line is automatically selected'); diff --git a/addons/point_of_sale/views/pos_assets_common.xml b/addons/point_of_sale/views/pos_assets_common.xml index 05729983f5d..89548966c7e 100644 --- a/addons/point_of_sale/views/pos_assets_common.xml +++ b/addons/point_of_sale/views/pos_assets_common.xml @@ -36,6 +36,7 @@ <script type="text/javascript" src="/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreenNumpad.js"></script> <script type="text/javascript" src="/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreenElectronicPayment.js"></script> <script type="text/javascript" src="/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreenPaymentLines.js"></script> + <script type="text/javascript" src="/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreenStatus.js"></script> <script type="text/javascript" src="/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentMethodButton.js"></script> <script type="text/javascript" src="/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreen.js"></script> <script type="text/javascript" src="/point_of_sale/static/src/js/Screens/ProductScreen/Orderline.js"></script> @@ -81,6 +82,7 @@ <script type="text/javascript" src="/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/SetFiscalPositionButton.js"></script> <script type="text/javascript" src="/point_of_sale/static/src/js/ChromeWidgets/ClientScreenButton.js"></script> <script type="text/javascript" src="/point_of_sale/static/src/js/Misc/NumberBuffer.js"></script> + <script type="text/javascript" src="/point_of_sale/static/src/js/Misc/MobileOrderWidget.js"></script> </template> <template id="point_of_sale.assets_backend" name="hr assets" inherit_id="web.assets_backend"> diff --git a/addons/point_of_sale/views/pos_assets_index.xml b/addons/point_of_sale/views/pos_assets_index.xml index 057f72454d8..730624eeb54 100644 --- a/addons/point_of_sale/views/pos_assets_index.xml +++ b/addons/point_of_sale/views/pos_assets_index.xml @@ -28,7 +28,7 @@ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/> <meta http-equiv="content-type" content="text/html, charset=utf-8" /> - <meta name="viewport" content=" width=1024, user-scalable=no"/> + <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/> <meta name="apple-mobile-web-app-capable" content="yes"/> <meta name="mobile-web-app-capable" content="yes"/> diff --git a/addons/point_of_sale/views/pos_config_view.xml b/addons/point_of_sale/views/pos_config_view.xml index 6f7300cfb14..232c00ff0be 100644 --- a/addons/point_of_sale/views/pos_config_view.xml +++ b/addons/point_of_sale/views/pos_config_view.xml @@ -384,17 +384,6 @@ </div> </div> </div> - <div class="col-12 col-lg-6 o_setting_box"> - <div class="o_setting_left_pane"> - <field name="iface_precompute_cash"/> - </div> - <div class="o_setting_right_pane"> - <label for="iface_precompute_cash"/> - <div class="text-muted"> - Prefill amount paid with the exact due amount - </div> - </div> - </div> <div class="col-12 col-lg-6 o_setting_box" id="iface_tipproduct" title="This product is used as reference on customer receipts."> diff --git a/addons/pos_hr/static/src/css/pos.css b/addons/pos_hr/static/src/css/pos.css index 84b66fe9554..aa0bae46af6 100644 --- a/addons/pos_hr/static/src/css/pos.css +++ b/addons/pos_hr/static/src/css/pos.css @@ -25,7 +25,8 @@ position: absolute; top: 0; left: 0; right: 0; bottom: 0; margin: auto; - width:550px; + max-width: 550px; + width:100%; height:300px; text-align:center; font-size:20px; diff --git a/addons/pos_mercury/static/src/xml/PaymentScreenPaymentLines.xml b/addons/pos_mercury/static/src/xml/PaymentScreenPaymentLines.xml index c44071847de..0a01d9f8be6 100644 --- a/addons/pos_mercury/static/src/xml/PaymentScreenPaymentLines.xml +++ b/addons/pos_mercury/static/src/xml/PaymentScreenPaymentLines.xml @@ -2,7 +2,7 @@ <templates id="template" xml:space="preserve"> <t t-name="pos_mercury.PaymentScreenPaymentLines" t-inherit="point_of_sale.PaymentScreenPaymentLines" t-inherit-mode="extension" owl="1"> - <xpath expr="//div[hasclass('col-name')]//t[@t-esc='line.payment_method.name']" position="replace"> + <xpath expr="//div[hasclass('paymentline')]//t[@t-esc='line.payment_method.name']" position="replace"> <t t-if="!line.payment_method.is_cash_count and line.mercury_swipe_pending"> <span>WAITING FOR SWIPE</span> </t> |
