From 0c7910529df44aee742efe49cd896b44e062618f Mon Sep 17 00:00:00 2001 From: Bruno Boi Date: Wed, 2 Feb 2022 10:35:46 +0000 Subject: [PATCH] [REF] point_of_sale,pos*: adapt code to owl 2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Owl 2 changelog: https://github.com/odoo/owl/blob/a9f29c4caad4f32d06be1ec4780572825781cd9b/CHANGELOG.md Part-of: odoo/odoo#80156 Co-authored-by: Aaron Bohy Co-authored-by: Bruno Boi Co-authored-by: Géry Debongnie Co-authored-by: Samuel Degueldre Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: Simon Genin (ges) Co-authored-by: Francois (fge) Co-authored-by: Michael Mattiello (mcm) Co-authored-by: Julien Mougenot Co-authored-by: luvi Co-authored-by: Lucas Perais (lpe) Co-authored-by: Jorge Pinna Puissant --- addons/point_of_sale/static/src/css/pos.css | 8 +- .../static/src/entry/chrome_adapter.js | 87 +++--- addons/point_of_sale/static/src/entry/main.js | 3 +- addons/point_of_sale/static/src/js/Chrome.js | 70 +++-- .../src/js/ChromeWidgets/CashMoveButton.js | 3 +- .../js/ChromeWidgets/ClientScreenButton.js | 4 +- .../src/js/ChromeWidgets/DebugWidget.js | 20 +- .../src/js/ChromeWidgets/ProxyStatus.js | 20 +- .../src/js/ChromeWidgets/SaleDetailsButton.js | 3 +- addons/point_of_sale/static/src/js/Gui.js | 4 +- .../src/js/Misc/AbstractReceiptScreen.js | 6 +- .../static/src/js/Misc/Draggable.js | 61 ++-- .../static/src/js/Misc/NotificationSound.js | 6 +- .../static/src/js/Misc/NumberBuffer.js | 6 +- .../static/src/js/Misc/SearchBar.js | 6 +- .../static/src/js/Notification.js | 19 +- .../static/src/js/PopupControllerMixin.js | 6 +- .../src/js/Popups/AbstractAwaitablePopup.js | 6 +- .../static/src/js/Popups/CashMovePopup.js | 1 + .../static/src/js/Popups/CashOpeningPopup.js | 26 +- .../static/src/js/Popups/ClosePosPopup.js | 37 +-- .../src/js/Popups/ControlButtonPopup.js | 4 +- .../static/src/js/Popups/EditListPopup.js | 4 +- .../static/src/js/Popups/ErrorPopup.js | 6 +- .../static/src/js/Popups/MoneyDetailsPopup.js | 23 +- .../static/src/js/Popups/NumberPopup.js | 6 +- .../src/js/Popups/ProductConfiguratorPopup.js | 15 +- .../static/src/js/Popups/ProductInfoPopup.js | 10 +- .../static/src/js/Popups/SelectionPopup.js | 4 +- .../static/src/js/Popups/TextAreaPopup.js | 9 +- .../static/src/js/Popups/TextInputPopup.js | 9 +- .../static/src/js/PosComponent.js | 17 +- .../point_of_sale/static/src/js/PosContext.js | 5 +- .../ClientListScreen/ClientDetailsEdit.js | 20 +- .../ClientListScreen/ClientListScreen.js | 29 +- .../js/Screens/PaymentScreen/PaymentScreen.js | 6 +- .../PaymentScreen/PaymentScreenNumpad.js | 4 +- .../OrderlineCustomerNoteButton.js | 6 +- .../ControlButtons/ProductInfoButton.js | 6 +- .../ControlButtons/RefundButton.js | 6 +- .../ControlButtons/SetFiscalPositionButton.js | 6 +- .../ControlButtons/SetPricelistButton.js | 6 +- .../js/Screens/ProductScreen/OrderWidget.js | 6 +- .../js/Screens/ProductScreen/ProductScreen.js | 9 +- .../Screens/ProductScreen/ProductsWidget.js | 11 +- .../ProductsWidgetControlPanel.js | 22 +- .../js/Screens/ReceiptScreen/OrderReceipt.js | 13 +- .../js/Screens/ReceiptScreen/ReceiptScreen.js | 40 +-- .../src/js/Screens/ScaleScreen/ScaleScreen.js | 12 +- .../ControlButtons/InvoiceButton.js | 6 +- .../ControlButtons/ReprintReceiptButton.js | 6 +- .../TicketScreen/ReprintReceiptScreen.js | 6 +- .../js/Screens/TicketScreen/TicketScreen.js | 15 +- .../static/src/js/custom_hooks.js | 8 +- addons/point_of_sale/static/src/js/models.js | 27 +- addons/point_of_sale/static/src/js/pos_env.js | 2 +- .../point_of_sale/static/src/js/reactivity.js | 271 ------------------ addons/point_of_sale/static/src/js/utils.js | 26 +- .../point_of_sale/static/src/xml/Chrome.xml | 34 ++- .../src/xml/ChromeWidgets/DebugWidget.xml | 8 +- .../src/xml/ChromeWidgets/ProxyStatus.xml | 2 +- .../static/src/xml/Misc/MobileOrderWidget.xml | 4 +- .../static/src/xml/Misc/SearchBar.xml | 6 +- .../static/src/xml/Notification.xml | 2 +- .../static/src/xml/Popups/CashMovePopup.xml | 4 +- .../src/xml/Popups/CashOpeningPopup.xml | 13 +- .../static/src/xml/Popups/ClosePosPopup.xml | 19 +- .../static/src/xml/Popups/EditListInput.xml | 2 +- .../src/xml/Popups/ErrorTracebackPopup.xml | 2 +- .../src/xml/Popups/MoneyDetailsPopup.xml | 10 +- .../static/src/xml/Popups/NumberPopup.xml | 36 +-- .../xml/Popups/ProductConfiguratorPopup.xml | 8 +- .../src/xml/Popups/ProductInfoPopup.xml | 2 +- .../static/src/xml/Popups/SelectionPopup.xml | 2 +- .../static/src/xml/SaleDetailsReport.xml | 4 +- .../Screens/ClientListScreen/ClientLine.xml | 4 +- .../ClientListScreen/ClientListScreen.xml | 11 +- .../PaymentScreen/PSNumpadInputButton.xml | 2 +- .../PaymentScreen/PaymentMethodButton.xml | 2 +- .../Screens/PaymentScreen/PaymentScreen.xml | 10 +- .../PaymentScreenElectronicPayment.xml | 10 +- .../PaymentScreenPaymentLines.xml | 8 +- .../Screens/ProductScreen/ActionpadWidget.xml | 6 +- .../ProductScreen/CategoryBreadcrumb.xml | 2 +- .../Screens/ProductScreen/CategoryButton.xml | 2 +- .../ProductScreen/CategorySimpleButton.xml | 2 +- .../Screens/ProductScreen/NumpadWidget.xml | 32 +-- .../xml/Screens/ProductScreen/ProductItem.xml | 4 +- .../Screens/ProductScreen/ProductScreen.xml | 4 +- .../ProductsWidgetControlPanel.xml | 6 +- .../Screens/ReceiptScreen/OrderReceipt.xml | 8 +- .../Screens/ReceiptScreen/ReceiptScreen.xml | 4 +- .../ReceiptScreen/WrappedProductNameLines.xml | 4 +- .../Screens/TicketScreen/OrderlineDetails.xml | 4 +- .../TicketScreen/ReprintReceiptScreen.xml | 4 +- .../xml/Screens/TicketScreen/TicketScreen.xml | 16 +- .../static/tests/tours/point_of_sale.js | 2 +- .../static/tests/unit/helpers/test_env.js | 4 - .../static/tests/unit/test_NumberBuffer.js | 31 +- .../src/js/ControlButtons/PromoCodeButton.js | 6 +- .../js/ControlButtons/ResetProgramsButton.js | 6 +- .../pos_coupon/static/src/js/ProductScreen.js | 4 +- .../static/src/js/DiscountButton.js | 6 +- .../static/src/js/GiftCardButton.js | 7 +- .../static/src/js/GiftCardPopup.js | 4 +- addons/pos_hr/static/src/js/CashierName.js | 4 +- .../pos_hr/static/src/js/HeaderLockButton.js | 5 +- addons/pos_hr/static/src/js/LoginScreen.js | 4 +- .../pos_hr/static/src/js/useSelectEmployee.js | 4 +- .../static/src/xml/HeaderLockButton.xml | 4 +- .../static/src/js/PaymentScreen.js | 4 +- .../static/src/js/PaymentTransactionPopup.js | 4 +- .../static/src/js/ProductScreen.js | 4 +- .../static/src/js/Resizeable.js | 52 ++-- .../src/js/Screens/FloorScreen/EditBar.js | 4 +- .../js/Screens/FloorScreen/EditableTable.js | 6 +- .../src/js/Screens/FloorScreen/FloorScreen.js | 18 +- .../src/js/Screens/FloorScreen/TableWidget.js | 5 +- .../static/src/js/Screens/PaymentScreen.js | 6 +- .../ControlButtons/OrderlineNoteButton.js | 6 +- .../ControlButtons/PrintBillButton.js | 6 +- .../ControlButtons/SplitBillButton.js | 6 +- .../ControlButtons/SubmitOrderButton.js | 8 +- .../ControlButtons/TableGuestsButton.js | 6 +- .../ControlButtons/TransferOrderButton.js | 6 +- .../SplitBillScreen/SplitBillScreen.js | 6 +- .../Screens/SplitBillScreen/SplitOrderline.js | 6 +- .../static/src/js/Screens/TicketScreen.js | 6 +- .../static/src/js/Screens/TipScreen.js | 16 +- .../pos_restaurant/static/src/xml/Chrome.xml | 2 +- .../xml/ChromeWidgets/BackToFloorButton.xml | 7 +- .../static/src/xml/Screens/BillScreen.xml | 4 +- .../src/xml/Screens/FloorScreen/EditBar.xml | 54 ++-- .../xml/Screens/FloorScreen/FloorScreen.xml | 4 +- .../xml/Screens/FloorScreen/TableWidget.xml | 2 +- .../PaymentScreenElectronicPayment.xml | 4 +- .../static/src/xml/Screens/TipScreen.xml | 8 +- .../static/src/xml/TipReceipt.xml | 4 +- .../MobileSaleOrderManagementScreen.js | 6 +- .../js/OrderManagementScreen/SaleOrderList.js | 6 +- .../SaleOrderManagementControlPanel.js | 12 +- .../SaleOrderManagementScreen.js | 17 +- .../static/src/js/SetSaleOrderButton.js | 8 +- .../MobileSaleOrderManagementScreen.xml | 2 +- .../SaleOrderManagementControlPanel.xml | 8 +- .../OrderManagementScreen/SaleOrderRow.xml | 2 +- .../src/xml/Popups/ProductInfoPopup.xml | 2 +- 147 files changed, 813 insertions(+), 991 deletions(-) delete mode 100644 addons/point_of_sale/static/src/js/reactivity.js diff --git a/addons/point_of_sale/static/src/css/pos.css b/addons/point_of_sale/static/src/css/pos.css index 7704d608e0f..3a91bc84c79 100644 --- a/addons/point_of_sale/static/src/css/pos.css +++ b/addons/point_of_sale/static/src/css/pos.css @@ -2817,19 +2817,19 @@ td { border-collapse: separate; } -.fade-enter-active, .fade-leave-active { +.o-fade { transition: opacity .2s; } -.fade-enter, .fade-leave-to { +.o-fade-enter, .o-fade-leave { opacity: 0; } -.swing-enter-active, .swing-leave-active { +.o-swing { transition: opacity 0.8s; } -.swing-enter, .swing-leave-to { +.o-swing-enter, .o-swing-leave { opacity: 0; } diff --git a/addons/point_of_sale/static/src/entry/chrome_adapter.js b/addons/point_of_sale/static/src/entry/chrome_adapter.js index 1839f97cbec..bc5853144e9 100644 --- a/addons/point_of_sale/static/src/entry/chrome_adapter.js +++ b/addons/point_of_sale/static/src/entry/chrome_adapter.js @@ -5,26 +5,14 @@ import { useService } from "@web/core/utils/hooks"; import Chrome from "point_of_sale.Chrome"; import ProductScreen from "point_of_sale.ProductScreen"; import Registries from "point_of_sale.Registries"; -import { reactive, batched, markRaw } from "@point_of_sale/js/reactivity"; import { PosGlobalState } from "point_of_sale.models"; import { configureGui } from "point_of_sale.Gui"; -import { useBus } from "@web/core/utils/hooks"; import { registry } from "@web/core/registry"; import env from "point_of_sale.env"; +import { debounce } from "@web/core/utils/timing"; +import { batched } from "point_of_sale.utils"; -const { Component, debounce, onMounted, useRef, xml } = owl; - -function setupResponsivePlugin(env) { - const isMobile = () => window.innerWidth <= 768; - env.isMobile = isMobile(); - const updateEnv = debounce(() => { - if (env.isMobile !== isMobile()) { - env.isMobile = !env.isMobile; - env.qweb.forceUpdate(); - } - }, 15); - window.addEventListener("resize", updateEnv); -} +const { Component, reactive, markRaw, useExternalListener, useSubEnv, onWillUnmount, xml } = owl; export class ChromeAdapter extends Component { setup() { @@ -36,10 +24,10 @@ export class ChromeAdapter extends Component { // (or class overloads) is taken into consideration. const pos = PosGlobalState.create({ env: markRaw(env) }); - const batchedCustomerDisplayRender = batched(() => { + this.batchedCustomerDisplayRender = batched(() => { reactivePos.send_current_order_to_customer_facing_display(); }); - const reactivePos = reactive(pos, batchedCustomerDisplayRender); + const reactivePos = reactive(pos, this.batchedCustomerDisplayRender); env.pos = reactivePos; env.legacyActionManager = legacyActionManager; @@ -51,33 +39,44 @@ export class ChromeAdapter extends Component { window.posmodel = pos.debug ? reactivePos : pos; this.env = env; - - useBus(this.env.qweb, "update", () => this.render()); - setupResponsivePlugin(this.env); - - const chrome = useRef("chrome"); - onMounted(async () => { - // Add the pos error handler when the chrome component is available. - registry.category('error_handlers').add( - 'posErrorHandler', - (env, ...noEnvArgs) => { - if (chrome.comp) { - return chrome.comp.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.comp }); - await chrome.comp.start(); - registry.category("main_components").add("BlockUI", BlockUiFromRegistry); - - // Subscribe to the changes in the models. - batchedCustomerDisplayRender(); + 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(); + } + }, 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``; +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 933a95af71b..0327bea5d45 100644 --- a/addons/point_of_sale/static/src/entry/main.js +++ b/addons/point_of_sale/static/src/entry/main.js @@ -6,7 +6,7 @@ import { ChromeAdapter } from "@point_of_sale/entry/chrome_adapter"; import Registries from "point_of_sale.Registries"; import { registry } from "@web/core/registry"; -const { Component, Portal, xml } = owl; +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 @@ -28,7 +28,6 @@ PosApp.template = xml` PosApp.components = { ChromeAdapter }; function startPosApp() { - Registries.Component.add(Portal); Registries.Component.freeze(); Registries.Model.freeze(); startWebClient(PosApp); diff --git a/addons/point_of_sale/static/src/js/Chrome.js b/addons/point_of_sale/static/src/js/Chrome.js index 8e6557fc72b..9eaa93f02fc 100644 --- a/addons/point_of_sale/static/src/js/Chrome.js +++ b/addons/point_of_sale/static/src/js/Chrome.js @@ -2,7 +2,7 @@ odoo.define('point_of_sale.Chrome', function(require) { 'use strict'; const { loadCSS } = require('web.ajax'); - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const { BarcodeEvents } = require('barcodes.BarcodeEvents'); const BarcodeParser = require('barcodes.BarcodeParser'); const PosComponent = require('point_of_sale.PosComponent'); @@ -14,16 +14,26 @@ odoo.define('point_of_sale.Chrome', function(require) { const { odooExceptionTitleMap } = require("@web/core/errors/error_dialogs"); const { ConnectionLostError, ConnectionAbortedError, RPCError } = require('@web/core/network/rpc_service'); const { useBus } = require("@web/core/utils/hooks"); - const reactivity = require("@point_of_sale/js/reactivity"); + const { debounce } = require("@web/core/utils/timing"); + const { Transition } = require("@web/core/transition"); - const { debounce, useExternalListener, useRef, useState } = owl; + const { + onError, + onMounted, + onWillDestroy, + onWillUnmount, + useExternalListener, + useRef, + useState, + useSubEnv, + } = owl; /** * Chrome is the root component of the PoS App. */ class Chrome extends PopupControllerMixin(PosComponent) { - constructor() { - super(...arguments); + setup() { + super.setup(); useExternalListener(window, 'beforeunload', this._onBeforeUnload); useListener('show-main-screen', this.__showScreen); useListener('toggle-debug-widget', debounce(this._toggleDebugWidget, 100)); @@ -64,29 +74,31 @@ odoo.define('point_of_sale.Chrome', function(require) { this.previous_touch_y_coordinate = -1; - this.env.pos = reactivity.useState(this.env.pos); - } + useSubEnv({ pos: useState(this.env.pos) }); - // OVERLOADED METHODS // + onMounted(() => { + // remove default webclient handlers that induce click delay + $(document).off(); + $(window).off(); + $('html').off(); + $('body').off(); + // The above lines removed the bindings, but we really need them for the barcode + BarcodeEvents.start(); + }); - mounted() { - // remove default webclient handlers that induce click delay - $(document).off(); - $(window).off(); - $('html').off(); - $('body').off(); - // The above lines removed the bindings, but we really need them for the barcode - BarcodeEvents.start(); - } - willUnmount() { - BarcodeEvents.stop(); - } - destroy() { - super.destroy(...arguments); - this.env.pos.destroy(); - } - catchError(error) { - console.error(error); + onWillUnmount(() => { + BarcodeEvents.stop(); + }); + + onWillDestroy(() => { + this.env.pos.destroy(); + }); + + onError((error) => { + console.error(error); + }); + + this.props.setupIsDone(this); } // GETTERS // @@ -362,7 +374,6 @@ odoo.define('point_of_sale.Chrome', function(require) { } _onCloseNotification() { this.state.notification.isShown = false; - this.state.notification.message = ''; } /** * Save `env.pos.toRefundLines` in localStorage on beforeunload - closing the @@ -535,6 +546,11 @@ odoo.define('point_of_sale.Chrome', function(require) { } } Chrome.template = 'Chrome'; + Object.defineProperty(Chrome, "components", { + get () { + return Object.assign({ Transition }, PosComponent.components); + } + }) Registries.Component.add(Chrome); diff --git a/addons/point_of_sale/static/src/js/ChromeWidgets/CashMoveButton.js b/addons/point_of_sale/static/src/js/ChromeWidgets/CashMoveButton.js index 29c074cef44..9289c0c37a1 100644 --- a/addons/point_of_sale/static/src/js/ChromeWidgets/CashMoveButton.js +++ b/addons/point_of_sale/static/src/js/ChromeWidgets/CashMoveButton.js @@ -4,6 +4,7 @@ odoo.define('point_of_sale.CashMoveButton', function (require) { const PosComponent = require('point_of_sale.PosComponent'); const Registries = require('point_of_sale.Registries'); const { _t } = require('web.core'); + const { renderToString } = require('@web/core/utils/render'); const TRANSLATED_CASH_MOVE_TYPE = { in: _t('in'), @@ -30,7 +31,7 @@ odoo.define('point_of_sale.CashMoveButton', function (require) { args: [[this.env.pos.pos_session.id], type, amount, reason, extras], }); if (this.env.proxy.printer) { - const renderedReceipt = this.env.qweb.renderToString('point_of_sale.CashMoveReceipt', { + const renderedReceipt = renderToString('point_of_sale.CashMoveReceipt', { _receipt: this._getReceiptInfo({ ...payload, translatedType, formattedAmount }), }); const printResult = await this.env.proxy.printer.print_receipt(renderedReceipt); diff --git a/addons/point_of_sale/static/src/js/ChromeWidgets/ClientScreenButton.js b/addons/point_of_sale/static/src/js/ChromeWidgets/ClientScreenButton.js index 8acd3a6681a..941e59f2d00 100644 --- a/addons/point_of_sale/static/src/js/ChromeWidgets/ClientScreenButton.js +++ b/addons/point_of_sale/static/src/js/ChromeWidgets/ClientScreenButton.js @@ -8,8 +8,8 @@ odoo.define('point_of_sale.ClientScreenButton', function(require) { // Formerly ClientScreenWidget class ClientScreenButton extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); this.local = this.env.pos.config.iface_customer_facing_display_local && !this.env.pos.config.iface_customer_facing_display_proxy; this.state = useState({ status: this.local ? 'success' : 'failure' }); this._start(); 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 b4f4b92bbb7..de0c1a83b0b 100644 --- a/addons/point_of_sale/static/src/js/ChromeWidgets/DebugWidget.js +++ b/addons/point_of_sale/static/src/js/ChromeWidgets/DebugWidget.js @@ -7,11 +7,11 @@ odoo.define('point_of_sale.DebugWidget', function (require) { const PosComponent = require('point_of_sale.PosComponent'); const Registries = require('point_of_sale.Registries'); - const { useRef, useState } = owl; + const { onMounted, onWillUnmount, useRef, useState } = owl; class DebugWidget extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); this.state = useState({ barcodeInput: '', weightInput: '', @@ -44,12 +44,14 @@ odoo.define('point_of_sale.DebugWidget', function (require) { }).bind(this) ); } - } - mounted() { - NumberBuffer.on('buffer-update', this, this._onBufferUpdate); - } - willUnmount() { - NumberBuffer.off('buffer-update', this, this._onBufferUpdate); + + onMounted(() => { + NumberBuffer.on('buffer-update', this, this._onBufferUpdate); + }); + + onWillUnmount(() => { + NumberBuffer.off('buffer-update', this, this._onBufferUpdate); + }); } toggleWidget() { this.state.isShown = !this.state.isShown; diff --git a/addons/point_of_sale/static/src/js/ChromeWidgets/ProxyStatus.js b/addons/point_of_sale/static/src/js/ChromeWidgets/ProxyStatus.js index 837f02c0bfe..2dd9955be11 100644 --- a/addons/point_of_sale/static/src/js/ChromeWidgets/ProxyStatus.js +++ b/addons/point_of_sale/static/src/js/ChromeWidgets/ProxyStatus.js @@ -4,12 +4,12 @@ odoo.define('point_of_sale.ProxyStatus', function(require) { const PosComponent = require('point_of_sale.PosComponent'); const Registries = require('point_of_sale.Registries'); - const { useState } = owl; + const { onMounted, onWillUnmount, useState } = owl; // Previously ProxyStatusWidget class ProxyStatus extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); const initialProxyStatus = this.env.proxy.get('status'); this.state = useState({ status: initialProxyStatus.status, @@ -17,12 +17,14 @@ odoo.define('point_of_sale.ProxyStatus', function(require) { }); this.statuses = ['connected', 'connecting', 'disconnected', 'warning']; this.index = 0; - } - mounted() { - this.env.proxy.on('change:status', this, this._onChangeStatus); - } - willUnmount() { - this.env.proxy.off('change:status', this, this._onChangeStatus); + + onMounted(() => { + this.env.proxy.on('change:status', this, this._onChangeStatus); + }); + + onWillUnmount(() => { + this.env.proxy.off('change:status', this, this._onChangeStatus); + }); } _onChangeStatus(posProxy, statusChange) { this._setStatus(statusChange.newValue); diff --git a/addons/point_of_sale/static/src/js/ChromeWidgets/SaleDetailsButton.js b/addons/point_of_sale/static/src/js/ChromeWidgets/SaleDetailsButton.js index 0a40efd3543..3e9690fab70 100644 --- a/addons/point_of_sale/static/src/js/ChromeWidgets/SaleDetailsButton.js +++ b/addons/point_of_sale/static/src/js/ChromeWidgets/SaleDetailsButton.js @@ -3,6 +3,7 @@ odoo.define('point_of_sale.SaleDetailsButton', function(require) { const PosComponent = require('point_of_sale.PosComponent'); const Registries = require('point_of_sale.Registries'); + const { renderToString } = require('@web/core/utils/render'); class SaleDetailsButton extends PosComponent { async onClick() { @@ -14,7 +15,7 @@ odoo.define('point_of_sale.SaleDetailsButton', function(require) { method: 'get_sale_details', args: [false, false, false, [this.env.pos.pos_session.id]], }); - const report = this.env.qweb.renderToString( + const report = renderToString( 'SaleDetailsReport', Object.assign({}, saleDetails, { date: new Date().toLocaleString(), diff --git a/addons/point_of_sale/static/src/js/Gui.js b/addons/point_of_sale/static/src/js/Gui.js index c2c039768cd..a4317f09f76 100644 --- a/addons/point_of_sale/static/src/js/Gui.js +++ b/addons/point_of_sale/static/src/js/Gui.js @@ -1,7 +1,7 @@ odoo.define('point_of_sale.Gui', function (require) { 'use strict'; - const { Component, component } = owl; + const { status } = owl; /** * This module bridges the data classes (such as those defined in @@ -52,7 +52,7 @@ odoo.define('point_of_sale.Gui', function (require) { get(target, key) { const { component, availableMethods } = target; if (!component) throw new Error(`Call 'configureGui' before using Gui.`); - const isMounted = component.__owl__.status === 3 /* mounted */; + const isMounted = status(component) === 'mounted'; if (availableMethods.has(key) && isMounted) { return component[key].bind(component); } diff --git a/addons/point_of_sale/static/src/js/Misc/AbstractReceiptScreen.js b/addons/point_of_sale/static/src/js/Misc/AbstractReceiptScreen.js index 3defc8c20d0..af391dcee7f 100644 --- a/addons/point_of_sale/static/src/js/Misc/AbstractReceiptScreen.js +++ b/addons/point_of_sale/static/src/js/Misc/AbstractReceiptScreen.js @@ -14,13 +14,13 @@ odoo.define('point_of_sale.AbstractReceiptScreen', function (require) { * this abstract component. */ class AbstractReceiptScreen extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); this.orderReceipt = useRef('order-receipt'); } async _printReceipt() { if (this.env.proxy.printer) { - const printResult = await this.env.proxy.printer.print_receipt(this.orderReceipt.el.outerHTML); + const printResult = await this.env.proxy.printer.print_receipt(this.orderReceipt.el.innerHTML); if (printResult.successful) { return true; } else { diff --git a/addons/point_of_sale/static/src/js/Misc/Draggable.js b/addons/point_of_sale/static/src/js/Misc/Draggable.js index f7e092304ee..fcbe2d9a9a5 100644 --- a/addons/point_of_sale/static/src/js/Misc/Draggable.js +++ b/addons/point_of_sale/static/src/js/Misc/Draggable.js @@ -1,11 +1,11 @@ odoo.define('point_of_sale.Draggable', function(require) { 'use strict'; - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const PosComponent = require('point_of_sale.PosComponent'); const Registries = require('point_of_sale.Registries'); - const { useExternalListener } = owl; + const { onMounted, useExternalListener } = owl; /** * Wrap an element or a component with { position: absolute } to make it @@ -30,8 +30,8 @@ odoo.define('point_of_sale.Draggable', function(require) { * @trigger 'drag-end' when dragging ended with payload `{ loc: { top, left } }` */ class Draggable extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); this.isDragging = false; this.dx = 0; this.dy = 0; @@ -44,33 +44,34 @@ odoo.define('point_of_sale.Draggable', function(require) { useListener('mousedown', '.drag-handle', this.startDrag); useListener('touchstart', '.drag-handle', this.startDrag); - } - mounted() { - this.limitArea = this.props.limitArea - ? document.querySelector(this.props.limitArea) - : this.el.offsetParent; - this.limitAreaBoundingRect = this.limitArea.getBoundingClientRect(); - if (this.limitArea === this.el.offsetParent) { - this.limitLeft = 0; - this.limitTop = 0; - this.limitRight = this.limitAreaBoundingRect.width; - this.limitBottom = this.limitAreaBoundingRect.height; - } else { - this.limitLeft = -this.el.offsetParent.offsetLeft; - this.limitTop = -this.el.offsetParent.offsetTop; - this.limitRight = - this.limitAreaBoundingRect.width - this.el.offsetParent.offsetLeft; - this.limitBottom = - this.limitAreaBoundingRect.height - this.el.offsetParent.offsetTop; - } - this.limitAreaWidth = this.limitAreaBoundingRect.width; - this.limitAreaHeight = this.limitAreaBoundingRect.height; - // absolutely position the element then remove the transform. - const elBoundingRect = this.el.getBoundingClientRect(); - this.el.style.top = `${elBoundingRect.top}px`; - this.el.style.left = `${elBoundingRect.left}px`; - this.el.style.transform = 'none'; + onMounted(() => { + this.limitArea = this.props.limitArea + ? document.querySelector(this.props.limitArea) + : this.el.offsetParent; + this.limitAreaBoundingRect = this.limitArea.getBoundingClientRect(); + if (this.limitArea === this.el.offsetParent) { + this.limitLeft = 0; + this.limitTop = 0; + this.limitRight = this.limitAreaBoundingRect.width; + this.limitBottom = this.limitAreaBoundingRect.height; + } else { + this.limitLeft = -this.el.offsetParent.offsetLeft; + this.limitTop = -this.el.offsetParent.offsetTop; + this.limitRight = + this.limitAreaBoundingRect.width - this.el.offsetParent.offsetLeft; + this.limitBottom = + this.limitAreaBoundingRect.height - this.el.offsetParent.offsetTop; + } + this.limitAreaWidth = this.limitAreaBoundingRect.width; + this.limitAreaHeight = this.limitAreaBoundingRect.height; + + // absolutely position the element then remove the transform. + const elBoundingRect = this.el.getBoundingClientRect(); + this.el.style.top = `${elBoundingRect.top}px`; + this.el.style.left = `${elBoundingRect.left}px`; + this.el.style.transform = 'none'; + }); } startDrag(event) { let realEvent; diff --git a/addons/point_of_sale/static/src/js/Misc/NotificationSound.js b/addons/point_of_sale/static/src/js/Misc/NotificationSound.js index 540e84f1767..e783225e38b 100644 --- a/addons/point_of_sale/static/src/js/Misc/NotificationSound.js +++ b/addons/point_of_sale/static/src/js/Misc/NotificationSound.js @@ -1,13 +1,13 @@ odoo.define('point_of_sale.NotificationSound', function (require) { 'use strict'; - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const PosComponent = require('point_of_sale.PosComponent'); const Registries = require('point_of_sale.Registries'); class NotificationSound extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); useListener('ended', () => (this.props.sound.src = null)); } } diff --git a/addons/point_of_sale/static/src/js/Misc/NumberBuffer.js b/addons/point_of_sale/static/src/js/Misc/NumberBuffer.js index b45a8329cf2..3e868be320d 100644 --- a/addons/point_of_sale/static/src/js/Misc/NumberBuffer.js +++ b/addons/point_of_sale/static/src/js/Misc/NumberBuffer.js @@ -1,13 +1,13 @@ odoo.define('point_of_sale.NumberBuffer', function(require) { 'use strict'; - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const { parse } = require('web.field_utils'); const { BarcodeEvents } = require('barcodes.BarcodeEvents'); const { _t } = require('web.core'); const { Gui } = require('point_of_sale.Gui'); - const { Component, EventBus, onMounted, onWillUnmount, useExternalListener } = owl; + const { EventBus, onMounted, onWillUnmount, useComponent, useExternalListener } = owl; const INPUT_KEYS = new Set( ['Delete', 'Backspace', '+1', '+2', '+5', '+10', '+20', '+50'].concat('0123456789+-.,'.split('')) ); @@ -132,7 +132,7 @@ odoo.define('point_of_sale.NumberBuffer', function(require) { */ use(config) { this.eventsBuffer = []; - const currentComponent = Component.current; + const currentComponent = useComponent(); config = Object.assign(getDefaultConfig(), config); onMounted(() => { this.bufferHolderStack.push({ diff --git a/addons/point_of_sale/static/src/js/Misc/SearchBar.js b/addons/point_of_sale/static/src/js/Misc/SearchBar.js index 262573f09c4..283df21e31d 100644 --- a/addons/point_of_sale/static/src/js/Misc/SearchBar.js +++ b/addons/point_of_sale/static/src/js/Misc/SearchBar.js @@ -1,7 +1,7 @@ odoo.define('point_of_sale.SearchBar', function (require) { 'use strict'; - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const PosComponent = require('point_of_sale.PosComponent'); const Registries = require('point_of_sale.Registries'); @@ -31,8 +31,8 @@ odoo.define('point_of_sale.SearchBar', function (require) { * field of the event. */ class SearchBar extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); useExternalListener(window, 'click', this._hideOptions); useListener('click-search-field', this._onClickSearchField); useListener('select-filter', this._onSelectFilter); diff --git a/addons/point_of_sale/static/src/js/Notification.js b/addons/point_of_sale/static/src/js/Notification.js index 0c99e761df0..cf470676614 100644 --- a/addons/point_of_sale/static/src/js/Notification.js +++ b/addons/point_of_sale/static/src/js/Notification.js @@ -1,19 +1,22 @@ odoo.define('point_of_sale.Notification', function (require) { 'use strict'; - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const PosComponent = require('point_of_sale.PosComponent'); const Registries = require('point_of_sale.Registries'); + const { onMounted } = owl; + class Notification extends PosComponent { - constructor() { - super(...arguments) + setup() { + super.setup(); useListener('click', this.closeNotification); - } - mounted() { - setTimeout(() => { - this.closeNotification(); - }, this.props.duration) + + onMounted(() => { + setTimeout(() => { + this.closeNotification(); + }, this.props.duration) + }); } } Notification.template = 'Notification'; diff --git a/addons/point_of_sale/static/src/js/PopupControllerMixin.js b/addons/point_of_sale/static/src/js/PopupControllerMixin.js index 428430fb241..bcafe4a6a06 100644 --- a/addons/point_of_sale/static/src/js/PopupControllerMixin.js +++ b/addons/point_of_sale/static/src/js/PopupControllerMixin.js @@ -1,7 +1,7 @@ odoo.define('point_of_sale.PopupControllerMixin', function(require) { 'use strict'; - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const { useState } = owl; @@ -16,8 +16,8 @@ odoo.define('point_of_sale.PopupControllerMixin', function(require) { */ const PopupControllerMixin = x => class extends x { - constructor() { - super(...arguments); + setup() { + super.setup(); useListener('show-popup', this.__showPopup); useListener('close-popup', this.__closePopup); diff --git a/addons/point_of_sale/static/src/js/Popups/AbstractAwaitablePopup.js b/addons/point_of_sale/static/src/js/Popups/AbstractAwaitablePopup.js index 95de85b634b..ce9d00ae85f 100644 --- a/addons/point_of_sale/static/src/js/Popups/AbstractAwaitablePopup.js +++ b/addons/point_of_sale/static/src/js/Popups/AbstractAwaitablePopup.js @@ -3,7 +3,7 @@ odoo.define('point_of_sale.AbstractAwaitablePopup', function (require) { const PosComponent = require('point_of_sale.PosComponent'); - const { useExternalListener, xml } = owl; + const { useExternalListener } = owl; /** * Implement this abstract class by extending it like so: @@ -31,8 +31,8 @@ odoo.define('point_of_sale.AbstractAwaitablePopup', function (require) { * ``` */ class AbstractAwaitablePopup extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); if (!this.props.notEscapable) { useExternalListener(window, 'keyup', this._cancelAtEscape); } diff --git a/addons/point_of_sale/static/src/js/Popups/CashMovePopup.js b/addons/point_of_sale/static/src/js/Popups/CashMovePopup.js index bc930a30466..fc29541cafd 100644 --- a/addons/point_of_sale/static/src/js/Popups/CashMovePopup.js +++ b/addons/point_of_sale/static/src/js/Popups/CashMovePopup.js @@ -10,6 +10,7 @@ odoo.define('point_of_sale.CashMovePopup', function (require) { class CashMovePopup extends AbstractAwaitablePopup { setup() { + super.setup(); this.state = useState({ inputType: '', // '' | 'in' | 'out' inputAmount: '', diff --git a/addons/point_of_sale/static/src/js/Popups/CashOpeningPopup.js b/addons/point_of_sale/static/src/js/Popups/CashOpeningPopup.js index 5e527556bc3..de6fb069a16 100644 --- a/addons/point_of_sale/static/src/js/Popups/CashOpeningPopup.js +++ b/addons/point_of_sale/static/src/js/Popups/CashOpeningPopup.js @@ -4,27 +4,25 @@ odoo.define('point_of_sale.CashOpeningPopup', function(require) { const AbstractAwaitablePopup = require('point_of_sale.AbstractAwaitablePopup'); const Registries = require('point_of_sale.Registries'); - const { useRef, useState } = owl; + const { useState } = owl; class CashOpeningPopup extends AbstractAwaitablePopup { - constructor() { - super(...arguments); + setup() { + super.setup(); this.manualInputCashCount = null; this.state = useState({ notes: "", openingCash: this.env.pos.bank_statement.balance_start || 0, + displayMoneyDetailsPopup: false, }); - this.moneyDetailsRef = useRef('moneyDetails'); } openDetailsPopup() { - if (this.moneyDetailsRef.comp.isClosed()){ - this.moneyDetailsRef.comp.openPopup(); - this.state.openingCash = 0; - this.state.notes = ""; - if (this.manualInputCashCount) { - this.moneyDetailsRef.comp.reset(); - } - } + this.state.openingCash = 0; + this.state.notes = ""; + this.state.displayMoneyDetailsPopup = true; + } + closeDetailsPopup() { + this.state.displayMoneyDetailsPopup = false; } startSession() { this.env.pos.bank_statement.balance_start = this.state.openingCash; @@ -36,13 +34,13 @@ odoo.define('point_of_sale.CashOpeningPopup', function(require) { }); this.cancel(); // close popup } - updateCashOpening(event) { - const { total, moneyDetailsNotes } = event.detail; + updateCashOpening({ total, moneyDetailsNotes }) { this.state.openingCash = total; if (moneyDetailsNotes) { this.state.notes = moneyDetailsNotes; } this.manualInputCashCount = false; + this.closeDetailsPopup(); } handleInputChange() { this.manualInputCashCount = true; 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 720c38d9a4b..efaf7ee7e7d 100644 --- a/addons/point_of_sale/static/src/js/Popups/ClosePosPopup.js +++ b/addons/point_of_sale/static/src/js/Popups/ClosePosPopup.js @@ -6,22 +6,25 @@ odoo.define('point_of_sale.ClosePosPopup', function(require) { const { identifyError } = require('point_of_sale.utils'); const { ConnectionLostError, ConnectionAbortedError} = require('@web/core/network/rpc_service') - const { useRef, useState } = owl; + const { onWillStart, onMounted, useState } = owl; /** * This popup needs to be self-dependent because it needs to be called from different place. */ class ClosePosPopup extends AbstractAwaitablePopup { - constructor() { - super(...arguments); + setup() { + super.setup(); this.manualInputCashCount = false; this.cashControl = this.env.pos.config.cash_control; - this.moneyDetailsRef = useRef('moneyDetails'); this.closeSessionClicked = false; this.moneyDetails = null; - this.state = useState({}); + this.state = useState({ + displayMoneyDetailsPopup: false, + }); + onWillStart(this.onWillStart); + onMounted(this.onMounted); } - async willStart() { + async onWillStart() { try { const closingData = await this.rpc({ model: 'pos.session', @@ -57,7 +60,7 @@ odoo.define('point_of_sale.ClosePosPopup', function(require) { /* * Since this popup need to be self dependent, in case of an error, the popup need to be closed on its own. */ - mounted() { + onMounted() { if (this.error) { this.cancel(); if (identifyError(this.error) instanceof ConnectionLostError) { @@ -71,15 +74,13 @@ odoo.define('point_of_sale.ClosePosPopup', function(require) { } } openDetailsPopup() { - if (this.moneyDetailsRef.comp.isClosed()){ - this.moneyDetailsRef.comp.openPopup(); - this.state.payments[this.defaultCashDetails.id].counted = 0; - this.state.payments[this.defaultCashDetails.id].difference = -this.defaultCashDetails.amount; - this.state.notes = ''; - if (this.manualInputCashCount) { - this.moneyDetailsRef.comp.reset(); - } - } + this.state.payments[this.defaultCashDetails.id].counted = 0; + this.state.payments[this.defaultCashDetails.id].difference = -this.defaultCashDetails.amount; + this.state.notes = ""; + this.state.displayMoneyDetailsPopup = true; + } + closeDetailsPopup() { + this.state.displayMoneyDetailsPopup = false; } handleInputChange(paymentId) { let expectedAmount; @@ -94,8 +95,7 @@ odoo.define('point_of_sale.ClosePosPopup', function(require) { this.env.pos.round_decimals_currency(this.state.payments[paymentId].counted - expectedAmount); this.state.acceptClosing = false; } - updateCountedCash(event) { - const { total, moneyDetailsNotes, moneyDetails } = event.detail; + updateCountedCash({ total, moneyDetailsNotes, moneyDetails }) { this.state.payments[this.defaultCashDetails.id].counted = total; this.state.payments[this.defaultCashDetails.id].difference = this.env.pos.round_decimals_currency(this.state.payments[[this.defaultCashDetails.id]].counted - this.defaultCashDetails.amount); @@ -105,6 +105,7 @@ odoo.define('point_of_sale.ClosePosPopup', function(require) { this.manualInputCashCount = false; this.moneyDetails = moneyDetails; this.state.acceptClosing = false; + this.closeDetailsPopup(); } hasDifference() { return Object.entries(this.state.payments).find(pm => pm[1].difference != 0); diff --git a/addons/point_of_sale/static/src/js/Popups/ControlButtonPopup.js b/addons/point_of_sale/static/src/js/Popups/ControlButtonPopup.js index f2bf23ea97b..2da01ee821e 100644 --- a/addons/point_of_sale/static/src/js/Popups/ControlButtonPopup.js +++ b/addons/point_of_sale/static/src/js/Popups/ControlButtonPopup.js @@ -10,8 +10,8 @@ odoo.define('point_of_sale.ControlButtonPopup', function(require) { * @param {Object} props * @param {string} props.startingValue */ - constructor() { - super(...arguments); + setup() { + super.setup(); this.controlButtons = this.props.controlButtons; } } diff --git a/addons/point_of_sale/static/src/js/Popups/EditListPopup.js b/addons/point_of_sale/static/src/js/Popups/EditListPopup.js index 01a279f8ce8..3d5f563a330 100644 --- a/addons/point_of_sale/static/src/js/Popups/EditListPopup.js +++ b/addons/point_of_sale/static/src/js/Popups/EditListPopup.js @@ -46,8 +46,8 @@ odoo.define('point_of_sale.EditListPopup', function(require) { * @param {Array} [props.array=[]] the array of { id, text } to be edited or an array of strings * @param {Boolean} [props.isSingleItem=false] true if only allowed to edit single item (the first item) */ - constructor() { - super(...arguments); + setup() { + super.setup(); this._id = 0; this.state = useState({ array: this._initialize(this.props.array) }); useAutoFocusToLast(); diff --git a/addons/point_of_sale/static/src/js/Popups/ErrorPopup.js b/addons/point_of_sale/static/src/js/Popups/ErrorPopup.js index 8c07dc1e8f0..3d2bfc0f311 100644 --- a/addons/point_of_sale/static/src/js/Popups/ErrorPopup.js +++ b/addons/point_of_sale/static/src/js/Popups/ErrorPopup.js @@ -7,7 +7,11 @@ odoo.define('point_of_sale.ErrorPopup', function(require) { // formerly ErrorPopupWidget class ErrorPopup extends AbstractAwaitablePopup { - mounted() { + setup() { + super.setup(); + owl.onMounted(this.onMounted); + } + onMounted() { this.playSound('error'); } } diff --git a/addons/point_of_sale/static/src/js/Popups/MoneyDetailsPopup.js b/addons/point_of_sale/static/src/js/Popups/MoneyDetailsPopup.js index fd8f6127d5d..4cedd9ea95e 100644 --- a/addons/point_of_sale/static/src/js/Popups/MoneyDetailsPopup.js +++ b/addons/point_of_sale/static/src/js/Popups/MoneyDetailsPopup.js @@ -12,13 +12,16 @@ odoo.define('point_of_sale.MoneyDetailsPopup', function(require) { * and hide it with some css. The confirm button will just trigger an event to the parent. */ class MoneyDetailsPopup extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); this.currency = this.env.pos.currency; this.state = useState({ moneyDetails: Object.fromEntries(this.env.pos.bills.map(bill => ([bill.value, 0]))), total: 0, }); + if (this.props.manualInputCashCount) { + this.reset(); + } } get firstHalfMoneyDetails() { const moneyDetailsKeys = Object.keys(this.state.moneyDetails).sort((a, b) => a - b); @@ -28,19 +31,10 @@ odoo.define('point_of_sale.MoneyDetailsPopup', function(require) { const moneyDetailsKeys = Object.keys(this.state.moneyDetails).sort((a, b) => a - b); return moneyDetailsKeys.slice(moneyDetailsKeys.length/2, moneyDetailsKeys.length); } - isClosed() { - return this.el.classList.contains('invisible') - } - openPopup() { - this.el.classList.remove('invisible'); - } updateMoneyDetailsAmount() { let total = Object.entries(this.state.moneyDetails).reduce((total, money) => total + money[0] * money[1], 0); this.state.total = this.env.pos.round_decimals_currency(total); } - _closePopup() { - this.el.classList.add('invisible'); - } confirm() { let moneyDetailsNotes = this.state.total ? 'Money details: \n' : null; this.env.pos.bills.forEach(bill => { @@ -48,9 +42,8 @@ odoo.define('point_of_sale.MoneyDetailsPopup', function(require) { moneyDetailsNotes += ` - ${this.state.moneyDetails[bill.value]} x ${this.env.pos.format_currency(bill.value)}\n`; } }) - const payload = { total: this.state.total, moneyDetailsNotes, moneyDetails: { ...this.state.moneyDetails }}; - this.trigger('money-details-validated', payload); - this._closePopup(); + const payload = { total: this.state.total, moneyDetailsNotes, moneyDetails: { ...this.state.moneyDetails } }; + this.props.onConfirm(payload); } reset() { for (let key in this.state.moneyDetails) { this.state.moneyDetails[key] = 0 } @@ -58,7 +51,7 @@ odoo.define('point_of_sale.MoneyDetailsPopup', function(require) { } discard() { this.reset(); - this._closePopup(); + this.props.onDiscard(); } } 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 267ac299dbd..d4e6a44626f 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,7 @@ odoo.define('point_of_sale.NumberPopup', function(require) { const AbstractAwaitablePopup = require('point_of_sale.AbstractAwaitablePopup'); const NumberBuffer = require('point_of_sale.NumberBuffer'); - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const Registries = require('point_of_sale.Registries'); const { useState } = owl; @@ -22,8 +22,8 @@ odoo.define('point_of_sale.NumberPopup', function(require) { * @confirmed {Boolean} * @payload {String} */ - constructor() { - super(...arguments); + setup() { + super.setup(); useListener('accept-input', this.confirm); useListener('close-this-popup', this.cancel); let startingBuffer = ''; diff --git a/addons/point_of_sale/static/src/js/Popups/ProductConfiguratorPopup.js b/addons/point_of_sale/static/src/js/Popups/ProductConfiguratorPopup.js index 4ffca834817..ab9928a1ae5 100644 --- a/addons/point_of_sale/static/src/js/Popups/ProductConfiguratorPopup.js +++ b/addons/point_of_sale/static/src/js/Popups/ProductConfiguratorPopup.js @@ -8,8 +8,8 @@ odoo.define('point_of_sale.ProductConfiguratorPopup', function(require) { const { useState, useSubEnv } = owl; class ProductConfiguratorPopup extends AbstractAwaitablePopup { - constructor() { - super(...arguments); + setup() { + super.setup(); useSubEnv({ attribute_components: [] }); } @@ -33,9 +33,8 @@ odoo.define('point_of_sale.ProductConfiguratorPopup', function(require) { Registries.Component.add(ProductConfiguratorPopup); class BaseProductAttribute extends PosComponent { - constructor() { - super(...arguments); - + setup() { + super.setup(); this.env.attribute_components.push(this); this.attribute = this.props.attribute; @@ -61,7 +60,11 @@ odoo.define('point_of_sale.ProductConfiguratorPopup', function(require) { } class RadioProductAttribute extends BaseProductAttribute { - mounted() { + setup() { + super.setup(); + owl.onMounted(this.onMounted); + } + onMounted() { // With radio buttons `t-model` selects the default input by searching for inputs with // a matching `value` attribute. In our case, we use `t-att-value` so `value` is // not found yet and no radio is selected by default. diff --git a/addons/point_of_sale/static/src/js/Popups/ProductInfoPopup.js b/addons/point_of_sale/static/src/js/Popups/ProductInfoPopup.js index 0b36756df70..b29a9531b05 100644 --- a/addons/point_of_sale/static/src/js/Popups/ProductInfoPopup.js +++ b/addons/point_of_sale/static/src/js/Popups/ProductInfoPopup.js @@ -14,10 +14,12 @@ odoo.define('point_of_sale.ProductInfoPopup', function(require) { * } */ class ProductInfoPopup extends AbstractAwaitablePopup { - constructor() { - super(...arguments); + setup() { + super.setup(); + owl.onWillStart(this.onWillStart); + owl.onMounted(this.onMounted); } - async willStart() { + async onWillStart() { const order = this.env.pos.get_order(); try { // check back-end method `get_product_info_pos` to see what it returns @@ -52,7 +54,7 @@ odoo.define('point_of_sale.ProductInfoPopup', function(require) { /* * Since this popup need to be self dependent, in case of an error, the popup need to be closed on its own. */ - mounted() { + onMounted() { if (this.error) { this.cancel(); if (this.error.message instanceof ConnectionLostError) { diff --git a/addons/point_of_sale/static/src/js/Popups/SelectionPopup.js b/addons/point_of_sale/static/src/js/Popups/SelectionPopup.js index 30ad70b1b15..944fb7c11fa 100644 --- a/addons/point_of_sale/static/src/js/Popups/SelectionPopup.js +++ b/addons/point_of_sale/static/src/js/Popups/SelectionPopup.js @@ -26,8 +26,8 @@ odoo.define('point_of_sale.SelectionPopup', function (require) { * item: any, * } */ - constructor() { - super(...arguments); + setup() { + super.setup(); this.state = useState({ selectedId: this.props.list.find((item) => item.isSelected) }); } selectItem(itemId) { diff --git a/addons/point_of_sale/static/src/js/Popups/TextAreaPopup.js b/addons/point_of_sale/static/src/js/Popups/TextAreaPopup.js index b62b1f248ab..4c271288085 100644 --- a/addons/point_of_sale/static/src/js/Popups/TextAreaPopup.js +++ b/addons/point_of_sale/static/src/js/Popups/TextAreaPopup.js @@ -5,7 +5,7 @@ odoo.define('point_of_sale.TextAreaPopup', function(require) { const Registries = require('point_of_sale.Registries'); const { _lt } = require('@web/core/l10n/translation'); - const { useRef, useState } = owl; + const { onMounted, useRef, useState } = owl; // formerly TextAreaPopupWidget // IMPROVEMENT: This code is very similar to TextInputPopup. @@ -15,12 +15,13 @@ odoo.define('point_of_sale.TextAreaPopup', function(require) { * @param {Object} props * @param {string} props.startingValue */ - constructor() { - super(...arguments); + setup() { + super.setup(); this.state = useState({ inputValue: this.props.startingValue }); this.inputRef = useRef('input'); + onMounted(this.onMounted); } - mounted() { + onMounted() { this.inputRef.el.focus(); } getPayload() { diff --git a/addons/point_of_sale/static/src/js/Popups/TextInputPopup.js b/addons/point_of_sale/static/src/js/Popups/TextInputPopup.js index f01dbd3090f..aee6ea77394 100644 --- a/addons/point_of_sale/static/src/js/Popups/TextInputPopup.js +++ b/addons/point_of_sale/static/src/js/Popups/TextInputPopup.js @@ -5,16 +5,17 @@ odoo.define('point_of_sale.TextInputPopup', function(require) { const Registries = require('point_of_sale.Registries'); const { _lt } = require('@web/core/l10n/translation'); - const { useRef, useState } = owl; + const { onMounted, useRef, useState } = owl; // formerly TextInputPopupWidget class TextInputPopup extends AbstractAwaitablePopup { - constructor() { - super(...arguments); + setup() { + super.setup(); this.state = useState({ inputValue: this.props.startingValue }); this.inputRef = useRef('input'); + onMounted(this.onMounted); } - mounted() { + onMounted() { this.inputRef.el.focus(); } getPayload() { diff --git a/addons/point_of_sale/static/src/js/PosComponent.js b/addons/point_of_sale/static/src/js/PosComponent.js index c1d69552471..ad56a24ef0e 100644 --- a/addons/point_of_sale/static/src/js/PosComponent.js +++ b/addons/point_of_sale/static/src/js/PosComponent.js @@ -1,9 +1,16 @@ odoo.define('point_of_sale.PosComponent', function (require) { 'use strict'; - const { Component } = owl; + const { Component, onRendered } = owl; class PosComponent extends Component { + 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 @@ -59,14 +66,6 @@ odoo.define('point_of_sale.PosComponent', function (require) { closeNotification() { this.trigger('close-notification'); } - render() { - return super.render().then((res) => { - if (this.env.isDebug()) { - console.log('Rendered:', this.constructor.name); - } - return res; - }) - } } return PosComponent; diff --git a/addons/point_of_sale/static/src/js/PosContext.js b/addons/point_of_sale/static/src/js/PosContext.js index ee774436c94..cf6ce355160 100644 --- a/addons/point_of_sale/static/src/js/PosContext.js +++ b/addons/point_of_sale/static/src/js/PosContext.js @@ -1,11 +1,10 @@ odoo.define('point_of_sale.PosContext', function (require) { 'use strict'; - - const { Context } = owl; + const { reactive } = owl; // Create global context objects // e.g. component.env.device = new Context({ isMobile: false }); return { - orderManagement: new Context({ searchString: '', selectedOrder: null }), + orderManagement: reactive({ searchString: '', selectedOrder: null }), }; }); diff --git a/addons/point_of_sale/static/src/js/Screens/ClientListScreen/ClientDetailsEdit.js b/addons/point_of_sale/static/src/js/Screens/ClientListScreen/ClientDetailsEdit.js index 3984b2483e9..89afc6be087 100644 --- a/addons/point_of_sale/static/src/js/Screens/ClientListScreen/ClientDetailsEdit.js +++ b/addons/point_of_sale/static/src/js/Screens/ClientListScreen/ClientDetailsEdit.js @@ -6,21 +6,25 @@ odoo.define('point_of_sale.ClientDetailsEdit', function(require) { const PosComponent = require('point_of_sale.PosComponent'); const Registries = require('point_of_sale.Registries'); + const { onMounted, onWillUnmount } = owl; + class ClientDetailsEdit extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); this.intFields = ['country_id', 'state_id', 'property_product_pricelist']; const partner = this.props.partner; this.changes = { 'country_id': partner.country_id && partner.country_id[0], 'state_id': partner.state_id && partner.state_id[0], }; - } - mounted() { - this.env.bus.on('save-customer', this, this.saveChanges); - } - willUnmount() { - this.env.bus.off('save-customer', this); + + onMounted(() => { + this.env.bus.on('save-customer', this, this.saveChanges); + }); + + onWillUnmount(() => { + this.env.bus.off('save-customer', this); + }); } get partnerImageUrl() { // We prioritize image_1920 in the `changes` field because we want diff --git a/addons/point_of_sale/static/src/js/Screens/ClientListScreen/ClientListScreen.js b/addons/point_of_sale/static/src/js/Screens/ClientListScreen/ClientListScreen.js index a96c6c825ad..ae21fc4b1f7 100644 --- a/addons/point_of_sale/static/src/js/Screens/ClientListScreen/ClientListScreen.js +++ b/addons/point_of_sale/static/src/js/Screens/ClientListScreen/ClientListScreen.js @@ -3,11 +3,13 @@ odoo.define('point_of_sale.ClientListScreen', function(require) { const PosComponent = require('point_of_sale.PosComponent'); const Registries = require('point_of_sale.Registries'); - const { useListener } = require('web.custom_hooks'); - - const { Observer, debounce } = owl; const { isConnectionError } = require('point_of_sale.utils'); + const { debounce } = require("@web/core/utils/timing"); + const { useListener } = require("@web/core/utils/hooks"); + + const { onWillUnmount } = owl; + /** * Render this screen using `showTempScreen` to select client. * When the shown screen is confirmed ('Set Customer' or 'Deselect Customer' @@ -24,8 +26,8 @@ odoo.define('point_of_sale.ClientListScreen', function(require) { * @props client - originally selected client */ class ClientListScreen extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); useListener('click-save', () => this.env.bus.trigger('save-customer')); useListener('click-edit', () => this.editClient()); useListener('save-changes', this.saveChanges); @@ -48,6 +50,7 @@ odoo.define('point_of_sale.ClientListScreen', function(require) { }, }; this.updateClientList = debounce(this.updateClientList, 70); + onWillUnmount(this.updateClientList.cancel); } // Lifecycle hooks back() { @@ -110,8 +113,7 @@ odoo.define('point_of_sale.ClientListScreen', function(require) { this.render(); } } - clickClient(event) { - let partner = event.detail.client; + clickClient(partner) { if (this.state.selectedClient === partner) { this.state.selectedClient = null; } else { @@ -142,16 +144,6 @@ odoo.define('point_of_sale.ClientListScreen', function(require) { } this.render(); } - deactivateEditMode() { - this.state.isEditMode = false; - this.state.editModeProps = { - partner: { - country_id: this.env.pos.company.country_id, - state_id: this.env.pos.company.state_id, - }, - }; - this.render(); - } async saveChanges(event) { try { let partnerId = await this.rpc({ @@ -174,9 +166,6 @@ odoo.define('point_of_sale.ClientListScreen', function(require) { } } } - cancelEdit() { - this.deactivateEditMode(); - } async searchClient() { let result = await this.getNewClient(); this.env.pos.db.add_partners(result); 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 2fdec1e13d1..a33ef5d66a6 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 @@ -5,13 +5,13 @@ odoo.define('point_of_sale.PaymentScreen', function (require) { const PosComponent = require('point_of_sale.PosComponent'); const { useErrorHandlers } = require('point_of_sale.custom_hooks'); const NumberBuffer = require('point_of_sale.NumberBuffer'); - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const Registries = require('point_of_sale.Registries'); const { isConnectionError } = require('point_of_sale.utils'); class PaymentScreen extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); useListener('delete-payment-line', this.deletePaymentLine); useListener('select-payment-line', this.selectPaymentLine); useListener('new-payment-line', this.addNewPaymentLine); 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 e661722f21e..4ec3536734f 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 @@ -5,8 +5,8 @@ odoo.define('point_of_sale.PaymentScreenNumpad', function(require) { const Registries = require('point_of_sale.Registries'); class PaymentScreenNumpad extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); this.decimalPoint = this.env._t.database.parameters.decimal_point; } } diff --git a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/OrderlineCustomerNoteButton.js b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/OrderlineCustomerNoteButton.js index 6bf3b9da9bd..bc4f18e5588 100644 --- a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/OrderlineCustomerNoteButton.js +++ b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/OrderlineCustomerNoteButton.js @@ -3,12 +3,12 @@ odoo.define('point_of_sale.OrderlineCustomerNoteButton', function(require) { const PosComponent = require('point_of_sale.PosComponent'); const ProductScreen = require('point_of_sale.ProductScreen'); - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const Registries = require('point_of_sale.Registries'); class OrderlineCustomerNoteButton extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); useListener('click', this.onClick); } async onClick() { diff --git a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/ProductInfoButton.js b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/ProductInfoButton.js index 35b02ed53b1..766bcb6d641 100644 --- a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/ProductInfoButton.js +++ b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/ProductInfoButton.js @@ -3,12 +3,12 @@ odoo.define('point_of_sale.ProductInfoButton', function(require) { const PosComponent = require('point_of_sale.PosComponent'); const ProductScreen = require('point_of_sale.ProductScreen'); - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const Registries = require('point_of_sale.Registries'); class ProductInfoButton extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); useListener('click', this.onClick); } onClick() { diff --git a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/RefundButton.js b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/RefundButton.js index 38a35ee204d..429c4c48a85 100644 --- a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/RefundButton.js +++ b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/RefundButton.js @@ -4,11 +4,11 @@ odoo.define('point_of_sale.RefundButton', function (require) { const PosComponent = require('point_of_sale.PosComponent'); const ProductScreen = require('point_of_sale.ProductScreen'); const Registries = require('point_of_sale.Registries'); - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); class RefundButton extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); useListener('click', this._onClick); } _onClick() { diff --git a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/SetFiscalPositionButton.js b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/SetFiscalPositionButton.js index f17da83ca92..226ff0971cc 100644 --- a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/SetFiscalPositionButton.js +++ b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/SetFiscalPositionButton.js @@ -3,12 +3,12 @@ odoo.define('point_of_sale.SetFiscalPositionButton', function(require) { const PosComponent = require('point_of_sale.PosComponent'); const ProductScreen = require('point_of_sale.ProductScreen'); - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const Registries = require('point_of_sale.Registries'); class SetFiscalPositionButton extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); useListener('click', this.onClick); } get currentOrder() { diff --git a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/SetPricelistButton.js b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/SetPricelistButton.js index 37765666e02..0370543a387 100644 --- a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/SetPricelistButton.js +++ b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ControlButtons/SetPricelistButton.js @@ -3,12 +3,12 @@ odoo.define('point_of_sale.SetPricelistButton', function(require) { const PosComponent = require('point_of_sale.PosComponent'); const ProductScreen = require('point_of_sale.ProductScreen'); - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const Registries = require('point_of_sale.Registries'); class SetPricelistButton extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); useListener('click', this.onClick); } get currentOrder() { diff --git a/addons/point_of_sale/static/src/js/Screens/ProductScreen/OrderWidget.js b/addons/point_of_sale/static/src/js/Screens/ProductScreen/OrderWidget.js index 8199dd1a1a3..4b460cc752a 100644 --- a/addons/point_of_sale/static/src/js/Screens/ProductScreen/OrderWidget.js +++ b/addons/point_of_sale/static/src/js/Screens/ProductScreen/OrderWidget.js @@ -1,15 +1,15 @@ odoo.define('point_of_sale.OrderWidget', function(require) { 'use strict'; - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const PosComponent = require('point_of_sale.PosComponent'); const Registries = require('point_of_sale.Registries'); const { onPatched, useRef } = owl; class OrderWidget extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); useListener('select-line', this._selectLine); useListener('edit-pack-lot-lines', this._editPackLotLines); this.scrollableRef = useRef('scrollable'); diff --git a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ProductScreen.js b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ProductScreen.js index d7c44eabf82..905fa3ebf34 100644 --- a/addons/point_of_sale/static/src/js/Screens/ProductScreen/ProductScreen.js +++ b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ProductScreen.js @@ -4,7 +4,7 @@ odoo.define('point_of_sale.ProductScreen', function(require) { const PosComponent = require('point_of_sale.PosComponent'); const ControlButtonsMixin = require('point_of_sale.ControlButtonsMixin'); const NumberBuffer = require('point_of_sale.NumberBuffer'); - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const Registries = require('point_of_sale.Registries'); const { useBarcodeReader } = require('point_of_sale.custom_hooks'); const { isConnectionError } = require('point_of_sale.utils'); @@ -13,8 +13,8 @@ odoo.define('point_of_sale.ProductScreen', function(require) { const { onMounted, useState } = owl; class ProductScreen extends ControlButtonsMixin(PosComponent) { - constructor() { - super(...arguments); + setup() { + super.setup(); useListener('update-selected-orderline', this._updateSelectedOrderline); useListener('set-numpad-mode', this._setNumpadMode); useListener('click-product', this._clickProduct); @@ -33,6 +33,7 @@ odoo.define('point_of_sale.ProductScreen', function(require) { triggerAtInput: 'update-selected-orderline', useWithBarcode: true, }); + onMounted(this.onMounted); // Call `reset` when the `onMounted` callback in `NumberBuffer.use` is done. // We don't do this in the `mounted` lifecycle method because it is called before // the callbacks in `onMounted` hook. @@ -41,7 +42,7 @@ odoo.define('point_of_sale.ProductScreen', function(require) { mobile_pane: this.props.mobile_pane || 'right', }); } - mounted() { + onMounted() { this.env.posbus.trigger('start-cash-control'); } /** 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 314050f702f..10f2cdb859d 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 @@ -2,26 +2,27 @@ odoo.define('point_of_sale.ProductsWidget', function(require) { 'use strict'; const PosComponent = require('point_of_sale.PosComponent'); - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const Registries = require('point_of_sale.Registries'); - const { useState } = owl; + const { onWillUnmount, useState } = owl; class ProductsWidget extends PosComponent { /** * @param {Object} props * @param {number?} props.startCategoryId */ - constructor() { - super(...arguments); + setup() { + super.setup(); useListener('switch-category', this._switchCategory); useListener('update-search', this._updateSearch); useListener('try-add-product', this._tryAddProduct); useListener('clear-search', this._clearSearch); useListener('update-product-list', this._updateProductList); this.state = useState({ searchWord: '' }); + onWillUnmount(this.onWillUnmount); } - willUnmount() { + onWillUnmount() { this.trigger('toggle-mobile-searchbar', false); } get 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 13b26c009c4..8ee678c4a06 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,22 +5,24 @@ odoo.define('point_of_sale.ProductsWidgetControlPanel', function(require) { const { ConnectionLostError, ConnectionAbortedError } = require('@web/core/network/rpc_service'); const PosComponent = require('point_of_sale.PosComponent'); const Registries = require('point_of_sale.Registries'); + const { debounce } = require("@web/core/utils/timing"); - const { debounce, useRef } = owl; + const { onMounted, onWillUnmount, useRef } = owl; class ProductsWidgetControlPanel extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); this.searchWordInput = useRef('search-word-input'); this.updateSearch = debounce(this.updateSearch, 100); - } - mounted() { - this.env.posbus.on('search-product-from-info-popup', this, this.searchProductFromInfo) - } - willUnmount() { - this.env.posbus.off('search-product-from-info-popup', this); - } + onMounted(() => { + this.env.posbus.on('search-product-from-info-popup', this, this.searchProductFromInfo) + }); + + onWillUnmount(() => { + this.env.posbus.off('search-product-from-info-popup', this); + }); + } clearSearch() { this.searchWordInput.el.value = ''; this.trigger('clear-search'); diff --git a/addons/point_of_sale/static/src/js/Screens/ReceiptScreen/OrderReceipt.js b/addons/point_of_sale/static/src/js/Screens/ReceiptScreen/OrderReceipt.js index c06b63399f4..8b358a5bb6c 100644 --- a/addons/point_of_sale/static/src/js/Screens/ReceiptScreen/OrderReceipt.js +++ b/addons/point_of_sale/static/src/js/Screens/ReceiptScreen/OrderReceipt.js @@ -4,13 +4,16 @@ odoo.define('point_of_sale.OrderReceipt', function(require) { const PosComponent = require('point_of_sale.PosComponent'); const Registries = require('point_of_sale.Registries'); + const { onWillUpdateProps } = owl; + class OrderReceipt extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); this._receiptEnv = this.props.order.getOrderReceiptEnv(); - } - willUpdateProps(nextProps) { - this._receiptEnv = nextProps.order.getOrderReceiptEnv(); + + onWillUpdateProps((nextProps) => { + this._receiptEnv = nextProps.order.getOrderReceiptEnv(); + }); } get receipt() { return this.receiptEnv.receipt; 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 5f73f5da561..98258034489 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 @@ -7,11 +7,12 @@ odoo.define('point_of_sale.ReceiptScreen', function (require) { const Registries = require('point_of_sale.Registries'); const AbstractReceiptScreen = require('point_of_sale.AbstractReceiptScreen'); - const { useRef } = owl; + const { onMounted, useRef, status } = owl; + const ReceiptScreen = (AbstractReceiptScreen) => { class ReceiptScreen extends AbstractReceiptScreen { - constructor() { - super(...arguments); + setup() { + super.setup(); useErrorHandlers(); this.orderReceipt = useRef('order-receipt'); const order = this.currentOrder; @@ -19,20 +20,23 @@ odoo.define('point_of_sale.ReceiptScreen', function (require) { this.orderUiState = order.uiState.ReceiptScreen; this.orderUiState.inputEmail = this.orderUiState.inputEmail || (client && client.email) || ''; this.is_email = is_email; - } - mounted() { - // Here, we send a task to the event loop that handles - // the printing of the receipt when the component is mounted. - // We are doing this because we want the receipt screen to be - // displayed regardless of what happen to the handleAutoPrint - // call. - setTimeout(async () => { - let images = this.orderReceipt.el.getElementsByTagName('img'); - for(let image of images) { - await image.decode(); - } - await this.handleAutoPrint(); - }, 0); + + onMounted(() => { + // Here, we send a task to the event loop that handles + // the printing of the receipt when the component is mounted. + // We are doing this because we want the receipt screen to be + // displayed regardless of what happen to the handleAutoPrint + // call. + setTimeout(async () => { + if (status(this) === "mounted") { + let images = this.orderReceipt.el.getElementsByTagName('img'); + for (let image of images) { + await image.decode(); + } + await this.handleAutoPrint(); + } + }, 0); + }); } async onSendEmail() { if (!is_email(this.orderUiState.inputEmail)) { @@ -104,7 +108,7 @@ odoo.define('point_of_sale.ReceiptScreen', function (require) { } async _sendReceiptToCustomer() { const printer = new Printer(null, this.env.pos); - const receiptString = this.orderReceipt.comp.el.outerHTML; + const receiptString = this.orderReceipt.el.innerHTML; const ticketImage = await printer.htmlToImg(receiptString); const order = this.currentOrder; const client = order.get_client(); diff --git a/addons/point_of_sale/static/src/js/Screens/ScaleScreen/ScaleScreen.js b/addons/point_of_sale/static/src/js/Screens/ScaleScreen/ScaleScreen.js index 497b6a195c1..172574a6c42 100644 --- a/addons/point_of_sale/static/src/js/Screens/ScaleScreen/ScaleScreen.js +++ b/addons/point_of_sale/static/src/js/Screens/ScaleScreen/ScaleScreen.js @@ -5,23 +5,25 @@ odoo.define('point_of_sale.ScaleScreen', function(require) { const { round_precision: round_pr } = require('web.utils'); const Registries = require('point_of_sale.Registries'); - const { useExternalListener, useState } = owl; + const { onMounted, onWillUnmount, useExternalListener, useState } = owl; class ScaleScreen extends PosComponent { /** * @param {Object} props * @param {Object} props.product The product to weight. */ - constructor() { - super(...arguments); + setup() { + super.setup(); useExternalListener(document, 'keyup', this._onHotkeys); this.state = useState({ weight: 0 }); + onMounted(this.onMounted); + onWillUnmount(this.onWillUnmount); } - mounted() { + onMounted() { // start the scale reading this._readScale(); } - willUnmount() { + onWillUnmount() { // stop the scale reading this.env.proxy_queue.clear(); } 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 3e2fa383890..faafbc7a854 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 @@ -1,14 +1,14 @@ odoo.define('point_of_sale.InvoiceButton', function (require) { 'use strict'; - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const { isConnectionError } = require('point_of_sale.utils'); const PosComponent = require('point_of_sale.PosComponent'); const Registries = require('point_of_sale.Registries'); class InvoiceButton extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); useListener('click', this._onClick); } get isAlreadyInvoiced() { diff --git a/addons/point_of_sale/static/src/js/Screens/TicketScreen/ControlButtons/ReprintReceiptButton.js b/addons/point_of_sale/static/src/js/Screens/TicketScreen/ControlButtons/ReprintReceiptButton.js index 3de046f10dc..1306f51494c 100644 --- a/addons/point_of_sale/static/src/js/Screens/TicketScreen/ControlButtons/ReprintReceiptButton.js +++ b/addons/point_of_sale/static/src/js/Screens/TicketScreen/ControlButtons/ReprintReceiptButton.js @@ -1,13 +1,13 @@ odoo.define('point_of_sale.ReprintReceiptButton', function (require) { 'use strict'; - const { useListener } = require('web.custom_hooks'); + const { useListener } = require("@web/core/utils/hooks"); const PosComponent = require('point_of_sale.PosComponent'); const Registries = require('point_of_sale.Registries'); class ReprintReceiptButton extends PosComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); useListener('click', this._onClick); } async _onClick() { diff --git a/addons/point_of_sale/static/src/js/Screens/TicketScreen/ReprintReceiptScreen.js b/addons/point_of_sale/static/src/js/Screens/TicketScreen/ReprintReceiptScreen.js index 1b7828e6243..f860ba47150 100644 --- a/addons/point_of_sale/static/src/js/Screens/TicketScreen/ReprintReceiptScreen.js +++ b/addons/point_of_sale/static/src/js/Screens/TicketScreen/ReprintReceiptScreen.js @@ -6,7 +6,11 @@ odoo.define('point_of_sale.ReprintReceiptScreen', function (require) { const ReprintReceiptScreen = (AbstractReceiptScreen) => { class ReprintReceiptScreen extends AbstractReceiptScreen { - mounted() { + setup() { + super.setup(); + owl.onMounted(this.onMounted); + } + onMounted() { this.printReceipt(); } confirm() { diff --git a/addons/point_of_sale/static/src/js/Screens/TicketScreen/TicketScreen.js b/addons/point_of_sale/static/src/js/Screens/TicketScreen/TicketScreen.js index 11933920f59..4f117962b68 100644 --- a/addons/point_of_sale/static/src/js/Screens/TicketScreen/TicketScreen.js +++ b/addons/point_of_sale/static/src/js/Screens/TicketScreen/TicketScreen.js @@ -5,14 +5,14 @@ odoo.define('point_of_sale.TicketScreen', function (require) { const Registries = require('point_of_sale.Registries'); const IndependentToOrderScreen = require('point_of_sale.IndependentToOrderScreen'); const NumberBuffer = require('point_of_sale.NumberBuffer'); - const { useListener, useAutofocus } = require('web.custom_hooks'); + const { useAutofocus, useListener } = require("@web/core/utils/hooks"); const { parse } = require('web.field_utils'); - const { useState } = owl; + const { onMounted, onWillUnmount, useState } = owl; class TicketScreen extends IndependentToOrderScreen { - constructor() { - super(...arguments); + setup() { + super.setup(); useListener('close-screen', this._onCloseScreen); useListener('filter-selected', this._onFilterSelected); useListener('search', this._onSearch); @@ -44,16 +44,19 @@ odoo.define('point_of_sale.TicketScreen', function (require) { selectedOrderlineIds: {}, }; Object.assign(this._state.ui, defaultUIState, this.props.ui || {}); + + onMounted(this.onMounted); + onWillUnmount(this.onWillUnmount); } //#region LIFECYCLE METHODS - mounted() { + onMounted() { this.env.posbus.on('ticket-button-clicked', this, this.close); setTimeout(() => { // Show updated list of synced orders when going back to the screen. this._onFilterSelected({ detail: { filter: this._state.ui.filter } }); }); } - willUnmount() { + onWillUnmount() { this.env.posbus.off('ticket-button-clicked', this); } //#endregion diff --git a/addons/point_of_sale/static/src/js/custom_hooks.js b/addons/point_of_sale/static/src/js/custom_hooks.js index eb00c032b61..b2131f398cd 100644 --- a/addons/point_of_sale/static/src/js/custom_hooks.js +++ b/addons/point_of_sale/static/src/js/custom_hooks.js @@ -1,7 +1,7 @@ odoo.define('point_of_sale.custom_hooks', function (require) { 'use strict'; - const { Component, onMounted, onPatched, onWillUnmount } = owl; + const { onMounted, onPatched, onWillUnmount, useComponent } = owl; /** * Introduce error handlers in the component. @@ -10,7 +10,7 @@ odoo.define('point_of_sale.custom_hooks', function (require) { * the error when the order failed to sync. */ function useErrorHandlers() { - const component = Component.current; + const component = useComponent(); component._handlePushOrderError = async function (error) { // This error handler receives `error` equivalent to `error.message` of the rpc error. @@ -58,7 +58,7 @@ odoo.define('point_of_sale.custom_hooks', function (require) { } function useAutoFocusToLast() { - const current = Component.current; + const current = useComponent(); let target = null; function autofocus() { const prevTarget = target; @@ -74,7 +74,7 @@ odoo.define('point_of_sale.custom_hooks', function (require) { } function useBarcodeReader(callbackMap, exclusive = false) { - const current = Component.current; + const current = useComponent(); const barcodeReader = current.env.barcode_reader; for (let [key, callback] of Object.entries(callbackMap)) { callbackMap[key] = callback.bind(current); diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 3dee0263978..54441dcfa0b 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -9,6 +9,7 @@ var field_utils = require('web.field_utils'); var time = require('web.time'); var utils = require('web.utils'); var { Gui } = require('point_of_sale.Gui'); +const { batched } = require("point_of_sale.utils"); var QWeb = core.qweb; var _t = core._t; @@ -16,7 +17,7 @@ var round_di = utils.round_decimals; var round_pr = utils.round_precision; const Registries = require('point_of_sale.Registries'); -const reactivity = require('@point_of_sale/js/reactivity'); +const { markRaw, reactive } = owl; // Container of the product images fetched during rendering // of customer display. There is no need to observe it, thus, @@ -87,7 +88,7 @@ class PosGlobalState extends PosModel { this.db = new PosDB(); // a local database used to search trough products and categories & store pending orders this.debug = config.isDebug(); //debug mode - this.unwatched = reactivity.markRaw({}); + this.unwatched = markRaw({}); // Business data; loaded from the server at launch this.company_logo = null; @@ -356,10 +357,10 @@ class PosGlobalState extends PosModel { options.json = json; } let order = Order.create({}, options); - const batchedSaveToDb = reactivity.batched(() => { + const batchedSaveToDb = batched(() => { order.save_to_db(); }); - order = reactivity.reactive(order, batchedSaveToDb); + order = reactive(order, batchedSaveToDb); order.save_to_db(); return order; } @@ -521,7 +522,7 @@ class PosGlobalState extends PosModel { } set customer_display(value) { - this.unwatched.customer_display = reactivity.markRaw(value); + this.unwatched.customer_display = markRaw(value); } send_current_order_to_customer_facing_display() { @@ -1559,15 +1560,7 @@ class Orderline extends PosModel { } get_unit_display_price(){ if (this.pos.config.iface_tax_included === 'total') { - let price; - // this is used to update the attribute object without triggering the rendering of the components - reactivity.inSilentContext(() => { - let quantity = this.quantity; - this.quantity = 1.0; - price = this.get_all_prices().priceWithTax; - this.quantity = quantity; - }); - return price; + return this.get_all_prices(1).priceWithTax; } else { return this.get_unit_price(); } @@ -1854,7 +1847,7 @@ class Orderline extends PosModel { 'total_included': sign * round_pr(total_included, this.pos.currency.rounding), } } - get_all_prices(){ + get_all_prices(qty = this.get_quantity()){ var self = this; var price_unit = this.get_unit_price() * (1.0 - (this.get_discount() / 100.0)); @@ -1875,8 +1868,8 @@ class Orderline extends PosModel { }); product_taxes = _.uniq(product_taxes, function(tax) { return tax.id; }); - var all_taxes = this.compute_all(product_taxes, price_unit, this.get_quantity(), this.pos.currency.rounding); - var all_taxes_before_discount = this.compute_all(product_taxes, this.get_unit_price(), this.get_quantity(), this.pos.currency.rounding); + var all_taxes = this.compute_all(product_taxes, price_unit, qty, this.pos.currency.rounding); + var all_taxes_before_discount = this.compute_all(product_taxes, this.get_unit_price(), qty, this.pos.currency.rounding); _(all_taxes.taxes).each(function(tax) { taxtotal += tax.amount; taxdetail[tax.id] = tax.amount; diff --git a/addons/point_of_sale/static/src/js/pos_env.js b/addons/point_of_sale/static/src/js/pos_env.js index 32dc554a98b..5347ac0bf9d 100644 --- a/addons/point_of_sale/static/src/js/pos_env.js +++ b/addons/point_of_sale/static/src/js/pos_env.js @@ -15,7 +15,7 @@ let pos_env = Object.create(env); pos_env.proxy_queue = new devices.JobQueue(); // used to prevent parallels communications to the proxy pos_env.proxy = new devices.ProxyDevice({ env: pos_env }); // used to communicate to the hardware devices via a local proxy pos_env.barcode_reader = new BarcodeReader({ env: pos_env, proxy: pos_env.proxy }); -pos_env.posbus = new owl.core.EventBus(); +pos_env.posbus = new owl.EventBus(); pos_env.posMutex = new concurrency.Mutex(); export default pos_env; diff --git a/addons/point_of_sale/static/src/js/reactivity.js b/addons/point_of_sale/static/src/js/reactivity.js deleted file mode 100644 index 31d23907618..00000000000 --- a/addons/point_of_sale/static/src/js/reactivity.js +++ /dev/null @@ -1,271 +0,0 @@ -/* @odoo-module */ -/* global owl */ - -// Allows to get the target of a Reactive (used for making a new Reactive from the underlying object) -const TARGET = Symbol('Target'); -// Escape hatch to prevent reactivity system to turn something into a reactive -const SKIP = Symbol("Skip"); -// Special key to subscribe to, to be notified of key creation/deletion -const KEYCHANGES = Symbol('Key changes'); - -let silent = false; - -export function inSilentContext(cb) { - silent = true; - cb(); - silent = false; -} - -/** - * Checks whether a given value can be made into a reactive object. - * - * @param value the value to check - * @returns whether the value can be made reactive - */ -function canBeMadeReactive(value) { - return ( - typeof value === 'object' && - value !== null && - !(value instanceof Date) && - !(value instanceof Promise) && - !(value instanceof String) && - !(value instanceof Number) && - !(value instanceof Image) && - !(value instanceof Set) - ); -} - -/** - * Mark an object or array so that it is ignored by the reactivity system - * - * @param value the value to mark - * @returns the object itself - */ -export function markRaw(value) { - value[SKIP] = true; - return value; -} - -/** - * Given a reactive objet, return the raw (non reactive) underlying object - * - * @param value a reactive value - * @returns the underlying value - */ -export function toRaw(value) { - return value[TARGET]; -} - -const targetToKeysToCallbacks = new WeakMap(); -/** - * Observes a given key on a target with an callback. The callback will be - * called when the given key changes on the target. - * - * @param target the target whose key should be observed - * @param key the key to observe (or Symbol(KEYCHANGES) for key creation - * or deletion) - * @param callback the function to call when the key changes - */ -function observeTargetKey(target, key, callback) { - if (!targetToKeysToCallbacks.get(target)) { - targetToKeysToCallbacks.set(target, new Map()); - } - const keyToCallbacks = targetToKeysToCallbacks.get(target); - if (!keyToCallbacks.get(key)) { - keyToCallbacks.set(key, new Set()); - } - keyToCallbacks.get(key).add(callback); - if (!callbacksToTargets.has(callback)) { - callbacksToTargets.set(callback, new Set()); - } - callbacksToTargets.get(callback).add(target); -} -/** - * Notify Reactives that are observing a given target that a key has changed on - * the target. - * - * @param target target whose Reactives should be notified that the target was - * changed. - * @param key the key that changed (or Symbol `KEYCHANGES` if a key was created - * or deleted) - */ -function notifyReactives(target, key) { - if (silent) return; - const keyToCallbacks = targetToKeysToCallbacks.get(target); - if (!keyToCallbacks) { - return; - } - const callbacks = keyToCallbacks.get(key); - if (!callbacks) { - return; - } - // Loop on copy because clearReactivesForCallback will modify the set in place - for (const callback of [...callbacks]) { - clearReactivesForCallback(callback); - callback(); - } -} - -const callbacksToTargets = new WeakMap(); -/** - * Clears all subscriptions of the Reactives associated with a given callback. - * - * @param callback the callback for which the reactives need to be cleared - */ -export function clearReactivesForCallback(callback) { - const targetsToClear = callbacksToTargets.get(callback); - if (!targetsToClear) { - return; - } - for (const target of targetsToClear) { - const observedKeys = targetToKeysToCallbacks.get(target); - if (!observedKeys) { - continue; - } - for (const callbacks of observedKeys.values()) { - callbacks.delete(callback); - } - } - targetsToClear.clear(); -} - -const reactiveCache = new WeakMap(); -/** - * Creates a reactive proxy for an object. Reading data on the reactive object - * subscribes to changes to the data. Writing data on the object will cause the - * notify callback to be called if there are suscriptions to that data. Nested - * objects and arrays are automatically made reactive as well. - * - * Whenever you are notified of a change, all subscriptions are cleared, and if - * you would like to be notified of any further changes, you should go read - * the underlying data again. We assume that if you don't go read it again after - * being notified, it means that you are no longer interested in that data. - * - * Subscriptions: - * + Reading a property on an object will subscribe you to changes in the value - * of that property. - * + Accessing an object keys (eg with Object.keys or with `for..in`) will - * subscribe you to the creation/deletion of keys. Checking the presence of a - * key on the object with 'in' has the same effect. - * - getOwnPropertyDescriptor does not currently subscribe you to the property. - * This is a choice that was made because changing a key's value will trigger - * this trap and we do not want to subscribe by writes. This also means that - * Object.hasOwnProperty doesn't subscribe as it goes through this trap. - * - * @param target the object for which to create a reactive proxy - * @param callback the function to call when an observed property of the - * reactive has changed - * @returns a proxy that tracks changes to it - */ -export function reactive(target, callback) { - if (!canBeMadeReactive(target)) { - throw new Error(`Cannot make the given value reactive`); - } - if (SKIP in target) { - return target; - } - const originalTarget = target[TARGET]; - if (originalTarget) { - return reactive(originalTarget, callback); - } - if (!reactiveCache.has(target)) { - reactiveCache.set(target, new Map()); - } - const reactivesForTarget = reactiveCache.get(target); - if (!reactivesForTarget.has(callback)) { - const proxy = new Proxy(target, { - get(target, key, proxy) { - if (key === TARGET) { - return target; - } - observeTargetKey(target, key, callback); - const value = Reflect.get(target, key, proxy); - if (!canBeMadeReactive(value)) { - return value; - } - return reactive(value, callback); - }, - set(target, key, value, proxy) { - const isNewKey = !Object.hasOwnProperty.call(target, key); - const originalValue = Reflect.get(target, key, proxy); - const ret = Reflect.set(target, key, value, proxy); - if (isNewKey) { - notifyReactives(target, KEYCHANGES); - } - // While Array length may trigger the set trap, it's not actually set by this - // method but is updated behind the scenes, and the trap is not called with the - // new value. We disable the "same-value-optimization" for it because of that. - if (originalValue !== value || (Array.isArray(target) && key === 'length')) { - notifyReactives(target, key); - } - return ret; - }, - deleteProperty(target, key) { - const ret = Reflect.deleteProperty(target, key); - notifyReactives(target, KEYCHANGES); - notifyReactives(target, key); - return ret; - }, - ownKeys(target) { - observeTargetKey(target, KEYCHANGES, callback); - return Reflect.ownKeys(target); - }, - has(target, key) { - // TODOhis observes all key changes instead of only the presence of the argument key - observeTargetKey(target, KEYCHANGES, callback); - return Reflect.has(target, key); - }, - }); - reactivesForTarget.set(callback, proxy); - } - return reactivesForTarget.get(callback); -} - -/** - * Creates a batched version of a callback so that all calls to it in the same - * microtick will only call the original callback once. - * - * @param callback the callback to batch - * @returns a batched version of the original callback - */ -export function batched(callback) { - let called = false; - return async () => { - // 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(); - if (!called) { - called = true; - callback(); - // wait for all calls in this microtick to fall through before resetting "called" - // so that only the first call to the batched function calls the original callback - await Promise.resolve(); - called = false; - } - }; -} - -export const batchedRenderFunctions = new WeakMap(); -/** - * Creates a reactive object that will be observed by the current component. - * Reading data from the returned object (eg during rendering) will cause the - * component to subscribe to that data and be rerendered when it changes. - * - * @param state the state to observe - * @returns a reactive object that will cause the component to re-render on - * relevant changes - * @see reactive - */ -export function useState(state) { - const node = owl.Component.current; - if (!batchedRenderFunctions.has(node)) { - batchedRenderFunctions.set( - node, - batched(() => node.render()) - ); - } - const render = batchedRenderFunctions.get(node); - const reactiveState = reactive(state, render); - owl.hooks.onWillUnmount(() => clearReactivesForCallback(render)); - return reactiveState; -} diff --git a/addons/point_of_sale/static/src/js/utils.js b/addons/point_of_sale/static/src/js/utils.js index 31ed750c60f..3ec44b28759 100644 --- a/addons/point_of_sale/static/src/js/utils.js +++ b/addons/point_of_sale/static/src/js/utils.js @@ -63,5 +63,29 @@ odoo.define('point_of_sale.utils', function (require) { return errorToHandle || error; } - return { getFileAsText, nextFrame, identifyError, isConnectionError }; + /** + * Creates a batched version of a callback so that all calls to it in the same + * microtick will only call the original callback once. + * + * @param callback the callback to batch + * @returns a batched version of the original callback + */ + function batched(callback) { + let called = false; + return async () => { + // 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(); + if (!called) { + called = true; + callback(); + // wait for all calls in this microtick to fall through before resetting "called" + // so that only the first call to the batched function calls the original callback + await Promise.resolve(); + called = false; + } + }; + } + + return { getFileAsText, nextFrame, identifyError, isConnectionError, batched }; }); diff --git a/addons/point_of_sale/static/src/xml/Chrome.xml b/addons/point_of_sale/static/src/xml/Chrome.xml index 43dee1af385..96642f9c29a 100644 --- a/addons/point_of_sale/static/src/xml/Chrome.xml +++ b/addons/point_of_sale/static/src/xml/Chrome.xml @@ -7,7 +7,7 @@
- @@ -40,22 +40,26 @@
- + + +
-
-
- -
- -

