From a2ff6d53835a4337b4b76b4a64f623929f57e233 Mon Sep 17 00:00:00 2001 From: Joseph Caburnay Date: Wed, 18 Aug 2021 15:08:28 +0000 Subject: [PATCH] [IMP] point_of_sale,pos_*: combine ticket and management screens We are referring to TicketScreen and OrderManagementScreen. The two screens almost serves the same purpose. There is not much reason to keep them separate. We combine the two in this commit. Basically, we remove OrderManagementScreen and its components. Some are kept and moved to the TicketScreen. Task-id: 2481404 Part-of: odoo/odoo#75322 --- addons/point_of_sale/__manifest__.py | 17 +- addons/point_of_sale/models/pos_config.py | 10 - addons/point_of_sale/static/src/css/pos.css | 125 ++---- .../js/ChromeWidgets/OrderManagementButton.js | 36 -- .../static/src/js/Misc/SearchBar.js | 81 ++-- .../point_of_sale/static/src/js/PosContext.js | 1 - .../MobileOrderManagementScreen.js | 25 -- .../OrderManagementScreen/OrderFetcher.js | 214 --------- .../OrderManagementScreen/OrderList.js | 31 -- .../OrderManagementControlPanel.js | 124 ----- .../OrderManagementScreen.js | 101 ----- .../Screens/OrderManagementScreen/OrderRow.js | 42 -- .../ControlButtons/InvoiceButton.js | 50 +-- .../ControlButtons/ReprintReceiptButton.js | 18 +- .../OrderDetails.js | 0 .../OrderlineDetails.js | 2 +- .../ReprintReceiptScreen.js | 4 +- .../js/Screens/TicketScreen/TicketScreen.js | 424 ++++++++++++------ .../point_of_sale/static/src/xml/Chrome.xml | 1 - .../ChromeWidgets/OrderManagementButton.xml | 12 - .../static/src/xml/Misc/SearchBar.xml | 18 +- .../MobileOrderManagementScreen.xml | 30 -- .../OrderManagementScreen/OrderList.xml | 20 - .../OrderManagementControlPanel.xml | 36 -- .../OrderManagementScreen.xml | 32 -- .../OrderManagementScreen/OrderRow.xml | 15 - .../Screens/ProductScreen/ProductScreen.xml | 2 +- .../ControlButtons/InvoiceButton.xml | 2 +- .../ControlButtons/ReprintReceiptButton.xml | 0 .../OrderDetails.xml | 18 +- .../OrderlineDetails.xml | 0 .../ReprintReceiptScreen.xml | 0 .../xml/Screens/TicketScreen/TicketScreen.xml | 136 ++++-- .../static/tests/tours/Chrome.tour.js | 1 + .../tests/tours/OrderManagementScreen.tour.js | 145 ------ .../static/tests/tours/TicketScreen.tour.js | 38 +- .../tests/tours/helpers/ChromeTourMethods.js | 13 - .../OrderManagementScreenTourMethods.js | 196 -------- .../tours/helpers/ReceiptScreenTourMethods.js | 7 + .../tours/helpers/TicketScreenTourMethods.js | 37 +- addons/point_of_sale/tests/test_frontend.py | 7 - .../point_of_sale/views/pos_config_view.xml | 15 - addons/pos_restaurant/__manifest__.py | 2 - .../OrderManagementScreen.js | 28 -- .../Screens/OrderManagementScreen/OrderRow.js | 17 - .../static/src/js/Screens/TicketScreen.js | 43 +- .../OrderManagementScreen/OrderList.xml | 12 - .../OrderManagementScreen/OrderRow.xml | 12 - .../static/src/xml/Screens/TicketScreen.xml | 6 +- .../tests/tours/OrderManagementScreen.tour.js | 124 ----- .../tests/tours/SplitBillScreen.tour.js | 1 + .../static/tests/tours/TicketScreen.tour.js | 1 + .../static/tests/tours/TipScreen.tour.js | 4 +- addons/pos_restaurant/tests/test_frontend.py | 5 - 54 files changed, 606 insertions(+), 1735 deletions(-) delete mode 100644 addons/point_of_sale/static/src/js/ChromeWidgets/OrderManagementButton.js delete mode 100644 addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/MobileOrderManagementScreen.js delete mode 100644 addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderFetcher.js delete mode 100644 addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderList.js delete mode 100644 addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderManagementControlPanel.js delete mode 100644 addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderManagementScreen.js delete mode 100644 addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderRow.js rename addons/point_of_sale/static/src/js/Screens/{OrderManagementScreen => TicketScreen}/ControlButtons/InvoiceButton.js (72%) rename addons/point_of_sale/static/src/js/Screens/{OrderManagementScreen => TicketScreen}/ControlButtons/ReprintReceiptButton.js (51%) rename addons/point_of_sale/static/src/js/Screens/{OrderManagementScreen => TicketScreen}/OrderDetails.js (100%) rename addons/point_of_sale/static/src/js/Screens/{OrderManagementScreen => TicketScreen}/OrderlineDetails.js (96%) rename addons/point_of_sale/static/src/js/Screens/{OrderManagementScreen => TicketScreen}/ReprintReceiptScreen.js (86%) delete mode 100644 addons/point_of_sale/static/src/xml/ChromeWidgets/OrderManagementButton.xml delete mode 100644 addons/point_of_sale/static/src/xml/Screens/OrderManagementScreen/MobileOrderManagementScreen.xml delete mode 100644 addons/point_of_sale/static/src/xml/Screens/OrderManagementScreen/OrderList.xml delete mode 100644 addons/point_of_sale/static/src/xml/Screens/OrderManagementScreen/OrderManagementControlPanel.xml delete mode 100644 addons/point_of_sale/static/src/xml/Screens/OrderManagementScreen/OrderManagementScreen.xml delete mode 100644 addons/point_of_sale/static/src/xml/Screens/OrderManagementScreen/OrderRow.xml rename addons/point_of_sale/static/src/xml/Screens/{OrderManagementScreen => TicketScreen}/ControlButtons/InvoiceButton.xml (78%) rename addons/point_of_sale/static/src/xml/Screens/{OrderManagementScreen => TicketScreen}/ControlButtons/ReprintReceiptButton.xml (100%) rename addons/point_of_sale/static/src/xml/Screens/{OrderManagementScreen => TicketScreen}/OrderDetails.xml (60%) rename addons/point_of_sale/static/src/xml/Screens/{OrderManagementScreen => TicketScreen}/OrderlineDetails.xml (100%) rename addons/point_of_sale/static/src/xml/Screens/{OrderManagementScreen => TicketScreen}/ReprintReceiptScreen.xml (100%) delete mode 100644 addons/point_of_sale/static/tests/tours/OrderManagementScreen.tour.js delete mode 100644 addons/point_of_sale/static/tests/tours/helpers/OrderManagementScreenTourMethods.js delete mode 100644 addons/pos_restaurant/static/src/js/Screens/OrderManagementScreen/OrderManagementScreen.js delete mode 100644 addons/pos_restaurant/static/src/js/Screens/OrderManagementScreen/OrderRow.js delete mode 100644 addons/pos_restaurant/static/src/xml/Screens/OrderManagementScreen/OrderList.xml delete mode 100644 addons/pos_restaurant/static/src/xml/Screens/OrderManagementScreen/OrderRow.xml delete mode 100644 addons/pos_restaurant/static/tests/tours/OrderManagementScreen.tour.js diff --git a/addons/point_of_sale/__manifest__.py b/addons/point_of_sale/__manifest__.py index e4c2c85e1f2..f297901642d 100644 --- a/addons/point_of_sale/__manifest__.py +++ b/addons/point_of_sale/__manifest__.py @@ -87,17 +87,11 @@ 'point_of_sale/static/src/js/Screens/ClientListScreen/ClientLine.js', 'point_of_sale/static/src/js/Screens/ClientListScreen/ClientDetailsEdit.js', 'point_of_sale/static/src/js/Screens/ClientListScreen/ClientListScreen.js', - 'point_of_sale/static/src/js/Screens/OrderManagementScreen/ControlButtons/InvoiceButton.js', - 'point_of_sale/static/src/js/Screens/OrderManagementScreen/ControlButtons/ReprintReceiptButton.js', - 'point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderFetcher.js', - 'point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderManagementScreen.js', - 'point_of_sale/static/src/js/Screens/OrderManagementScreen/MobileOrderManagementScreen.js', - 'point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderManagementControlPanel.js', - 'point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderList.js', - 'point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderRow.js', - 'point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderDetails.js', - 'point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderlineDetails.js', - 'point_of_sale/static/src/js/Screens/OrderManagementScreen/ReprintReceiptScreen.js', + 'point_of_sale/static/src/js/Screens/TicketScreen/ControlButtons/InvoiceButton.js', + 'point_of_sale/static/src/js/Screens/TicketScreen/ControlButtons/ReprintReceiptButton.js', + 'point_of_sale/static/src/js/Screens/TicketScreen/OrderDetails.js', + 'point_of_sale/static/src/js/Screens/TicketScreen/OrderlineDetails.js', + 'point_of_sale/static/src/js/Screens/TicketScreen/ReprintReceiptScreen.js', 'point_of_sale/static/src/js/Screens/TicketScreen/TicketScreen.js', 'point_of_sale/static/src/js/Screens/PaymentScreen/PSNumpadInputButton.js', 'point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreenNumpad.js', @@ -126,7 +120,6 @@ 'point_of_sale/static/src/js/ChromeWidgets/CashierName.js', 'point_of_sale/static/src/js/ChromeWidgets/ProxyStatus.js', 'point_of_sale/static/src/js/ChromeWidgets/SyncNotification.js', - 'point_of_sale/static/src/js/ChromeWidgets/OrderManagementButton.js', 'point_of_sale/static/src/js/ChromeWidgets/HeaderButton.js', 'point_of_sale/static/src/js/ChromeWidgets/SaleDetailsButton.js', 'point_of_sale/static/src/js/ChromeWidgets/CashMoveButton.js', diff --git a/addons/point_of_sale/models/pos_config.py b/addons/point_of_sale/models/pos_config.py index 34fe57c8096..7ff965b2cd5 100644 --- a/addons/point_of_sale/models/pos_config.py +++ b/addons/point_of_sale/models/pos_config.py @@ -203,7 +203,6 @@ class PosConfig(models.Model): module_pos_discount = fields.Boolean("Global Discounts") module_pos_loyalty = fields.Boolean("Loyalty Program") module_pos_mercury = fields.Boolean(string="Integrated Card Payments") - manage_orders = fields.Boolean(string="Manage Orders") product_configurator = fields.Boolean(string="Product Configurator") is_posbox = fields.Boolean("PosBox") is_header_or_footer = fields.Boolean("Header & Footer") @@ -220,7 +219,6 @@ class PosConfig(models.Model): cash_rounding = fields.Boolean(string="Cash Rounding") only_round_cash_method = fields.Boolean(string="Only apply rounding on cash") has_active_session = fields.Boolean(compute='_compute_current_session') - show_allow_invoicing_alert = fields.Boolean(compute="_compute_show_allow_invoicing_alert") manual_discount = fields.Boolean(string="Manual Discounts", default=True) ship_later = fields.Boolean(string="Ship Later") warehouse_id = fields.Many2one('stock.warehouse', default=_default_warehouse_id, ondelete='restrict') @@ -274,14 +272,6 @@ class PosConfig(models.Model): pos_config.current_session_id = session and session[0].id or False pos_config.current_session_state = session and session[0].state or False - @api.depends('module_account', 'manage_orders') - def _compute_show_allow_invoicing_alert(self): - for pos_config in self: - if not pos_config.manage_orders: - pos_config.show_allow_invoicing_alert = False - else: - pos_config.show_allow_invoicing_alert = not pos_config.module_account - @api.depends('session_ids') def _compute_last_session(self): PosSession = self.env['pos.session'] diff --git a/addons/point_of_sale/static/src/css/pos.css b/addons/point_of_sale/static/src/css/pos.css index 330d2de850d..7397c45df46 100644 --- a/addons/point_of_sale/static/src/css/pos.css +++ b/addons/point_of_sale/static/src/css/pos.css @@ -589,10 +589,11 @@ td { color: #6ec89b; } - +.pos .pane-border { + border-right: solid 3px #787878; +} .pos .leftpane { - border-right: solid 3px #787878; background: #e2e2e2; height: 100%; display: flex; @@ -2915,119 +2916,30 @@ td { text-align: right; } -.order-management-screen .flex-container { - display: flex; - flex-direction: column; - height: 100%; -} - -.order-management-screen .orders { - display: flex; - flex-direction: column; -} - -.order-management-screen .order-list { - flex: 1; - overflow-y: auto; -} - -.order-management-screen .control-panel { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - margin: 0.5rem; -} - -.order-management-screen .control-panel .item { - font-size: medium; -} - -.order-management-screen .control-panel .item .page-controls { +.item .page-controls { display: flex; flex-direction: row; align-items: center; font-size: x-large; } -.order-management-screen .control-panel .page-controls > div { +.page-controls > div { border: darkgray solid 1px; border-radius: 2px; } -.order-management-screen .control-panel .page-controls > div:hover { +.page-controls > div:hover { color: rgb(110,200,155); } -.order-management-screen .control-panel .page-controls .previous { +.page-controls .previous { margin-right: 0.2rem; } -.order-management-screen .control-panel .page-controls .next { +.page-controls .next { margin-left: 0.2rem; } - -.order-management-screen .control-panel .search-box { - flex: 1; - position: relative; - text-align: center; - margin: 0.2rem; -} - -.order-management-screen .control-panel .search-box .clear { - position: relative; - right: 25px; - cursor: pointer; - color: #808080; - font-size: 13px; -} - -.order-management-screen .control-panel .search-box .icon { - position: relative; - left: 25px; - color: #808080; - font-size: 13px; -} - -.order-management-screen .control-panel .search-box input { - border: 1px solid #cecbcb; - padding: 10px 30px; - margin: auto; - background-color: white; - border-radius: 20px; - font-family: "Lato","Lucida Grande", Helvetica, Verdana, Arial; - font-size: 13px; -} - -.order-management-screen .control-panel .search-box input:focus { - outline: none; - box-shadow: 0px 0px 0px 2px rgb(153, 153, 255) inset; - color: rgb(153, 153, 255); -} - -.order-management-screen .control-panel .button { - line-height: 32px; - padding: 3px 13px; - font-size: 20px; - background: rgb(230, 230, 230); - border-radius: 3px; - border: solid 1px rgb(209, 209, 209); - cursor: pointer; - transition: all 150ms linear; -} - -.order-management-screen .control-panel .button:hover { - background: #efefef; -} - -.order-management-screen .back-to-list { - font-size: large; - padding: 10px; - background-color: #6EC89B; - color: white; -} - .order-receipt { color: white; font-size: medium; @@ -3200,10 +3112,16 @@ td { justify-content: space-evenly; } +.ticket-screen .orders .order-row.highlight { + color: white; + background-color: rgb(110,200,155); +} + .ticket-screen .orders .col { display: flex; flex: 1; padding: 10px; + text-align: start; } .ticket-screen .orders .col.start { @@ -3243,6 +3161,20 @@ td { color: white; } +.ticket-screen .empty-order-list { + text-align: center; + margin: 48px; + color: #adadad; +} + +.ticket-screen .empty-order-list h1 { + font-size: 20px; +} + +.ticket-screen .empty-order-list .fa { + font-size: 64px; +} + .ticket-screen .pointer { cursor: pointer; } @@ -3336,6 +3268,7 @@ td { padding-top: 0.2em; padding-bottom: 0.2em; padding-left: 35px; + text-align: left; } .ticket-screen .pos-search-bar .search li:hover { diff --git a/addons/point_of_sale/static/src/js/ChromeWidgets/OrderManagementButton.js b/addons/point_of_sale/static/src/js/ChromeWidgets/OrderManagementButton.js deleted file mode 100644 index 0bee888052d..00000000000 --- a/addons/point_of_sale/static/src/js/ChromeWidgets/OrderManagementButton.js +++ /dev/null @@ -1,36 +0,0 @@ -odoo.define('point_of_sale.OrderManagementButton', function (require) { - 'use strict'; - - const PosComponent = require('point_of_sale.PosComponent'); - const Registries = require('point_of_sale.Registries'); - const { isRpcError } = require('point_of_sale.utils'); - - class OrderManagementButton extends PosComponent { - async onClick() { - try { - // ping the server, if no error, show the screen - await this.rpc({ - model: 'pos.order', - method: 'browse', - args: [[]], - kwargs: { context: this.env.session.user_context }, - }); - this.showScreen('OrderManagementScreen'); - } catch (error) { - if (isRpcError(error) && error.message.code < 0) { - this.showPopup('ErrorPopup', { - title: this.env._t('Network Error'), - body: this.env._t('Cannot access order management screen if offline.'), - }); - } else { - throw error; - } - } - } - } - OrderManagementButton.template = 'OrderManagementButton'; - - Registries.Component.add(OrderManagementButton); - - return OrderManagementButton; -}); diff --git a/addons/point_of_sale/static/src/js/Misc/SearchBar.js b/addons/point_of_sale/static/src/js/Misc/SearchBar.js index e9f56feaf81..48cab1e7ab5 100644 --- a/addons/point_of_sale/static/src/js/Misc/SearchBar.js +++ b/addons/point_of_sale/static/src/js/Misc/SearchBar.js @@ -2,6 +2,7 @@ odoo.define('point_of_sale.SearchBar', function (require) { 'use strict'; const { useState, useExternalListener } = owl.hooks; + const { useListener } = require('web.custom_hooks'); const PosComponent = require('point_of_sale.PosComponent'); const Registries = require('point_of_sale.Registries'); @@ -17,12 +18,12 @@ odoo.define('point_of_sale.SearchBar', function (require) { * * @prop {{ * config: { - * searchFields: string[], - * filter: { show: boolean, options: string[] } + * searchFields: Map, + * filter: { show: boolean, options: Map } * }, * placeholder: string, * }} - * @emits search @payload { fieldValue: string, searchTerm: '' } + * @emits search @payload { fieldName: string, searchTerm: '' } * @emits filter-selected @payload { filter: string } * * NOTE: The payload of the emitted event is accessible via the `detail` @@ -31,40 +32,46 @@ odoo.define('point_of_sale.SearchBar', function (require) { class SearchBar extends PosComponent { constructor() { super(...arguments); - this.config = this.props.config; + useExternalListener(window, 'click', this._hideOptions); + useListener('click-search-field', this._onClickSearchField); + useListener('select-filter', this._onSelectFilter); + this.filterOptionsList = [...this.props.config.filter.options.keys()]; + this.searchFieldsList = [...this.props.config.searchFields.keys()]; + const defaultSearchFieldId = this.searchFieldsList.indexOf( + this.props.config.defaultSearchDetails.fieldName + ); this.state = useState({ - searchInput: '', - selectedFieldId: this.config.searchFields.length ? 0 : null, + searchInput: this.props.config.defaultSearchDetails.searchTerm || '', + selectedSearchFieldId: defaultSearchFieldId == -1 ? 0 : defaultSearchFieldId, showSearchFields: false, showFilterOptions: false, - selectedFilter: this.config.filter.options[0] || this.env._t('Select'), + selectedFilter: this.props.config.defaultFilter || this.filterOptionsList[0], }); - useExternalListener(window, 'click', this._hideOptions); } - selectFilter(option) { - this.state.selectedFilter = option; + _onSelectFilter({ detail: key }) { + this.state.selectedFilter = key; this.trigger('filter-selected', { filter: this.state.selectedFilter }); } - get placeholder() { - return this.props.placeholder; + /** + * When pressing vertical arrow keys, do not move the input cursor. + */ + onSearchInputKeydown(event) { + if (['ArrowUp', 'ArrowDown'].includes(event.key)) { + event.preventDefault(); + } } /** * When vertical arrow keys are pressed, select fields for searching. * When enter key is pressed, trigger search event if there is searchInput. */ - onKeydown(event) { + onSearchInputKeyup(event) { if (['ArrowUp', 'ArrowDown'].includes(event.key)) { - event.preventDefault(); - this.state.selectedFieldId = this._fieldIdToSelect(event.key); - } else if (event.key === 'Enter') { - this.trigger('search', { - fieldValue: this.config.searchFields[this.state.selectedFieldId], - searchTerm: this.state.searchInput, - }); - this.state.showSearchFields = false; + this.state.selectedSearchFieldId = this._fieldIdToSelect(event.key); + } else if (event.key === 'Enter' || this.state.searchInput == '') { + this._onClickSearchField({ detail: this.searchFieldsList[this.state.selectedSearchFieldId] }); } else { - if (this.state.selectedFieldId === null && this.config.searchFields.length) { - this.state.selectedFieldId = 0; + if (this.state.selectedSearchFieldId === -1 && this.searchFieldsList.length) { + this.state.selectedSearchFieldId = 0; } this.state.showSearchFields = true; } @@ -72,24 +79,21 @@ odoo.define('point_of_sale.SearchBar', function (require) { /** * Called when a search field is clicked. */ - onClickSearchField(id) { + _onClickSearchField({ detail: fieldName }) { this.state.showSearchFields = false; - this.trigger('search', { - fieldValue: this.config.searchFields[id], - searchTerm: this.state.searchInput, - }); + this.trigger('search', { fieldName, searchTerm: this.state.searchInput }); } /** - * Given an arrow key, return the next selectedFieldId. - * E.g. If the selectedFieldId is 1 and ArrowDown is pressed, return 2. + * Given an arrow key, return the next selectedSearchFieldId. + * E.g. If the selectedSearchFieldId is 1 and ArrowDown is pressed, return 2. * * @param {string} key vertical arrow key */ _fieldIdToSelect(key) { - const length = this.config.searchFields.length; + const length = this.searchFieldsList.length; if (!length) return null; - if (this.state.selectedFieldId === null) return 0; - const current = this.state.selectedFieldId || length; + if (this.state.selectedSearchFieldId === -1) return 0; + const current = this.state.selectedSearchFieldId || length; return (current + (key === 'ArrowDown' ? 1 : -1)) % length; } _hideOptions() { @@ -98,17 +102,6 @@ odoo.define('point_of_sale.SearchBar', function (require) { } } SearchBar.template = 'point_of_sale.SearchBar'; - SearchBar.defaultProps = { - config: { - searchFields: [], - filter: { - show: false, - options: [], - }, - }, - placeholder: 'Search ...', - }; - Registries.Component.add(SearchBar); return SearchBar; diff --git a/addons/point_of_sale/static/src/js/PosContext.js b/addons/point_of_sale/static/src/js/PosContext.js index b52a155d3cc..e4214e0aead 100644 --- a/addons/point_of_sale/static/src/js/PosContext.js +++ b/addons/point_of_sale/static/src/js/PosContext.js @@ -6,7 +6,6 @@ odoo.define('point_of_sale.PosContext', function (require) { // Create global context objects // e.g. component.env.device = new Context({ isMobile: false }); return { - orderManagement: new Context({ searchString: '', selectedOrder: null }), chrome: new Context({ showOrderSelector: true }), }; }); diff --git a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/MobileOrderManagementScreen.js b/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/MobileOrderManagementScreen.js deleted file mode 100644 index b5766ccf486..00000000000 --- a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/MobileOrderManagementScreen.js +++ /dev/null @@ -1,25 +0,0 @@ -odoo.define('point_of_sale.MobileOrderManagementScreen', function (require) { - const OrderManagementScreen = require('point_of_sale.OrderManagementScreen'); - const Registries = require('point_of_sale.Registries'); - const { useListener } = require('web.custom_hooks'); - const { useState } = owl.hooks; - - const MobileOrderManagementScreen = (OrderManagementScreen) => { - class MobileOrderManagementScreen extends OrderManagementScreen { - constructor() { - super(...arguments); - useListener('click-order', this._onShowDetails) - this.mobileState = useState({ showDetails: false }); - } - _onShowDetails() { - this.mobileState.showDetails = true; - } - } - MobileOrderManagementScreen.template = 'MobileOrderManagementScreen'; - return MobileOrderManagementScreen; - }; - - Registries.Component.addByExtending(MobileOrderManagementScreen, OrderManagementScreen); - - return MobileOrderManagementScreen; -}); diff --git a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderFetcher.js b/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderFetcher.js deleted file mode 100644 index 57a0263567b..00000000000 --- a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderFetcher.js +++ /dev/null @@ -1,214 +0,0 @@ -odoo.define('point_of_sale.OrderFetcher', function (require) { - 'use strict'; - - const { EventBus } = owl.core; - const { Gui } = require('point_of_sale.Gui'); - const { isRpcError } = require('point_of_sale.utils'); - const models = require('point_of_sale.models'); - - class OrderFetcher extends EventBus { - constructor() { - super(); - this.currentPage = 1; - this.ordersToShow = []; - this.cache = {}; - this.totalCount = 0; - } - get activeOrders() { - const allActiveOrders = this.comp.env.pos.get('orders').models; - return this.searchDomain - ? allActiveOrders.filter(this._predicateBasedOnSearchDomain.bind(this)) - : allActiveOrders; - } - _predicateBasedOnSearchDomain(order) { - function check(order, field, searchWord) { - searchWord = searchWord.toLowerCase(); - switch (field) { - case 'pos_reference': - return order.name.toLowerCase().includes(searchWord); - case 'partner_id.display_name': - const client = order.get_client(); - return client ? client.name.toLowerCase().includes(searchWord) : false; - case 'date_order': - return moment(order.creation_date).format('YYYY-MM-DD hh:mm A').includes(searchWord); - default: - return false; - } - } - for (let [field, _, searchWord] of (this.searchDomain || []).filter((item) => item !== '|')) { - // remove surrounding "%" from `searchWord` - searchWord = searchWord.substring(1, searchWord.length - 1); - if (check(order, field, searchWord)) { - return true; - } - } - return false; - } - get nActiveOrders() { - return this.activeOrders.length; - } - get lastPageFullOfActiveOrders() { - return Math.trunc(this.nActiveOrders / this.nPerPage); - } - get remainingActiveOrders() { - return this.nActiveOrders % this.nPerPage; - } - /** - * for nPerPage = 10 - * +--------+----------+ - * | nItems | lastPage | - * +--------+----------+ - * | 2 | 1 | - * | 10 | 1 | - * | 11 | 2 | - * | 30 | 3 | - * | 35 | 4 | - * +--------+----------+ - */ - get lastPage() { - const nItems = this.nActiveOrders + this.totalCount; - return Math.trunc(nItems / (this.nPerPage + 1)) + 1; - } - /** - * Calling this methods populates the `ordersToShow` then trigger `update` event. - * @related get - * - * NOTE: This is tightly-coupled with pagination. So if the current page contains all - * active orders, it will not fetch anything from the server but only sets `ordersToShow` - * to the active orders that fits the current page. - */ - async fetch() { - try { - let limit, offset; - let start, end; - if (this.currentPage <= this.lastPageFullOfActiveOrders) { - // Show only active orders. - start = (this.currentPage - 1) * this.nPerPage; - end = this.currentPage * this.nPerPage; - this.ordersToShow = this.activeOrders.slice(start, end); - } else if (this.currentPage === this.lastPageFullOfActiveOrders + 1) { - // Show partially the remaining active orders and - // some orders from the backend. - offset = 0; - limit = this.nPerPage - this.remainingActiveOrders; - start = (this.currentPage - 1) * this.nPerPage; - end = this.nActiveOrders; - this.ordersToShow = [ - ...this.activeOrders.slice(start, end), - ...(await this._fetch(limit, offset)), - ]; - } else { - // Show orders from the backend. - offset = - this.nPerPage - - this.remainingActiveOrders + - (this.currentPage - (this.lastPageFullOfActiveOrders + 1) - 1) * - this.nPerPage; - limit = this.nPerPage; - this.ordersToShow = await this._fetch(limit, offset); - } - this.trigger('update'); - } catch (error) { - if (isRpcError(error) && error.message.code < 0) { - Gui.showPopup('ErrorPopup', { - title: this.comp.env._t('Network Error'), - body: this.comp.env._t('Unable to fetch orders if offline.'), - }); - Gui.setSyncStatus('error'); - } else { - throw error; - } - } - } - /** - * This returns the orders from the backend that needs to be shown. - * If the order is already in cache, the full information about that - * order is not fetched anymore, instead, we use info from cache. - * - * @param {number} limit - * @param {number} offset - */ - async _fetch(limit, offset) { - const { ids, totalCount } = await this._getOrderIdsForCurrentPage(limit, offset); - const idsNotInCache = ids.filter((id) => !(id in this.cache)); - if (idsNotInCache.length > 0) { - const fetchedOrders = await this._fetchOrders(idsNotInCache); - // Cache these fetched orders so that next time, no need to fetch - // them again, unless invalidated. See `invalidateCache`. - fetchedOrders.forEach((order) => { - this.cache[order.id] = new models.Order( - {}, - { pos: this.comp.env.pos, json: order } - ); - }); - } - this.totalCount = totalCount; - return ids.map((id) => this.cache[id]); - } - async _getOrderIdsForCurrentPage(limit, offset) { - return await this.rpc({ - model: 'pos.order', - method: 'search_paid_order_ids', - kwargs: { config_id: this.configId, domain: this.searchDomain ? this.searchDomain : [], limit, offset }, - context: this.comp.env.session.user_context, - }); - } - async _fetchOrders(ids) { - return await this.rpc({ - model: 'pos.order', - method: 'export_for_ui', - args: [ids], - context: this.comp.env.session.user_context, - }); - } - nextPage() { - if (this.currentPage < this.lastPage) { - this.currentPage += 1; - this.fetch(); - } - } - prevPage() { - if (this.currentPage > 1) { - this.currentPage -= 1; - this.fetch(); - } - } - /** - * @param {integer|undefined} id id of the cached order - * @returns {Array} - */ - get(id) { - if (id) return this.cache[id]; - return this.ordersToShow; - } - setSearchDomain(searchDomain) { - this.searchDomain = searchDomain; - } - setComponent(comp) { - this.comp = comp; - return this; - } - setConfigId(configId) { - this.configId = configId; - } - setNPerPage(val) { - this.nPerPage = val; - } - setPage(page) { - this.currentPage = page; - } - invalidateCache(ids) { - for (let id of ids) { - delete this.cache[id]; - } - } - async rpc() { - Gui.setSyncStatus('connecting'); - const result = await this.comp.rpc(...arguments); - Gui.setSyncStatus('connected'); - return result; - } - } - - return new OrderFetcher(); -}); diff --git a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderList.js b/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderList.js deleted file mode 100644 index 2b4d3cd9654..00000000000 --- a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderList.js +++ /dev/null @@ -1,31 +0,0 @@ -odoo.define('point_of_sale.OrderList', function (require) { - 'use strict'; - - const { useState } = owl.hooks; - const { useListener } = require('web.custom_hooks'); - const PosComponent = require('point_of_sale.PosComponent'); - const Registries = require('point_of_sale.Registries'); - - /** - * @props {models.Order} [initHighlightedOrder] initially highligted order - * @props {Array} orders - */ - class OrderList extends PosComponent { - constructor() { - super(...arguments); - useListener('click-order', this._onClickOrder); - this.state = useState({ highlightedOrder: this.props.initHighlightedOrder || null }); - } - get highlightedOrder() { - return this.state.highlightedOrder; - } - _onClickOrder({ detail: order }) { - this.state.highlightedOrder = order; - } - } - OrderList.template = 'OrderList'; - - Registries.Component.add(OrderList); - - return OrderList; -}); diff --git a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderManagementControlPanel.js b/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderManagementControlPanel.js deleted file mode 100644 index 951a09561af..00000000000 --- a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderManagementControlPanel.js +++ /dev/null @@ -1,124 +0,0 @@ -odoo.define('point_of_sale.OrderManagementControlPanel', function (require) { - 'use strict'; - - const { useContext } = owl.hooks; - const { useAutofocus, useListener } = require('web.custom_hooks'); - const PosComponent = require('point_of_sale.PosComponent'); - const Registries = require('point_of_sale.Registries'); - const OrderFetcher = require('point_of_sale.OrderFetcher'); - const contexts = require('point_of_sale.PosContext'); - - // NOTE: These are constants so that they are only instantiated once - // and they can be used efficiently by the OrderManagementControlPanel. - const VALID_SEARCH_TAGS = new Set(['date', 'customer', 'client', 'name', 'order']); - const FIELD_MAP = { - date: 'date_order', - customer: 'partner_id.display_name', - client: 'partner_id.display_name', - name: 'pos_reference', - order: 'pos_reference', - }; - const SEARCH_FIELDS = ['pos_reference', 'partner_id.display_name', 'date_order']; - - function getDomainForSingleCondition(fields, toSearch) { - const orSymbols = Array(fields.length - 1).fill('|'); - return orSymbols.concat(fields.map((field) => [field, 'ilike', `%${toSearch}%`])); - } - - /** - * @emits close-screen - * @emits prev-page - * @emits next-page - * @emits search - */ - class OrderManagementControlPanel extends PosComponent { - constructor() { - super(...arguments); - // We are using context because we want the `searchString` to be alive - // even if this component is destroyed (unmounted). - this.orderManagementContext = useContext(contexts.orderManagement); - useListener('clear-search', this._onClearSearch); - useAutofocus({ selector: 'input' }); - } - onInputKeydown(event) { - if (event.key === 'Enter') { - this.trigger('search', this._computeDomain()); - } - } - get showPageControls() { - return OrderFetcher.lastPage > 1; - } - get pageNumber() { - const currentPage = OrderFetcher.currentPage; - const lastPage = OrderFetcher.lastPage; - return isNaN(lastPage) ? '' : `(${currentPage}/${lastPage})`; - } - get validSearchTags() { - return VALID_SEARCH_TAGS; - } - get fieldMap() { - return FIELD_MAP; - } - get searchFields() { - return SEARCH_FIELDS; - } - /** - * E.g. 1 - * ``` - * searchString = 'Customer 1' - * result = [ - * '|', - * '|', - * ['pos_reference', 'ilike', '%Customer 1%'], - * ['partner_id.display_name', 'ilike', '%Customer 1%'], - * ['date_order', 'ilike', '%Customer 1%'] - * ] - * ``` - * - * E.g. 2 - * ``` - * searchString = 'date: 2020-05' - * result = [ - * ['date_order', 'ilike', '%2020-05%'] - * ] - * ``` - * - * E.g. 3 - * ``` - * searchString = 'customer: Steward, date: 2020-05-01' - * result = [ - * ['partner_id.display_name', 'ilike', '%Steward%'], - * ['date_order', 'ilike', '%2020-05-01%'] - * ] - * ``` - */ - _computeDomain() { - const input = this.orderManagementContext.searchString.trim(); - if (!input) return; - - const searchConditions = this.orderManagementContext.searchString.split(/[,&]\s*/); - if (searchConditions.length === 1) { - let cond = searchConditions[0].split(/:\s*/); - if (cond.length === 1) { - return getDomainForSingleCondition(this.searchFields, cond[0]); - } - } - const domain = []; - for (let cond of searchConditions) { - let [tag, value] = cond.split(/:\s*/); - if (!this.validSearchTags.has(tag)) continue; - domain.push([this.fieldMap[tag], 'ilike', `%${value}%`]); - } - return domain; - } - _onClearSearch() { - this.orderManagementContext.searchString = ''; - this.onInputKeydown({ key: 'Enter' }); - } - } - OrderManagementControlPanel.template = 'OrderManagementControlPanel'; - - Registries.Component.add(OrderManagementControlPanel); - - return OrderManagementControlPanel; -}); diff --git a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderManagementScreen.js b/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderManagementScreen.js deleted file mode 100644 index dcde9739f60..00000000000 --- a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderManagementScreen.js +++ /dev/null @@ -1,101 +0,0 @@ -odoo.define('point_of_sale.OrderManagementScreen', function (require) { - 'use strict'; - - const { useContext } = owl.hooks; - const { useListener } = require('web.custom_hooks'); - const ControlButtonsMixin = require('point_of_sale.ControlButtonsMixin'); - const NumberBuffer = require('point_of_sale.NumberBuffer'); - const Registries = require('point_of_sale.Registries'); - const OrderFetcher = require('point_of_sale.OrderFetcher'); - const IndependentToOrderScreen = require('point_of_sale.IndependentToOrderScreen'); - const contexts = require('point_of_sale.PosContext'); - - class OrderManagementScreen extends ControlButtonsMixin(IndependentToOrderScreen) { - constructor() { - super(...arguments); - useListener('close-screen', this.close); - useListener('set-numpad-mode', this._setNumpadMode); - useListener('click-order', this._onClickOrder); - useListener('next-page', this._onNextPage); - useListener('prev-page', this._onPrevPage); - useListener('search', this._onSearch); - NumberBuffer.use({ - nonKeyboardInputEvent: 'numpad-click-input', - useWithBarcode: true, - }); - this.numpadMode = 'quantity'; - OrderFetcher.setComponent(this); - OrderFetcher.setConfigId(this.env.pos.config_id); - this.orderManagementContext = useContext(contexts.orderManagement); - } - mounted() { - OrderFetcher.on('update', this, this.render); - this.env.pos.get('orders').on('add remove', this.render, this); - - // calculate how many can fit in the screen. - // It is based on the height of the header element. - // So the result is only accurate if each row is just single line. - const flexContainer = this.el.querySelector('.flex-container'); - const cpEl = this.el.querySelector('.control-panel'); - const headerEl = this.el.querySelector('.order-row.header'); - const val = Math.trunc( - (flexContainer.offsetHeight - cpEl.offsetHeight - headerEl.offsetHeight) / - headerEl.offsetHeight - ); - OrderFetcher.setNPerPage(val); - - // Fetch the order after mounting so that order management screen - // is shown while fetching. - setTimeout(() => OrderFetcher.fetch(), 0); - } - willUnmount() { - OrderFetcher.off('update', this); - this.env.pos.get('orders').off('add remove', null, this); - } - get selectedClient() { - const order = this.orderManagementContext.selectedOrder; - return order ? order.get_client() : null; - } - get orders() { - return OrderFetcher.get(); - } - async _setNumpadMode(event) { - const { mode } = event.detail; - this.numpadMode = mode; - NumberBuffer.reset(); - } - _onNextPage() { - OrderFetcher.nextPage(); - } - _onPrevPage() { - OrderFetcher.prevPage(); - } - _onSearch({ detail: domain }) { - OrderFetcher.setSearchDomain(domain); - OrderFetcher.setPage(1); - OrderFetcher.fetch(); - } - _onClickOrder({ detail: clickedOrder }) { - if (!clickedOrder || clickedOrder.locked) { - this.orderManagementContext.selectedOrder = clickedOrder; - } else { - this._setOrder(clickedOrder); - } - } - /** - * @param {models.Order} order - */ - _setOrder(order) { - this.env.pos.set_order(order); - if (order === this.env.pos.get_order()) { - this.close(); - } - } - } - OrderManagementScreen.template = 'OrderManagementScreen'; - OrderManagementScreen.hideOrderSelector = true; - - Registries.Component.add(OrderManagementScreen); - - return OrderManagementScreen; -}); diff --git a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderRow.js b/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderRow.js deleted file mode 100644 index 959ea5a1477..00000000000 --- a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderRow.js +++ /dev/null @@ -1,42 +0,0 @@ -odoo.define('point_of_sale.OrderRow', function (require) { - 'use strict'; - - const PosComponent = require('point_of_sale.PosComponent'); - const Registries = require('point_of_sale.Registries'); - - /** - * @props {models.Order} order - * @props columns - * @emits click-order - */ - class OrderRow extends PosComponent { - get order() { - return this.props.order; - } - get highlighted() { - const highlightedOrder = this.props.highlightedOrder; - return !highlightedOrder ? false : highlightedOrder.backendId === this.props.order.backendId; - } - - // Column getters // - - get name() { - return this.order.get_name(); - } - get date() { - return moment(this.order.validation_date).format('YYYY-MM-DD hh:mm A'); - } - get customer() { - const customer = this.order.get('client'); - return customer ? customer.name : null; - } - get total() { - return this.env.pos.format_currency(this.order.get_total_with_tax()); - } - } - OrderRow.template = 'OrderRow'; - - Registries.Component.add(OrderRow); - - return OrderRow; -}); diff --git a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/ControlButtons/InvoiceButton.js b/addons/point_of_sale/static/src/js/Screens/TicketScreen/ControlButtons/InvoiceButton.js similarity index 72% rename from addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/ControlButtons/InvoiceButton.js rename to addons/point_of_sale/static/src/js/Screens/TicketScreen/ControlButtons/InvoiceButton.js index f8e6dd6564c..c6e47c385e5 100644 --- a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/ControlButtons/InvoiceButton.js +++ b/addons/point_of_sale/static/src/js/Screens/TicketScreen/ControlButtons/InvoiceButton.js @@ -2,44 +2,30 @@ odoo.define('point_of_sale.InvoiceButton', function (require) { 'use strict'; const { useListener } = require('web.custom_hooks'); - const { useContext } = owl.hooks; const { isRpcError } = require('point_of_sale.utils'); const PosComponent = require('point_of_sale.PosComponent'); - const OrderManagementScreen = require('point_of_sale.OrderManagementScreen'); - const OrderFetcher = require('point_of_sale.OrderFetcher'); const Registries = require('point_of_sale.Registries'); - const contexts = require('point_of_sale.PosContext'); class InvoiceButton extends PosComponent { constructor() { super(...arguments); useListener('click', this._onClick); - this.orderManagementContext = useContext(contexts.orderManagement); - } - get selectedOrder() { - return this.orderManagementContext.selectedOrder; - } - set selectedOrder(value) { - this.orderManagementContext.selectedOrder = value; } get isAlreadyInvoiced() { - if (!this.selectedOrder) return false; - return Boolean(this.selectedOrder.account_move); + if (!this.props.order) return false; + return Boolean(this.props.order.account_move); } get commandName() { - if (!this.selectedOrder) { - return 'Invoice'; + if (!this.props.order) { + return this.env._t('Invoice'); } else { return this.isAlreadyInvoiced - ? 'Reprint Invoice' - : this.selectedOrder.isFromClosedSession - ? 'Cannot Invoice' - : 'Invoice'; + ? this.env._t('Reprint Invoice') + : this.props.order.isFromClosedSession + ? this.env._t('Cannot Invoice') + : this.env._t('Invoice'); } } - get isHighlighted() { - return this.selectedOrder && !this.isAlreadyInvoiced && !this.selectedOrder.isFromClosedSession; - } async _downloadInvoice(orderId) { try { const [orderWithInvoice] = await this.rpc({ @@ -68,7 +54,7 @@ odoo.define('point_of_sale.InvoiceButton', function (require) { } } async _invoiceOrder() { - const order = this.selectedOrder; + const order = this.props.order; if (!order) return; const orderId = order.backendId; @@ -93,8 +79,8 @@ odoo.define('point_of_sale.InvoiceButton', function (require) { // Write to pos.order the selected client. if (!order.get_client()) { const { confirmed: confirmedPopup } = await this.showPopup('ConfirmPopup', { - title: 'Need customer to invoice', - body: 'Do you want to open the customer list to select customer?', + title: this.env._t('Need customer to invoice'), + body: this.env._t('Do you want to open the customer list to select customer?'), }); if (!confirmedPopup) return; @@ -127,11 +113,7 @@ odoo.define('point_of_sale.InvoiceButton', function (require) { // Part 3: Download invoice. await this._downloadInvoice(orderId); - - // Invalidate the cache then fetch the updated order. - OrderFetcher.invalidateCache([orderId]); - await OrderFetcher.fetch(); - this.selectedOrder = OrderFetcher.get(this.selectedOrder.backendId); + this.trigger('order-invoiced', orderId); } async _onClick() { try { @@ -149,14 +131,6 @@ odoo.define('point_of_sale.InvoiceButton', function (require) { } } InvoiceButton.template = 'InvoiceButton'; - - OrderManagementScreen.addControlButton({ - component: InvoiceButton, - condition: function () { - return this.env.pos.config.module_account; - }, - }); - Registries.Component.add(InvoiceButton); return InvoiceButton; diff --git a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/ControlButtons/ReprintReceiptButton.js b/addons/point_of_sale/static/src/js/Screens/TicketScreen/ControlButtons/ReprintReceiptButton.js similarity index 51% rename from addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/ControlButtons/ReprintReceiptButton.js rename to addons/point_of_sale/static/src/js/Screens/TicketScreen/ControlButtons/ReprintReceiptButton.js index 5a227827295..3de046f10dc 100644 --- a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/ControlButtons/ReprintReceiptButton.js +++ b/addons/point_of_sale/static/src/js/Screens/TicketScreen/ControlButtons/ReprintReceiptButton.js @@ -2,34 +2,20 @@ odoo.define('point_of_sale.ReprintReceiptButton', function (require) { 'use strict'; const { useListener } = require('web.custom_hooks'); - const { useContext } = owl.hooks; const PosComponent = require('point_of_sale.PosComponent'); - const OrderManagementScreen = require('point_of_sale.OrderManagementScreen'); const Registries = require('point_of_sale.Registries'); - const contexts = require('point_of_sale.PosContext'); class ReprintReceiptButton extends PosComponent { constructor() { super(...arguments); useListener('click', this._onClick); - this.orderManagementContext = useContext(contexts.orderManagement); } async _onClick() { - const order = this.orderManagementContext.selectedOrder; - if (!order) return; - - this.showScreen('ReprintReceiptScreen', { order: order }); + if (!this.props.order) return; + this.showScreen('ReprintReceiptScreen', { order: this.props.order }); } } ReprintReceiptButton.template = 'ReprintReceiptButton'; - - OrderManagementScreen.addControlButton({ - component: ReprintReceiptButton, - condition: function () { - return true; - }, - }); - Registries.Component.add(ReprintReceiptButton); return ReprintReceiptButton; diff --git a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderDetails.js b/addons/point_of_sale/static/src/js/Screens/TicketScreen/OrderDetails.js similarity index 100% rename from addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderDetails.js rename to addons/point_of_sale/static/src/js/Screens/TicketScreen/OrderDetails.js diff --git a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderlineDetails.js b/addons/point_of_sale/static/src/js/Screens/TicketScreen/OrderlineDetails.js similarity index 96% rename from addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderlineDetails.js rename to addons/point_of_sale/static/src/js/Screens/TicketScreen/OrderlineDetails.js index 08151f7c523..e7d1c340f65 100644 --- a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/OrderlineDetails.js +++ b/addons/point_of_sale/static/src/js/Screens/TicketScreen/OrderlineDetails.js @@ -38,7 +38,7 @@ odoo.define('point_of_sale.OrderlineDetails', function (require) { return this.line.quantity; } get unitPrice() { - return this.line.unitPrice; + return this.env.pos.format_currency(this.line.unitPrice); } get unit() { return this.line.unit; diff --git a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/ReprintReceiptScreen.js b/addons/point_of_sale/static/src/js/Screens/TicketScreen/ReprintReceiptScreen.js similarity index 86% rename from addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/ReprintReceiptScreen.js rename to addons/point_of_sale/static/src/js/Screens/TicketScreen/ReprintReceiptScreen.js index 7fcc514d1bf..64fc2d04ed8 100644 --- a/addons/point_of_sale/static/src/js/Screens/OrderManagementScreen/ReprintReceiptScreen.js +++ b/addons/point_of_sale/static/src/js/Screens/TicketScreen/ReprintReceiptScreen.js @@ -10,13 +10,13 @@ odoo.define('point_of_sale.ReprintReceiptScreen', function (require) { this.printReceipt(); } confirm() { - this.showScreen('OrderManagementScreen'); + this.showScreen('TicketScreen', { reuseSavedUIState: true }); } async printReceipt() { if(this.env.pos.proxy.printer && this.env.pos.config.iface_print_skip_screen) { let result = await this._printReceipt(); if(result) - this.showScreen('OrderManagementScreen'); + this.showScreen('TicketScreen', { reuseSavedUIState: true }); } } async tryReprint() { diff --git a/addons/point_of_sale/static/src/js/Screens/TicketScreen/TicketScreen.js b/addons/point_of_sale/static/src/js/Screens/TicketScreen/TicketScreen.js index 99e41ef8567..9a95700d052 100644 --- a/addons/point_of_sale/static/src/js/Screens/TicketScreen/TicketScreen.js +++ b/addons/point_of_sale/static/src/js/Screens/TicketScreen/TicketScreen.js @@ -1,85 +1,102 @@ odoo.define('point_of_sale.TicketScreen', function (require) { 'use strict'; + const models = require('point_of_sale.models'); const Registries = require('point_of_sale.Registries'); const IndependentToOrderScreen = require('point_of_sale.IndependentToOrderScreen'); - const { useListener } = require('web.custom_hooks'); + const { useListener, useAutofocus } = require('web.custom_hooks'); const { posbus } = require('point_of_sale.utils'); + const makeDefaultSearchDetails = () => ({ + fieldName: 'RECEIPT_NUMBER', + searchTerm: '', + }); + const TICKET_SCREEN_STATE = { + syncedOrders: { + currentPage: 1, + cache: {}, + toShow: [], + nPerPage: 80, + totalCount: null, + }, + ui: { + selectedSyncedOrderId: null, + searchDetails: makeDefaultSearchDetails(), + filter: null, + }, + }; + class TicketScreen extends IndependentToOrderScreen { constructor() { super(...arguments); useListener('close-screen', this.close); useListener('filter-selected', this._onFilterSelected); useListener('search', this._onSearch); - this.searchDetails = {}; - this.filter = null; - this._initializeSearchFieldConstants(); + useListener('click-order', this._onClickOrder); + useListener('create-new-order', this._onCreateNewOrder); + useListener('delete-order', this._onDeleteOrder); + useListener('next-page', this._onNextPage); + useListener('prev-page', this._onPrevPage); + useListener('order-invoiced', this._onInvoiceOrder); + useAutofocus({ selector: '.search input'}); + this._state = TICKET_SCREEN_STATE; + const defaultUIState = this.props.reuseSavedUIState + ? this._state.ui + : { + selectedSyncedOrderId: null, + searchDetails: makeDefaultSearchDetails(), + filter: null, + }; + Object.assign(this._state.ui, defaultUIState, this.props.ui || {}); } + //#region LIFECYCLE METHODS mounted() { posbus.on('ticket-button-clicked', this, this.close); this.env.pos.get('orders').on('add remove change', () => this.render(), this); this.env.pos.on('change:selectedOrder', () => this.render(), this); + setTimeout(() => { + // Show updated list of synced orders when going back to the screen. + this._onFilterSelected({ detail: { filter: this._state.ui.filter } }); + }); } willUnmount() { posbus.off('ticket-button-clicked', this); this.env.pos.get('orders').off('add remove change', null, this); this.env.pos.off('change:selectedOrder', null, this); } - _onFilterSelected(event) { - this.filter = event.detail.filter; - this.render(); - } - _onSearch(event) { - const searchDetails = event.detail; - Object.assign(this.searchDetails, searchDetails); - this.render(); - } - /** - * Override to conditionally show the new ticket button. - */ - get showNewTicketButton() { - return true; - } - get orderList() { - return this.env.pos.get_order_list(); - } - get filteredOrderList() { - const { AllTickets } = this.getOrderStates(); - const filterCheck = (order) => { - if (this.filter && this.filter !== AllTickets) { - const screen = order.get_screen_data(); - return this.filter === this.constants.screenToStatusMap[screen.name]; - } - return true; - }; - const { fieldValue, searchTerm } = this.searchDetails; - const fieldAccessor = this._searchFields[fieldValue]; - const searchCheck = (order) => { - if (!fieldAccessor) return true; - const fieldValue = fieldAccessor(order); - if (fieldValue === null) return true; - if (!searchTerm) return true; - return fieldValue && fieldValue.toString().toLowerCase().includes(searchTerm.toLowerCase()); - }; - const predicate = (order) => { - return filterCheck(order) && searchCheck(order); - }; - return this.orderList.filter(predicate); - } - selectOrder(order) { - this._setOrder(order); - if (order === this.env.pos.get_order()) { - this.close(); + //#endregion + //#region EVENT HANDLERS + async _onFilterSelected(event) { + this._state.ui.filter = event.detail.filter; + if (this._state.ui.filter == 'SYNCED') { + await this._fetchSyncedOrders(); } + this.render(); } - _setOrder(order) { - this.env.pos.set_order(order); + async _onSearch(event) { + Object.assign(this._state.ui.searchDetails, event.detail); + if (this._state.ui.filter == 'SYNCED') { + this._state.syncedOrders.currentPage = 1; + await this._fetchSyncedOrders(); + } + this.render(); } - createNewOrder() { + _onClickOrder({ detail: clickedOrder }) { + if (!clickedOrder || clickedOrder.locked) { + if (this._state.ui.selectedSyncedOrderId == clickedOrder.backendId) { + this._state.ui.selectedSyncedOrderId = null; + } else { + this._state.ui.selectedSyncedOrderId = clickedOrder.backendId; + } + } else { + this._setOrder(clickedOrder); + } + this.render(); + } + _onCreateNewOrder() { this.env.pos.add_new_order(); } - async deleteOrder(order) { + async _onDeleteOrder({ detail: order }) { const screen = order.get_screen_data(); if (['ProductScreen', 'PaymentScreen'].includes(screen.name) && order.get_orderlines().length > 0) { const { confirmed } = await this.showPopup('ConfirmPopup', { @@ -90,14 +107,70 @@ odoo.define('point_of_sale.TicketScreen', function (require) { }); if (!confirmed) return; } - if (order) { - await this._canDeleteOrder(order); + if (order && (await this._onBeforeDeleteOrder(order))) { order.destroy({ reason: 'abandon' }); + posbus.trigger('order-deleted'); } - posbus.trigger('order-deleted'); + } + async _onNextPage() { + if (this._state.syncedOrders.currentPage < this._getLastPage()) { + this._state.syncedOrders.currentPage += 1; + await this._fetchSyncedOrders(); + } + this.render(); + } + async _onPrevPage() { + if (this._state.syncedOrders.currentPage > 1) { + this._state.syncedOrders.currentPage -= 1; + await this._fetchSyncedOrders(); + } + this.render(); + } + async _onInvoiceOrder({ detail: orderId }) { + this._invalidateSyncedOrdersCache([orderId]); + await this._fetchSyncedOrders(); + this.render(); + } + //#endregion + //#region PUBLIC METHODS + getSelectedSyncedOrder() { + if (this._state.ui.filter == 'SYNCED') { + return this._state.syncedOrders.cache[this._state.ui.selectedSyncedOrderId]; + } else { + return null; + } + } + /** + * Override to conditionally show the new ticket button. + */ + shouldShowNewOrderButton() { + return true; + } + getFilteredOrderList() { + if (this._state.ui.filter == 'SYNCED') return this._state.syncedOrders.toShow; + const filterCheck = (order) => { + if (this._state.ui.filter && this._state.ui.filter !== 'ACTIVE_ORDERS') { + const screen = order.get_screen_data(); + return this._state.ui.filter === this._getScreenToStatusMap()[screen.name]; + } + return true; + }; + const { fieldName, searchTerm } = this._state.ui.searchDetails; + const searchField = this._getSearchFields()[fieldName]; + const searchCheck = (order) => { + if (!searchField) return true; + const repr = searchField.repr(order); + if (repr === null) return true; + if (!searchTerm) return true; + return repr && repr.toString().toLowerCase().includes(searchTerm.toLowerCase()); + }; + const predicate = (order) => { + return filterCheck(order) && searchCheck(order); + }; + return this._getOrderList().filter(predicate); } getDate(order) { - return moment(order.creation_date).format('YYYY-MM-DD hh:mm A'); + return moment(order.validation_date).format('YYYY-MM-DD hh:mm A'); } getTotal(order) { return this.env.pos.format_currency(order.get_total_with_tax()); @@ -112,64 +185,104 @@ odoo.define('point_of_sale.TicketScreen', function (require) { return order.employee ? order.employee.name : ''; } getStatus(order) { - const screen = order.get_screen_data(); - return this.constants.screenToStatusMap[screen.name]; + if (order.locked) { + return this.env._t('Paid'); + } else { + const screen = order.get_screen_data(); + return this._getOrderStates().get(this._getScreenToStatusMap()[screen.name]); + } } /** * Hide the delete button if one of the payments is a 'done' electronic payment. */ - hideDeleteButton(order) { - return order - .get_paymentlines() - .some((payment) => payment.is_electronic() && payment.get_payment_status() === 'done'); + shouldHideDeleteButton(order) { + return ( + order.locked || + order + .get_paymentlines() + .some((payment) => payment.is_electronic() && payment.get_payment_status() === 'done') + ); + } + isHighlighted(order) { + if (this._state.ui.filter == 'SYNCED') { + const selectedOrder = this.getSelectedSyncedOrder(); + return selectedOrder ? order.backendId == selectedOrder.backendId : false; + } else { + const activeOrder = this.env.pos.get_order(); + return activeOrder ? activeOrder.uid == order.uid : false; + } } showCardholderName() { - return this.env.pos.payment_methods.some(method => method.use_payment_terminal); + return this.env.pos.payment_methods.some((method) => method.use_payment_terminal); } - get searchBarConfig() { + getSearchBarConfig() { return { - searchFields: this.constants.searchFieldNames, - filter: { show: true, options: this.filterOptions }, + searchFields: new Map( + Object.entries(this._getSearchFields()).map(([key, val]) => [key, val.displayName]) + ), + filter: { show: true, options: this._getFilterOptions() }, + defaultSearchDetails: this._state.ui.searchDetails, + defaultFilter: this._state.ui.filter, }; } - get filterOptions() { - const { AllTickets, Ongoing, Payment, Receipt } = this.getOrderStates(); - return [AllTickets, Ongoing, Payment, Receipt]; + shouldShowPageControls() { + return this._state.ui.filter == 'SYNCED' && this._getLastPage() > 1; + } + getPageNumber() { + if (!this._state.syncedOrders.totalCount) { + return `1/1`; + } else { + return `${this._state.syncedOrders.currentPage}/${this._getLastPage()}`; + } + } + getSelectedClient() { + const order = this.getSelectedSyncedOrder(); + return order ? order.get_client() : null; + } + //#endregion + //#region PRIVATE METHODS + _setOrder(order) { + this.env.pos.set_order(order); + if (order === this.env.pos.get_order()) { + this.close(); + } + } + _getOrderList() { + return this.env.pos.get_order_list(); + } + _getFilterOptions() { + const orderStates = this._getOrderStates(); + orderStates.set('SYNCED', this.env._t('Paid')); + return orderStates; } /** - * An object with keys containing the search field names which map to functions. - * The mapped functions will be used to generate representative string for the order - * to match the search term when searching. - * E.g. Given 2 orders, search those with `Receipt Number` containing `1111`. - * ``` - * orders = [{ - * name: '000-1111-222' - * total: 10, - * }, { - * name: '444-5555-666' - * total: 15, - * }] - * ``` - * `Receipt Number` search field maps to the `name` of the order. So, the orders will be - * represented by their name, and the search will result to: - * ``` - * result = [{ - * name: '000-1111-222', - * total: 10, - * }] - * ``` - * @returns Record string> + * @returns {Record string, displayName: string, modelField: string }>} */ - get _searchFields() { - const { ReceiptNumber, Date, Customer, CardholderName } = this.getSearchFieldNames(); - var fields = { - [ReceiptNumber]: (order) => order.name, - [Date]: (order) => moment(order.creation_date).format('YYYY-MM-DD hh:mm A'), - [Customer]: (order) => order.get_client_name(), + _getSearchFields() { + const fields = { + RECEIPT_NUMBER: { + repr: (order) => order.name, + displayName: this.env._t('Receipt Number'), + modelField: 'pos_reference', + }, + DATE: { + repr: (order) => moment(order.creation_date).format('YYYY-MM-DD hh:mm A'), + displayName: this.env._t('Date'), + modelField: 'date_order', + }, + CUSTOMER: { + repr: (order) => order.get_client_name(), + displayName: this.env._t('Customer'), + modelField: 'partner_id.display_name', + }, }; if (this.showCardholderName()) { - fields[CardholderName] = (order) => order.get_cardholder_name(); + fields.CARDHOLDER_NAME = { + repr: (order) => order.get_cardholder_name(), + displayName: this.env._t('Cardholder Name'), + modelField: 'payment_ids.cardholder_name', + }; } return fields; @@ -177,42 +290,101 @@ odoo.define('point_of_sale.TicketScreen', function (require) { /** * Maps the order screen params to order status. */ - get _screenToStatusMap() { - const { Ongoing, Payment, Receipt } = this.getOrderStates(); + _getScreenToStatusMap() { return { - ProductScreen: Ongoing, - PaymentScreen: Payment, - ReceiptScreen: Receipt, + ProductScreen: 'ONGOING', + PaymentScreen: 'PAYMENT', + ReceiptScreen: 'RECEIPT', }; } - _initializeSearchFieldConstants() { - this.constants = {}; - Object.assign(this.constants, { - searchFieldNames: Object.keys(this._searchFields), - screenToStatusMap: this._screenToStatusMap, - }); - } - async _canDeleteOrder(order) { + /** + * Override to do something before deleting the order. + * Make sure to return true to proceed on deleting the order. + * @param {*} order + * @returns {boolean} + */ + async _onBeforeDeleteOrder(order) { return true; } - getOrderStates() { - return { - AllTickets: this.env._t('All Tickets'), - Ongoing: this.env._t('Ongoing'), - Payment: this.env._t('Payment'), - Receipt: this.env._t('Receipt'), - }; + _getOrderStates() { + // We need the items to be ordered, therefore, Map is used instead of normal object. + const states = new Map(); + states.set('ACTIVE_ORDERS', this.env._t('Active Orders')); + states.set('ONGOING', this.env._t('Ongoing')); + states.set('PAYMENT', this.env._t('Payment')); + states.set('RECEIPT', this.env._t('Receipt')); + return states; } - getSearchFieldNames() { - return { - ReceiptNumber: this.env._t('Receipt Number'), - Date: this.env._t('Date'), - Customer: this.env._t('Customer'), - CardholderName: this.env._t('Cardholder Name'), - }; + //#region SEARCH SYNCED ORDERS + _computeSyncedOrdersDomain() { + const { fieldName, searchTerm } = this._state.ui.searchDetails; + if (!searchTerm) return []; + const modelField = this._getSearchFields()[fieldName].modelField; + if (modelField) { + return [[modelField, 'ilike', `%${searchTerm}%`]]; + } else { + return []; + } } + /** + * Fetches the done orders from the backend that needs to be shown. + * If the order is already in cache, the full information about that + * order is not fetched anymore, instead, we use info from cache. + */ + async _fetchSyncedOrders() { + const domain = this._computeSyncedOrdersDomain(); + const limit = this._state.syncedOrders.nPerPage; + const offset = (this._state.syncedOrders.currentPage - 1) * this._state.syncedOrders.nPerPage; + const { ids, totalCount } = await this.rpc({ + model: 'pos.order', + method: 'search_paid_order_ids', + kwargs: { config_id: this.env.pos.config.id, domain, limit, offset }, + context: this.env.session.user_context, + }); + const idsNotInCache = ids.filter((id) => !(id in this._state.syncedOrders.cache)); + if (idsNotInCache.length > 0) { + const fetchedOrders = await this.rpc({ + model: 'pos.order', + method: 'export_for_ui', + args: [idsNotInCache], + context: this.env.session.user_context, + }); + // Cache these fetched orders so that next time, no need to fetch + // them again, unless invalidated. See `_onInvoiceOrder`. + fetchedOrders.forEach((order) => { + this._state.syncedOrders.cache[order.id] = new models.Order({}, { pos: this.env.pos, json: order }); + }); + } + this._state.syncedOrders.totalCount = totalCount; + this._state.syncedOrders.toShow = ids.map((id) => this._state.syncedOrders.cache[id]); + } + _getLastPage() { + const totalCount = this._state.syncedOrders.totalCount; + const nPerPage = this._state.syncedOrders.nPerPage; + const remainder = totalCount % nPerPage; + if (remainder == 0) { + return totalCount / nPerPage; + } else { + return Math.ceil(totalCount / nPerPage); + } + } + _invalidateSyncedOrdersCache(ids) { + for (let id of ids) { + delete this._state.syncedOrders.cache[id]; + } + } + //#endregion + //#endregion } TicketScreen.template = 'TicketScreen'; + TicketScreen.defaultProps = { + // When passed as true, it will use the saved _state.ui as default + // value when this component is reinstantiated. + // After setting the default value, the _state.ui will be overridden + // by the passed props.ui if there is any. + reuseSavedUIState: false, + ui: {}, + }; Registries.Component.add(TicketScreen); diff --git a/addons/point_of_sale/static/src/xml/Chrome.xml b/addons/point_of_sale/static/src/xml/Chrome.xml index 10c9c3e074c..b6e51950e57 100644 --- a/addons/point_of_sale/static/src/xml/Chrome.xml +++ b/addons/point_of_sale/static/src/xml/Chrome.xml @@ -24,7 +24,6 @@ - diff --git a/addons/point_of_sale/static/src/xml/ChromeWidgets/OrderManagementButton.xml b/addons/point_of_sale/static/src/xml/ChromeWidgets/OrderManagementButton.xml deleted file mode 100644 index 062e11c3f49..00000000000 --- a/addons/point_of_sale/static/src/xml/ChromeWidgets/OrderManagementButton.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - -
-
- -
-
-
- -
diff --git a/addons/point_of_sale/static/src/xml/Misc/SearchBar.xml b/addons/point_of_sale/static/src/xml/Misc/SearchBar.xml index a480f169520..615944c1e9b 100644 --- a/addons/point_of_sale/static/src/xml/Misc/SearchBar.xml +++ b/addons/point_of_sale/static/src/xml/Misc/SearchBar.xml @@ -6,13 +6,13 @@ diff --git a/addons/point_of_sale/static/src/xml/Screens/OrderManagementScreen/OrderlineDetails.xml b/addons/point_of_sale/static/src/xml/Screens/TicketScreen/OrderlineDetails.xml similarity index 100% rename from addons/point_of_sale/static/src/xml/Screens/OrderManagementScreen/OrderlineDetails.xml rename to addons/point_of_sale/static/src/xml/Screens/TicketScreen/OrderlineDetails.xml diff --git a/addons/point_of_sale/static/src/xml/Screens/OrderManagementScreen/ReprintReceiptScreen.xml b/addons/point_of_sale/static/src/xml/Screens/TicketScreen/ReprintReceiptScreen.xml similarity index 100% rename from addons/point_of_sale/static/src/xml/Screens/OrderManagementScreen/ReprintReceiptScreen.xml rename to addons/point_of_sale/static/src/xml/Screens/TicketScreen/ReprintReceiptScreen.xml diff --git a/addons/point_of_sale/static/src/xml/Screens/TicketScreen/TicketScreen.xml b/addons/point_of_sale/static/src/xml/Screens/TicketScreen/TicketScreen.xml index 40bfc501839..b242188b225 100644 --- a/addons/point_of_sale/static/src/xml/Screens/TicketScreen/TicketScreen.xml +++ b/addons/point_of_sale/static/src/xml/Screens/TicketScreen/TicketScreen.xml @@ -2,56 +2,94 @@ -
-
-
-
- - -
- Search Tickets... - -
-
-
-
Date
-
Receipt Number
-
Customer
-
Cardholder Name
-
Employee
-
Total
-
Status
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
+
+
+
+
+
+ +
- + Search Orders... + +
+
+ + +
+
+ +
+
+
+
+ + +
+
Date
+
Receipt Number
+
Customer
+
Cardholder Name
+
Employee
+
Total
+
Status
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+ +

