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 @@
-
+
+
+
-
-
-
-
-
- Connecting to devices
-
- Skip
-
-
+
+
+
+
+
+
+ Connecting to devices
+
+ Skip
+
+
+
-
+
-
+
+
+
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 @@
-