From 73b9d48703873b45b6bb9b88d563cad1bfbd61bb Mon Sep 17 00:00:00 2001 From: Joseph Caburnay Date: Mon, 20 Mar 2023 15:44:16 +0000 Subject: [PATCH] [REF] point_of_sale,pos_*: convert barcode_reader to service **Notable feature changes** - PoS app continues to load even if no nomenclature_id is configured. - When scanning, an error popup is shown to the user mentioning about the misconfiguration. - We introduce a default error handler that shows the ErrorBarcodePopup when there are no registered barcode handlers via the `useBarcodeReader` hook. closes odoo/odoo#115906 Related: odoo/enterprise#38444 Signed-off-by: Samuel Degueldre --- addons/point_of_sale/controllers/main.py | 1 + .../static/src/app/barcode_reader_hook.js | 18 ++ .../static/src/app/barcode_reader_service.js | 142 +++++++++++++++ .../static/src/app/debug/debug_widget.js | 15 +- .../static/src/app/debug/debug_widget.xml | 2 +- .../point_of_sale/static/src/app/pos_store.js | 11 +- addons/point_of_sale/static/src/js/Chrome.js | 21 +-- .../js/Screens/ProductScreen/ProductScreen.js | 17 +- .../static/src/js/barcode_reader.js | 165 ------------------ .../static/src/js/custom_hooks.js | 32 +--- addons/point_of_sale/static/src/js/pos_env.js | 2 - .../static/src/js/SelectCashierMixin.js | 2 +- .../static/src/js/ProductScreen.js | 2 +- .../static/src/js/PaymentScreen.js | 2 +- .../static/src/js/ProductScreen.js | 2 +- 15 files changed, 185 insertions(+), 249 deletions(-) create mode 100644 addons/point_of_sale/static/src/app/barcode_reader_hook.js create mode 100644 addons/point_of_sale/static/src/app/barcode_reader_service.js delete mode 100644 addons/point_of_sale/static/src/js/barcode_reader.js diff --git a/addons/point_of_sale/controllers/main.py b/addons/point_of_sale/controllers/main.py index 5e753b831a7..1ffee22f674 100644 --- a/addons/point_of_sale/controllers/main.py +++ b/addons/point_of_sale/controllers/main.py @@ -55,6 +55,7 @@ class PosController(PortalAccount): session_info = request.env['ir.http'].session_info() session_info['user_context']['allowed_company_ids'] = company.ids session_info['user_companies'] = {'current_company': company.id, 'allowed_companies': {company.id: session_info['user_companies']['allowed_companies'][company.id]}} + session_info['nomenclature_id'] = pos_session.company_id.nomenclature_id.id context = { 'session_info': session_info, 'login_number': pos_session.login(), diff --git a/addons/point_of_sale/static/src/app/barcode_reader_hook.js b/addons/point_of_sale/static/src/app/barcode_reader_hook.js new file mode 100644 index 00000000000..ebab8d653c7 --- /dev/null +++ b/addons/point_of_sale/static/src/app/barcode_reader_hook.js @@ -0,0 +1,18 @@ +/** @odoo-module */ + +import { useService } from "@web/core/utils/hooks"; +import { useComponent, useEffect } from "@odoo/owl"; + +export function useBarcodeReader(callbackMap, exclusive = false) { + const current = useComponent(); + const barcodeReader = useService("barcode_reader"); + if (barcodeReader) { + for (const [key, callback] of Object.entries(callbackMap)) { + callbackMap[key] = callback.bind(current); + } + useEffect( + () => barcodeReader.register(callbackMap, exclusive), + () => [] + ); + } +} diff --git a/addons/point_of_sale/static/src/app/barcode_reader_service.js b/addons/point_of_sale/static/src/app/barcode_reader_service.js new file mode 100644 index 00000000000..599922f80b9 --- /dev/null +++ b/addons/point_of_sale/static/src/app/barcode_reader_service.js @@ -0,0 +1,142 @@ +/** @odoo-module */ + +import { registry } from "@web/core/registry"; +import { Mutex } from "@web/core/utils/concurrency"; +import { session } from "@web/session"; +import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup"; +import { ErrorBarcodePopup } from "@point_of_sale/js/Popups/ErrorBarcodePopup"; +import BarcodeParser from "barcodes.BarcodeParser"; + +export class BarcodeReader { + constructor({ parser, popup }) { + this.parser = parser; + this.popup = popup; + this.setup(); + } + + setup() { + this.mutex = new Mutex(); + this.cbMaps = new Set(); + // FIXME POSREF: When LoginScreen becomes a normal screen, we can remove this exclusive callback handling. + this.exclusiveCbMap = null; + this.remoteScanning = false; + this.remoteActive = 0; + } + + register(cbMap, exclusive) { + if (exclusive) { + this.exclusiveCbMap = cbMap; + } else { + this.cbMaps.add(cbMap); + } + return () => { + if (exclusive) { + this.exclusiveCbMap = null; + } else { + this.cbMaps.delete(cbMap); + } + }; + } + + scan(code) { + return this.mutex.exec(() => this._scan(code)); + } + + async _scan(code) { + if (!code) { + return; + } + + const cbMaps = this.exclusiveCbMap ? [this.exclusiveCbMap] : [...this.cbMaps]; + + let parsedResult = this.parser.parse_barcode(code); + if (!Array.isArray(parsedResult)) { + parsedResult = [parsedResult]; + } + + for (const parseBarcode of parsedResult) { + const cbs = cbMaps.map((cbMap) => cbMap[parseBarcode.type]).filter(Boolean); + if (cbs.length === 0) { + this.popup.add(ErrorBarcodePopup, { code: this.codeRepr(parseBarcode) }); + } + for (const cb of cbs) { + await cb(parseBarcode); + } + } + } + + codeRepr(parsedBarcode) { + if (parsedBarcode.code.length > 32) { + return parsedBarcode.code.substring(0, 29) + "..."; + } else { + return parsedBarcode.code; + } + } + + // the barcode scanner will listen on the hw_proxy/scanner interface for + // scan events until disconnectFromProxy is called + connectToProxy(hwProxy) { + this.remoteScanning = true; + if (this.remoteActive >= 1) { + return; + } + this.remoteActive = 1; + this.waitForBarcode(hwProxy); + } + + async waitForBarcode(hwProxy) { + try { + const barcode = await hwProxy.connection.rpc( + "/hw_proxy/scanner", + {}, + { shadow: true, timeout: 7500 } + ); + if (!this.remoteScanning) { + this.remoteActive = 0; + return; + } + this.scan(barcode); + } catch { + if (!this.remoteScanning) { + this.remoteActive = 0; + return; + } + } + this.waitForBarcode(); + } + + // the barcode scanner will stop listening on the hw_proxy/scanner remote interface + disconnectFromProxy() { + this.remoteScanning = false; + } +} + +export const barcodeReader = { + dependencies: ["barcode", "popup"], + async start(env, { barcode, popup }) { + let barcodeReader = null; + + if (session.nomenclature_id) { + const parser = new BarcodeParser({ nomenclature_id: [session.nomenclature_id] }); + await parser.is_loaded(); + barcodeReader = new BarcodeReader({ parser, popup }); + } + + barcode.bus.addEventListener("barcode_scanned", (ev) => { + if (barcodeReader) { + barcodeReader.scan(ev.detail.barcode); + } else { + popup.add(ErrorPopup, { + title: env._t("Unable to parse barcode"), + body: env._t( + "No barcode nomenclature has been configured. This can be changed in the configuration settings." + ), + }); + } + }); + + return barcodeReader; + }, +}; + +registry.category("services").add("barcode_reader", barcodeReader); diff --git a/addons/point_of_sale/static/src/app/debug/debug_widget.js b/addons/point_of_sale/static/src/app/debug/debug_widget.js index c2b3a0bfa32..ce6a092243b 100644 --- a/addons/point_of_sale/static/src/app/debug/debug_widget.js +++ b/addons/point_of_sale/static/src/app/debug/debug_widget.js @@ -17,6 +17,7 @@ export class DebugWidget extends Component { super.setup(); this.debug = useService("debug"); this.popup = useService("popup"); + this.barcodeReader = useService("barcode_reader"); const numberBuffer = useService("number_buffer"); useBus(numberBuffer, "buffer-update", this._onBufferUpdate); this.state = useState({ @@ -87,14 +88,18 @@ export class DebugWidget extends Component { this.env.proxy.debug_reset_weight(); } async barcodeScan() { - await this.env.barcode_reader.scan(this.state.barcodeInput); + if (!this.barcodeReader) { + return; + } + await this.barcodeReader.scan(this.state.barcodeInput); } async barcodeScanEAN() { - const ean = this.env.barcode_reader.barcode_parser.sanitize_ean( - this.state.barcodeInput || "0" - ); + if (!this.barcodeReader) { + return; + } + const ean = this.barcodeReader.parser.sanitize_ean(this.state.barcodeInput || "0"); this.state.barcodeInput = ean; - await this.env.barcode_reader.scan(ean); + await this.barcodeReader.scan(ean); } async deleteOrders() { const { confirmed } = await this.popup.add(ConfirmPopup, { diff --git a/addons/point_of_sale/static/src/app/debug/debug_widget.xml b/addons/point_of_sale/static/src/app/debug/debug_widget.xml index 8962ce39e34..b029d5398d2 100644 --- a/addons/point_of_sale/static/src/app/debug/debug_widget.xml +++ b/addons/point_of_sale/static/src/app/debug/debug_widget.xml @@ -22,7 +22,7 @@

Barcode Scanner