No orders found

+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
diff --git a/addons/point_of_sale/static/tests/tours/Chrome.tour.js b/addons/point_of_sale/static/tests/tours/Chrome.tour.js index a1c992dec5b..c27221771d6 100644 --- a/addons/point_of_sale/static/tests/tours/Chrome.tour.js +++ b/addons/point_of_sale/static/tests/tours/Chrome.tour.js @@ -56,6 +56,7 @@ odoo.define('point_of_sale.tour.Chrome', function (require) { // Pay order 1, with change Chrome.do.clickTicketButton(); TicketScreen.do.selectOrder('-0001'); + ProductScreen.check.isShown(); ProductScreen.do.clickPayButton(); PaymentScreen.do.clickPaymentMethod('Cash'); PaymentScreen.do.pressNumpad('2 0'); diff --git a/addons/point_of_sale/static/tests/tours/OrderManagementScreen.tour.js b/addons/point_of_sale/static/tests/tours/OrderManagementScreen.tour.js deleted file mode 100644 index 26d7f661bba..00000000000 --- a/addons/point_of_sale/static/tests/tours/OrderManagementScreen.tour.js +++ /dev/null @@ -1,145 +0,0 @@ -odoo.define('point_of_sale.tour.OrderManagementScreen', function (require) { - 'use strict'; - - const { OrderManagementScreen } = require('point_of_sale.tour.OrderManagementScreenTourMethods'); - const { ProductScreen } = require('point_of_sale.tour.ProductScreenTourMethods'); - const { PaymentScreen } = require('point_of_sale.tour.PaymentScreenTourMethods'); - const { ClientListScreen } = require('point_of_sale.tour.ClientListScreenTourMethods'); - const { TicketScreen } = require('point_of_sale.tour.TicketScreenTourMethods'); - const { Chrome } = require('point_of_sale.tour.ChromeTourMethods'); - const { makeFullOrder } = require('point_of_sale.tour.CompositeTourMethods'); - const { getSteps, startSteps } = require('point_of_sale.tour.utils'); - var Tour = require('web_tour.tour'); - - // signal to start generating steps - // when finished, steps can be taken from getSteps - startSteps(); - - // Go by default to home category - ProductScreen.do.clickHomeCategory(); - - // make one order and check if it can be seen from the management screen. - // order 0001 - makeFullOrder({ orderlist: [['Whiteboard Pen', '5', '6']], payment: ['Cash', '30'], customerNote: 'Customer Note' }); - Chrome.do.clickOrderManagementButton(); - OrderManagementScreen.check.isShown(); - OrderManagementScreen.check.orderlistHas({ orderName: '-0001', total: '30' }); - - OrderManagementScreen.do.clickBack(); - - // make multiple orders and check them in the management screen. - // order 0002 - makeFullOrder({ - orderlist: [ - ['Desk Pad', '1', '2'], - ['Monitor Stand', '3', '4'], - ['Whiteboard Pen', '5', '6'], - ], - payment: ['Bank', '44'], - }); - // order 0003 - makeFullOrder({ - orderlist: [ - ['Desk Pad', '1', '2'], - ['Whiteboard Pen', '5', '6'], - ], - customer: 'Colleen Diaz', - payment: ['Cash', '50'], - }); - // order 0004 - makeFullOrder({ - orderlist: [ - ['Monitor Stand', '3', '4'], - ['Whiteboard Pen', '5', '6'], - ], - payment: ['Bank', '42'], - }); - - Chrome.do.clickOrderManagementButton(); - OrderManagementScreen.check.isShown(); - OrderManagementScreen.check.orderlistHas({ orderName: '-0002', total: '44' }); - OrderManagementScreen.check.orderlistHas({ - orderName: '0003', - total: '32', - customer: 'Colleen Diaz', - }); - OrderManagementScreen.check.orderlistHas({ orderName: '-0004', total: '42' }); - - // click the currently active order - OrderManagementScreen.do.clickOrder('-0005'); - ProductScreen.check.isShown(); - - // Add 2 orders, they should appear in order management screen - // order 0006 - Chrome.do.clickTicketButton(); - TicketScreen.do.clickNewTicket(); - ProductScreen.exec.addOrderline('Whiteboard Pen', '66', '6'); - - // order 0007, should be at payment screen - Chrome.do.clickTicketButton(); - TicketScreen.do.clickNewTicket(); - ProductScreen.exec.addOrderline('Monitor Stand', '55', '5'); - ProductScreen.do.clickCustomerButton(); - ClientListScreen.exec.setClient('Azure Interior'); - ProductScreen.do.clickPayButton(); - - Chrome.do.clickOrderManagementButton(); - OrderManagementScreen.check.orderlistHas({ orderName: '-0006', total: '396' }); - OrderManagementScreen.check.orderlistHas({ - orderName: '-0007', - total: '275', - customer: 'Azure Interior', - }); - - // select a paid order, order row should be highlighted and should show order details - OrderManagementScreen.do.clickOrder('-0004'); - OrderManagementScreen.check.highlightedOrderRowHas('-0004'); - OrderManagementScreen.check.orderDetailsHas({ - lines: [ - { product: 'Monitor Stand', quantity: '3' }, - { product: 'Whiteboard Pen', quantity: '5' }, - ], - total: '42', - }); - OrderManagementScreen.do.clickOrder('-0001'); - OrderManagementScreen.check.highlightedOrderRowHas('-0001'); - // 0004 should not be highlighted anymore - OrderManagementScreen.check.orderRowIsNotHighlighted('-0004'); - OrderManagementScreen.check.orderDetailsHas({ - lines: [{ product: 'Whiteboard Pen', quantity: '5' }], - total: '30', - customerNote: 'Customer Note', - }); - //verify the customer note in the receipt - OrderManagementScreen.do.clickPrintReceiptButton(); - OrderManagementScreen.check.reprintReceiptIsShown(); - OrderManagementScreen.check.receiptCustomerNoteIs('Customer Note'); - OrderManagementScreen.do.closeReceipt(); - - - // Select a paid order then invoice it. The selected order should remain selected - // and will contain a new customer. After invoice, the current customer should be removed. - // TODO: enable the following steps once the issue in invoicing is solved. - // OrderManagementScreen.do.clickInvoiceButton(); - // Chrome.do.confirmPopup(); - // ClientListScreen.check.isShown(); - // ClientListScreen.exec.setClient('Jesse Brown'); - // OrderManagementScreen.check.highlightedOrderRowHas('Jesse Brown'); - - // Check if order 0007 is selected, it should be at payment screen - OrderManagementScreen.do.clickOrder('-0007'); - PaymentScreen.check.isShown(); - - Chrome.do.clickOrderManagementButton(); - OrderManagementScreen.check.isShown(); - OrderManagementScreen.do.clickOrder('-0003'); - OrderManagementScreen.do.clickPrintReceiptButton(); - OrderManagementScreen.check.reprintReceiptIsShown(); - OrderManagementScreen.check.receiptChangeIs('18.0'); - OrderManagementScreen.check.receiptOrderDataContains('-0003'); - OrderManagementScreen.check.receiptAmountIs('32.0'); - OrderManagementScreen.do.closeReceipt(); - OrderManagementScreen.check.isNotHidden(); - - Tour.register('OrderManagementScreenTour', { test: true, url: '/pos/ui' }, getSteps()); -}); diff --git a/addons/point_of_sale/static/tests/tours/TicketScreen.tour.js b/addons/point_of_sale/static/tests/tours/TicketScreen.tour.js index a26c0b36608..e7ba14aae1f 100644 --- a/addons/point_of_sale/static/tests/tours/TicketScreen.tour.js +++ b/addons/point_of_sale/static/tests/tours/TicketScreen.tour.js @@ -4,6 +4,7 @@ odoo.define('point_of_sale.tour.TicketScreen', function (require) { const { ProductScreen } = require('point_of_sale.tour.ProductScreenTourMethods'); const { ReceiptScreen } = require('point_of_sale.tour.ReceiptScreenTourMethods'); const { PaymentScreen } = require('point_of_sale.tour.PaymentScreenTourMethods'); + const { ClientListScreen } = require('point_of_sale.tour.ClientListScreenTourMethods'); const { TicketScreen } = require('point_of_sale.tour.TicketScreenTourMethods'); const { Chrome } = require('point_of_sale.tour.ChromeTourMethods'); const { getSteps, startSteps } = require('point_of_sale.tour.utils'); @@ -41,7 +42,7 @@ odoo.define('point_of_sale.tour.TicketScreen', function (require) { TicketScreen.check.nthRowContains(2, 'Payment'); TicketScreen.do.selectFilter('Ongoing'); TicketScreen.check.nthRowContains(2, 'Ongoing'); - TicketScreen.do.selectFilter('All'); + TicketScreen.do.selectFilter('Active Orders'); TicketScreen.check.nthRowContains(4, 'Receipt'); TicketScreen.do.search('Customer', 'Nicole'); TicketScreen.check.nthRowContains(2, 'Nicole'); @@ -49,6 +50,41 @@ odoo.define('point_of_sale.tour.TicketScreen', function (require) { TicketScreen.check.nthRowContains(2, 'Brandon'); TicketScreen.do.search('Receipt Number', '-0003'); TicketScreen.check.nthRowContains(2, 'Receipt'); + // Close the TicketScreen to see the current order which is in ReceiptScreen. + // This is just to remove the search string in the search bar. + TicketScreen.do.clickDiscard(); + ReceiptScreen.check.isShown(); + // Open again the TicketScreen to check the Paid filter. + Chrome.do.clickTicketButton(); + TicketScreen.do.selectFilter('Paid'); + TicketScreen.check.nthRowContains(2, '-0003'); + // Pay the order that was in PaymentScreen. + TicketScreen.do.selectFilter('Payment'); + TicketScreen.do.selectOrder('-0002'); + PaymentScreen.do.clickPaymentMethod('Cash'); + PaymentScreen.do.clickValidate(); + ReceiptScreen.check.isShown(); + ReceiptScreen.do.clickNextOrder(); + ProductScreen.check.isShown(); + // Check that the Paid filter will show the 2 synced orders. + Chrome.do.clickTicketButton(); + TicketScreen.do.selectFilter('Paid'); + TicketScreen.check.nthRowContains(2, 'Brandon Freeman'); + TicketScreen.check.nthRowContains(3, '-0003'); + // Invoice order + TicketScreen.do.selectOrder('-0003'); + TicketScreen.check.orderWidgetIsNotEmpty(); + TicketScreen.do.clickControlButton('Invoice'); + Chrome.do.confirmPopup(); + ClientListScreen.check.isShown(); + ClientListScreen.exec.setClient('Colleen Diaz'); + TicketScreen.check.customerIs('Colleen Diaz'); + // Reprint receipt + TicketScreen.do.clickControlButton('Print Receipt'); + ReceiptScreen.check.isShown(); + ReceiptScreen.do.clickBack(); + // When going back, the ticket screen should be in its previous state. + TicketScreen.check.filterIs('Paid'); Tour.register('TicketScreenTour', { test: true, url: '/pos/ui' }, getSteps()); }); diff --git a/addons/point_of_sale/static/tests/tours/helpers/ChromeTourMethods.js b/addons/point_of_sale/static/tests/tours/helpers/ChromeTourMethods.js index 30609a9f6be..5dc93867ddd 100644 --- a/addons/point_of_sale/static/tests/tours/helpers/ChromeTourMethods.js +++ b/addons/point_of_sale/static/tests/tours/helpers/ChromeTourMethods.js @@ -12,19 +12,6 @@ odoo.define('point_of_sale.tour.ChromeTourMethods', function (require) { }, ]; } - clickOrderManagementButton() { - return [ - { - content: 'check order management button is shown', - trigger: '.pos .pos-rightheader .order-management', - run: () => {}, - }, - { - content: 'click order management button', - trigger: '.pos .pos-rightheader .order-management', - }, - ]; - } clickTicketButton() { return [ { diff --git a/addons/point_of_sale/static/tests/tours/helpers/OrderManagementScreenTourMethods.js b/addons/point_of_sale/static/tests/tours/helpers/OrderManagementScreenTourMethods.js deleted file mode 100644 index 93d9366a063..00000000000 --- a/addons/point_of_sale/static/tests/tours/helpers/OrderManagementScreenTourMethods.js +++ /dev/null @@ -1,196 +0,0 @@ -odoo.define('point_of_sale.tour.OrderManagementScreenTourMethods', function (require) { - 'use strict'; - - const { createTourMethods } = require('point_of_sale.tour.utils'); - - class Do { - clickBack() { - return [ - { - content: 'order management screen, click back button', - trigger: '.order-management-screen .control-panel .button.back', - }, - ]; - } - clickOrder(name, [otherCol, otherColVal] = [null, null]) { - let trigger = `.order-management-screen .order-list .order-row .item.name:contains("${name}")`; - if (otherCol) { - trigger = `${trigger} ~ .item.${otherCol}:contains("${otherColVal}")`; - } - return [ - { - content: `clicking order '${name}' from orderlist`, - trigger, - }, - ]; - } - clickInvoiceButton() { - return [ - { - content: 'click invoice button', - trigger: '.order-management-screen .control-button span:contains("Invoice")', - }, - ]; - } - clickPrintReceiptButton() { - return [ - { - content: 'click reprint receipt button', - trigger: '.order-management-screen .control-button span:contains("Print Receipt")' - } - ] - } - clickCustomerButton() { - return [ - { - content: 'click customer button', - trigger: '.order-management-screen .actionpad .button.set-customer', - }, - ]; - } - closeReceipt() { - return [ - { - content: 'close receipt', - trigger: '.receipt-screen .button.back', - } - ] - } - } - - class Check { - isShown() { - return [ - { - content: 'order management screen is shown', - trigger: '.pos .pos-content .order-management-screen', - run: () => {}, - }, - ]; - } - orderlistHas({ orderName, total, customer }) { - const steps = []; - steps.push({ - content: `order list has row having: name '${orderName}', total '${total}'`, - trigger: `.order-list .order-row .item:contains("${orderName}") ~ .item:contains("${total}")`, - run: () => {}, - }); - if (customer) { - steps.push({ - content: `order list has row having: name '${orderName}', customer '${customer}'`, - trigger: `.order-list .order-row .item:contains("${orderName}") ~ .item:contains("${customer}")`, - run: () => {}, - }); - } - return steps; - } - highlightedOrderRowHas(name) { - return [ - { - content: `order '${name}' in orderlist is highligted`, - trigger: `.order-list .order-row.highlight:has(> .item:contains("${name}"))`, - run: () => {}, - }, - ]; - } - orderRowIsNotHighlighted(name) { - return [ - { - content: `order '${name}' in orderlist is not highligted`, - trigger: `.order-list .order-row:not(:has(.highlight)):has(> .item:contains("${name}"))`, - run: () => {}, - }, - ]; - } - orderDetailsHas({ lines, total, customerNote }) { - const steps = []; - for (let { product, quantity } of lines) { - steps.push({ - content: `order details has product '${product}' and quantity '${quantity}'`, - trigger: `.orderlines .product-name:contains("${product}") ~ .info strong:contains("${quantity}")`, - run: () => {}, - }); - } - if (total) { - steps.push({ - content: `order details has total amount of ${total}`, - trigger: `.order-container .summary .total .value:contains("${total}")`, - run: () => {}, - }); - } - if (customerNote) { - steps.push({ - content: `order details has a line with a customer note: ${customerNote}`, - trigger: `.orderlines .orderline-note:contains("${customerNote}")`, - run: () => {}, - }) - } - return steps; - } - customerIs(name) { - return [ - { - content: `set customer is '${name}'`, - trigger: `.order-management-screen .actionpad .set-customer:contains("${name}")`, - run: () => {}, - }, - ]; - } - reprintReceiptIsShown() { - return [ - { - content: 'reprint receipt screen is shown', - trigger: '.pos .receipt-screen', - run: () => {}, - } - ] - } - receiptChangeIs(amount) { - return [ - { - content: `receipt change is ${amount}`, - trigger: `.pos-receipt-amount.receipt-change:contains("${amount}")`, - run: () => {}, - } - ] - } - receiptOrderDataContains(orderInfo) { - return [ - { - content: `order data contains ${orderInfo}`, - trigger: `.pos-receipt-order-data:contains("${orderInfo}")`, - run: () => {}, - } - ] - } - receiptAmountIs(amount) { - return [ - { - content: `receipt amount is ${amount}`, - trigger: `.pos-receipt-amount:contains("${amount}")`, - run: () => {}, - } - ] - } - isNotHidden() { - return [ - { - content: 'order management screen is not hidden', - trigger: `.order-management-screen:not(:has(.oe_hidden))`, - run: () => {}, - } - ] - } - receiptCustomerNoteIs(note) { - return [ - { - content: `receipt has customer note: ${note}`, - trigger: `.orderlines .pos-receipt-left-padding:contains("${note}")`, - run: () => {}, - } - ] - } - } - - return createTourMethods('OrderManagementScreen', Do, Check); -}); diff --git a/addons/point_of_sale/static/tests/tours/helpers/ReceiptScreenTourMethods.js b/addons/point_of_sale/static/tests/tours/helpers/ReceiptScreenTourMethods.js index c9c5a74e006..e209b0dbf06 100644 --- a/addons/point_of_sale/static/tests/tours/helpers/ReceiptScreenTourMethods.js +++ b/addons/point_of_sale/static/tests/tours/helpers/ReceiptScreenTourMethods.js @@ -27,6 +27,13 @@ odoo.define('point_of_sale.tour.ReceiptScreenTourMethods', function (require) { }, ]; } + clickBack() { + return [ + { + trigger: '.receipt-screen .button.back', + }, + ]; + } } class Check { diff --git a/addons/point_of_sale/static/tests/tours/helpers/TicketScreenTourMethods.js b/addons/point_of_sale/static/tests/tours/helpers/TicketScreenTourMethods.js index fe8f8127b6a..7a798405b01 100644 --- a/addons/point_of_sale/static/tests/tours/helpers/TicketScreenTourMethods.js +++ b/addons/point_of_sale/static/tests/tours/helpers/TicketScreenTourMethods.js @@ -46,14 +46,14 @@ odoo.define('point_of_sale.tour.TicketScreenTourMethods', function (require) { }, { /** - * Manually trigger keydown event to show the search field list - * because the previous step do not trigger keydown event. + * Manually trigger keyup event to show the search field list + * because the previous step do not trigger keyup event. */ trigger: '.pos-search-bar input', run: function () { document .querySelector('.pos-search-bar input') - .dispatchEvent(new KeyboardEvent('keydown', { key: '' })); + .dispatchEvent(new KeyboardEvent('keyup', { key: '' })); }, }, { @@ -68,6 +68,13 @@ odoo.define('point_of_sale.tour.TicketScreenTourMethods', function (require) { }, ]; } + clickControlButton(name) { + return [ + { + trigger: `.ticket-screen .control-button:contains("${name}")`, + }, + ]; + } } class Check { @@ -99,6 +106,30 @@ odoo.define('point_of_sale.tour.TicketScreenTourMethods', function (require) { }, ]; } + orderWidgetIsNotEmpty() { + return [ + { + trigger: '.ticket-screen:not(:has(.order-empty))', + run: () => {}, + }, + ]; + } + customerIs(name) { + return [ + { + trigger: `.ticket-screen .set-customer:contains("${name}")`, + run: () => {}, + }, + ]; + } + filterIs(name) { + return [ + { + trigger: `.ticket-screen .filter:contains("${name}")`, + run: () => {}, + }, + ]; + } } class Execute {} diff --git a/addons/point_of_sale/tests/test_frontend.py b/addons/point_of_sale/tests/test_frontend.py index bba9b1f850e..577c88c68ab 100644 --- a/addons/point_of_sale/tests/test_frontend.py +++ b/addons/point_of_sale/tests/test_frontend.py @@ -519,13 +519,6 @@ class TestUi(TestPointOfSaleHttpCommon): self.assertEqual(n_invoiced, 1, 'There should be 1 invoiced order.') self.assertEqual(n_paid, 2, 'There should be 2 paid order.') - def test_03_order_management(self): - if hasattr(self.main_pos_config, 'module_pos_restaurant'): - self.main_pos_config.module_pos_restaurant = False - self.main_pos_config.write({ 'manage_orders': True, 'module_account': True }) - self.main_pos_config.open_session_cb(check_coa=False) - self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'OrderManagementScreenTour', login="accountman") - def test_04_product_configurator(self): self.main_pos_config.write({ 'product_configurator': True }) self.main_pos_config.open_session_cb(check_coa=False) diff --git a/addons/point_of_sale/views/pos_config_view.xml b/addons/point_of_sale/views/pos_config_view.xml index 3246f02ee2c..ea948de5019 100644 --- a/addons/point_of_sale/views/pos_config_view.xml +++ b/addons/point_of_sale/views/pos_config_view.xml @@ -37,7 +37,6 @@ -

PoS Interface

-
-
- -
-
-
-
diff --git a/addons/pos_restaurant/__manifest__.py b/addons/pos_restaurant/__manifest__.py index ef7021e3f07..d36fcd99ecc 100644 --- a/addons/pos_restaurant/__manifest__.py +++ b/addons/pos_restaurant/__manifest__.py @@ -51,8 +51,6 @@ This module adds several features to the Point of Sale that are specific to rest 'pos_restaurant/static/src/js/Screens/FloorScreen/EditBar.js', 'pos_restaurant/static/src/js/Screens/FloorScreen/TableWidget.js', 'pos_restaurant/static/src/js/Screens/FloorScreen/EditableTable.js', - 'pos_restaurant/static/src/js/Screens/OrderManagementScreen/OrderManagementScreen.js', - 'pos_restaurant/static/src/js/Screens/OrderManagementScreen/OrderRow.js', 'pos_restaurant/static/src/js/Screens/TicketScreen.js', 'pos_restaurant/static/src/js/ChromeWidgets/BackToFloorButton.js', 'pos_restaurant/static/src/js/ChromeWidgets/TicketButton.js', diff --git a/addons/pos_restaurant/static/src/js/Screens/OrderManagementScreen/OrderManagementScreen.js b/addons/pos_restaurant/static/src/js/Screens/OrderManagementScreen/OrderManagementScreen.js deleted file mode 100644 index 8753be25753..00000000000 --- a/addons/pos_restaurant/static/src/js/Screens/OrderManagementScreen/OrderManagementScreen.js +++ /dev/null @@ -1,28 +0,0 @@ -odoo.define('pos_restaurant.OrderManagementScreen', function (require) { - 'use strict'; - - const OrderManagementScreen = require('point_of_sale.OrderManagementScreen'); - const Registries = require('point_of_sale.Registries'); - - const PosResOrderManagementScreen = (OrderManagementScreen) => - class extends OrderManagementScreen { - /** - * @override - */ - _setOrder(order) { - if (this.env.pos.config.module_pos_restaurant) { - const currentOrder = this.env.pos.get_order(); - this.env.pos.set_table(order.table, order); - if (currentOrder && currentOrder.uid === order.uid) { - this.close(); - } - } else { - super._setOrder(order); - } - } - }; - - Registries.Component.extend(OrderManagementScreen, PosResOrderManagementScreen); - - return OrderManagementScreen; -}); diff --git a/addons/pos_restaurant/static/src/js/Screens/OrderManagementScreen/OrderRow.js b/addons/pos_restaurant/static/src/js/Screens/OrderManagementScreen/OrderRow.js deleted file mode 100644 index f5a32114553..00000000000 --- a/addons/pos_restaurant/static/src/js/Screens/OrderManagementScreen/OrderRow.js +++ /dev/null @@ -1,17 +0,0 @@ -odoo.define('pos_restaurant.OrderRow', function (require) { - 'use strict'; - - const OrderRow = require('point_of_sale.OrderRow'); - const Registries = require('point_of_sale.Registries'); - - const PosResOrderRow = (OrderRow) => - class extends OrderRow { - get table() { - return this.order.table ? this.order.table.name : ''; - } - }; - - Registries.Component.extend(OrderRow, PosResOrderRow); - - return OrderRow; -}); diff --git a/addons/pos_restaurant/static/src/js/Screens/TicketScreen.js b/addons/pos_restaurant/static/src/js/Screens/TicketScreen.js index 00331a70181..6a5bb78bb43 100644 --- a/addons/pos_restaurant/static/src/js/Screens/TicketScreen.js +++ b/addons/pos_restaurant/static/src/js/Screens/TicketScreen.js @@ -19,20 +19,10 @@ odoo.define('pos_restaurant.TicketScreen', function (require) { posbus.trigger('table-set'); } } - get filterOptions() { - const { Payment, Open, Tipping } = this.getOrderStates(); - var filterOptions = super.filterOptions; - if (this.env.pos.config.set_tip_after_payment) { - var idx = filterOptions.indexOf(Payment); - filterOptions[idx] = Open; - } - return [...filterOptions, Tipping]; - } - get _screenToStatusMap() { - const { Open, Tipping } = this.getOrderStates(); - return Object.assign(super._screenToStatusMap, { - PaymentScreen: this.env.pos.config.set_tip_after_payment ? Open : super._screenToStatusMap.PaymentScreen, - TipScreen: Tipping, + _getScreenToStatusMap() { + return Object.assign(super._getScreenToStatusMap(), { + PaymentScreen: this.env.pos.config.set_tip_after_payment ? 'OPEN' : super._getScreenToStatusMap().PaymentScreen, + TipScreen: 'TIPPING', }); } getTable(order) { @@ -47,7 +37,7 @@ odoo.define('pos_restaurant.TicketScreen', function (require) { }); } _setOrder(order) { - if (!this.env.pos.config.iface_floorplan) { + if (!this.env.pos.config.iface_floorplan || order === this.env.pos.get_order()) { super._setOrder(order); } else if (order !== this.env.pos.get_order()) { // Only call set_table if the order is not the same as the current order. @@ -56,19 +46,19 @@ odoo.define('pos_restaurant.TicketScreen', function (require) { this.env.pos.set_table(order.table, order); } } - get showNewTicketButton() { - return this.env.pos.config.iface_floorplan ? Boolean(this.env.pos.table) : super.showNewTicketButton; + shouldShowNewOrderButton() { + return this.env.pos.config.iface_floorplan ? Boolean(this.env.pos.table) : super.shouldShowNewOrderButton(); } - get orderList() { + _getOrderList() { if (this.env.pos.table) { - return super.orderList; + return super._getOrderList(); } else { return this.env.pos.get('orders').models; } } async settleTips() { // set tip in each order - for (const order of this.filteredOrderList) { + for (const order of this.getFilteredOrderList()) { const tipAmount = parse.float(order.uiState.TipScreen.state.inputTipAmount || '0'); const serverId = this.env.pos.validated_orders_name_server_id_map[order.name]; if (!serverId) { @@ -118,11 +108,14 @@ odoo.define('pos_restaurant.TicketScreen', function (require) { args: [serverId], }); } - getOrderStates() { - return Object.assign(super.getOrderStates(), { - Tipping: this.env._t('Tipping'), - Open: this.env._t('Open'), - }); + _getOrderStates() { + const result = super._getOrderStates(); + if (this.env.pos.config.set_tip_after_payment) { + result.delete('PAYMENT'); + result.set('OPEN', this.env._t('Open')); + result.set('TIPPING', this.env._t('Tipping')); + } + return result; } }; diff --git a/addons/pos_restaurant/static/src/xml/Screens/OrderManagementScreen/OrderList.xml b/addons/pos_restaurant/static/src/xml/Screens/OrderManagementScreen/OrderList.xml deleted file mode 100644 index 7960710260f..00000000000 --- a/addons/pos_restaurant/static/src/xml/Screens/OrderManagementScreen/OrderList.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - -
- Table -
-
-
- -
diff --git a/addons/pos_restaurant/static/src/xml/Screens/OrderManagementScreen/OrderRow.xml b/addons/pos_restaurant/static/src/xml/Screens/OrderManagementScreen/OrderRow.xml deleted file mode 100644 index 3006e5bfb42..00000000000 --- a/addons/pos_restaurant/static/src/xml/Screens/OrderManagementScreen/OrderRow.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - -
- -
-
-
- -
diff --git a/addons/pos_restaurant/static/src/xml/Screens/TicketScreen.xml b/addons/pos_restaurant/static/src/xml/Screens/TicketScreen.xml index 1c0ac2608e6..845164f5e47 100644 --- a/addons/pos_restaurant/static/src/xml/Screens/TicketScreen.xml +++ b/addons/pos_restaurant/static/src/xml/Screens/TicketScreen.xml @@ -4,18 +4,18 @@
Table
-
Tip
+
Tip
-
+
- + diff --git a/addons/pos_restaurant/static/tests/tours/OrderManagementScreen.tour.js b/addons/pos_restaurant/static/tests/tours/OrderManagementScreen.tour.js deleted file mode 100644 index bc64f36829c..00000000000 --- a/addons/pos_restaurant/static/tests/tours/OrderManagementScreen.tour.js +++ /dev/null @@ -1,124 +0,0 @@ -odoo.define('pos_restaurant.tour.OrderManagementScreen', function (require) { - 'use strict'; - - // This tour tests the integration of OrderManagementScreen - // to some pos_restaurant features. - - const { makeFullOrder } = require('point_of_sale.tour.CompositeTourMethods'); - const { Chrome } = require('pos_restaurant.tour.ChromeTourMethods'); - const { - OrderManagementScreen, - } = require('point_of_sale.tour.OrderManagementScreenTourMethods'); - const { FloorScreen } = require('pos_restaurant.tour.FloorScreenTourMethods'); - const { ProductScreen } = require('pos_restaurant.tour.ProductScreenTourMethods'); - const { TicketScreen } = require('point_of_sale.tour.TicketScreenTourMethods'); - const { getSteps, startSteps } = require('point_of_sale.tour.utils'); - var Tour = require('web_tour.tour'); - - // signal to start generating steps - // when finished, steps can be taken from getSteps - startSteps(); - - FloorScreen.do.clickTable('T2'); - - // make one order and check if it can be seen from the management screen. - makeFullOrder({ orderlist: [['Minute Maid', '5', '6']], payment: ['Cash', '30'] }); - Chrome.do.clickOrderManagementButton(); - OrderManagementScreen.check.isShown(); - OrderManagementScreen.check.orderlistHas({ orderName: '-0001', total: '30' }); - - // go back to create multiple unpaid orders - OrderManagementScreen.do.clickBack(); - - // create 2 unpaid orders on T2 (orders 0002 and 0003) - FloorScreen.do.clickTable('T2'); - ProductScreen.exec.addMultiOrderlines( - ['Coca-Cola', '1', '2'], - ['Water', '3', '4'], - ['Minute Maid', '5', '6'] - ); - Chrome.do.clickTicketButton(); - TicketScreen.do.clickNewTicket(); - ProductScreen.exec.addMultiOrderlines(['Coca-Cola', '1', '2'], ['Minute Maid', '5', '6']); - Chrome.do.backToFloor(); - - // create 2 unpaid orders on T5 (orders 0004 and 0005) - FloorScreen.do.clickTable('T5'); - ProductScreen.exec.addMultiOrderlines( - ['Coca-Cola', '7', '8'], - ['Water', '9', '10'], - ['Minute Maid', '11', '12'] - ); - Chrome.do.clickTicketButton(); - TicketScreen.do.clickNewTicket(); - ProductScreen.exec.addMultiOrderlines(['Coca-Cola', '13', '14'], ['Minute Maid', '15', '16']); - Chrome.do.backToFloor(); - - // check that the unpaid orders are listed in the management screen - Chrome.do.clickOrderManagementButton(); - OrderManagementScreen.check.isShown(); - OrderManagementScreen.check.orderlistHas({ orderName: '-0002', total: '44.0' }); - OrderManagementScreen.check.orderlistHas({ orderName: '-0003', total: '32.0' }); - OrderManagementScreen.check.orderlistHas({ orderName: '-0004', total: '278.0' }); - OrderManagementScreen.check.orderlistHas({ orderName: '-0005', total: '422.0' }); - - // select order 0003 and check if it is really in T2 - OrderManagementScreen.do.clickOrder('-0003'); - ProductScreen.check.isShown(); - ProductScreen.check.totalAmountIs('32'); - Chrome.check.backToFloorTextIs('Main Floor', 'T2'); - - Chrome.do.clickOrderManagementButton(); - - // select order 0005 and check it it is really in T5 - OrderManagementScreen.check.isShown(); - OrderManagementScreen.do.clickOrder('-0005'); - ProductScreen.check.totalAmountIs('422'); - Chrome.check.backToFloorTextIs('Main Floor', 'T5'); - - // go back to floor screen and start order management from there - Chrome.do.backToFloor(); - FloorScreen.check.selectedFloorIs('Main Floor'); - Chrome.do.clickOrderManagementButton(); - OrderManagementScreen.check.isShown(); - - // select order 0002 and check if it is really in T2 - OrderManagementScreen.do.clickOrder('-0002'); - ProductScreen.check.isShown(); - ProductScreen.check.totalAmountIs('44'); - Chrome.check.backToFloorTextIs('Main Floor', 'T2'); - - Chrome.do.clickOrderManagementButton(); - OrderManagementScreen.check.isShown(); - - // select order 0004 and check if it is really in T5 - OrderManagementScreen.do.clickOrder('-0004'); - ProductScreen.check.isShown(); - ProductScreen.check.totalAmountIs('278'); - Chrome.check.backToFloorTextIs('Main Floor', 'T5'); - - // transfer order 0004 to T2 - ProductScreen.do.clickTransferButton(); - FloorScreen.do.clickTable('T2'); - ProductScreen.check.isShown(); - - Chrome.do.clickOrderManagementButton(); - OrderManagementScreen.check.isShown(); - - // select order 0004 and check if it is now in T2 - OrderManagementScreen.do.clickOrder('-0004', ['table', 'T2']); - ProductScreen.check.isShown(); - ProductScreen.check.totalAmountIs('278'); - Chrome.check.backToFloorTextIs('Main Floor', 'T2'); - - Chrome.do.clickOrderManagementButton(); - OrderManagementScreen.check.isShown(); - - // finally, select order 0002 - OrderManagementScreen.do.clickOrder('-0002'); - ProductScreen.check.isShown(); - ProductScreen.check.totalAmountIs('44'); - Chrome.check.backToFloorTextIs('Main Floor', 'T2'); - - Tour.register('PosResOrderManagementScreenTour', { test: true, url: '/pos/ui' }, getSteps()); -}); diff --git a/addons/pos_restaurant/static/tests/tours/SplitBillScreen.tour.js b/addons/pos_restaurant/static/tests/tours/SplitBillScreen.tour.js index 8b9c0f7d6e0..529898ae36d 100644 --- a/addons/pos_restaurant/static/tests/tours/SplitBillScreen.tour.js +++ b/addons/pos_restaurant/static/tests/tours/SplitBillScreen.tour.js @@ -45,6 +45,7 @@ odoo.define('pos_restaurant.tour.SplitBillScreen', function (require) { // go back to the original order and see if the order is changed Chrome.do.clickTicketButton(); TicketScreen.do.selectOrder('-0001'); + ProductScreen.check.isShown() ProductScreen.do.clickOrderline('Water', '2.0') ProductScreen.do.clickOrderline('Minute Maid', '3.0') diff --git a/addons/pos_restaurant/static/tests/tours/TicketScreen.tour.js b/addons/pos_restaurant/static/tests/tours/TicketScreen.tour.js index 278bbfc5930..682d2444aea 100644 --- a/addons/pos_restaurant/static/tests/tours/TicketScreen.tour.js +++ b/addons/pos_restaurant/static/tests/tours/TicketScreen.tour.js @@ -38,6 +38,7 @@ odoo.define('pos_restaurant.tour.TicketScreen', function (require) { TicketScreen.do.deleteOrder('-0003'); Chrome.do.confirmPopup(); TicketScreen.do.selectOrder('-0002'); + ProductScreen.check.isShown(); ProductScreen.check.totalAmountIs('2.0'); Chrome.check.backToFloorTextIs('Main Floor', 'T2'); Chrome.do.backToFloor(); diff --git a/addons/pos_restaurant/static/tests/tours/TipScreen.tour.js b/addons/pos_restaurant/static/tests/tours/TipScreen.tour.js index 430c7a2f12c..9d8bc9f3d61 100644 --- a/addons/pos_restaurant/static/tests/tours/TipScreen.tour.js +++ b/addons/pos_restaurant/static/tests/tours/TipScreen.tour.js @@ -85,6 +85,7 @@ odoo.define('pos_restaurant.tour.TipScreen', function (require) { // finalize order 4 then tip custom amount TicketScreen.do.selectOrder('-0004'); + ProductScreen.check.isShown(); ProductScreen.check.totalAmountIs('8.0'); ProductScreen.do.clickPayButton(); PaymentScreen.do.clickPaymentMethod('Bank'); @@ -103,12 +104,13 @@ odoo.define('pos_restaurant.tour.TipScreen', function (require) { Chrome.do.clickTicketButton(); TicketScreen.do.selectFilter('Tipping'); TicketScreen.do.settleTips(); - TicketScreen.do.selectFilter('All Tickets'); + TicketScreen.do.selectFilter('Active Orders'); TicketScreen.check.nthRowContains(2, 'Ongoing'); // tip order2 during payment // tip screen should not show after validating payment screen TicketScreen.do.selectOrder('-0002'); + ProductScreen.check.isShown(); ProductScreen.do.clickPayButton(); PaymentScreen.do.clickTipButton(); NumberPopup.check.isShown(); diff --git a/addons/pos_restaurant/tests/test_frontend.py b/addons/pos_restaurant/tests/test_frontend.py index 3050cd46b61..15bd01e013f 100644 --- a/addons/pos_restaurant/tests/test_frontend.py +++ b/addons/pos_restaurant/tests/test_frontend.py @@ -174,11 +174,6 @@ class TestFrontend(odoo.tests.HttpCase): self.start_tour("/pos/ui?config_id=%d" % self.pos_config.id, 'ControlButtonsTour', login="admin") self.start_tour("/pos/ui?config_id=%d" % self.pos_config.id, 'FloorScreenTour', login="admin") - def test_03_order_management_integration(self): - self.pos_config.write({'manage_orders': True}) - self.pos_config.with_user(self.env.ref('base.user_admin')).open_session_cb(check_coa=False) - self.start_tour("/pos/ui?config_id=%d" % self.pos_config.id, 'PosResOrderManagementScreenTour', login="admin") - def test_04_ticket_screen(self): self.pos_config.with_user(self.env.ref('base.user_admin')).open_session_cb(check_coa=False) self.start_tour("/pos/ui?config_id=%d" % self.pos_config.id, 'PosResTicketScreenTour', login="admin")