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,