From 8f9602c3a1d5ad30eefeed22f0e7bdf54bf37938 Mon Sep 17 00:00:00 2001 From: Joseph Caburnay Date: Wed, 19 Jan 2022 15:11:36 +0000 Subject: [PATCH] [IMP] point_of_sale: allow multiple popups at a time Previously, only one popup is allowed such that when one is not yet close, but another one is requested, the new one will replace the old. The function that tries to wait for the replaced popup will never resolve resulting to some potential broken state. This commit resolves the said issue which now allows multiple popups at a time. We can use POS instance without demo as an example. Before this commit: The popup that asks whether to load the demo products will be shown but it will be replaced by the cash opening popup. After this commit: The popup that asks whether to load the demo products will be shown, then the cash opening popup will show. When the user is done with the cash opening popup, the demo products confirmation is shown next. closes odoo/odoo#83661 Related: odoo/enterprise#23859 Signed-off-by: Masereel Pierre --- addons/point_of_sale/static/src/js/Chrome.js | 5 +- .../static/src/js/PopupControllerMixin.js | 45 ----- .../src/js/Popups/AbstractAwaitablePopup.js | 27 +-- .../src/js/Popups/PosPopupController.js | 120 +++++++++++++ .../static/src/js/PosComponent.js | 4 +- .../ControlButtons/RefundButton.js | 1 - .../point_of_sale/static/src/xml/Chrome.xml | 5 +- .../static/src/xml/Popups/CashMovePopup.xml | 2 - .../src/xml/Popups/CashOpeningPopup.xml | 2 - .../static/src/xml/Popups/ClosePosPopup.xml | 2 - .../static/src/xml/Popups/ConfirmPopup.xml | 2 - .../src/xml/Popups/ControlButtonPopup.xml | 5 +- .../static/src/xml/Popups/EditListPopup.xml | 2 - .../src/xml/Popups/ErrorBarcodePopup.xml | 2 - .../static/src/xml/Popups/ErrorPopup.xml | 2 - .../src/xml/Popups/ErrorTracebackPopup.xml | 2 - .../static/src/xml/Popups/NumberPopup.xml | 3 - .../src/xml/Popups/OfflineErrorPopup.xml | 2 - .../src/xml/Popups/OrderImportPopup.xml | 2 - .../src/xml/Popups/PosPopupController.xml | 14 ++ .../xml/Popups/ProductConfiguratorPopup.xml | 2 - .../src/xml/Popups/ProductInfoPopup.xml | 2 - .../static/src/xml/Popups/SelectionPopup.xml | 2 - .../static/src/xml/Popups/TextAreaPopup.xml | 2 - .../static/src/xml/Popups/TextInputPopup.xml | 2 - .../tests/unit/test_PosPopupController.js | 165 ++++++++++++++++++ .../js/ControlButtons/ResetProgramsButton.js | 1 - .../static/src/xml/GiftCardPopup.xml | 2 - .../src/xml/PaymentTransactionPopup.xml | 2 - .../ControlButtons/PrintBillButton.js | 1 - .../ControlButtons/SplitBillButton.js | 1 - .../ControlButtons/TransferOrderButton.js | 1 - .../static/src/js/SetSaleOrderButton.js | 1 - 33 files changed, 321 insertions(+), 112 deletions(-) delete mode 100644 addons/point_of_sale/static/src/js/PopupControllerMixin.js create mode 100644 addons/point_of_sale/static/src/js/Popups/PosPopupController.js create mode 100644 addons/point_of_sale/static/src/xml/Popups/PosPopupController.xml create mode 100644 addons/point_of_sale/static/tests/unit/test_PosPopupController.js diff --git a/addons/point_of_sale/static/src/js/Chrome.js b/addons/point_of_sale/static/src/js/Chrome.js index 730d77b5c42..7ebe3857406 100644 --- a/addons/point_of_sale/static/src/js/Chrome.js +++ b/addons/point_of_sale/static/src/js/Chrome.js @@ -7,7 +7,6 @@ odoo.define('point_of_sale.Chrome', function(require) { const BarcodeParser = require('barcodes.BarcodeParser'); const PosComponent = require('point_of_sale.PosComponent'); const NumberBuffer = require('point_of_sale.NumberBuffer'); - const PopupControllerMixin = require('point_of_sale.PopupControllerMixin'); const Registries = require('point_of_sale.Registries'); const IndependentToOrderScreen = require('point_of_sale.IndependentToOrderScreen'); const { identifyError, batched } = require('point_of_sale.utils'); @@ -32,7 +31,7 @@ odoo.define('point_of_sale.Chrome', function(require) { /** * Chrome is the root component of the PoS App. */ - class Chrome extends PopupControllerMixin(PosComponent) { + class Chrome extends PosComponent { setup() { super.setup(); useExternalListener(window, 'beforeunload', this._onBeforeUnload); @@ -249,7 +248,7 @@ odoo.define('point_of_sale.Chrome', function(require) { openCashControl() { if (this.shouldShowCashControl()) { - this.showPopup('CashOpeningPopup', { notEscapable: true }); + this.showPopup('CashOpeningPopup', { cancelKey: false }); } } diff --git a/addons/point_of_sale/static/src/js/PopupControllerMixin.js b/addons/point_of_sale/static/src/js/PopupControllerMixin.js deleted file mode 100644 index bcafe4a6a06..00000000000 --- a/addons/point_of_sale/static/src/js/PopupControllerMixin.js +++ /dev/null @@ -1,45 +0,0 @@ -odoo.define('point_of_sale.PopupControllerMixin', function(require) { - 'use strict'; - - const { useListener } = require("@web/core/utils/hooks"); - - const { useState } = owl; - - /** - * Allows the component declared with this mixin the ability show popup dynamically, - * provided the following: - * 1. The following element is declared in the template. It is where the Popup will be rendered. - * `` - * 2. The component should trigger `show-popup` event to show the popup and `close-popup` event - * to close. In PosComponent, `showPopup` is conveniently declared to satisfy this requirement. - * @param {Function} x class definition to mix with during extension - */ - const PopupControllerMixin = x => - class extends x { - setup() { - super.setup(); - useListener('show-popup', this.__showPopup); - useListener('close-popup', this.__closePopup); - - this.popup = useState({ isShown: false, name: null, component: null }); - this.popupProps = {}; // We want to avoid making the props to become Proxy! - } - __showPopup(event) { - const { name, props, resolve } = event.detail; - const popupConstructor = this.constructor.components[name]; - if (popupConstructor.dontShow) { - resolve(); - return; - } - this.popup.isShown = true; - this.popup.name = name; - this.popup.component = popupConstructor; - this.popupProps = Object.assign({}, props, { resolve }); - } - __closePopup() { - this.popup.isShown = false; - } - }; - - return PopupControllerMixin; -}); diff --git a/addons/point_of_sale/static/src/js/Popups/AbstractAwaitablePopup.js b/addons/point_of_sale/static/src/js/Popups/AbstractAwaitablePopup.js index ce9d00ae85f..ce88df5fbc2 100644 --- a/addons/point_of_sale/static/src/js/Popups/AbstractAwaitablePopup.js +++ b/addons/point_of_sale/static/src/js/Popups/AbstractAwaitablePopup.js @@ -2,8 +2,7 @@ odoo.define('point_of_sale.AbstractAwaitablePopup', function (require) { 'use strict'; const PosComponent = require('point_of_sale.PosComponent'); - - const { useExternalListener } = owl; + const { useBus } = require('@web/core/utils/hooks'); /** * Implement this abstract class by extending it like so: @@ -33,22 +32,24 @@ odoo.define('point_of_sale.AbstractAwaitablePopup', function (require) { class AbstractAwaitablePopup extends PosComponent { setup() { super.setup(); - if (!this.props.notEscapable) { - useExternalListener(window, 'keyup', this._cancelAtEscape); + if (this.props.confirmKey) { + useBus(this.env.posbus, `confirm-popup-${this.props.id}`, this.confirm); + } + if (this.props.cancelKey) { + useBus(this.env.posbus, `cancel-popup-${this.props.id}`, this.cancel); } } async confirm() { - this.props.resolve({ confirmed: true, payload: await this.getPayload() }); - this.trigger('close-popup'); + this.env.posbus.trigger('close-popup', { + popupId: this.props.id, + response: { confirmed: true, payload: await this.getPayload() }, + }); } cancel() { - this.props.resolve({ confirmed: false, payload: null }); - this.trigger('close-popup'); - } - _cancelAtEscape(event) { - if (event.key === 'Escape') { - this.cancel(); - } + this.env.posbus.trigger('close-popup', { + popupId: this.props.id, + response: { confirmed: false, payload: null }, + }); } /** * Override this in the concrete popup implementation to set the diff --git a/addons/point_of_sale/static/src/js/Popups/PosPopupController.js b/addons/point_of_sale/static/src/js/Popups/PosPopupController.js new file mode 100644 index 00000000000..1a4eba4c962 --- /dev/null +++ b/addons/point_of_sale/static/src/js/Popups/PosPopupController.js @@ -0,0 +1,120 @@ +odoo.define('point_of_sale.PosPopupController', function(require) { + 'use strict'; + + const Registries = require('point_of_sale.Registries'); + const PosComponent = require('point_of_sale.PosComponent'); + const { useBus } = require('@web/core/utils/hooks'); + + /** + * This component is responsible in controlling the popups. It does so + * by coordinating with them thru the `env.posbus`. The basic steps follow: + * 1. `showPopup` method triggers `show-popup` event resulting to the + * mounting of the requested popup. + * 2. When the popup is shown, the `confirm`/`cancel` method of the popup + * will be called after the popup is used. `confirming`/`cancelling` + * will trigger the `close-popup`, which this component also listens to, + * resulting to closing of the popup. + * + * Furthermore, Pressing `confirmKey`/`cancelKey` which defaults to + * 'Enter'/'Escape', will automatically `confirm`/`cancel` the `topPopup`. + * This behavior is accomplished by listening to `keyup` event of the window. + * When the `confirmKey`/`cancelKey` of the `topPopup` is pressed, + * 'cancel-popup-{top-popup-id}'/'confirm-popup-{top-popup-id}' will be triggered + * and since the popup is listening to that event (@see AbstractAwaitablePopup), + * it will result to the call of `confirm`/`cancel` method. + * + * @typedef {{ id: number, resolve: Function, keepBehind?: boolean, cancelKey?: string, confirmKey?: string }} BasePopupProps + * @typedef {{ name: string, component: AbstractAwaitablePopup, props: BasePopupProps, key: string }} Popup + */ + class PosPopupController extends PosComponent { + setup() { + super.setup(); + useBus(this.env.posbus, 'show-popup', this._showPopup); + useBus(this.env.posbus, 'close-popup', this._closePopup); + owl.useExternalListener(window, 'keyup', this._onWindowKeyup); + this.popups = owl.useState([]); + } + _showPopup(event) { + let { id, name, props, resolve } = event.detail; + props = Object.assign(props || {}, { id, resolve }); + const component = this.constructor.components[name]; + if (!component) { + throw new Error(`'${name}' is not found. Make sure the file is loaded and the component is properly registered using 'Registries.Component.add'.`); + } + if (component.dontShow) { + resolve(); + return; + } + this.popups.push({ + name, + component, + props: this._constructPopupProps(component, props), + key: `${name}-${id}`, + }); + } + _closePopup(event) { + const { popupId, response } = event.detail; + const index = this.popups.findIndex((popup) => popup.props.id == popupId); + if (index != -1) { + const popup = this.popups[index]; + popup.props.resolve(response); + this.popups.splice(index, 1); + } + } + _onWindowKeyup(event) { + if (!this.topPopup) return; + if (event.key === this.topPopup.props.cancelKey) { + this.env.posbus.trigger(`cancel-popup-${this.topPopup.props.id}`); + } else if (event.key === this.topPopup.props.confirmKey) { + this.env.posbus.trigger(`confirm-popup-${this.topPopup.props.id}`); + } + } + /** + * A popup can be cancelled/confirmed with 'Escape'/'Enter' key by default. + * Also, if it's not the top popup, it is hidden from the view. + * This can be overridden by the default props of the popop component + * and the props used in requesting to show the popup. + * + * @param {AbstractAwaitablePopup} popupComponent + * @param {Object} props + * @returns {BasePopupProps} + */ + _constructPopupProps(popupComponent, props) { + const defaultProps = popupComponent.defaultProps || {}; + return Object.assign( + { + keepBehind: false, + cancelKey: 'Escape', + confirmKey: 'Enter', + }, + defaultProps, + props + ); + } + /** + * @returns {boolean} Hide the element of this component when this returns false. + */ + isShown() { + return this.popups.length > 0; + } + get topPopup() { + return this.popups[this.popups.length - 1]; + } + /** + * By default, only show the top popup. But always show a popup if + * `keepBehind` props is true. Meaning, if you have 2 popups, and + * the bottom popup has `keepBehind = true`, then the bottom popup + * will be visible if it's not blocked in the view by the top popup. + * + * @param {Popup} popup + * @returns {boolean} + */ + shouldShow(popup) { + return this.topPopup === popup || popup.props.keepBehind; + } + } + PosPopupController.template = 'point_of_sale.PosPopupController'; + Registries.Component.add(PosPopupController); + + return PosPopupController; +}); diff --git a/addons/point_of_sale/static/src/js/PosComponent.js b/addons/point_of_sale/static/src/js/PosComponent.js index 51b82078721..2f969a687ad 100644 --- a/addons/point_of_sale/static/src/js/PosComponent.js +++ b/addons/point_of_sale/static/src/js/PosComponent.js @@ -4,6 +4,8 @@ odoo.define('point_of_sale.PosComponent', function (require) { const { LegacyComponent } = require("@web/legacy/legacy_component"); const { onRendered } = owl; + let nextId = 0; + class PosComponent extends LegacyComponent { setup() { onRendered(() => { @@ -36,7 +38,7 @@ odoo.define('point_of_sale.PosComponent', function (require) { */ showPopup(name, props) { return new Promise((resolve) => { - this.trigger('show-popup', { name, props, resolve }); + this.env.posbus.trigger('show-popup', { name, props, resolve, id: nextId++ }); }); } showTempScreen(name, props) { diff --git a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/RefundButton.js b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/RefundButton.js index 8054aa9f752..0c0799fdffa 100644 --- a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/RefundButton.js +++ b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/RefundButton.js @@ -14,7 +14,6 @@ odoo.define('point_of_sale.RefundButton', function (require) { _onClick() { const partner = this.env.pos.get_order().get_partner(); const searchDetails = partner ? { fieldName: 'PARTNER', searchTerm: partner.name } : {}; - this.trigger('close-popup'); this.showScreen('TicketScreen', { ui: { filter: 'SYNCED', searchDetails }, destinationOrder: this.env.pos.get_order(), diff --git a/addons/point_of_sale/static/src/xml/Chrome.xml b/addons/point_of_sale/static/src/xml/Chrome.xml index a721a6647cb..ae9164120d5 100644 --- a/addons/point_of_sale/static/src/xml/Chrome.xml +++ b/addons/point_of_sale/static/src/xml/Chrome.xml @@ -62,10 +62,7 @@ -
- -
+ diff --git a/addons/point_of_sale/static/src/xml/Popups/CashMovePopup.xml b/addons/point_of_sale/static/src/xml/Popups/CashMovePopup.xml index 377af74f076..053bc04a5e8 100644 --- a/addons/point_of_sale/static/src/xml/Popups/CashMovePopup.xml +++ b/addons/point_of_sale/static/src/xml/Popups/CashMovePopup.xml @@ -2,7 +2,6 @@ - diff --git a/addons/point_of_sale/static/src/xml/Popups/CashOpeningPopup.xml b/addons/point_of_sale/static/src/xml/Popups/CashOpeningPopup.xml index cb6544456f0..60e518373d6 100644 --- a/addons/point_of_sale/static/src/xml/Popups/CashOpeningPopup.xml +++ b/addons/point_of_sale/static/src/xml/Popups/CashOpeningPopup.xml @@ -1,7 +1,6 @@ - diff --git a/addons/point_of_sale/static/src/xml/Popups/NumberPopup.xml b/addons/point_of_sale/static/src/xml/Popups/NumberPopup.xml index 3a6d191f35f..4c9eeafb815 100644 --- a/addons/point_of_sale/static/src/xml/Popups/NumberPopup.xml +++ b/addons/point_of_sale/static/src/xml/Popups/NumberPopup.xml @@ -2,7 +2,6 @@ - diff --git a/addons/point_of_sale/static/src/xml/Popups/OfflineErrorPopup.xml b/addons/point_of_sale/static/src/xml/Popups/OfflineErrorPopup.xml index 9950bc3d28d..6be9723f2d0 100644 --- a/addons/point_of_sale/static/src/xml/Popups/OfflineErrorPopup.xml +++ b/addons/point_of_sale/static/src/xml/Popups/OfflineErrorPopup.xml @@ -2,7 +2,6 @@ - diff --git a/addons/point_of_sale/static/src/xml/Popups/OrderImportPopup.xml b/addons/point_of_sale/static/src/xml/Popups/OrderImportPopup.xml index b2f142b9536..ce286454cd2 100644 --- a/addons/point_of_sale/static/src/xml/Popups/OrderImportPopup.xml +++ b/addons/point_of_sale/static/src/xml/Popups/OrderImportPopup.xml @@ -2,7 +2,6 @@ - diff --git a/addons/point_of_sale/static/src/xml/Popups/PosPopupController.xml b/addons/point_of_sale/static/src/xml/Popups/PosPopupController.xml new file mode 100644 index 00000000000..78392b7ef8f --- /dev/null +++ b/addons/point_of_sale/static/src/xml/Popups/PosPopupController.xml @@ -0,0 +1,14 @@ + + + + +
+ + + +
+
+ +
diff --git a/addons/point_of_sale/static/src/xml/Popups/ProductConfiguratorPopup.xml b/addons/point_of_sale/static/src/xml/Popups/ProductConfiguratorPopup.xml index 55ef49a16d7..3188264256a 100644 --- a/addons/point_of_sale/static/src/xml/Popups/ProductConfiguratorPopup.xml +++ b/addons/point_of_sale/static/src/xml/Popups/ProductConfiguratorPopup.xml @@ -2,7 +2,6 @@ - - diff --git a/addons/point_of_sale/static/src/xml/Popups/ProductInfoPopup.xml b/addons/point_of_sale/static/src/xml/Popups/ProductInfoPopup.xml index 9ccfbdcf204..6398d626dd4 100644 --- a/addons/point_of_sale/static/src/xml/Popups/ProductInfoPopup.xml +++ b/addons/point_of_sale/static/src/xml/Popups/ProductInfoPopup.xml @@ -1,7 +1,6 @@ - - diff --git a/addons/point_of_sale/static/src/xml/Popups/SelectionPopup.xml b/addons/point_of_sale/static/src/xml/Popups/SelectionPopup.xml index 249ac8c6815..12add8473ab 100644 --- a/addons/point_of_sale/static/src/xml/Popups/SelectionPopup.xml +++ b/addons/point_of_sale/static/src/xml/Popups/SelectionPopup.xml @@ -2,7 +2,6 @@ - diff --git a/addons/point_of_sale/static/src/xml/Popups/TextAreaPopup.xml b/addons/point_of_sale/static/src/xml/Popups/TextAreaPopup.xml index a142c995719..2e2c67a3212 100644 --- a/addons/point_of_sale/static/src/xml/Popups/TextAreaPopup.xml +++ b/addons/point_of_sale/static/src/xml/Popups/TextAreaPopup.xml @@ -2,7 +2,6 @@ - diff --git a/addons/point_of_sale/static/src/xml/Popups/TextInputPopup.xml b/addons/point_of_sale/static/src/xml/Popups/TextInputPopup.xml index cc28d70638c..3cfb2c21364 100644 --- a/addons/point_of_sale/static/src/xml/Popups/TextInputPopup.xml +++ b/addons/point_of_sale/static/src/xml/Popups/TextInputPopup.xml @@ -2,7 +2,6 @@ - - diff --git a/addons/point_of_sale/static/tests/unit/test_PosPopupController.js b/addons/point_of_sale/static/tests/unit/test_PosPopupController.js new file mode 100644 index 00000000000..51e9e39914c --- /dev/null +++ b/addons/point_of_sale/static/tests/unit/test_PosPopupController.js @@ -0,0 +1,165 @@ +odoo.define('point_of_sale.tests.PosPopupController', function(require) { + 'use strict'; + + const PosPopupController = require('point_of_sale.PosPopupController'); + const AbstractAwaitablePopup = require('point_of_sale.AbstractAwaitablePopup'); + const PosComponent = require('point_of_sale.PosComponent'); + const makeTestEnvironment = require('web.test_env'); + const testUtils = require('web.test_utils'); + const Registries = require('point_of_sale.Registries'); + const { mount } = require('@web/../tests/helpers/utils'); + + const { EventBus, useSubEnv, xml } = owl; + + QUnit.module('unit tests for PosPopupController', { + before() { + Registries.Component.freeze(); + + // Note that we are creating new popups here to decouple this test from the pos app. + class CustomPopup1 extends AbstractAwaitablePopup {} + CustomPopup1.template = xml/* html */` + + `; + + class CustomPopup2 extends AbstractAwaitablePopup {} + CustomPopup2.template = xml/* html */` + + `; + + PosPopupController.components = { CustomPopup1, CustomPopup2 }; + }, + }); + + QUnit.test('allow multiple popups at the same time', async function(assert) { + assert.expect(12); + + class Root extends PosComponent { + setup() { + super.setup(); + useSubEnv({ + isDebug: () => false, + posbus: new EventBus(), + }); + } + } + Root.env = makeTestEnvironment(); + Root.template = xml/* html */ ` +
+ +
+ `; + + const root = await mount(Root, testUtils.prepareTarget()); + + // Check 1 popup + let popup1Promise = root.showPopup('CustomPopup1', {}); + await testUtils.nextTick(); + assert.strictEqual(root.el.querySelectorAll('.popup').length, 1); + testUtils.dom.click(root.el.querySelector('.modal-dialog .custom-popup-1 .confirm')); + let result1 = await popup1Promise; + assert.strictEqual(result1.confirmed, true); + await testUtils.nextTick(); + assert.strictEqual(root.el.querySelectorAll('.popup').length, 0); + + // Check multiple popups + popup1Promise = root.showPopup('CustomPopup1', {}); + await testUtils.nextTick(); + + // Check if the first popup is shown. + assert.strictEqual(root.el.querySelectorAll('.popup').length, 1); + + let popup2Promise = root.showPopup('CustomPopup2', {}); + await testUtils.nextTick(); + + // Check for the second popup. + assert.strictEqual(root.el.querySelectorAll('.popup').length, 2); + + // popup 1 should be hidden + assert.strictEqual(root.el.querySelectorAll('.modal-dialog.oe_hidden').length, 1); + + // click confirm on popup 2 + testUtils.dom.click(root.el.querySelector('.modal-dialog .custom-popup-2 .confirm')); + await testUtils.nextTick(); + + // after confirming on popup 2, only 1 should remain. + assert.strictEqual(root.el.querySelectorAll('.popup').length, 1); + assert.strictEqual(root.el.querySelectorAll('.modal-dialog .custom-popup-2').length, 0); + + // popup 1 should not be hidden + const CustomPopup1 = root.el.querySelector('.modal-dialog') + assert.strictEqual(![...CustomPopup1.classList].includes('oe_hidden'), true); + testUtils.dom.click(root.el.querySelector('.modal-dialog .custom-popup-1 .cancel')); + await testUtils.nextTick(); + + // after cancelling popup 1, no popup should remain. + assert.strictEqual(root.el.querySelectorAll('.popup').length, 0); + + result1 = await popup1Promise; + let result2 = await popup2Promise; + assert.strictEqual(result1.confirmed, false); // false because it's cancelled. + assert.strictEqual(result2.confirmed, true); // true because it's confirmed. + }); + + QUnit.test('pressing cancel/confirm key should only close the top popup', async function(assert) { + assert.expect(6); + + class Root extends PosComponent { + setup() { + super.setup(); + useSubEnv({ + isDebug: () => false, + posbus: new EventBus(), + }); + } + } + Root.env = makeTestEnvironment(); + Root.template = xml/* html */ ` +
+ +
+ `; + + const root = await mount(Root, testUtils.prepareTarget()); + + let popup1Promise = root.showPopup('CustomPopup1', { confirmKey: 'Enter', cancelKey: 'Escape' }); + await testUtils.nextTick(); + assert.strictEqual(root.el.querySelectorAll('.popup').length, 1); + + let popup2Promise = root.showPopup('CustomPopup2', { confirmKey: 'Enter', cancelKey: 'Escape' }); + await testUtils.nextTick(); + assert.strictEqual(root.el.querySelectorAll('.popup').length, 2); + + // Pressing 'Escape' should cancel the top popup which is the CustomPopup2. + testUtils.dom.triggerEvent(window, 'keyup', { key: 'Escape' }); + await testUtils.nextTick(); + + // Therefore, the popup2Promise has now resolved with `confirmed` value = false. + const result2 = await popup2Promise; + assert.strictEqual(result2.confirmed, false); + + assert.strictEqual(root.el.querySelectorAll('.popup').length, 1); + + testUtils.dom.triggerEvent(window, 'keyup', { key: 'Enter' }); + await testUtils.nextTick(); + + assert.strictEqual(root.el.querySelectorAll('.popup').length, 0); + + const result1 = await popup1Promise; + assert.strictEqual(result1.confirmed, true); + }); +}); diff --git a/addons/pos_coupon/static/src/js/ControlButtons/ResetProgramsButton.js b/addons/pos_coupon/static/src/js/ControlButtons/ResetProgramsButton.js index 5b6b0894756..dade62630c0 100644 --- a/addons/pos_coupon/static/src/js/ControlButtons/ResetProgramsButton.js +++ b/addons/pos_coupon/static/src/js/ControlButtons/ResetProgramsButton.js @@ -14,7 +14,6 @@ odoo.define('pos_coupon.ResetProgramsButton', function (require) { async onClick() { const order = this.env.pos.get_order(); order.resetPrograms(); - this.trigger('close-popup'); } } ResetProgramsButton.template = 'ResetProgramsButton'; diff --git a/addons/pos_gift_card/static/src/xml/GiftCardPopup.xml b/addons/pos_gift_card/static/src/xml/GiftCardPopup.xml index a61f3a7e0cf..aea1ffc0478 100644 --- a/addons/pos_gift_card/static/src/xml/GiftCardPopup.xml +++ b/addons/pos_gift_card/static/src/xml/GiftCardPopup.xml @@ -2,7 +2,6 @@ - diff --git a/addons/pos_mercury/static/src/xml/PaymentTransactionPopup.xml b/addons/pos_mercury/static/src/xml/PaymentTransactionPopup.xml index 39c05a8fce0..7bac9c11868 100644 --- a/addons/pos_mercury/static/src/xml/PaymentTransactionPopup.xml +++ b/addons/pos_mercury/static/src/xml/PaymentTransactionPopup.xml @@ -2,7 +2,6 @@ - - diff --git a/addons/pos_restaurant/static/src/js/Screens/ProductScreen/ControlButtons/PrintBillButton.js b/addons/pos_restaurant/static/src/js/Screens/ProductScreen/ControlButtons/PrintBillButton.js index e892a479a75..7d048f0a48f 100644 --- a/addons/pos_restaurant/static/src/js/Screens/ProductScreen/ControlButtons/PrintBillButton.js +++ b/addons/pos_restaurant/static/src/js/Screens/ProductScreen/ControlButtons/PrintBillButton.js @@ -14,7 +14,6 @@ odoo.define('pos_restaurant.PrintBillButton', function(require) { async onClick() { const order = this.env.pos.get_order(); if (order.get_orderlines().length > 0) { - this.trigger('close-popup'); await this.showTempScreen('BillScreen'); } else { await this.showPopup('ErrorPopup', { diff --git a/addons/pos_restaurant/static/src/js/Screens/ProductScreen/ControlButtons/SplitBillButton.js b/addons/pos_restaurant/static/src/js/Screens/ProductScreen/ControlButtons/SplitBillButton.js index 5e5fdc79ae0..5ecd9627412 100644 --- a/addons/pos_restaurant/static/src/js/Screens/ProductScreen/ControlButtons/SplitBillButton.js +++ b/addons/pos_restaurant/static/src/js/Screens/ProductScreen/ControlButtons/SplitBillButton.js @@ -14,7 +14,6 @@ odoo.define('pos_restaurant.SplitBillButton', function(require) { async onClick() { const order = this.env.pos.get_order(); if (order.get_orderlines().length > 0) { - this.trigger('close-popup'); this.showScreen('SplitBillScreen'); } } diff --git a/addons/pos_restaurant/static/src/js/Screens/ProductScreen/ControlButtons/TransferOrderButton.js b/addons/pos_restaurant/static/src/js/Screens/ProductScreen/ControlButtons/TransferOrderButton.js index 67a2c4fb0dc..67211231dfb 100644 --- a/addons/pos_restaurant/static/src/js/Screens/ProductScreen/ControlButtons/TransferOrderButton.js +++ b/addons/pos_restaurant/static/src/js/Screens/ProductScreen/ControlButtons/TransferOrderButton.js @@ -12,7 +12,6 @@ odoo.define('pos_restaurant.TransferOrderButton', function(require) { useListener('click', this.onClick); } async onClick() { - this.trigger('close-popup'); this.env.pos.transfer_order_to_different_table(); this.showScreen('FloorScreen'); } diff --git a/addons/pos_sale/static/src/js/SetSaleOrderButton.js b/addons/pos_sale/static/src/js/SetSaleOrderButton.js index d0dd57ecb89..3c7ad5792ef 100644 --- a/addons/pos_sale/static/src/js/SetSaleOrderButton.js +++ b/addons/pos_sale/static/src/js/SetSaleOrderButton.js @@ -24,7 +24,6 @@ odoo.define('pos_sale.SetSaleOrderButton', function(require) { args: [[]], kwargs: { context: this.env.session.user_context }, }); - this.trigger('close-popup'); this.showScreen('SaleOrderManagementScreen'); } catch (error) { if (isConnectionError(error)) {