diff --git a/addons/point_of_sale/static/src/app/number_buffer_service.js b/addons/point_of_sale/static/src/app/number_buffer_service.js index 761422227a6..ba2ef7e9861 100644 --- a/addons/point_of_sale/static/src/app/number_buffer_service.js +++ b/addons/point_of_sale/static/src/app/number_buffer_service.js @@ -2,9 +2,8 @@ import { parse } from "web.field_utils"; import { barcodeService } from "@barcodes/barcode_service"; -import { _t } from "web.core"; import { registry } from "@web/core/registry"; -import { EventBus, onWillDestroy, useComponent, useExternalListener } from "@odoo/owl"; +import { EventBus, onWillDestroy, useComponent } from "@odoo/owl"; const INPUT_KEYS = new Set( ["Delete", "Backspace", "+1", "+2", "+5", "+10", "+20", "+50"].concat( @@ -36,7 +35,6 @@ const getDefaultConfig = () => ({ * * Usage * ===== - * - Activate in the construction of root component. `NumberBuffer.activate()` * - Use the buffer in a child component by calling `NumberBuffer.use()` * in the constructor of the child component. * - The component that `uses` the buffer has access to the following instance @@ -58,11 +56,17 @@ const getDefaultConfig = () => ({ * - Write more integration tests. NumberPopup can be used as test component. */ class NumberBuffer extends EventBus { - constructor({ sound }) { + static serviceDependencies = ["sound", "localization"]; + constructor() { super(); + this.setup(...arguments); + } + setup({ sound, localization }) { this.isReset = false; this.bufferHolderStack = []; this.sound = sound; + this.defaultDecimalPoint = localization.decimalPoint; + window.addEventListener("keyup", this._onKeyboardInput.bind(this)); } /** * @returns {String} value of the buffer, e.g. '-95.79' @@ -107,15 +111,6 @@ class NumberBuffer extends EventBus { getFloat() { return parse.float(this.get()); } - /** - * Add keyup listener to window via the useExternalListener hook. - * When the component calling this is unmounted, the listener is also - * removed from window. - */ - activate() { - this.defaultDecimalPoint = _t.database.parameters.decimal_point; - useExternalListener(window, "keyup", this._onKeyboardInput.bind(this)); - } /** * @param {Object} config Use to setup the buffer * @param {String|null} config.decimalPoint The decimal character. @@ -324,9 +319,9 @@ class NumberBuffer extends EventBus { } export const numberBuffer = { - dependencies: ["sound"], - start(env, { sound }) { - return new NumberBuffer({ sound }); + dependencies: NumberBuffer.serviceDependencies, + start(env, deps) { + return new NumberBuffer(deps); }, }; diff --git a/addons/point_of_sale/static/src/app/pos_store.js b/addons/point_of_sale/static/src/app/pos_store.js index d20667ec66a..748d3429631 100644 --- a/addons/point_of_sale/static/src/app/pos_store.js +++ b/addons/point_of_sale/static/src/app/pos_store.js @@ -1,15 +1,14 @@ /** @odoo-module */ import { PosGlobalState } from "@point_of_sale/js/models"; -import legacyEnv from "web.env"; import { registry } from "@web/core/registry"; import { ConfirmPopup } from "@point_of_sale/js/Popups/ConfirmPopup"; -import { reactive, markRaw } from "@odoo/owl"; import { Reactive } from "@point_of_sale/utils"; import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup"; import { _t } from "@web/core/l10n/translation"; import { CashOpeningPopup } from "@point_of_sale/js/Popups/CashOpeningPopup"; +import { sprintf } from "@web/core/utils/strings"; export class PosStore extends Reactive { /** @type {'LOADING' | 'READY' | 'CLOSING'} */ @@ -18,7 +17,6 @@ export class PosStore extends Reactive { loadingSkipButtonIsShown = false; mainScreen = { name: null, component: null }; tempScreen = null; - legacyEnv = legacyEnv; static serviceDependencies = [ "popup", @@ -32,16 +30,16 @@ export class PosStore extends Reactive { this.setup(...arguments); } // use setup instead of constructor because setup can be patched. - setup({ popup, orm, number_buffer, hardware_proxy, barcode_reader }) { + setup(env, { popup, orm, number_buffer, hardware_proxy, barcode_reader }) { this.orm = orm; this.popup = popup; this.numberBuffer = number_buffer; this.barcodeReader = barcode_reader; + this.globalState = new PosGlobalState({ orm, env, hardwareProxy: hardware_proxy }); this.hardwareProxy = hardware_proxy; - this.globalState = new PosGlobalState({ - env: markRaw(legacyEnv), - hardwareProxy: hardware_proxy, - }); + // FIXME POSREF: the hardwareProxy needs the pos and the pos needs the hardwareProxy. Maybe + // the hardware proxy should just be part of the pos service? + this.hardwareProxy.pos = this.globalState; } showScreen(name, props) { @@ -77,7 +75,7 @@ export class PosStore extends Reactive { // FIXME POSREF this looks like it's dead code. reject({ title: _t("HTTPS connection to IoT Box failed"), - body: _.str.sprintf( + body: sprintf( _t( "Make sure you are using IoT Box v18.12 or higher. Navigate to %s to accept the certificate of your IoT Box." ), @@ -100,40 +98,38 @@ export class PosStore extends Reactive { window.location = "/web#action=point_of_sale.action_client_pos_menu"; } - if (this.globalState.db.get_orders().length) { - // If there are orders in the db left unsynced, we try to sync. - // If sync successful, close without asking. - // Otherwise, ask again saying that some orders are not yet synced. - try { - await this.globalState.push_orders(); + // If there are orders in the db left unsynced, we try to sync. + // If sync successful, close without asking. + // Otherwise, ask again saying that some orders are not yet synced. + try { + await this.globalState.push_orders(); + window.location = "/web#action=point_of_sale.action_client_pos_menu"; + } catch (error) { + console.warn(error); + const reason = this.globalState.failed + ? _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." + ) + : _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." + ); + const { confirmed } = await this.popup.add(ConfirmPopup, { + title: _t("Offline Orders"), + body: reason, + }); + if (confirmed) { + // FIXME POSREF setting the location prevents the next render, the loading screen never shows + this.globalState.uiState = "CLOSING"; + this.globalState.loadingSkipButtonIsShown = false; window.location = "/web#action=point_of_sale.action_client_pos_menu"; - } catch (error) { - console.warn(error); - const reason = this.globalState.failed - ? _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." - ) - : _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." - ); - const { confirmed } = await this.popup.add(ConfirmPopup, { - title: _t("Offline Orders"), - body: reason, - }); - if (confirmed) { - // FIXME POSREF setting the location prevents the next render, the loading screen never shows - this.globalState.uiState = "CLOSING"; - this.globalState.loadingSkipButtonIsShown = false; - window.location = "/web#action=point_of_sale.action_client_pos_menu"; - } } } } @@ -146,9 +142,9 @@ export class PosStore extends Reactive { const currentPartner = currentOrder.get_partner(); if (currentPartner && currentOrder.getHasRefundLines()) { this.popup.add(ErrorPopup, { - title: this.env._t("Can't change customer"), - body: _.str.sprintf( - this.env._t( + title: _t("Can't change customer"), + body: sprintf( + _t( "This order already has refund lines for %s. We can't change the customer associated to it. Create a new order for the new customer." ), currentPartner.name @@ -218,7 +214,7 @@ export class PosStore extends Reactive { export const posService = { dependencies: PosStore.serviceDependencies, start(env, deps) { - return reactive(new PosStore(deps)); + return new PosStore(env, deps); }, }; diff --git a/addons/point_of_sale/static/src/css/chrome50.css b/addons/point_of_sale/static/src/css/chrome50.css deleted file mode 100644 index 67ecddccfde..00000000000 --- a/addons/point_of_sale/static/src/css/chrome50.css +++ /dev/null @@ -1,6 +0,0 @@ -.pos .screen .content-cell{ - height: 100%; -} -.pos .subwindow .subwindow-container{ - height: 100%; -} \ No newline at end of file diff --git a/addons/point_of_sale/static/src/js/Chrome.js b/addons/point_of_sale/static/src/js/Chrome.js index e04ee9b229a..26f37266ff3 100644 --- a/addons/point_of_sale/static/src/js/Chrome.js +++ b/addons/point_of_sale/static/src/js/Chrome.js @@ -1,242 +1,112 @@ /** @odoo-module */ -import { loadCSS } from "@web/core/assets"; import { useService } from "@web/core/utils/hooks"; import { batched } from "@point_of_sale/js/utils"; -import { debounce } from "@web/core/utils/timing"; +import { throttleForAnimation } from "@web/core/utils/timing"; import { Transition } from "@web/core/transition"; import { MainComponentsContainer } from "@web/core/main_components_container"; -import { WithEnv, ErrorHandler } from "@web/core/utils/components"; +import { ErrorHandler } from "@web/core/utils/components"; import { Navbar } from "@point_of_sale/app/navbar/navbar"; - -// ChromeAdapter imports -import { ProductScreen } from "@point_of_sale/js/Screens/ProductScreen/ProductScreen"; -import { registry } from "@web/core/registry"; -import env from "web.env"; - -import { ErrorTracebackPopup } from "./Popups/ErrorTracebackPopup"; - -import { - onMounted, - onWillDestroy, - useExternalListener, - useSubEnv, - reactive, - onWillUnmount, - Component, -} from "@odoo/owl"; import { usePos } from "@point_of_sale/app/pos_hook"; +import { useExternalListener, useSubEnv, reactive, onWillUnmount, Component } from "@odoo/owl"; /** * Chrome is the root component of the PoS App. */ export class Chrome extends Component { static template = "Chrome"; // FIXME POSREF namespace templates - static components = { Transition, MainComponentsContainer, WithEnv, ErrorHandler, Navbar }; + static components = { Transition, MainComponentsContainer, ErrorHandler, Navbar }; setup() { this.pos = usePos(); this.popup = useService("popup"); - // BEGIN ChromeAdapter - ProductScreen.sortControlButtons(); - const legacyActionManager = useService("legacy_action_manager"); - - this.batchedCustomerDisplayRender = batched(() => { - reactivePos.send_current_order_to_customer_facing_display(); - }); - const reactivePos = reactive(this.pos.globalState, this.batchedCustomerDisplayRender); - env.pos = reactivePos; - env.legacyActionManager = legacyActionManager; - - // The proxy requires the instance of PosGlobalState to function properly. - this.hardwareProxy = useService("hardware_proxy"); - this.hardwareProxy.pos = reactivePos; + const reactivePos = reactive(this.pos.globalState); // TODO: Should we continue on exposing posmodel as global variable? - // Expose only the reactive version of `pos` when in debug mode. - window.posmodel = this.pos.globalState.debug ? reactivePos : this.pos.globalState; - - this.wowlEnv = this.env; - // FIXME POSREF: make wowl services available in legacy env - Object.setPrototypeOf(env.services, this.wowlEnv.services); - this.env = env; - this.__owl__.childEnv = env; + window.posmodel = reactivePos; + // FIXME POSREF: remove 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); - useService("number_buffer").activate(); - - useSubEnv({ pos: reactive( - this.env.pos, + reactivePos, 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(); - $("body").off(); + let currentIsMobile = this.env.isMobile; + const updateUI = throttleForAnimation(() => { + if (this.env.isMobile !== currentIsMobile) { + currentIsMobile = this.env.isMobile; + // FIXME POSREF + this.render(true); + } }); + useExternalListener(window, "resize", updateUI); + onWillUnmount(updateUI.cancel); - onWillDestroy(() => { - try { - // FIXME POSREF is this needed? - this.env.pos.destroy(); - } catch { - // throwing here causes loop + // prevent backspace from performing a 'back' navigation + document.addEventListener("keydown", (ev) => { + if (ev.key === "Backspace" && !ev.target.matches("input, textarea")) { + ev.preventDefault(); } }); + // This is not done in onWillStart because we want to show the loader immediately this.start(); } + async start() { + await this.pos.globalState.load_server_data(); + if (this.pos.globalState.config.use_proxy) { + await this.pos.connectToProxy(); + } + this._closeOtherTabs(); + this.pos.uiState = "READY"; + const { name, props } = this.startScreen; + this.pos.showScreen(name, props); + this.runBackgroundTasks(); + } // GETTERS // /** * Startup screen can be based on pos config so the startup screen * is only determined after pos data is completely loaded. - * - * NOTE: Wait for pos data to be completed before calling this getter. */ get startScreen() { - if (this.pos.uiState !== "READY") { - console.warn( - `Accessing startScreen of Chrome component before 'pos.uiState' to be 'READY' is not recommended.` - ); - } return { name: "ProductScreen" }; } // CONTROL METHODS // - /** - * Call this function after the Chrome component is mounted. - * This will load pos and assign it to the environment. - */ - async start() { - // Little trick to avoid displaying the block ui during the POS models loading - // FIXME POSREF: use a silent RPC instead - const BlockUiFromRegistry = registry.category("main_components").get("BlockUI"); - registry.category("main_components").remove("BlockUI"); - - try { - await this.env.pos.load_server_data(); - if (this.env.pos.config.use_proxy) { - await this.pos.connectToProxy(); - } - // Load the saved `env.pos.toRefundLines` from localStorage when - // the PosGlobalState is ready. - Object.assign( - this.env.pos.toRefundLines, - this.env.pos.db.load("TO_REFUND_LINES") || {} - ); - this._buildChrome(); - this._closeOtherTabs(); - this.env.pos.selectedCategoryId = - this.env.pos.config.start_category && this.env.pos.config.iface_start_categ_id - ? this.env.pos.config.iface_start_categ_id[0] - : 0; - this.pos.uiState = "READY"; - const { name, props } = this.startScreen; - this.pos.showScreen(name, props); - setTimeout(() => this._runBackgroundTasks()); - } catch (error) { - let title = "Unknown Error"; - let body; - - if (error.message && [100, 200, 404, -32098].includes(error.message.code)) { - // this is the signature of rpc error - if (error.message.code === -32098) { - title = "Network Failure (XmlHttpRequestError)"; - body = - "The Point of Sale could not be loaded due to a network problem.\n" + - "Please check your internet connection."; - } else if (error.message.code === 200) { - title = error.message.data.message || this.env._t("Server Error"); - body = - error.message.data.debug || - this.env._t("The server encountered an error while receiving your order."); - } - } else if (error instanceof Error) { - title = error.message; - if (error.cause) { - body = error.cause.message; - } else { - body = error.stack; - } - } - - return this.popup.add(ErrorTracebackPopup, { title, body, exitButtonIsShown: true }); - } - registry.category("main_components").add("BlockUI", BlockUiFromRegistry); - - // Subscribe to the changes in the models. - this.batchedCustomerDisplayRender(); - } - - _runBackgroundTasks() { + runBackgroundTasks() { // push order in the background, no need to await - this.env.pos.push_orders(); + this.pos.globalState.push_orders(); // Allow using the app even if not all the images are loaded. // Basically, preload the images in the background. this._preloadImages(); - if ( - this.env.pos.config.limited_partners_loading && - this.env.pos.config.partner_load_background - ) { + const { + limited_partners_loading, + partner_load_background, + limited_products_loading, + product_load_background, + } = this.pos.globalState.config; + if (limited_partners_loading && partner_load_background) { // Wrap in fresh reactive: none of the reads during loading should subscribe to anything - reactive(this.env.pos).loadPartnersBackground(); + reactive(this.pos.globalState).loadPartnersBackground(); } - if ( - this.env.pos.config.limited_products_loading && - this.env.pos.config.product_load_background - ) { + if (limited_products_loading && product_load_background) { // Wrap in fresh reactive: none of the reads during loading should subscribe to anything - reactive(this.env.pos) - .loadProductsBackground() - .then(() => { - this.render(true); - }); + reactive(this.pos.globalState).loadProductsBackground(); } } - /** - * Save `env.pos.toRefundLines` in localStorage on beforeunload - closing the - * browser, reloading or going to other page. - */ - _onBeforeUnload() { - this.env.pos.db.save("TO_REFUND_LINES", this.env.pos.toRefundLines); - } - _onSetSyncStatus({ detail: { status, pending } }) { - this.env.pos.synch.status = status; - this.env.pos.synch.pending = pending; - } - // MISC METHODS // _preloadImages() { - for (const product of this.env.pos.db.get_product_by_category(0)) { + for (const product of this.pos.globalState.db.get_product_by_category(0)) { const image = new Image(); image.src = `/web/image?model=product.product&field=image_128&id=${product.id}&unique=${product.write_date}`; } - for (const category of Object.values(this.env.pos.db.category_by_id)) { + for (const category of Object.values(this.pos.globalState.db.category_by_id)) { if (category.id == 0) { continue; } @@ -249,34 +119,15 @@ export class Chrome extends Component { image.src = `/point_of_sale/static/src/img/${imageName}`; } } - - _buildChrome() { - if ($.browser.chrome) { - var chrome_version = $.browser.version.split(".")[0]; - if (parseInt(chrome_version, 10) >= 50) { - loadCSS("/point_of_sale/static/src/css/chrome50.css"); - } - } - - if (this.env.pos.config.iface_big_scrollbars) { - this.pos.hasBigScrollBars = true; - } - - this._disableBackspaceBack(); - } - // prevent backspace from performing a 'back' navigation - _disableBackspaceBack() { - $(document).on("keydown", function (e) { - if (e.which === 8 && !$(e.target).is("input, textarea")) { - e.preventDefault(); - } - }); - } + /** + * Close other tabs that contain the same pos session. + */ _closeOtherTabs() { + // FIXME POSREF use the bus? localStorage["message"] = ""; localStorage["message"] = JSON.stringify({ message: "close_tabs", - session: this.env.pos.pos_session.id, + session: this.pos.globalState.pos_session.id, }); window.addEventListener( @@ -286,7 +137,7 @@ export class Chrome extends Component { const msg = JSON.parse(event.newValue); if ( msg.message === "close_tabs" && - msg.session == this.env.pos.pos_session.id + msg.session == this.pos.globalState.pos_session.id ) { console.info("POS / Session opened in another window. EXITING POS"); this.pos.closePos(); @@ -297,7 +148,7 @@ export class Chrome extends Component { ); } get showCashMoveButton() { - return this.env.pos && this.env.pos.config && this.env.pos.config.cash_control; + return Boolean(this.pos.globalState?.config?.config?.cash_control); } /** * Unmounts the tempScreen on error and dispatches the error in a separate diff --git a/addons/point_of_sale/static/src/js/ControlButtonsMixin.js b/addons/point_of_sale/static/src/js/ControlButtonsMixin.js index 8d76cac4c01..17dc38cbd25 100644 --- a/addons/point_of_sale/static/src/js/ControlButtonsMixin.js +++ b/addons/point_of_sale/static/src/js/ControlButtonsMixin.js @@ -11,7 +11,8 @@ */ export const ControlButtonsMixin = (x) => { const controlButtonsToPosition = []; - const sortedControlButtons = []; + const orderedControlButtons = []; + let sortedControlButtons = []; class Extended extends x { get controlButtons() { @@ -42,10 +43,11 @@ export const ControlButtonsMixin = (x) => { // If no position is set, we just push it to the array. if (!controlButton.position) { - sortedControlButtons.push(controlButton); + orderedControlButtons.push(controlButton); } else { controlButtonsToPosition.push(controlButton); } + Extended.sortControlButtons(); }; /** @@ -56,6 +58,7 @@ export const ControlButtonsMixin = (x) => { * purposes, it is enough. */ Extended.sortControlButtons = function () { + sortedControlButtons = [...orderedControlButtons]; function setControlButton(locator, index, cb) { if (locator == "replace") { sortedControlButtons[index] = cb; @@ -68,7 +71,7 @@ export const ControlButtonsMixin = (x) => { function locate(cb) { const [locator, reference] = cb.position; const index = sortedControlButtons.findIndex((cb) => cb.name == reference); - return [locator, index, reference]; + return [locator, index]; } const cbMissingReference = []; // 1. First pass. If the reference control button isn't there, collect it for second pass. @@ -88,11 +91,8 @@ export const ControlButtonsMixin = (x) => { // ii) They really have missing reference. // Thus, we have to iterate the cb with missing reference in reverse. for (const cb of cbMissingReference.reverse()) { - const [locator, index, reference] = locate(cb); + const [locator, index] = locate(cb); if (index == -1) { - console.warn( - `'${cb.name}' is not properly position because '${reference}' is not found. Is '${reference}' spelled correctly?` - ); sortedControlButtons.push(cb); } else { setControlButton(locator, index, cb); diff --git a/addons/point_of_sale/static/src/js/Popups/ClosePosPopup.js b/addons/point_of_sale/static/src/js/Popups/ClosePosPopup.js index 55f49d36b17..f1660e1c911 100644 --- a/addons/point_of_sale/static/src/js/Popups/ClosePosPopup.js +++ b/addons/point_of_sale/static/src/js/Popups/ClosePosPopup.js @@ -22,8 +22,8 @@ export class ClosePosPopup extends AbstractAwaitablePopup { this.pos = usePos(); this.popup = useService("popup"); this.orm = useService("orm"); + this.action = useService("action"); this.hardwareProxy = useService("hardware_proxy"); - this.legacyActionManager = useService("legacy_action_manager"); this.manualInputCashCount = false; this.cashControl = this.pos.globalState.config.cash_control; this.closeSessionClicked = false; @@ -91,9 +91,9 @@ export class ClosePosPopup extends AbstractAwaitablePopup { } } async downloadSalesReport() { - await this.legacyActionManager.do_action("point_of_sale.sale_details_report", { - additional_context: { - active_ids: [this.pos.globalState.pos_session.id], + await this.action.doAction("point_of_sale.sale_details_report", { + additionalContext: { + active_ids: [this.env.pos.pos_session.id], }, }); } diff --git a/addons/point_of_sale/static/src/js/Popups/NumberPopup.js b/addons/point_of_sale/static/src/js/Popups/NumberPopup.js index f098c21f95b..e373d5565da 100644 --- a/addons/point_of_sale/static/src/js/Popups/NumberPopup.js +++ b/addons/point_of_sale/static/src/js/Popups/NumberPopup.js @@ -5,7 +5,6 @@ var _t = core._t; import { AbstractAwaitablePopup } from "@point_of_sale/js/Popups/AbstractAwaitablePopup"; import { useService } from "@web/core/utils/hooks"; import { useState } from "@odoo/owl"; -import { localization } from "@web/core/l10n/localization"; export class NumberPopup extends AbstractAwaitablePopup { static template = "NumberPopup"; @@ -31,6 +30,7 @@ export class NumberPopup extends AbstractAwaitablePopup { */ setup() { super.setup(); + this.localization = useService("localization"); let startingBuffer = ""; if (typeof this.props.startingValue === "number" && this.props.startingValue > 0) { startingBuffer = this.props.startingValue @@ -46,7 +46,7 @@ export class NumberPopup extends AbstractAwaitablePopup { }); } get decimalSeparator() { - return localization.decimalPoint; + return this.localization.decimalPoint; } get inputBuffer() { if (this.state.buffer === null) { diff --git a/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreen.js b/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreen.js index 88fc7c62dae..f411ec0dc24 100644 --- a/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreen.js +++ b/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreen.js @@ -30,6 +30,7 @@ export class PaymentScreen extends Component { this.pos = usePos(); this.orm = useService("orm"); this.popup = useService("popup"); + this.action = useService("action"); this.notification = useService("pos_notification"); this.hardwareProxy = useService("hardware_proxy"); this.payment_methods_from_config = this.env.pos.payment_methods.filter((method) => @@ -233,8 +234,8 @@ export class PaymentScreen extends Component { // 2. Invoice. if (this.currentOrder.is_to_invoice()) { if (syncOrderResult.length) { - await this.env.legacyActionManager.do_action("account.account_invoices", { - additional_context: { + await this.action.doAction("account.account_invoices", { + additionalContext: { active_ids: [syncOrderResult[0].account_move], }, }); diff --git a/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreenNumpad.js b/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreenNumpad.js index 6ed639b62c7..62715e4421e 100644 --- a/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreenNumpad.js +++ b/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreenNumpad.js @@ -2,6 +2,7 @@ import { PSNumpadInputButton } from "./PSNumpadInputButton"; import { Component } from "@odoo/owl"; +import { useService } from "@web/core/utils/hooks"; export class PaymentScreenNumpad extends Component { static components = { PSNumpadInputButton }; @@ -9,6 +10,6 @@ export class PaymentScreenNumpad extends Component { setup() { super.setup(); - this.decimalPoint = this.env._t.database.parameters.decimal_point; + this.decimalPoint = useService("localization").decimalPoint; } } diff --git a/addons/point_of_sale/static/src/js/Screens/ProductScreen/NumpadWidget.js b/addons/point_of_sale/static/src/js/Screens/ProductScreen/NumpadWidget.js index 1e2469d401f..2e5cabc793d 100644 --- a/addons/point_of_sale/static/src/js/Screens/ProductScreen/NumpadWidget.js +++ b/addons/point_of_sale/static/src/js/Screens/ProductScreen/NumpadWidget.js @@ -16,6 +16,7 @@ export class NumpadWidget extends Component { }; setup() { this.numberBuffer = useService("number_buffer"); + this.localization = useService("localization"); } get hasPriceControlRights() { return ( @@ -43,6 +44,6 @@ export class NumpadWidget extends Component { this.numberBuffer.sendKey(key); } get decimalSeparator() { - return this.env._t.database.parameters.decimal_point; + return this.localization.decimalPoint; } } diff --git a/addons/point_of_sale/static/src/js/Screens/TicketScreen/ControlButtons/InvoiceButton.js b/addons/point_of_sale/static/src/js/Screens/TicketScreen/ControlButtons/InvoiceButton.js index a5d8f2df7c2..2e6c84cd1ac 100644 --- a/addons/point_of_sale/static/src/js/Screens/TicketScreen/ControlButtons/InvoiceButton.js +++ b/addons/point_of_sale/static/src/js/Screens/TicketScreen/ControlButtons/InvoiceButton.js @@ -15,6 +15,7 @@ export class InvoiceButton extends Component { this.invoiceButton = useRef("invoice-button"); this.popup = useService("popup"); this.orm = useService("orm"); + this.action = useService("action"); } get isAlreadyInvoiced() { if (!this.props.order) { @@ -38,8 +39,8 @@ export class InvoiceButton extends Component { { load: false } ); if (orderWithInvoice && orderWithInvoice.account_move) { - await this.env.legacyActionManager.do_action("account.account_invoices", { - additional_context: { + await this.action.doAction("account.account_invoices", { + additionalContext: { active_ids: [orderWithInvoice.account_move], }, }); diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 857e6926caa..439ee66d5a0 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -193,7 +193,7 @@ export class PosGlobalState extends PosModel { }; } async load_product_uom_unit() { - const uom_id = await this.env.services.orm.call("ir.model.data", "check_object_reference", [ + const uom_id = await this.orm.call("ir.model.data", "check_object_reference", [ "uom", "product_uom_unit", ]); @@ -204,11 +204,18 @@ export class PosGlobalState extends PosModel { await this.load_product_uom_unit(); await this.load_orders(); this.set_start_order(); + Object.assign(this.toRefundLines, this.db.load("TO_REFUND_LINES") || {}); + window.addEventListener("beforeunload", () => + this.db.save("TO_REFUND_LINES", this.toRefundLines) + ); + const { start_category, iface_start_categ_id } = this.config; + this.selectedCategoryId = (start_category && iface_start_categ_id?.[0]) || 0; + this.hasBigScrollBars = this.config.iface_big_scrollbars; this.markReady(); } async load_server_data() { - const loadedData = await this.env.services.orm.call("pos.session", "load_pos_data", [ + const loadedData = await this.orm.silent.call("pos.session", "load_pos_data", [ [odoo.pos_session_id], ]); await this._processData(loadedData); @@ -370,32 +377,28 @@ export class PosGlobalState extends PosModel { // updated partners, and fails if not async load_new_partners() { const search_params = { domain: this.prepare_new_partners_domain() }; - if (this.env.pos.config.limited_partners_loading) { + if (this.config.limited_partners_loading) { search_params["order"] = "write_date desc"; - if (this.env.pos.config.partner_load_background) { - search_params["limit"] = this.env.pos.config.limited_partners_amount || 1; + if (this.config.partner_load_background) { + search_params["limit"] = this.config.limited_partners_amount || 1; } else { search_params["limit"] = 1; } } // FIXME POSREF TIMEOUT 3000 - const partners = await this.env.services.orm.silent.call( + const partners = await this.orm.silent.call( "pos.session", "get_pos_ui_res_partner_by_params", [[odoo.pos_session_id], search_params] ); - if (this.env.pos.config.partner_load_background) { + if (this.config.partner_load_background) { this.loadPartnersBackground( search_params["domain"], - this.env.pos.config.limited_partners_amount || 1, + this.config.limited_partners_amount || 1, "write_date desc" ); } - if (this.addPartners(partners)) { - return true; - } else { - return false; - } + return this.addPartners(partners); } setSelectedCategoryId(categoryId) { @@ -557,7 +560,7 @@ export class PosGlobalState extends PosModel { } } } - const products = await this.env.services.orm.call( + const products = await this.orm.call( "pos.session", "get_pos_ui_product_product_by_params", [odoo.pos_session_id, { domain: [["id", "in", [...missingProductIds]]] }] @@ -568,7 +571,7 @@ export class PosGlobalState extends PosModel { async _loadPartners(partnerIds) { if (partnerIds.length > 0) { // FIXME POSREF TIMEOUT - const fetchedPartners = await this.env.services.orm.silent.call( + const fetchedPartners = await this.orm.silent.call( "pos.session", "get_pos_ui_res_partner_by_params", [[odoo.pos_session_id], { domain: [["id", "in", partnerIds]] }] @@ -593,7 +596,7 @@ export class PosGlobalState extends PosModel { let page = 0; let products = []; do { - products = await this.env.services.orm.silent.call( + products = await this.orm.silent.call( "pos.session", "get_pos_ui_product_product_by_params", [ @@ -614,7 +617,7 @@ export class PosGlobalState extends PosModel { let i = 0; let partners = []; do { - partners = await this.env.services.orm.silent.call( + partners = await this.orm.silent.call( "pos.session", "get_pos_ui_res_partner_by_params", [ @@ -642,7 +645,7 @@ export class PosGlobalState extends PosModel { this.set_synch("connecting", removedOrdersIds.length); try { - const removeOrdersResponseData = await this.env.services.orm.silent.call( + const removeOrdersResponseData = await this.orm.silent.call( "pos.order", "remove_from_ui", [removedOrdersIds] @@ -736,7 +739,7 @@ export class PosGlobalState extends PosModel { return message; } async _getOrdersJson() { - return await this.env.services.orm.call("pos.order", "get_draft_share_order_ids", [], { + return await this.orm.call("pos.order", "get_draft_share_order_ids", [], { config_id: this.config.id, }); } @@ -762,11 +765,10 @@ export class PosGlobalState extends PosModel { return message; } async _getPricelistJson(pricelistsToGet) { - return await this.env.services.orm.call( - "pos.session", - "get_pos_ui_product_pricelists_by_ids", - [[odoo.pos_session_id], pricelistsToGet] - ); + return await this.orm.call("pos.session", "get_pos_ui_product_pricelists_by_ids", [ + [odoo.pos_session_id], + pricelistsToGet, + ]); } _addPosPricelists(pricelistsJson) { if (!this.config.use_pricelist) { @@ -805,11 +807,10 @@ export class PosGlobalState extends PosModel { return message; } async _getFiscalPositionJson(fiscalPositionToGet) { - return await this.env.services.orm.call( - "pos.session", - "get_pos_ui_account_fiscal_positions_by_ids", - [[odoo.pos_session_id], fiscalPositionToGet] - ); + return await this.orm.call("pos.session", "get_pos_ui_account_fiscal_positions_by_ids", [ + [odoo.pos_session_id], + fiscalPositionToGet, + ]); } _addPosFiscalPosition(fiscalPositionJson) { this.fiscal_positions.push(...fiscalPositionJson); @@ -830,11 +831,12 @@ export class PosGlobalState extends PosModel { const order = this.get_order(); // check back-end method `get_product_info_pos` to see what it returns // We do this so it's easier to override the value returned and use it in the component template later - const productInfo = await this.env.services.orm.call( - "product.product", - "get_product_info_pos", - [[product.id], product.get_price(order.pricelist, quantity), quantity, this.config.id] - ); + const productInfo = await this.orm.call("product.product", "get_product_info_pos", [ + [product.id], + product.get_price(order.pricelist, quantity), + quantity, + this.config.id, + ]); const priceWithoutTax = productInfo["all_prices"]["price_without_tax"]; const margin = priceWithoutTax - product.standard_price; @@ -865,11 +867,9 @@ export class PosGlobalState extends PosModel { }; } async getClosePosInfo() { - const closingData = await this.env.services.orm.call( - "pos.session", - "get_closing_control_data", - [[this.pos_session.id]] - ); + const closingData = await this.orm.call("pos.session", "get_closing_control_data", [ + [this.pos_session.id], + ]); const ordersDetails = closingData.orders_details; const paymentsAmount = closingData.payments_amount; const payLaterAmount = closingData.pay_later_amount; @@ -1057,7 +1057,7 @@ export class PosGlobalState extends PosModel { } // we try to send the order. silent prevents a spinner if it takes too long. (unless we are sending an invoice, // then we want to notify the user that we are waiting on something ) - const orm = options.to_invoice ? this.env.services.orm : this.env.services.orm.silent; + const orm = options.to_invoice ? this.orm : this.orm.silent; // FIXME POSREF timeout // const timeout = typeof options.timeout === "number" ? options.timeout : 30000 * orders.length; return orm @@ -1555,17 +1555,16 @@ export class PosGlobalState extends PosModel { */ async _addProducts(ids, setAvailable = true) { if (setAvailable) { - await this.env.services.orm.write("product.product", ids, { available_in_pos: true }); + await this.orm.write("product.product", ids, { available_in_pos: true }); } - const product = await this.env.services.orm.call( - "pos.session", - "get_pos_ui_product_product_by_params", - [odoo.pos_session_id, { domain: [["id", "in", ids]] }] - ); + const product = await this.orm.call("pos.session", "get_pos_ui_product_product_by_params", [ + odoo.pos_session_id, + { domain: [["id", "in", ids]] }, + ]); this._loadProductProduct(product); } async refreshTotalDueOfPartner(partner) { - const partnerWithUpdatedTotalDue = await this.env.services.orm.searchRead( + const partnerWithUpdatedTotalDue = await this.orm.searchRead( "res.partner", [["id", "=", partner.id]], ["total_due"] diff --git a/addons/point_of_sale/static/src/js/utils.js b/addons/point_of_sale/static/src/js/utils.js index bf9e15c28fe..499a82d0b7d 100644 --- a/addons/point_of_sale/static/src/js/utils.js +++ b/addons/point_of_sale/static/src/js/utils.js @@ -9,7 +9,7 @@ */ export function batched(callback) { let called = false; - return async () => { + return async (...args) => { // This await blocks all calls to the callback here, then releases them sequentially // in the next microtick. This line decides the granularity of the batch. await Promise.resolve(); @@ -20,7 +20,7 @@ export function batched(callback) { // within the callback will proceed only after resetting called to false, and have // a chance to execute the callback again Promise.resolve().then(() => (called = false)); - callback(); + callback(...args); } }; } diff --git a/addons/point_of_sale/static/src/xml/Chrome.xml b/addons/point_of_sale/static/src/xml/Chrome.xml index 0df3e6e76a8..e20bbdc9b41 100644 --- a/addons/point_of_sale/static/src/xml/Chrome.xml +++ b/addons/point_of_sale/static/src/xml/Chrome.xml @@ -38,9 +38,7 @@ - - - + diff --git a/addons/point_of_sale/static/tests/unit/number_buffer_service_tests.js b/addons/point_of_sale/static/tests/unit/number_buffer_service_tests.js index 82340787c4a..b16b70b09aa 100644 --- a/addons/point_of_sale/static/tests/unit/number_buffer_service_tests.js +++ b/addons/point_of_sale/static/tests/unit/number_buffer_service_tests.js @@ -26,7 +26,6 @@ QUnit.test("simple fast inputs with capture in between", async function (assert) setup() { this.state = useState({ buffer: "" }); this.numberBuffer = useService("number_buffer"); - this.numberBuffer.activate(); this.numberBuffer.use({ state: this.state, }); diff --git a/addons/pos_loyalty/static/src/js/PaymentScreen.js b/addons/pos_loyalty/static/src/js/PaymentScreen.js index e83ceb6790c..4bd3fa5590c 100644 --- a/addons/pos_loyalty/static/src/js/PaymentScreen.js +++ b/addons/pos_loyalty/static/src/js/PaymentScreen.js @@ -156,12 +156,8 @@ patch(PaymentScreen.prototype, "pos_loyalty.PaymentScreen", { } } if (payload.coupon_report) { - for (const report_entry of Object.entries(payload.coupon_report)) { - await this.env.legacyActionManager.do_action(report_entry[0], { - additional_context: { - active_ids: report_entry[1], - }, - }); + for (const [actionId, active_ids] of Object.entries(payload.coupon_report)) { + await this.action.doAction(actionId, { additionalContext: { active_ids } }); } } order.new_coupon_info = payload.new_coupon_info; diff --git a/addons/pos_restaurant/static/src/js/models.js b/addons/pos_restaurant/static/src/js/models.js index bcf315ad600..f9126bc33b8 100644 --- a/addons/pos_restaurant/static/src/js/models.js +++ b/addons/pos_restaurant/static/src/js/models.js @@ -45,7 +45,7 @@ patch(PosGlobalState.prototype, "pos_restaurant.PosGlobalState", { } }, isInterfacePrinter() { - return this.env.pos.config.iface_printers; + return this.config.iface_printers; }, addSubmitOrderButton() { return this.config.module_pos_restaurant && this.unwatched.printers.length; diff --git a/addons/web/static/src/core/l10n/localization_service.js b/addons/web/static/src/core/l10n/localization_service.js index 2188cfb05f0..553edf9b4a0 100644 --- a/addons/web/static/src/core/l10n/localization_service.js +++ b/addons/web/static/src/core/l10n/localization_service.js @@ -87,6 +87,7 @@ export const localizationService = { thousandsSep: userLocalization.thousands_sep, weekStart: userLocalization.week_start, }); + return localization; }, }; diff --git a/addons/web/static/tests/helpers/mock_services.js b/addons/web/static/tests/helpers/mock_services.js index 2e147246037..dc2e2778662 100644 --- a/addons/web/static/tests/helpers/mock_services.js +++ b/addons/web/static/tests/helpers/mock_services.js @@ -43,6 +43,7 @@ export function makeFakeLocalizationService(config = {}) { name: "localization", start: async (env) => { env._t = _t; + return localization; }, }; }