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")