From 43529c857375a3bf357e09f7494fb5b8cf50dc39 Mon Sep 17 00:00:00 2001 From: Samuel Degueldre Date: Tue, 3 Jan 2023 08:17:20 +0000 Subject: [PATCH] [REF] point_of_sale, *: extract navbar and remove ChromeAdapter *: pos_hr, pos_restaurant, pos_six The Chrome component a bloated "god component" that coordinates entirely too many things which makes it hard to reason about, navigate, and debug. This commit extracts the navbar section of the chrome component into its own component, and makes the sound player into its own self-contained service. This commit also merges the ChromeAdapter component into the Chrome component, there is no reason for these to be two separate components. Part-of: odoo/odoo#108891 --- addons/point_of_sale/__manifest__.py | 2 +- .../static/src/app/navbar/navbar.js | 17 ++ .../static/src/app/navbar/navbar.xml | 24 ++ .../point_of_sale/static/src/app/pos_store.js | 41 ++++ .../static/src/app/sound/sound_service.js | 45 ++++ .../static/src/entry/chrome_adapter.js | 82 ------- addons/point_of_sale/static/src/entry/main.js | 28 +-- addons/point_of_sale/static/src/js/Chrome.js | 208 ++++++++++-------- .../src/js/ChromeWidgets/DebugWidget.js | 2 + .../src/js/ChromeWidgets/TicketButton.js | 9 +- .../static/src/js/Misc/NotificationSound.js | 17 -- .../static/src/js/PosComponent.js | 8 - .../Screens/ProductScreen/ProductsWidget.js | 8 +- .../ProductsWidgetControlPanel.js | 12 +- .../js/Screens/ReceiptScreen/ReceiptScreen.js | 2 +- .../point_of_sale/static/src/xml/Chrome.xml | 27 +-- .../src/xml/ChromeWidgets/DebugWidget.xml | 2 +- .../src/xml/ChromeWidgets/TicketButton.xml | 2 +- .../static/src/xml/Misc/NotificationSound.xml | 8 - .../Screens/ProductScreen/ProductScreen.xml | 2 +- .../Screens/ProductScreen/ProductsWidget.xml | 3 +- .../ProductsWidgetControlPanel.xml | 8 +- addons/pos_hr/__manifest__.py | 1 + addons/pos_hr/static/src/app/navbar.xml | 11 + addons/pos_hr/static/src/js/Chrome.js | 11 +- addons/pos_hr/static/src/xml/Chrome.xml | 11 - addons/pos_restaurant/__manifest__.py | 1 + .../src/{xml/Chrome.xml => app/navbar.xml} | 4 +- .../src/js/ChromeWidgets/BackToFloorButton.js | 13 +- .../src/js/ChromeWidgets/TicketButton.js | 2 +- .../static/src/js/Screens/PaymentScreen.js | 1 + .../js/Screens/ProductScreen/ProductScreen.js | 8 + .../js/Screens/ReceiptScreen/ReceiptScreen.js | 17 +- .../static/src/js/Screens/TipScreen.js | 1 + .../xml/ChromeWidgets/BackToFloorButton.xml | 1 - .../Screens/PaymentScreen/PaymentScreen.xml | 6 - .../Screens/ProductScreen/ProductScreen.xml | 10 - .../Screens/ReceiptScreen/ReceiptScreen.xml | 12 - .../static/src/xml/Screens/TipScreen.xml | 3 - addons/pos_six/__manifest__.py | 1 + addons/pos_six/static/app/navbar.xml | 10 + addons/pos_six/static/src/js/Chrome.js | 15 -- addons/pos_six/static/src/xml/Chrome.xml | 10 - addons/web/tooling/_eslintrc.json | 3 +- 44 files changed, 344 insertions(+), 365 deletions(-) create mode 100644 addons/point_of_sale/static/src/app/navbar/navbar.js create mode 100644 addons/point_of_sale/static/src/app/navbar/navbar.xml create mode 100644 addons/point_of_sale/static/src/app/pos_store.js create mode 100644 addons/point_of_sale/static/src/app/sound/sound_service.js delete mode 100644 addons/point_of_sale/static/src/entry/chrome_adapter.js delete mode 100644 addons/point_of_sale/static/src/js/Misc/NotificationSound.js delete mode 100644 addons/point_of_sale/static/src/xml/Misc/NotificationSound.xml create mode 100644 addons/pos_hr/static/src/app/navbar.xml delete mode 100644 addons/pos_hr/static/src/xml/Chrome.xml rename addons/pos_restaurant/static/src/{xml/Chrome.xml => app/navbar.xml} (52%) create mode 100644 addons/pos_restaurant/static/src/js/Screens/ProductScreen/ProductScreen.js delete mode 100644 addons/pos_restaurant/static/src/xml/Screens/ProductScreen/ProductScreen.xml delete mode 100644 addons/pos_restaurant/static/src/xml/Screens/ReceiptScreen/ReceiptScreen.xml create mode 100644 addons/pos_six/static/app/navbar.xml delete mode 100644 addons/pos_six/static/src/js/Chrome.js delete mode 100644 addons/pos_six/static/src/xml/Chrome.xml diff --git a/addons/point_of_sale/__manifest__.py b/addons/point_of_sale/__manifest__.py index 89ef744e40a..0e42a7cb1ea 100644 --- a/addons/point_of_sale/__manifest__.py +++ b/addons/point_of_sale/__manifest__.py @@ -125,11 +125,11 @@ 'point_of_sale/static/src/js/**/*.js', 'web/static/lib/zxing-library/zxing-library.js', 'point_of_sale/static/src/xml/**/*.xml', + 'point_of_sale/static/src/app/**/*', ], # This bundle contains the code responsible for starting the POS UI. # It is practically the entry point. 'point_of_sale.assets_backend_prod_only': [ - 'point_of_sale/static/src/entry/chrome_adapter.js', 'point_of_sale/static/src/entry/main.js', 'web/static/src/start.js', 'web/static/src/legacy/legacy_setup.js', diff --git a/addons/point_of_sale/static/src/app/navbar/navbar.js b/addons/point_of_sale/static/src/app/navbar/navbar.js new file mode 100644 index 00000000000..3e6fe88a877 --- /dev/null +++ b/addons/point_of_sale/static/src/app/navbar/navbar.js @@ -0,0 +1,17 @@ +/** @odoo-module */ + +import PosComponent from "@point_of_sale/js/PosComponent"; +import { usePos } from "@point_of_sale/app/pos_store"; + +export class Navbar extends PosComponent { + static template = "point_of_sale.Navbar"; + static props = { + showCashMoveButton: Boolean, + }; + setup() { + this.pos = usePos(); + } + get customerFacingDisplayButtonIsShown() { + return this.env.pos.config.iface_customer_facing_display; + } +} diff --git a/addons/point_of_sale/static/src/app/navbar/navbar.xml b/addons/point_of_sale/static/src/app/navbar/navbar.xml new file mode 100644 index 00000000000..906623852f6 --- /dev/null +++ b/addons/point_of_sale/static/src/app/navbar/navbar.xml @@ -0,0 +1,24 @@ + + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+
+ +
diff --git a/addons/point_of_sale/static/src/app/pos_store.js b/addons/point_of_sale/static/src/app/pos_store.js new file mode 100644 index 00000000000..b572ab73009 --- /dev/null +++ b/addons/point_of_sale/static/src/app/pos_store.js @@ -0,0 +1,41 @@ +/** @odoo-module */ + +import { registry } from "@web/core/registry"; +import { useService } from "@web/core/utils/hooks"; +import { debounce } from "@web/core/utils/timing"; +import { reactive, useState, toRaw } from "@odoo/owl"; + +export class PosStore { + /** @type {'LOADING' | 'READY' | 'CLOSING'} */ + uiState = "LOADING"; + debugWidgetIsShown = true; + hasBigScrollBars = false; + notification = { + isShown: false, + message: "", + duration: 2000, + }; + loadingSkipButtonIsShown = false; + // not using an arrow function here because we need the correct `this` + toggleDebugWidget = debounce(function toggleDebugWidget() { + this.debugWidgetIsShown = !toRaw(this).debugWidgetIsShown; + }, 100); + mainScreen = { name: null, component: null }; + constructor() { + this.setup(); + } + // to allow other modules to add things to the store. + setup() {} +} + +export const posService = { + start() { + return reactive(new PosStore()); + }, +}; + +registry.category("services").add("pos", posService); + +export function usePos() { + return useState(useService("pos")); +} diff --git a/addons/point_of_sale/static/src/app/sound/sound_service.js b/addons/point_of_sale/static/src/app/sound/sound_service.js new file mode 100644 index 00000000000..cdcae67b93a --- /dev/null +++ b/addons/point_of_sale/static/src/app/sound/sound_service.js @@ -0,0 +1,45 @@ +/** @odoo-module */ + +import { Component, reactive, xml } from "@odoo/owl"; +import { registry } from "@web/core/registry"; + +registry + .category("sounds") + .add("error", "/point_of_sale/static/src/sounds/error.wav") + .add("bell", "/point_of_sale/static/src/sounds/bell.wav"); + +class SoundContainer extends Component { + static template = xml` + `; + static props = { + sounds: Object, + }; + onError(sound) { + sound.cleanup(); + const err = new Error(`Couldn't load sound at "${sound.src}"`); + throw err; + } +} + +export const soundService = { + start() { + let soundId = 0; + const sounds = reactive({}); + registry.category("main_components").add("SoundContainer", { + Component: SoundContainer, + props: { sounds }, + }); + return { + play(name) { + const id = soundId++; + sounds[id] = { + src: registry.category("sounds").get(name), + cleanup: () => delete sounds[id], + }; + }, + }; + }, +}; + +registry.category("services").add("sound", soundService); diff --git a/addons/point_of_sale/static/src/entry/chrome_adapter.js b/addons/point_of_sale/static/src/entry/chrome_adapter.js deleted file mode 100644 index e69b8be5c43..00000000000 --- a/addons/point_of_sale/static/src/entry/chrome_adapter.js +++ /dev/null @@ -1,82 +0,0 @@ -/** @odoo-module */ - -import { useService } from "@web/core/utils/hooks"; - -import Chrome from "@point_of_sale/js/Chrome"; -import ProductScreen from "@point_of_sale/js/Screens/ProductScreen/ProductScreen"; -import Registries from "@point_of_sale/js/Registries"; -import { PosGlobalState } from "@point_of_sale/js/models"; -import { configureGui } from "@point_of_sale/js/Gui"; -import { registry } from "@web/core/registry"; -import env from "@point_of_sale/js/pos_env"; -import { debounce } from "@web/core/utils/timing"; -import { batched } from "@point_of_sale/js/utils"; - -const { Component, reactive, markRaw, useExternalListener, useSubEnv, onWillUnmount, xml } = owl; - -export class ChromeAdapter extends Component { - setup() { - this.PosChrome = Registries.Component.get(Chrome); - ProductScreen.sortControlButtons(); - const legacyActionManager = useService("legacy_action_manager"); - - // Instantiate PosGlobalState here to ensure that every extension - // (or class overloads) is taken into consideration. - const pos = PosGlobalState.create({ env: markRaw(env) }); - - this.batchedCustomerDisplayRender = batched(() => { - reactivePos.send_current_order_to_customer_facing_display(); - }); - const reactivePos = reactive(pos, this.batchedCustomerDisplayRender); - env.pos = reactivePos; - env.legacyActionManager = legacyActionManager; - - // The proxy requires the instance of PosGlobalState to function properly. - env.proxy.set_pos(reactivePos); - - // TODO: Should we continue on exposing posmodel as global variable? - // Expose only the reactive version of `pos` when in debug mode. - window.posmodel = pos.debug ? reactivePos : pos; - - this.env = env; - this.__owl__.childEnv = env; - useSubEnv({ - get isMobile() { - return window.innerWidth <= 768; - }, - }); - let currentIsMobile = this.env.isMobile; - const updateUI = debounce(() => { - if (this.env.isMobile !== currentIsMobile) { - currentIsMobile = this.env.isMobile; - this.render(true); - } - }, 15); - useExternalListener(window, "resize", updateUI); - onWillUnmount(updateUI.cancel); - } - - async configureAndStart(chrome) { - // Add the pos error handler when the chrome component is available. - registry.category("error_handlers").add( - "posErrorHandler", - (env, ...noEnvArgs) => { - if (chrome) { - return chrome.errorHandler(this.env, ...noEnvArgs); - } - return false; - }, - { sequence: 0 } - ); - // Little trick to avoid displaying the block ui during the POS models loading - const BlockUiFromRegistry = registry.category("main_components").get("BlockUI"); - registry.category("main_components").remove("BlockUI"); - configureGui({ component: chrome }); - await chrome.start(); - registry.category("main_components").add("BlockUI", BlockUiFromRegistry); - - // Subscribe to the changes in the models. - this.batchedCustomerDisplayRender(); - } -} -ChromeAdapter.template = xml``; diff --git a/addons/point_of_sale/static/src/entry/main.js b/addons/point_of_sale/static/src/entry/main.js index d6b3d4c9fe1..72f106e122e 100644 --- a/addons/point_of_sale/static/src/entry/main.js +++ b/addons/point_of_sale/static/src/entry/main.js @@ -2,35 +2,15 @@ import { startWebClient } from "@web/start"; -import { ChromeAdapter } from "@point_of_sale/entry/chrome_adapter"; +import { Chrome } from "@point_of_sale/js/Chrome"; import Registries from "@point_of_sale/js/Registries"; -import { registry } from "@web/core/registry"; - -const { Component, xml } = owl; - -// For consistency's sake, we should trigger"WEB_CLIENT_READY" on the bus when PosApp is mounted -// But we can't since mail and some other poll react on that cue, and we don't want those services started -class PosApp extends Component { - setup() { - this.Components = registry.category("main_components").getEntries(); - } -} -PosApp.template = xml` - - -
- - - -
- -`; -PosApp.components = { ChromeAdapter }; function startPosApp() { Registries.Component.freeze(); Registries.Model.freeze(); - startWebClient(PosApp); + // For consistency's sake, we should trigger"WEB_CLIENT_READY" on the bus when PosApp is mounted + // But we can't since mail and some other poll react on that cue, and we don't want those services started + startWebClient(Registries.Component.get(Chrome)); } startPosApp(); diff --git a/addons/point_of_sale/static/src/js/Chrome.js b/addons/point_of_sale/static/src/js/Chrome.js index 3a7c2335559..c9646ff9f42 100644 --- a/addons/point_of_sale/static/src/js/Chrome.js +++ b/addons/point_of_sale/static/src/js/Chrome.js @@ -1,7 +1,7 @@ /** @odoo-module */ import { loadCSS } from "@web/core/assets"; -import { useListener, useBus } from "@web/core/utils/hooks"; +import { useListener, useBus, useService } from "@web/core/utils/hooks"; import BarcodeParser from "barcodes.BarcodeParser"; import PosComponent from "@point_of_sale/js/PosComponent"; import NumberBuffer from "@point_of_sale/js/Misc/NumberBuffer"; @@ -16,33 +16,87 @@ import { } from "@web/core/network/rpc_service"; import { debounce } from "@web/core/utils/timing"; import { Transition } from "@web/core/transition"; +import { MainComponentsContainer } from "@web/core/main_components_container"; +import { WithEnv } from "@web/core/utils/components"; +import { Navbar } from "@point_of_sale/app/navbar/navbar"; -const { - onError, +// ChromeAdapter imports +import ProductScreen from "@point_of_sale/js/Screens/ProductScreen/ProductScreen"; +import { PosGlobalState } from "@point_of_sale/js/models"; +import { configureGui } from "@point_of_sale/js/Gui"; +import { registry } from "@web/core/registry"; +import env from "@point_of_sale/js/pos_env"; + +import { onMounted, onWillDestroy, useExternalListener, - useRef, useState, useSubEnv, reactive, -} = owl; + markRaw, + onWillUnmount, +} from "@odoo/owl"; +import { usePos } from "@point_of_sale/app/pos_store"; /** * Chrome is the root component of the PoS App. */ -class Chrome extends PosComponent { +export class Chrome extends PosComponent { setup() { + // BEGIN ChromeAdapter + ProductScreen.sortControlButtons(); + const legacyActionManager = useService("legacy_action_manager"); + + // Instantiate PosGlobalState here to ensure that every extension + // (or class overloads) is taken into consideration. + const pos = PosGlobalState.create({ env: markRaw(env) }); + + this.batchedCustomerDisplayRender = batched(() => { + reactivePos.send_current_order_to_customer_facing_display(); + }); + const reactivePos = reactive(pos, this.batchedCustomerDisplayRender); + env.pos = reactivePos; + env.legacyActionManager = legacyActionManager; + + // The proxy requires the instance of PosGlobalState to function properly. + env.proxy.set_pos(reactivePos); + + // TODO: Should we continue on exposing posmodel as global variable? + // Expose only the reactive version of `pos` when in debug mode. + window.posmodel = pos.debug ? reactivePos : pos; + + this.wowlEnv = this.env; + env.services.pos = this.wowlEnv.services.pos; + env.services.sound = this.wowlEnv.services.sound; + window.sound = env.services.sound; + this.env = env; + this.__owl__.childEnv = env; + useSubEnv({ + get isMobile() { + return window.innerWidth <= 768; + }, + }); + let currentIsMobile = this.env.isMobile; + const updateUI = debounce(() => { + if (this.env.isMobile !== currentIsMobile) { + currentIsMobile = this.env.isMobile; + this.render(true); + } + }, 15); // FIXME POSREF use throttleForAnimation? + useExternalListener(window, "resize", updateUI); + onWillUnmount(updateUI.cancel); + // END ChromeAdapter + super.setup(); useExternalListener(window, "beforeunload", this._onBeforeUnload); useListener("show-main-screen", this.__showScreen); - useListener("toggle-debug-widget", debounce(this._toggleDebugWidget, 100)); - useListener("toggle-mobile-searchbar", this._toggleMobileSearchBar); useListener("show-temp-screen", this.__showTempScreen); useListener("close-temp-screen", this.__closeTempScreen); useListener("close-pos", this._closePos); useListener("loading-skip-callback", () => this.env.proxy.stop_searching()); - useListener("play-sound", this._onPlaySound); + const sound = useService("sound"); + useListener("play-sound", ({ detail: name }) => sound.play(name)); useListener("set-sync-status", this._onSetSyncStatus); useListener("show-notification", this._onShowNotification); useListener("close-notification", this._onCloseNotification); @@ -50,38 +104,24 @@ class Chrome extends PosComponent { useBus(this.env.posbus, "start-cash-control", this.openCashControl); NumberBuffer.activate(); - this.state = useState({ - uiState: "LOADING", // 'LOADING' | 'READY' | 'CLOSING' - debugWidgetIsShown: true, - mobileSearchBarIsShown: false, - hasBigScrollBars: false, - sound: { src: null }, - notification: { - isShown: false, - message: "", - duration: 2000, - }, - loadingSkipButtonIsShown: false, - }); + this.state = usePos(); - this.mainScreen = useState({ name: null, component: null }); + this.mainScreen = this.state.mainScreen; this.mainScreenProps = {}; this.tempScreen = useState({ isShown: false, name: null, component: null }); this.tempScreenProps = {}; - this.progressbar = useRef("progressbar"); - - this.previous_touch_y_coordinate = -1; - - const pos = reactive( - this.env.pos, - batched(() => this.render(true)) - ); - useSubEnv({ pos }); + useSubEnv({ + pos: reactive( + this.env.pos, + batched(() => this.render(true)) // FIXME POSREF remove render(true) + ), + }); onMounted(() => { // remove default webclient handlers that induce click delay + // FIXME POSREF if these handlers shouldn't be there we should not load the files that add them. $(document).off(); $(window).off(); $("html").off(); @@ -89,32 +129,19 @@ class Chrome extends PosComponent { }); onWillDestroy(() => { - this.env.pos.destroy(); + try { + // FIXME POSREF is this needed? + this.env.pos.destroy(); + } catch { + // throwing here causes loop + } }); - onError((error) => { - // error is an OwlError object. - console.error(error.cause); - }); - - this.props.setupIsDone(this); + this.start(); } // GETTERS // - get customerFacingDisplayButtonIsShown() { - return this.env.pos.config.iface_customer_facing_display; - } - - /** - * Used to give the `state.mobileSearchBarIsShown` value to main screen props - */ - get mainScreenPropsFielded() { - return Object.assign({}, this.mainScreenProps, { - mobileSearchBarIsShown: this.state.mobileSearchBarIsShown, - }); - } - /** * Startup screen can be based on pos config so the startup screen * is only determined after pos data is completely loaded. @@ -137,6 +164,18 @@ class Chrome extends PosComponent { * This will load pos and assign it to the environment. */ async start() { + registry.category("error_handlers").add( + "posErrorHandler", + (env, ...noEnvArgs) => { + return this.errorHandler(this.env, ...noEnvArgs); + }, + { sequence: 0 } + ); + // Little trick to avoid displaying the block ui during the POS models loading + const BlockUiFromRegistry = registry.category("main_components").get("BlockUI"); + registry.category("main_components").remove("BlockUI"); + configureGui({ component: this }); // FIXME POSREF + try { await this.env.pos.load_server_data(); await this.setupBarcodeParser(); @@ -186,6 +225,10 @@ class Chrome extends PosComponent { exitButtonIsShown: true, }); } + registry.category("main_components").add("BlockUI", BlockUiFromRegistry); + + // Subscribe to the changes in the models. + this.batchedCustomerDisplayRender(); } _runBackgroundTasks() { @@ -228,7 +271,7 @@ class Chrome extends PosComponent { this.env.proxy .autoconnect({ force_ip: this.env.pos.config.proxy_ip || undefined, - progress: function (prog) {}, + progress: function (prog) { }, }) .then( () => { @@ -340,19 +383,19 @@ class Chrome extends PosComponent { console.warn(error); const reason = this.env.pos.failed ? this.env._t( - "Some orders could not be submitted to " + - "the server due to configuration errors. " + - "You can exit the Point of Sale, but do " + - "not close the session before the issue " + - "has been resolved." - ) + "Some orders could not be submitted to " + + "the server due to configuration errors. " + + "You can exit the Point of Sale, but do " + + "not close the session before the issue " + + "has been resolved." + ) : this.env._t( - "Some orders could not be submitted to " + - "the server due to internet connection issues. " + - "You can exit the Point of Sale, but do " + - "not close the session before the issue " + - "has been resolved." - ); + "Some orders could not be submitted to " + + "the server due to internet connection issues. " + + "You can exit the Point of Sale, but do " + + "not close the session before the issue " + + "has been resolved." + ); const { confirmed } = await this.showPopup("ConfirmPopup", { title: this.env._t("Offline Orders"), body: reason, @@ -365,25 +408,6 @@ class Chrome extends PosComponent { } } } - _toggleDebugWidget() { - this.state.debugWidgetIsShown = !this.state.debugWidgetIsShown; - } - _toggleMobileSearchBar({ detail: isSearchBarEnabled }) { - if (isSearchBarEnabled !== null) { - this.state.mobileSearchBarIsShown = isSearchBarEnabled; - } else { - this.state.mobileSearchBarIsShown = !this.state.mobileSearchBarIsShown; - } - } - _onPlaySound({ detail: name }) { - let src; - if (name === "error") { - src = "/point_of_sale/static/src/sounds/error.wav"; - } else if (name === "bell") { - src = "/point_of_sale/static/src/sounds/bell.wav"; - } - this.state.sound.src = src; - } _onSetSyncStatus({ detail: { status, pending } }) { this.env.pos.synch.status = status; this.env.pos.synch.pending = pending; @@ -404,10 +428,6 @@ class Chrome extends PosComponent { this.env.pos.db.save("TO_REFUND_LINES", this.env.pos.toRefundLines); } - get isTicketScreenShown() { - return this.mainScreen.name === "TicketScreen"; - } - // MISC METHODS // _preloadImages() { for (const product of this.env.pos.db.get_product_by_category(0)) { @@ -474,7 +494,7 @@ class Chrome extends PosComponent { false ); } - showCashMoveButton() { + get showCashMoveButton() { return this.env.pos && this.env.pos.config && this.env.pos.config.cash_control; } @@ -550,7 +570,15 @@ class Chrome extends PosComponent { Chrome.template = "Chrome"; Object.defineProperty(Chrome, "components", { get() { - return Object.assign({ Transition }, PosComponent.components); + return Object.assign( + { + Transition, + MainComponentsContainer, + WithEnv, + Navbar, + }, + PosComponent.components + ); }, }); diff --git a/addons/point_of_sale/static/src/js/ChromeWidgets/DebugWidget.js b/addons/point_of_sale/static/src/js/ChromeWidgets/DebugWidget.js index 79f874d6bac..d462e44bbc2 100644 --- a/addons/point_of_sale/static/src/js/ChromeWidgets/DebugWidget.js +++ b/addons/point_of_sale/static/src/js/ChromeWidgets/DebugWidget.js @@ -5,12 +5,14 @@ import { parse } from "web.field_utils"; import NumberBuffer from "@point_of_sale/js/Misc/NumberBuffer"; import PosComponent from "@point_of_sale/js/PosComponent"; import Registries from "@point_of_sale/js/Registries"; +import { usePos } from "@point_of_sale/app/pos_store"; const { onMounted, onWillUnmount, useRef, useState } = owl; class DebugWidget extends PosComponent { setup() { super.setup(); + this.pos = usePos(); this.state = useState({ barcodeInput: "", weightInput: "", diff --git a/addons/point_of_sale/static/src/js/ChromeWidgets/TicketButton.js b/addons/point_of_sale/static/src/js/ChromeWidgets/TicketButton.js index de90148561c..611efa518fa 100644 --- a/addons/point_of_sale/static/src/js/ChromeWidgets/TicketButton.js +++ b/addons/point_of_sale/static/src/js/ChromeWidgets/TicketButton.js @@ -1,16 +1,23 @@ /** @odoo-module */ +import { usePos } from "@point_of_sale/app/pos_store"; import PosComponent from "@point_of_sale/js/PosComponent"; import Registries from "@point_of_sale/js/Registries"; class TicketButton extends PosComponent { + setup() { + this.pos = usePos(); + } onClick() { - if (this.props.isTicketScreenShown) { + if (this.isTicketScreenShown) { this.env.posbus.trigger("ticket-button-clicked"); } else { this.showScreen("TicketScreen"); } } + get isTicketScreenShown() { + return this.pos.mainScreen.name === "TicketScreen"; + } get count() { if (this.env.pos) { return this.env.pos.get_order_list().length; diff --git a/addons/point_of_sale/static/src/js/Misc/NotificationSound.js b/addons/point_of_sale/static/src/js/Misc/NotificationSound.js deleted file mode 100644 index 3132dc2b468..00000000000 --- a/addons/point_of_sale/static/src/js/Misc/NotificationSound.js +++ /dev/null @@ -1,17 +0,0 @@ -/** @odoo-module */ - -import { useListener } from "@web/core/utils/hooks"; -import PosComponent from "@point_of_sale/js/PosComponent"; -import Registries from "@point_of_sale/js/Registries"; - -class NotificationSound extends PosComponent { - setup() { - super.setup(); - useListener("ended", () => (this.props.sound.src = null)); - } -} -NotificationSound.template = "NotificationSound"; - -Registries.Component.add(NotificationSound); - -export default NotificationSound; diff --git a/addons/point_of_sale/static/src/js/PosComponent.js b/addons/point_of_sale/static/src/js/PosComponent.js index 26b5fc3fbe1..1e5d9a425ba 100644 --- a/addons/point_of_sale/static/src/js/PosComponent.js +++ b/addons/point_of_sale/static/src/js/PosComponent.js @@ -1,18 +1,10 @@ /** @odoo-module */ import { LegacyComponent } from "@web/legacy/legacy_component"; -const { onRendered } = owl; let nextId = 0; class PosComponent extends LegacyComponent { - setup() { - onRendered(() => { - if (this.env.isDebug()) { - console.log("Rendered:", this.constructor.name); - } - }); - } /** * This function is available to all Components that inherit this class. * The goal of this function is to show an awaitable dialog (popup) that diff --git a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ProductsWidget.js b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ProductsWidget.js index e5d027994a8..6a9805b8298 100644 --- a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ProductsWidget.js +++ b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ProductsWidget.js @@ -3,8 +3,9 @@ import PosComponent from "@point_of_sale/js/PosComponent"; import { useListener } from "@web/core/utils/hooks"; import Registries from "@point_of_sale/js/Registries"; +import { usePos } from "@point_of_sale/app/pos_store"; -const { onWillUnmount, useState } = owl; +const { useState } = owl; class ProductsWidget extends PosComponent { /** @@ -18,10 +19,7 @@ class ProductsWidget extends PosComponent { useListener("clear-search", this._clearSearch); useListener("update-product-list", this._updateProductList); this.state = useState({ searchWord: "" }); - onWillUnmount(this.onWillUnmount); - } - onWillUnmount() { - this.trigger("toggle-mobile-searchbar", false); + this.pos = usePos(); } get selectedCategoryId() { return this.env.pos.selectedCategoryId; diff --git a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ProductsWidgetControlPanel.js b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ProductsWidgetControlPanel.js index 9d09c8895a4..b8252852c0b 100644 --- a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ProductsWidgetControlPanel.js +++ b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ProductsWidgetControlPanel.js @@ -5,14 +5,16 @@ import { ConnectionLostError, ConnectionAbortedError } from "@web/core/network/r import PosComponent from "@point_of_sale/js/PosComponent"; import Registries from "@point_of_sale/js/Registries"; import { debounce } from "@web/core/utils/timing"; +import { usePos } from "@point_of_sale/app/pos_store"; -const { onMounted, onWillUnmount } = owl; +import { onMounted, onWillUnmount, useState } from "@odoo/owl"; class ProductsWidgetControlPanel extends PosComponent { setup() { super.setup(); + this.pos = usePos(); this.updateSearch = debounce(this.updateSearch, 100); - this.state = { searchInput: "" }; + this.state = useState({ searchInput: "", mobileSearchBarIsShown: false }); onMounted(() => { this.env.posbus.on("search-product-from-info-popup", this, this.searchProductFromInfo); @@ -25,6 +27,9 @@ class ProductsWidgetControlPanel extends PosComponent { this.env.posbus.off("search-product-from-info-popup", this); }); } + toggleMobileSearchBar() { + this.state.mobileSearchBarIsShown = !this.state.mobileSearchBarIsShown; + } _clearSearch() { this.state.searchInput = ""; this.trigger("clear-search"); @@ -65,9 +70,6 @@ class ProductsWidgetControlPanel extends PosComponent { this.trigger("switch-category", 0); this.trigger("update-search", productName); } - _toggleMobileSearchbar() { - this.trigger("toggle-mobile-searchbar"); - } async loadProductFromDB() { if (!this.state.searchInput) { return; diff --git a/addons/point_of_sale/static/src/js/Screens/ReceiptScreen/ReceiptScreen.js b/addons/point_of_sale/static/src/js/Screens/ReceiptScreen/ReceiptScreen.js index 5d4b5407441..1018af53cdd 100644 --- a/addons/point_of_sale/static/src/js/Screens/ReceiptScreen/ReceiptScreen.js +++ b/addons/point_of_sale/static/src/js/Screens/ReceiptScreen/ReceiptScreen.js @@ -6,7 +6,7 @@ import { useErrorHandlers } from "@point_of_sale/js/custom_hooks"; import Registries from "@point_of_sale/js/Registries"; import AbstractReceiptScreen from "@point_of_sale/js/Misc/AbstractReceiptScreen"; -const { onMounted, useRef, status } = owl; +import { onMounted, useRef, status } from "@odoo/owl"; const ReceiptScreen = (AbstractReceiptScreen) => { class ReceiptScreen extends AbstractReceiptScreen { diff --git a/addons/point_of_sale/static/src/xml/Chrome.xml b/addons/point_of_sale/static/src/xml/Chrome.xml index 8f5bbb2a220..9852e5025c9 100644 --- a/addons/point_of_sale/static/src/xml/Chrome.xml +++ b/addons/point_of_sale/static/src/xml/Chrome.xml @@ -4,33 +4,16 @@
-
-
-
- - - -
-
-
- - - - - - -
-
-
+
+
+ t-props="mainScreenProps" t-key="mainScreen.name" />
@@ -65,8 +48,10 @@ -
+ + + diff --git a/addons/point_of_sale/static/src/xml/ChromeWidgets/DebugWidget.xml b/addons/point_of_sale/static/src/xml/ChromeWidgets/DebugWidget.xml index 23c383f17ae..17b701d3eb9 100644 --- a/addons/point_of_sale/static/src/xml/ChromeWidgets/DebugWidget.xml +++ b/addons/point_of_sale/static/src/xml/ChromeWidgets/DebugWidget.xml @@ -7,7 +7,7 @@

Debug Window

-

Electronic Scale

diff --git a/addons/point_of_sale/static/src/xml/ChromeWidgets/TicketButton.xml b/addons/point_of_sale/static/src/xml/ChromeWidgets/TicketButton.xml index a18774428d9..d423bc12bcd 100644 --- a/addons/point_of_sale/static/src/xml/ChromeWidgets/TicketButton.xml +++ b/addons/point_of_sale/static/src/xml/ChromeWidgets/TicketButton.xml @@ -2,7 +2,7 @@ -
+
diff --git a/addons/point_of_sale/static/src/xml/Misc/NotificationSound.xml b/addons/point_of_sale/static/src/xml/Misc/NotificationSound.xml deleted file mode 100644 index 6467e807985..00000000000 --- a/addons/point_of_sale/static/src/xml/Misc/NotificationSound.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductScreen.xml b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductScreen.xml index 6325b86a41e..13327f28509 100644 --- a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductScreen.xml +++ b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductScreen.xml @@ -25,7 +25,7 @@
- +
diff --git a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidget.xml b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidget.xml index 4111c0b54fc..069acaff2c4 100644 --- a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidget.xml +++ b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidget.xml @@ -3,8 +3,7 @@
- +
diff --git a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidgetControlPanel.xml b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidgetControlPanel.xml index 5be5b18f1a3..f85f499bc1a 100644 --- a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidgetControlPanel.xml +++ b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidgetControlPanel.xml @@ -5,13 +5,13 @@
- - + + - + diff --git a/addons/pos_hr/__manifest__.py b/addons/pos_hr/__manifest__.py index a5f18813439..27dbc232991 100644 --- a/addons/pos_hr/__manifest__.py +++ b/addons/pos_hr/__manifest__.py @@ -32,6 +32,7 @@ The actual till still requires one user but an unlimited number of employees can 'pos_hr/static/src/js/LoginScreen.js', 'pos_hr/static/src/js/PaymentScreen.js', 'pos_hr/static/src/xml/**/*', + 'pos_hr/static/src/app/**/*', ], 'web.assets_tests': [ 'pos_hr/static/tests/**/*', diff --git a/addons/pos_hr/static/src/app/navbar.xml b/addons/pos_hr/static/src/app/navbar.xml new file mode 100644 index 00000000000..2f2353bb00b --- /dev/null +++ b/addons/pos_hr/static/src/app/navbar.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/addons/pos_hr/static/src/js/Chrome.js b/addons/pos_hr/static/src/js/Chrome.js index 9722c182af6..2fc07716279 100644 --- a/addons/pos_hr/static/src/js/Chrome.js +++ b/addons/pos_hr/static/src/js/Chrome.js @@ -11,16 +11,9 @@ const PosHrChrome = (Chrome) => this.showTempScreen("LoginScreen"); } } - get headerButtonIsShown() { + get showCashMoveButton() { return ( - !this.env.pos.config.module_pos_hr || - this.env.pos.get_cashier().role == "manager" || - this.env.pos.get_cashier_user_id() === this.env.pos.user.id - ); - } - showCashMoveButton() { - return ( - super.showCashMoveButton() && + super.showCashMoveButton && (!this.env.pos.cashier || this.env.pos.cashier.role == "manager") ); } diff --git a/addons/pos_hr/static/src/xml/Chrome.xml b/addons/pos_hr/static/src/xml/Chrome.xml deleted file mode 100644 index 430ed75dcdb..00000000000 --- a/addons/pos_hr/static/src/xml/Chrome.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/addons/pos_restaurant/__manifest__.py b/addons/pos_restaurant/__manifest__.py index b8b945f9ffa..2f26b053a78 100644 --- a/addons/pos_restaurant/__manifest__.py +++ b/addons/pos_restaurant/__manifest__.py @@ -34,6 +34,7 @@ This module adds several features to the Point of Sale that are specific to rest 'pos_restaurant/static/src/js/**/*.js', ('after', 'point_of_sale/static/src/scss/pos.scss', 'pos_restaurant/static/src/scss/restaurant.scss'), 'pos_restaurant/static/src/xml/**/*', + 'pos_restaurant/static/src/app/**/*', ], 'web.assets_backend': [ 'point_of_sale/static/src/scss/pos_dashboard.scss', diff --git a/addons/pos_restaurant/static/src/xml/Chrome.xml b/addons/pos_restaurant/static/src/app/navbar.xml similarity index 52% rename from addons/pos_restaurant/static/src/xml/Chrome.xml rename to addons/pos_restaurant/static/src/app/navbar.xml index b87b12905f9..b24cc6df53d 100644 --- a/addons/pos_restaurant/static/src/xml/Chrome.xml +++ b/addons/pos_restaurant/static/src/app/navbar.xml @@ -1,9 +1,9 @@ - + -
+ diff --git a/addons/pos_restaurant/static/src/js/ChromeWidgets/BackToFloorButton.js b/addons/pos_restaurant/static/src/js/ChromeWidgets/BackToFloorButton.js index 4600a3c6016..9b23c218cbf 100644 --- a/addons/pos_restaurant/static/src/js/ChromeWidgets/BackToFloorButton.js +++ b/addons/pos_restaurant/static/src/js/ChromeWidgets/BackToFloorButton.js @@ -1,14 +1,14 @@ /** @odoo-module */ +import { usePos } from "@point_of_sale/app/pos_store"; import PosComponent from "@point_of_sale/js/PosComponent"; import Registries from "@point_of_sale/js/Registries"; -/** - * Props: { - * onClick: callback - * } - */ class BackToFloorButton extends PosComponent { + setup() { + super.setup(); + this.pos = usePos(); + } get table() { return this.env.pos.table; } @@ -19,9 +19,6 @@ class BackToFloorButton extends PosComponent { return this.table != null; } backToFloorScreen() { - if (this.props.onClick) { - this.props.onClick(); - } this.showScreen("FloorScreen", { floor: this.floor }); } } diff --git a/addons/pos_restaurant/static/src/js/ChromeWidgets/TicketButton.js b/addons/pos_restaurant/static/src/js/ChromeWidgets/TicketButton.js index 90bda837c54..025acf4e915 100644 --- a/addons/pos_restaurant/static/src/js/ChromeWidgets/TicketButton.js +++ b/addons/pos_restaurant/static/src/js/ChromeWidgets/TicketButton.js @@ -9,7 +9,7 @@ const PosResTicketButton = (TicketButton) => async onClick() { if ( this.env.pos.config.iface_floorplan && - !this.props.isTicketScreenShown && + !this.isTicketScreenShown && !this.env.pos.table ) { try { diff --git a/addons/pos_restaurant/static/src/js/Screens/PaymentScreen.js b/addons/pos_restaurant/static/src/js/Screens/PaymentScreen.js index 3a6578fb768..84b6c08d266 100644 --- a/addons/pos_restaurant/static/src/js/Screens/PaymentScreen.js +++ b/addons/pos_restaurant/static/src/js/Screens/PaymentScreen.js @@ -6,6 +6,7 @@ import Registries from "@point_of_sale/js/Registries"; const PosResPaymentScreen = (PaymentScreen) => class extends PaymentScreen { + static showBackToFloorButton = true; setup() { super.setup(); useListener("send-payment-adjust", this._sendPaymentAdjust); diff --git a/addons/pos_restaurant/static/src/js/Screens/ProductScreen/ProductScreen.js b/addons/pos_restaurant/static/src/js/Screens/ProductScreen/ProductScreen.js new file mode 100644 index 00000000000..485528b7abb --- /dev/null +++ b/addons/pos_restaurant/static/src/js/Screens/ProductScreen/ProductScreen.js @@ -0,0 +1,8 @@ +/** @odoo-module */ + +import ProductScreen from "@point_of_sale/js/Screens/ProductScreen/ProductScreen"; +import { patch } from "@web/core/utils/patch"; + +patch(ProductScreen, "pos_restaurant.ProductScreen", { + showBackToFloorButton: true, +}); diff --git a/addons/pos_restaurant/static/src/js/Screens/ReceiptScreen/ReceiptScreen.js b/addons/pos_restaurant/static/src/js/Screens/ReceiptScreen/ReceiptScreen.js index cc37b65cfc6..92a8a61cf2f 100644 --- a/addons/pos_restaurant/static/src/js/Screens/ReceiptScreen/ReceiptScreen.js +++ b/addons/pos_restaurant/static/src/js/Screens/ReceiptScreen/ReceiptScreen.js @@ -2,9 +2,22 @@ import ReceiptScreen from "@point_of_sale/js/Screens/ReceiptScreen/ReceiptScreen"; import Registries from "@point_of_sale/js/Registries"; +import { usePos } from "@point_of_sale/app/pos_store"; +import { onWillUnmount } from "@odoo/owl"; const PosResReceiptScreen = (ReceiptScreen) => class extends ReceiptScreen { + static showBackToFloorButton = true; + setup() { + super.setup(); + this.pos = usePos(); + onWillUnmount(() => { + // When leaving the receipt screen to the floor screen the order is paid and can be removed + if (this.pos.mainScreen.name === "FloorScreen") { + this.env.pos.removeOrder(this.currentOrder); + } + }); + } //@override _addNewOrder() { if (!this.env.pos.config.iface_floorplan) { @@ -20,10 +33,6 @@ const PosResReceiptScreen = (ReceiptScreen) => return super.nextScreen; } } - onBackToFloorButtonClick() { - // If we're here and the order is paid, we can remove it from the orders - this.env.pos.removeOrder(this.currentOrder); - } }; Registries.Component.extend(ReceiptScreen, PosResReceiptScreen); diff --git a/addons/pos_restaurant/static/src/js/Screens/TipScreen.js b/addons/pos_restaurant/static/src/js/Screens/TipScreen.js index 5a3fe9c35a1..b55d8fc279a 100644 --- a/addons/pos_restaurant/static/src/js/Screens/TipScreen.js +++ b/addons/pos_restaurant/static/src/js/Screens/TipScreen.js @@ -8,6 +8,7 @@ import { renderToString } from "@web/core/utils/render"; const { onMounted } = owl; class TipScreen extends PosComponent { + static showBackToFloorButton = true; setup() { super.setup(); this.state = this.currentOrder.uiState.TipScreen; diff --git a/addons/pos_restaurant/static/src/xml/ChromeWidgets/BackToFloorButton.xml b/addons/pos_restaurant/static/src/xml/ChromeWidgets/BackToFloorButton.xml index 013d7c38c4b..aa43b676e5a 100644 --- a/addons/pos_restaurant/static/src/xml/ChromeWidgets/BackToFloorButton.xml +++ b/addons/pos_restaurant/static/src/xml/ChromeWidgets/BackToFloorButton.xml @@ -5,7 +5,6 @@ diff --git a/addons/pos_restaurant/static/src/xml/Screens/PaymentScreen/PaymentScreen.xml b/addons/pos_restaurant/static/src/xml/Screens/PaymentScreen/PaymentScreen.xml index cbdcc83d139..7a460e5ed9b 100644 --- a/addons/pos_restaurant/static/src/xml/Screens/PaymentScreen/PaymentScreen.xml +++ b/addons/pos_restaurant/static/src/xml/Screens/PaymentScreen/PaymentScreen.xml @@ -2,12 +2,6 @@ - - - - - - env.pos.config.set_tip_after_payment and !currentOrder.is_paid() diff --git a/addons/pos_restaurant/static/src/xml/Screens/ProductScreen/ProductScreen.xml b/addons/pos_restaurant/static/src/xml/Screens/ProductScreen/ProductScreen.xml deleted file mode 100644 index aad8e48b26a..00000000000 --- a/addons/pos_restaurant/static/src/xml/Screens/ProductScreen/ProductScreen.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/addons/pos_restaurant/static/src/xml/Screens/ReceiptScreen/ReceiptScreen.xml b/addons/pos_restaurant/static/src/xml/Screens/ReceiptScreen/ReceiptScreen.xml deleted file mode 100644 index f8bf56000b4..00000000000 --- a/addons/pos_restaurant/static/src/xml/Screens/ReceiptScreen/ReceiptScreen.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - diff --git a/addons/pos_restaurant/static/src/xml/Screens/TipScreen.xml b/addons/pos_restaurant/static/src/xml/Screens/TipScreen.xml index 212e2a2d7ac..688bcbcd58f 100644 --- a/addons/pos_restaurant/static/src/xml/Screens/TipScreen.xml +++ b/addons/pos_restaurant/static/src/xml/Screens/TipScreen.xml @@ -55,9 +55,6 @@
- - -
diff --git a/addons/pos_six/__manifest__.py b/addons/pos_six/__manifest__.py index e52f2f99ee4..f81dd7c7e8e 100644 --- a/addons/pos_six/__manifest__.py +++ b/addons/pos_six/__manifest__.py @@ -20,6 +20,7 @@ 'pos_six/static/src/js/models.js', 'pos_six/static/src/js/payment_six.js', 'pos_six/static/src/xml/**/*', + 'pos_six/static/src/app/**/*', ], } } diff --git a/addons/pos_six/static/app/navbar.xml b/addons/pos_six/static/app/navbar.xml new file mode 100644 index 00000000000..cad531e700a --- /dev/null +++ b/addons/pos_six/static/app/navbar.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/addons/pos_six/static/src/js/Chrome.js b/addons/pos_six/static/src/js/Chrome.js deleted file mode 100644 index 867c84b7009..00000000000 --- a/addons/pos_six/static/src/js/Chrome.js +++ /dev/null @@ -1,15 +0,0 @@ -/** @odoo-module */ - -import Chrome from "@point_of_sale/js/Chrome"; -import Registries from "@point_of_sale/js/Registries"; - -const PosSixChrome = (Chrome) => - class extends Chrome { - get balanceButtonIsShown() { - return this.env.pos.payment_methods.some((pm) => pm.use_payment_terminal === "six"); - } - }; - -Registries.Component.extend(Chrome, PosSixChrome); - -export default Chrome; diff --git a/addons/pos_six/static/src/xml/Chrome.xml b/addons/pos_six/static/src/xml/Chrome.xml deleted file mode 100644 index 1187e4f5a44..00000000000 --- a/addons/pos_six/static/src/xml/Chrome.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/addons/web/tooling/_eslintrc.json b/addons/web/tooling/_eslintrc.json index c902ad2e18c..308fb6eb1c8 100644 --- a/addons/web/tooling/_eslintrc.json +++ b/addons/web/tooling/_eslintrc.json @@ -1,7 +1,8 @@ { "extends": ["eslint:recommended", "plugin:prettier/recommended"], "parserOptions": { - "sourceType": "module" + "sourceType": "module", + "ecmaVersion": 2022 }, "env": { "browser": true,