diff --git a/addons/l10n_fr_pos_cert/static/src/js/Chrome.js b/addons/l10n_fr_pos_cert/static/src/js/Chrome.js index 03bdafa0eff..2221fc96b8a 100644 --- a/addons/l10n_fr_pos_cert/static/src/js/Chrome.js +++ b/addons/l10n_fr_pos_cert/static/src/js/Chrome.js @@ -2,8 +2,6 @@ import Chrome from "@point_of_sale/js/Chrome"; import Registries from "@point_of_sale/js/Registries"; -import { ConnectionLostError, ConnectionAbortedError } from "@web/core/network/rpc_service"; -import { identifyError } from "@point_of_sale/js/utils"; const PosFrCertChrome = (Chrome) => class extends Chrome { @@ -13,30 +11,9 @@ const PosFrCertChrome = (Chrome) => const now = Date.now(); const limitDate = new Date(this.env.pos.pos_session.start_at); limitDate.setDate(limitDate.getDate() + 1); - if (limitDate < now) { - try { - const info = await this.env.pos.getClosePosInfo(); - this.showPopup("ClosePosPopup", { info: info }); - } catch (e) { - if ( - identifyError(e) instanceof ConnectionLostError || - ConnectionAbortedError - ) { - this.showPopup("OfflineErrorPopup", { - title: this.env._t("Network Error"), - body: this.env._t( - "Please check your internet connection and try again." - ), - }); - } else { - this.showPopup("ErrorPopup", { - title: this.env._t("Unknown Error"), - body: this.env._t( - "An unknown error prevents us from getting closing information." - ), - }); - } - } + if (limitDate.getTime() < now) { + const info = await this.env.pos.getClosePosInfo(); + this.showPopup("ClosePosPopup", { info: info }); } } } diff --git a/addons/point_of_sale/static/src/app/error_handlers/error_handlers.js b/addons/point_of_sale/static/src/app/error_handlers/error_handlers.js new file mode 100644 index 00000000000..7b7ffe081d0 --- /dev/null +++ b/addons/point_of_sale/static/src/app/error_handlers/error_handlers.js @@ -0,0 +1,61 @@ +/** @odoo-module */ + +import { registry } from "@web/core/registry"; +import { odooExceptionTitleMap } from "@web/core/errors/error_dialogs"; +import { ConnectionLostError, RPCError } from "@web/core/network/rpc_service"; +import { Gui } from "@point_of_sale/js/Gui"; + +export function identifyError(error) { + return error && error.legacy ? error.message : error; +} + +function rpcErrorHandler(env, error, originalError) { + error = identifyError(originalError); + if (error instanceof RPCError) { + const { message, data } = error; + if (odooExceptionTitleMap.has(error.exceptionName)) { + const title = odooExceptionTitleMap.get(error.exceptionName).toString(); + Gui.showPopup("ErrorPopup", { title, body: data.message }); + } else { + Gui.showPopup("ErrorTracebackPopup", { + title: message, + body: data.message + "\n" + data.debug + "\n", + }); + } + return true; + } +} +registry.category("error_handlers").add("rpcErrorHandler", rpcErrorHandler); + +function offlineErrorHandler(env, error, originalError) { + error = identifyError(originalError); + if (error instanceof ConnectionLostError) { + Gui.showPopup("OfflineErrorPopup", { + title: env._t("Couldn't connect to the server"), + body: env._t( + "The operation couldn't be completed because you are offline. Check your internet connection and try again." + ), + }); + return true; + } +} +registry.category("error_handlers").add("offlineErrorHandler", offlineErrorHandler); + +function defaultErrorHandler(env, error, originalError) { + error = identifyError(originalError); + if (error instanceof Error) { + Gui.showPopup("ErrorTracebackPopup", { + title: `${error.name}: ${error.message}`, + body: error.traceback, + }); + } else { + Gui.showPopup("ErrorPopup", { + title: env._t("Unknown Error"), + body: env._t("Unable to show information about this error."), + }); + } + return true; +} +registry + .category("error_handlers") + .add("defaultErrorHandler", defaultErrorHandler, { sequence: 99 }); diff --git a/addons/point_of_sale/static/src/app/sound/sound_service.js b/addons/point_of_sale/static/src/app/sound/sound_service.js index cdcae67b93a..2feb82d25c0 100644 --- a/addons/point_of_sale/static/src/app/sound/sound_service.js +++ b/addons/point_of_sale/static/src/app/sound/sound_service.js @@ -10,16 +10,11 @@ registry class SoundContainer extends Component { static template = xml` - `; static props = { sounds: Object, }; - onError(sound) { - sound.cleanup(); - const err = new Error(`Couldn't load sound at "${sound.src}"`); - throw err; - } } export const soundService = { diff --git a/addons/point_of_sale/static/src/js/Chrome.js b/addons/point_of_sale/static/src/js/Chrome.js index c9646ff9f42..28df66f11fe 100644 --- a/addons/point_of_sale/static/src/js/Chrome.js +++ b/addons/point_of_sale/static/src/js/Chrome.js @@ -7,13 +7,7 @@ import PosComponent from "@point_of_sale/js/PosComponent"; import NumberBuffer from "@point_of_sale/js/Misc/NumberBuffer"; import Registries from "@point_of_sale/js/Registries"; import IndependentToOrderScreen from "@point_of_sale/js/Misc/IndependentToOrderScreen"; -import { identifyError, batched } from "@point_of_sale/js/utils"; -import { odooExceptionTitleMap } from "@web/core/errors/error_dialogs"; -import { - ConnectionLostError, - ConnectionAbortedError, - RPCError, -} from "@web/core/network/rpc_service"; +import { batched } from "@point_of_sale/js/utils"; import { debounce } from "@web/core/utils/timing"; import { Transition } from "@web/core/transition"; import { MainComponentsContainer } from "@web/core/main_components_container"; @@ -164,17 +158,11 @@ export class Chrome extends PosComponent { * This will load pos and assign it to the environment. */ async start() { - registry.category("error_handlers").add( - "posErrorHandler", - (env, ...noEnvArgs) => { - return this.errorHandler(this.env, ...noEnvArgs); - }, - { sequence: 0 } - ); // Little trick to avoid displaying the block ui during the POS models loading + // FIXME POSREF: use a silent RPC instead const BlockUiFromRegistry = registry.category("main_components").get("BlockUI"); registry.category("main_components").remove("BlockUI"); - configureGui({ component: this }); // FIXME POSREF + configureGui({ component: this }); // FIXME POSREF: move Gui functions to services try { await this.env.pos.load_server_data(); @@ -198,8 +186,8 @@ export class Chrome extends PosComponent { this._showStartScreen(); setTimeout(() => this._runBackgroundTasks()); } catch (error) { - let title = "Unknown Error", - body; + let title = "Unknown Error"; + let body; if (error.message && [100, 200, 404, -32098].includes(error.message.code)) { // this is the signature of rpc error @@ -219,11 +207,7 @@ export class Chrome extends PosComponent { body = error.stack; } - await this.showPopup("ErrorTracebackPopup", { - title, - body, - exitButtonIsShown: true, - }); + return this.showPopup("ErrorTracebackPopup", { title, body, exitButtonIsShown: true }); } registry.category("main_components").add("BlockUI", BlockUiFromRegistry); @@ -271,7 +255,7 @@ export class Chrome extends PosComponent { this.env.proxy .autoconnect({ force_ip: this.env.pos.config.proxy_ip || undefined, - progress: function (prog) { }, + progress: function (prog) {}, }) .then( () => { @@ -383,24 +367,25 @@ export class Chrome extends PosComponent { console.warn(error); const reason = this.env.pos.failed ? this.env._t( - "Some orders could not be submitted to " + - "the server due to configuration errors. " + - "You can exit the Point of Sale, but do " + - "not close the session before the issue " + - "has been resolved." - ) + "Some orders could not be submitted to " + + "the server due to configuration errors. " + + "You can exit the Point of Sale, but do " + + "not close the session before the issue " + + "has been resolved." + ) : this.env._t( - "Some orders could not be submitted to " + - "the server due to internet connection issues. " + - "You can exit the Point of Sale, but do " + - "not close the session before the issue " + - "has been resolved." - ); + "Some orders could not be submitted to " + + "the server due to internet connection issues. " + + "You can exit the Point of Sale, but do " + + "not close the session before the issue " + + "has been resolved." + ); const { confirmed } = await this.showPopup("ConfirmPopup", { title: this.env._t("Offline Orders"), body: reason, }); if (confirmed) { + // FIXME POSREF setting the location prevents the next render, the loading screen never shows this.state.uiState = "CLOSING"; this.state.loadingSkipButtonIsShown = false; window.location = "/web#action=point_of_sale.action_client_pos_menu"; @@ -497,75 +482,6 @@ export class Chrome extends PosComponent { get showCashMoveButton() { return this.env.pos && this.env.pos.config && this.env.pos.config.cash_control; } - - // UNEXPECTED ERROR HANDLING // - - /** - * This method is used to handle unexpected errors. It is registered to - * the `error_handlers` service when this component is properly mounted. - * See `onMounted` hook of the `ChromeAdapter` component. - * @param {*} env - * @param {UncaughtClientError | UncaughtPromiseError} error - * @param {*} originalError - * @returns {boolean} - */ - errorHandler(env, error, originalError) { - if (!env.pos) { - return false; - } - const errorToHandle = identifyError(originalError); - // Assume that the unhandled falsey rejections can be ignored. - if (errorToHandle) { - this._errorHandler(error, errorToHandle); - } - return true; - } - - _errorHandler(error, errorToHandle) { - if (errorToHandle instanceof RPCError) { - const { message, data } = errorToHandle; - if (odooExceptionTitleMap.has(errorToHandle.exceptionName)) { - const title = odooExceptionTitleMap.get(errorToHandle.exceptionName).toString(); - this.showPopup("ErrorPopup", { title, body: data.message }); - } else { - this.showPopup("ErrorTracebackPopup", { - title: message, - body: data.message + "\n" + data.debug + "\n", - }); - } - } else if (errorToHandle instanceof ConnectionLostError) { - this.showPopup("OfflineErrorPopup", { - title: this.env._t("Connection is lost"), - body: this.env._t("Check the internet connection then try again."), - }); - } else if (errorToHandle instanceof ConnectionAbortedError) { - this.showPopup("OfflineErrorPopup", { - title: this.env._t("Connection is aborted"), - body: this.env._t("Check the internet connection then try again."), - }); - } else if (errorToHandle instanceof Error) { - // If `errorToHandle` is a normal Error (such as TypeError), - // the annotated traceback can be found from `error`. - this.showPopup("ErrorTracebackPopup", { - // Hopefully the message is translated. - title: `${errorToHandle.name}: ${errorToHandle.message}`, - body: error.traceback, - }); - } else { - // Hey developer. It's your fault that the error reach here. - // Please, throw an Error object in order to get stack trace of the error. - // At least we can find the file that throws the error when you look - // at the console. - this.showPopup("ErrorPopup", { - title: this.env._t("Unknown Error"), - body: this.env._t("Unable to show information about this error."), - }); - console.error( - "Unknown error. Unable to show information about this error.", - errorToHandle - ); - } - } } Chrome.template = "Chrome"; Object.defineProperty(Chrome, "components", { diff --git a/addons/point_of_sale/static/src/js/ChromeWidgets/HeaderButton.js b/addons/point_of_sale/static/src/js/ChromeWidgets/HeaderButton.js index b94b7589e19..ff38bf77b1d 100644 --- a/addons/point_of_sale/static/src/js/ChromeWidgets/HeaderButton.js +++ b/addons/point_of_sale/static/src/js/ChromeWidgets/HeaderButton.js @@ -2,31 +2,13 @@ import PosComponent from "@point_of_sale/js/PosComponent"; import Registries from "@point_of_sale/js/Registries"; -import { ConnectionLostError, ConnectionAbortedError } from "@web/core/network/rpc_service"; -import { identifyError } from "@point_of_sale/js/utils"; // Previously HeaderButtonWidget // This is the close session button class HeaderButton extends PosComponent { async onClick() { - try { - const info = await this.env.pos.getClosePosInfo(); - this.showPopup("ClosePosPopup", { info: info, keepBehind: true }); - } catch (e) { - if (identifyError(e) instanceof ConnectionAbortedError || ConnectionLostError) { - this.showPopup("OfflineErrorPopup", { - title: this.env._t("Network Error"), - body: this.env._t("Please check your internet connection and try again."), - }); - } else { - this.showPopup("ErrorPopup", { - title: this.env._t("Unknown Error"), - body: this.env._t( - "An unknown error prevents us from getting closing information." - ), - }); - } - } + const info = await this.env.pos.getClosePosInfo(); + this.showPopup("ClosePosPopup", { info: info, keepBehind: true }); } } HeaderButton.template = "HeaderButton"; 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 f0e9e3fe780..befc433b188 100644 --- a/addons/point_of_sale/static/src/js/Popups/ClosePosPopup.js +++ b/addons/point_of_sale/static/src/js/Popups/ClosePosPopup.js @@ -2,8 +2,8 @@ import AbstractAwaitablePopup from "@point_of_sale/js/Popups/AbstractAwaitablePopup"; import Registries from "@point_of_sale/js/Registries"; -import { identifyError } from "@point_of_sale/js/utils"; -import { ConnectionLostError, ConnectionAbortedError } from "@web/core/network/rpc_service"; +import { identifyError } from "@point_of_sale/app/error_handlers/error_handlers"; +import { ConnectionLostError } from "@web/core/network/rpc_service"; const { useState } = owl; class ClosePosPopup extends AbstractAwaitablePopup { @@ -152,16 +152,14 @@ class ClosePosPopup extends AbstractAwaitablePopup { } window.location = "/web#action=point_of_sale.action_client_pos_menu"; } catch (error) { - const iError = identifyError(error); - if ( - iError instanceof ConnectionLostError || - iError instanceof ConnectionAbortedError - ) { - await this.showPopup("ErrorPopup", { - title: this.env._t("Network Error"), - body: this.env._t("Cannot close the session when offline."), - }); + if (identifyError(error) instanceof ConnectionLostError) { + // Cannot redirect to backend when offline, let error handlers show the offline popup + // FIXME POSREF: doing this means closing again when online will redo the beginning of the method + // although it's impossible to close again because this.closeSessionClicked isn't reset to false + // The application state is corrupted. + throw error; } else { + // FIXME POSREF: why are we catching errors here but not anywhere else in this method? await this.showPopup("ErrorPopup", { title: this.env._t("Closing session error"), body: this.env._t( diff --git a/addons/point_of_sale/static/src/js/Screens/PartnerListScreen/PartnerListScreen.js b/addons/point_of_sale/static/src/js/Screens/PartnerListScreen/PartnerListScreen.js index fc4a87ac6f2..2aa163a961e 100644 --- a/addons/point_of_sale/static/src/js/Screens/PartnerListScreen/PartnerListScreen.js +++ b/addons/point_of_sale/static/src/js/Screens/PartnerListScreen/PartnerListScreen.js @@ -2,7 +2,6 @@ import PosComponent from "@point_of_sale/js/PosComponent"; import Registries from "@point_of_sale/js/Registries"; -import { isConnectionError } from "@point_of_sale/js/utils"; import { debounce } from "@web/core/utils/timing"; import { useListener } from "@web/core/utils/hooks"; @@ -162,25 +161,14 @@ class PartnerListScreen extends PosComponent { this.activateEditMode(); } async saveChanges(event) { - try { - const partnerId = await this.rpc({ - model: "res.partner", - method: "create_from_ui", - args: [event.detail.processedChanges], - }); - await this.env.pos.load_new_partners(); - this.state.selectedPartner = this.env.pos.db.get_partner_by_id(partnerId); - this.confirm(); - } catch (error) { - if (isConnectionError(error)) { - await this.showPopup("OfflineErrorPopup", { - title: this.env._t("Offline"), - body: this.env._t("Unable to save changes."), - }); - } else { - throw error; - } - } + const partnerId = await this.rpc({ + model: "res.partner", + method: "create_from_ui", + args: [event.detail.processedChanges], + }); + await this.env.pos.load_new_partners(); + this.state.selectedPartner = this.env.pos.db.get_partner_by_id(partnerId); + this.confirm(); } async searchPartner() { const result = await this.getNewPartners(); 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 01e28665cb9..55115092265 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 @@ -6,7 +6,6 @@ import { useErrorHandlers } from "@point_of_sale/js/custom_hooks"; import NumberBuffer from "@point_of_sale/js/Misc/NumberBuffer"; import { useListener } from "@web/core/utils/hooks"; import Registries from "@point_of_sale/js/Registries"; -import { isConnectionError } from "@point_of_sale/js/utils"; import utils from "web.utils"; class PaymentScreen extends PosComponent { @@ -257,15 +256,7 @@ class PaymentScreen extends PosComponent { // introduce to handle invoicing error logic. await this._handlePushOrderError(error); } else { - // We don't block for connection error. But we rethrow for any other errors. - if (isConnectionError(error)) { - this.showPopup("OfflineErrorPopup", { - title: this.env._t("Connection Error"), - body: this.env._t("Order is not synced. Check your internet connection"), - }); - } else { - throw error; - } + throw error; } } finally { // Always show the next screen regardless of error since pos has to 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 38cafb7ada2..4c8440ca6fc 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 @@ -4,8 +4,6 @@ import PosComponent from "@point_of_sale/js/PosComponent"; import ProductScreen from "@point_of_sale/js/Screens/ProductScreen/ProductScreen"; import { useListener } from "@web/core/utils/hooks"; import Registries from "@point_of_sale/js/Registries"; -import { ConnectionLostError, ConnectionAbortedError } from "@web/core/network/rpc_service"; -import { identifyError } from "@point_of_sale/js/utils"; class ProductInfoButton extends PosComponent { setup() { @@ -17,24 +15,8 @@ class ProductInfoButton extends PosComponent { if (orderline) { const product = orderline.get_product(); const quantity = orderline.get_quantity(); - try { - const info = await this.env.pos.getProductInfo(product, quantity); - this.showPopup("ProductInfoPopup", { info: info, product: product }); - } catch (e) { - if (identifyError(e) instanceof ConnectionLostError || ConnectionAbortedError) { - this.showPopup("OfflineErrorPopup", { - title: this.env._t("Network Error"), - body: this.env._t("Cannot access product information screen if offline."), - }); - } else { - this.showPopup("ErrorPopup", { - title: this.env._t("Unknown error"), - body: this.env._t( - "An unknown error prevents us from loading product information." - ), - }); - } - } + const info = await this.env.pos.getProductInfo(product, quantity); + this.showPopup("ProductInfoPopup", { info: info, product: product }); } } } 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 57fab179bb0..d9f1ebe879b 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 @@ -6,7 +6,6 @@ import NumberBuffer from "@point_of_sale/js/Misc/NumberBuffer"; import { useListener } from "@web/core/utils/hooks"; import Registries from "@point_of_sale/js/Registries"; import { useBarcodeReader } from "@point_of_sale/js/custom_hooks"; -import { isConnectionError } from "@point_of_sale/js/utils"; import { parse } from "web.field_utils"; const { onMounted, useState } = owl; @@ -233,25 +232,12 @@ class ProductScreen extends ControlButtonsMixin(PosComponent) { if (!product) { // find the barcode in the backend let foundProductIds = []; - try { - foundProductIds = await this.rpc({ - model: "product.product", - method: "search", - args: [[["barcode", "=", code.base_code]]], - context: this.env.session.user_context, - }); - } catch (error) { - if (isConnectionError(error)) { - return this.showPopup("OfflineErrorPopup", { - title: this.env._t("Network Error"), - body: this.env._t( - "Product is not loaded. Tried loading the product from the server but there is a network error." - ), - }); - } else { - throw error; - } - } + foundProductIds = await this.rpc({ + model: "product.product", + method: "search", + args: [[["barcode", "=", code.base_code]]], + context: this.env.session.user_context, + }); if (foundProductIds.length) { await this.env.pos._addProducts(foundProductIds); // assume that the result is unique. 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 b8252852c0b..f51b26ad56a 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 @@ -1,7 +1,5 @@ /** @odoo-module */ -import { identifyError } from "@point_of_sale/js/utils"; -import { ConnectionLostError, ConnectionAbortedError } from "@web/core/network/rpc_service"; import PosComponent from "@point_of_sale/js/PosComponent"; import Registries from "@point_of_sale/js/Registries"; import { debounce } from "@web/core/utils/timing"; @@ -75,47 +73,30 @@ class ProductsWidgetControlPanel extends PosComponent { return; } - try { - const ProductIds = await this.rpc({ - model: "product.product", - method: "search", - args: [ - [ - "&", - ["available_in_pos", "=", true], - "|", - "|", - ["name", "ilike", this.state.searchInput], - ["default_code", "ilike", this.state.searchInput], - ["barcode", "ilike", this.state.searchInput], - ], + const ProductIds = await this.rpc({ + model: "product.product", + method: "search", + args: [ + [ + "&", + ["available_in_pos", "=", true], + "|", + "|", + ["name", "ilike", this.state.searchInput], + ["default_code", "ilike", this.state.searchInput], + ["barcode", "ilike", this.state.searchInput], ], - context: this.env.session.user_context, - }); - if (ProductIds.length) { - if (!this.env.pos.isEveryProductLoaded) { - await this.env.pos.updateIsEveryProductLoaded(); - } - await this.env.pos._addProducts(ProductIds, false); - } - this.trigger("update-product-list"); - return ProductIds; - } catch (error) { - const identifiedError = identifyError(error); - if ( - identifiedError instanceof ConnectionLostError || - identifiedError instanceof ConnectionAbortedError - ) { - return this.showPopup("OfflineErrorPopup", { - title: this.env._t("Network Error"), - body: this.env._t( - "Product is not loaded. Tried loading the product from the server but there is a network error." - ), - }); - } else { - throw error; + ], + context: this.env.session.user_context, + }); + if (ProductIds.length) { + if (!this.env.pos.isEveryProductLoaded) { + await this.env.pos.updateIsEveryProductLoaded(); } + await this.env.pos._addProducts(ProductIds, false); } + this.trigger("update-product-list"); + return ProductIds; } } ProductsWidgetControlPanel.template = "ProductsWidgetControlPanel"; 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 fd6c8005e6b..0fa138f85a0 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,7 +1,6 @@ /** @odoo-module */ import { useListener } from "@web/core/utils/hooks"; -import { isConnectionError } from "@point_of_sale/js/utils"; import PosComponent from "@point_of_sale/js/PosComponent"; import Registries from "@point_of_sale/js/Registries"; @@ -111,15 +110,6 @@ class InvoiceButton extends PosComponent { try { this.el.style.pointerEvents = "none"; await this._invoiceOrder(); - } catch (error) { - if (isConnectionError(error)) { - this.showPopup("ErrorPopup", { - title: this.env._t("Network Error"), - body: this.env._t("Unable to invoice order."), - }); - } else { - throw error; - } } finally { this.el.style.pointerEvents = "auto"; } diff --git a/addons/point_of_sale/static/src/js/utils.js b/addons/point_of_sale/static/src/js/utils.js index afefdc5e6bb..1d5cb197758 100644 --- a/addons/point_of_sale/static/src/js/utils.js +++ b/addons/point_of_sale/static/src/js/utils.js @@ -1,6 +1,4 @@ /** @odoo-module */ -import { ConnectionAbortedError, ConnectionLostError } from "@web/core/network/rpc_service"; - export function getFileAsText(file) { return new Promise((resolve, reject) => { if (!file) { @@ -35,32 +33,6 @@ export const nextFrame = () => { }); }; -export function isConnectionError(error) { - const _error = identifyError(error); - return _error instanceof ConnectionAbortedError || _error instanceof ConnectionLostError; -} - -export function identifyError(error) { - if (!error) { - return error; - } - let errorToHandle; - if (error.legacy) { - // error.message is either RPCError or ConnectionLostError - errorToHandle = error.message; - } else if (error.event && error.event.type == "abort") { - // Check if there is event and if the event type is abort. - // If so, then it's supposed to be a ConnectionAbortedError, - // however, it was stripped in the patch of rpc in `mapLegacyEnvToWowlEnv`. - // We recreate the error object here so that in the actual handler, - // ConnectionAbortedError and ConnectionLostError are handled properly. - errorToHandle = new ConnectionAbortedError(error.message); - } else if (error instanceof Error) { - errorToHandle = error; - } - return errorToHandle || error; -} - /** * Creates a batched version of a callback so that all calls to it in the same * microtick will only call the original callback once. diff --git a/addons/pos_restaurant/static/src/js/ChromeWidgets/TicketButton.js b/addons/pos_restaurant/static/src/js/ChromeWidgets/TicketButton.js index 025acf4e915..3a0d6df6dad 100644 --- a/addons/pos_restaurant/static/src/js/ChromeWidgets/TicketButton.js +++ b/addons/pos_restaurant/static/src/js/ChromeWidgets/TicketButton.js @@ -2,7 +2,6 @@ import TicketButton from "@point_of_sale/js/ChromeWidgets/TicketButton"; import Registries from "@point_of_sale/js/Registries"; -import { isConnectionError } from "@point_of_sale/js/utils"; const PosResTicketButton = (TicketButton) => class extends TicketButton { @@ -15,20 +14,6 @@ const PosResTicketButton = (TicketButton) => try { this.env.pos.setLoadingOrderState(true); await this.env.pos._syncAllOrdersFromServer(); - } catch (error) { - if (isConnectionError(error)) { - await this.showPopup("OfflineErrorPopup", { - title: this.env._t("Offline"), - body: this.env._t( - "Due to a connection error, the orders are not synchronized." - ), - }); - } else { - this.showPopup("ErrorPopup", { - title: this.env._t("Unknown error"), - body: error.message, - }); - } } finally { this.env.pos.setLoadingOrderState(false); this.showScreen("TicketScreen"); diff --git a/addons/pos_restaurant/static/src/js/Screens/FloorScreen/FloorScreen.js b/addons/pos_restaurant/static/src/js/Screens/FloorScreen/FloorScreen.js index c45c6d086ca..2ff1a6d2c0d 100644 --- a/addons/pos_restaurant/static/src/js/Screens/FloorScreen/FloorScreen.js +++ b/addons/pos_restaurant/static/src/js/Screens/FloorScreen/FloorScreen.js @@ -3,7 +3,6 @@ import PosComponent from "@point_of_sale/js/PosComponent"; import Registries from "@point_of_sale/js/Registries"; import { debounce } from "@web/core/utils/timing"; -import { isConnectionError } from "@point_of_sale/js/utils"; const { onPatched, onMounted, onWillUnmount, useRef, useState } = owl; @@ -87,21 +86,9 @@ class FloorScreen extends PosComponent { delete newTable.id; newTable.floor_id = [this.activeFloor.id, ""]; newTable.floor = this.activeFloor; - try { - await this._save(newTable); - this.activeTables.push(newTable); - return newTable; - } catch (error) { - if (isConnectionError(error)) { - await this.showPopup("ErrorPopup", { - title: this.env._t("Offline"), - body: this.env._t("Unable to create table because you are offline."), - }); - return; - } else { - throw error; - } - } + await this._save(newTable); + this.activeTables.push(newTable); + return newTable; } _getNewTableName(name) { if (name) { @@ -132,33 +119,22 @@ class FloorScreen extends PosComponent { if (this.state.isEditMode) { return; } - try { - const result = await this.rpc({ - model: "pos.config", - method: "get_tables_order_count", - args: [this.env.pos.config.id], - }); - result.forEach((table) => { - const table_obj = this.env.pos.tables_by_id[table.id]; - const unsynced_orders = this.env.pos.getTableOrders(table_obj.id).filter( - (o) => - o.server_id === undefined && - (o.orderlines.length !== 0 || o.paymentlines.length !== 0) && - // do not count the orders that are already finalized - !o.finalized - ).length; - table_obj.order_count = table.orders + unsynced_orders; - }); - } catch (error) { - if (isConnectionError(error)) { - await this.showPopup("OfflineErrorPopup", { - title: this.env._t("Offline"), - body: this.env._t("Unable to get orders count"), - }); - } else { - throw error; - } - } + const result = await this.rpc({ + model: "pos.config", + method: "get_tables_order_count", + args: [this.env.pos.config.id], + }); + result.forEach((table) => { + const table_obj = this.env.pos.tables_by_id[table.id]; + const unsynced_orders = this.env.pos.getTableOrders(table_obj.id).filter( + (o) => + o.server_id === undefined && + (o.orderlines.length !== 0 || o.paymentlines.length !== 0) && + // do not count the orders that are already finalized + !o.finalized + ).length; + table_obj.order_count = table.orders + unsynced_orders; + }); } get activeFloor() { return this.env.pos.floors_by_id[this.state.selectedFloorId]; @@ -211,21 +187,10 @@ class FloorScreen extends PosComponent { if (this.state.isEditMode) { this.state.selectedTableId = table.id; } else { - try { - if (this.env.pos.orderToTransfer) { - await this.env.pos.transferTable(table); - } else { - await this.env.pos.setTable(table); - } - } catch (error) { - if (isConnectionError(error)) { - await this.showPopup("OfflineErrorPopup", { - title: this.env._t("Offline"), - body: this.env._t("Unable to fetch orders"), - }); - } else { - throw error; - } + if (this.env.pos.orderToTransfer) { + await this.env.pos.transferTable(table); + } else { + await this.env.pos.setTable(table); } const order = this.env.pos.get_order(); this.showScreen(order.get_screen_data().name); @@ -302,22 +267,11 @@ class FloorScreen extends PosComponent { async setFloorColor(color) { this.state.floorBackground = color; this.activeFloor.background_color = color; - try { - await this.rpc({ - model: "restaurant.floor", - method: "write", - args: [[this.activeFloor.id], { background_color: color }], - }); - } catch (error) { - if (isConnectionError(error)) { - await this.showPopup("OfflineErrorPopup", { - title: this.env._t("Offline"), - body: this.env._t("Unable to change background color"), - }); - } else { - throw error; - } - } + await this.rpc({ + model: "restaurant.floor", + method: "write", + args: [[this.activeFloor.id], { background_color: color }], + }); } async deleteTable() { if (!this.selectedTable) { @@ -330,37 +284,26 @@ class FloorScreen extends PosComponent { if (!confirmed) { return; } - try { - const originalSelectedTableId = this.state.selectedTableId; - await this.rpc({ - model: "restaurant.table", - method: "create_from_ui", - args: [{ active: false, id: originalSelectedTableId }], - }); - this.activeFloor.tables = this.activeTables.filter( - (table) => table.id !== originalSelectedTableId - ); - // Value of an object can change inside async function call. - // Which means that in this code block, the value of `state.selectedTableId` - // before the await call can be different after the finishing the await call. - // Since we wanted to disable the selected table after deletion, we should be - // setting the selectedTableId to null. However, we only do this if nothing - // else is selected during the rpc call. - if (this.state.selectedTableId === originalSelectedTableId) { - this.state.selectedTableId = null; - } - delete this.env.pos.tables_by_id[originalSelectedTableId]; - this.env.pos.TICKET_SCREEN_STATE.syncedOrders.cache = {}; - } catch (error) { - if (isConnectionError(error)) { - await this.showPopup("OfflineErrorPopup", { - title: this.env._t("Offline"), - body: this.env._t("Unable to delete table"), - }); - } else { - throw error; - } + const originalSelectedTableId = this.state.selectedTableId; + await this.rpc({ + model: "restaurant.table", + method: "create_from_ui", + args: [{ active: false, id: originalSelectedTableId }], + }); + this.activeFloor.tables = this.activeTables.filter( + (table) => table.id !== originalSelectedTableId + ); + // Value of an object can change inside async function call. + // Which means that in this code block, the value of `state.selectedTableId` + // before the await call can be different after the finishing the await call. + // Since we wanted to disable the selected table after deletion, we should be + // setting the selectedTableId to null. However, we only do this if nothing + // else is selected during the rpc call. + if (this.state.selectedTableId === originalSelectedTableId) { + this.state.selectedTableId = null; } + delete this.env.pos.tables_by_id[originalSelectedTableId]; + this.env.pos.TICKET_SCREEN_STATE.syncedOrders.cache = {}; } } FloorScreen.template = "FloorScreen"; diff --git a/addons/pos_sale/static/src/js/OrderManagementScreen/SaleOrderFetcher.js b/addons/pos_sale/static/src/js/OrderManagementScreen/SaleOrderFetcher.js index 67fc3c55d43..3a9351b0c57 100644 --- a/addons/pos_sale/static/src/js/OrderManagementScreen/SaleOrderFetcher.js +++ b/addons/pos_sale/static/src/js/OrderManagementScreen/SaleOrderFetcher.js @@ -1,7 +1,6 @@ /** @odoo-module */ import { Gui } from "@point_of_sale/js/Gui"; -import { isConnectionError } from "@point_of_sale/js/utils"; const { EventBus } = owl; @@ -38,24 +37,12 @@ class SaleOrderFetcher extends EventBus { * to the active orders that fits the current page. */ async fetch() { - try { - // Show orders from the backend. - const offset = this.nPerPage + (this.currentPage - 1 - 1) * this.nPerPage; - const limit = this.nPerPage; - this.ordersToShow = await this._fetch(limit, offset); + // Show orders from the backend. + const offset = this.nPerPage + (this.currentPage - 1 - 1) * this.nPerPage; + const limit = this.nPerPage; + this.ordersToShow = await this._fetch(limit, offset); - this.trigger("update"); - } catch (error) { - if (isConnectionError(error)) { - Gui.showPopup("ErrorPopup", { - title: this.comp.env._t("Network Error"), - body: this.comp.env._t("Unable to fetch orders if offline."), - }); - Gui.setSyncStatus("error"); - } else { - throw error; - } - } + this.trigger("update"); } /** * This returns the orders from the backend that needs to be shown. diff --git a/addons/pos_sale/static/src/js/SetSaleOrderButton.js b/addons/pos_sale/static/src/js/SetSaleOrderButton.js index 6c17d1abb49..bac969024af 100644 --- a/addons/pos_sale/static/src/js/SetSaleOrderButton.js +++ b/addons/pos_sale/static/src/js/SetSaleOrderButton.js @@ -4,7 +4,6 @@ import PosComponent from "@point_of_sale/js/PosComponent"; import ProductScreen from "@point_of_sale/js/Screens/ProductScreen/ProductScreen"; import { useListener } from "@web/core/utils/hooks"; import Registries from "@point_of_sale/js/Registries"; -import { isConnectionError } from "@point_of_sale/js/utils"; import { Gui } from "@point_of_sale/js/Gui"; class SetSaleOrderButton extends PosComponent { @@ -16,33 +15,22 @@ class SetSaleOrderButton extends PosComponent { return this.env.pos.get_order(); } async onClick() { - try { - // ping the server, if no error, show the screen - // Use rpc from services which resolves even when this - // component is destroyed (removed together with the popup). - await this.env.services.rpc({ - model: "sale.order", - method: "browse", - args: [[]], - kwargs: { context: this.env.session.user_context }, - }); - // LegacyComponent doesn't work the same way as before. - // We need to use Gui here to show the screen. This will work - // because ui methods in Gui is bound to the root component. - const screen = this.env.isMobile - ? "MobileSaleOrderManagementScreen" - : "SaleOrderManagementScreen"; - Gui.showScreen(screen); - } catch (error) { - if (isConnectionError(error)) { - this.showPopup("ErrorPopup", { - title: this.env._t("Network Error"), - body: this.env._t("Cannot access order management screen if offline."), - }); - } else { - throw error; - } - } + // ping the server, if no error, show the screen + // Use rpc from services which resolves even when this + // component is destroyed (removed together with the popup). + await this.env.services.rpc({ + model: "sale.order", + method: "browse", + args: [[]], + kwargs: { context: this.env.session.user_context }, + }); + // LegacyComponent doesn't work the same way as before. + // We need to use Gui here to show the screen. This will work + // because ui methods in Gui is bound to the root component. + const screen = this.env.isMobile + ? "MobileSaleOrderManagementScreen" + : "SaleOrderManagementScreen"; + Gui.showScreen(screen); } } SetSaleOrderButton.template = "SetSaleOrderButton"; diff --git a/addons/pos_sale_product_configurator/static/src/js/models.js b/addons/pos_sale_product_configurator/static/src/js/models.js index 757a58c73a0..f1ba2094bf9 100644 --- a/addons/pos_sale_product_configurator/static/src/js/models.js +++ b/addons/pos_sale_product_configurator/static/src/js/models.js @@ -3,8 +3,6 @@ import { Gui } from "@point_of_sale/js/Gui"; import { Order } from "@point_of_sale/js/models"; import Registries from "@point_of_sale/js/Registries"; -import { ConnectionLostError, ConnectionAbortedError } from "@web/core/network/rpc_service"; -import { identifyError } from "@point_of_sale/js/utils"; const PosSaleProductConfiguratorOrder = (Order) => class PosSaleProductConfiguratorOrder extends Order { @@ -20,30 +18,9 @@ const PosSaleProductConfiguratorOrder = (Order) => args: [[product.id]], }); if (isProductLoaded) { - try { - const quantity = this.get_selected_orderline().get_quantity(); - const info = await this.pos.getProductInfo(product, quantity); - Gui.showPopup("ProductInfoPopup", { info: info, product: product }); - } catch (e) { - if ( - identifyError(e) instanceof ConnectionLostError || - ConnectionAbortedError - ) { - Gui.showPopup("OfflineErrorPopup", { - title: this.env._t("Network Error"), - body: this.env._t( - "Cannot access product information screen if offline." - ), - }); - } else { - Gui.showPopup("ErrorPopup", { - title: this.env._t("Unknown error"), - body: this.env._t( - "An unknown error prevents us from loading product information." - ), - }); - } - } + const quantity = this.get_selected_orderline().get_quantity(); + const info = await this.pos.getProductInfo(product, quantity); + Gui.showPopup("ProductInfoPopup", { info: info, product: product }); } } }