Connecting to devices

- -
+ +
+
+ +
+ +

Connecting to devices

+ +
+
-
+
@@ -63,7 +67,9 @@ t-key="popup.name" />
- + + +
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 6e67512e318..23c383f17ae 100644 --- a/addons/point_of_sale/static/src/xml/ChromeWidgets/DebugWidget.xml +++ b/addons/point_of_sale/static/src/xml/ChromeWidgets/DebugWidget.xml @@ -3,11 +3,11 @@ -
+

Debug Window

-

Electronic Scale

@@ -42,7 +42,7 @@ Export Paid Orders + t-on-click="() => { state.isPaidOrdersReady = !state.isPaidOrdersReady }">
  • Download Paid Orders
  • @@ -53,7 +53,7 @@
    + t-on-click="() => { state.isUnpaidOrdersReady = !state.isUnpaidOrdersReady }">
  • Download Unpaid Orders
  • diff --git a/addons/point_of_sale/static/src/xml/ChromeWidgets/ProxyStatus.xml b/addons/point_of_sale/static/src/xml/ChromeWidgets/ProxyStatus.xml index b58a315fc13..3e45e0085eb 100644 --- a/addons/point_of_sale/static/src/xml/ChromeWidgets/ProxyStatus.xml +++ b/addons/point_of_sale/static/src/xml/ChromeWidgets/ProxyStatus.xml @@ -2,7 +2,7 @@ -
    +
    diff --git a/addons/point_of_sale/static/src/xml/Misc/MobileOrderWidget.xml b/addons/point_of_sale/static/src/xml/Misc/MobileOrderWidget.xml index eb8e770c182..0f5841297aa 100644 --- a/addons/point_of_sale/static/src/xml/Misc/MobileOrderWidget.xml +++ b/addons/point_of_sale/static/src/xml/Misc/MobileOrderWidget.xml @@ -3,11 +3,11 @@
    - - diff --git a/addons/point_of_sale/static/src/xml/Misc/SearchBar.xml b/addons/point_of_sale/static/src/xml/Misc/SearchBar.xml index d257d0261b9..b14a30c5cd7 100644 --- a/addons/point_of_sale/static/src/xml/Misc/SearchBar.xml +++ b/addons/point_of_sale/static/src/xml/Misc/SearchBar.xml @@ -10,7 +10,7 @@
    • + t-on-click="() => this.trigger('click-search-field', fieldName)"> @@ -23,7 +23,7 @@
    + t-on-click.stop="() => { state.showFilterOptions = !state.showFilterOptions }"> @@ -33,7 +33,7 @@
      -
    • +
    • diff --git a/addons/point_of_sale/static/src/xml/Notification.xml b/addons/point_of_sale/static/src/xml/Notification.xml index 77c2a7a9d17..cf5d2de92d8 100644 --- a/addons/point_of_sale/static/src/xml/Notification.xml +++ b/addons/point_of_sale/static/src/xml/Notification.xml @@ -2,7 +2,7 @@ -
      +
      diff --git a/addons/point_of_sale/static/src/xml/Popups/CashMovePopup.xml b/addons/point_of_sale/static/src/xml/Popups/CashMovePopup.xml index 078461710d4..377af74f076 100644 --- a/addons/point_of_sale/static/src/xml/Popups/CashMovePopup.xml +++ b/addons/point_of_sale/static/src/xml/Popups/CashMovePopup.xml @@ -8,10 +8,10 @@
      - + Cash In - + Cash Out
      diff --git a/addons/point_of_sale/static/src/xml/Popups/CashOpeningPopup.xml b/addons/point_of_sale/static/src/xml/Popups/CashOpeningPopup.xml index 2f0e6857cf6..cb6544456f0 100644 --- a/addons/point_of_sale/static/src/xml/Popups/CashOpeningPopup.xml +++ b/addons/point_of_sale/static/src/xml/Popups/CashOpeningPopup.xml @@ -9,9 +9,9 @@
      Opening cash -
      +
      -
      +
      @@ -19,10 +19,15 @@