From 27ae8f73ace76934df22d9f4e6ef5ea0abdb1503 Mon Sep 17 00:00:00 2001 From: pedrambiria Date: Fri, 21 Apr 2023 12:57:33 +0000 Subject: [PATCH] [FIX] point_of_sale: accept GS1 barcodes in PoS This update enables PoS to handle GS1 barcodes, which was previously not possible even though the GS1 nomenclature could be selected. GS1 barcodes can now be used to extract product identifiers, lot numbers, and serial numbers from the code. opw-3204299 closes odoo/odoo#125906 X-original-commit: cd2061c02b6c7001558178ade025a1fc4917bf9e Signed-off-by: Joseph Caburnay (jcb) Signed-off-by: Pedram Bi Ria (pebr) --- .../static/src/js/barcode_parser.js | 5 +- .../src/app/barcode/barcode_reader_service.js | 10 ++-- .../screens/product_screen/product_screen.js | 26 +++++++++- .../static/src/app/store/models.js | 49 ++++++++++++------- 4 files changed, 64 insertions(+), 26 deletions(-) diff --git a/addons/barcodes_gs1_nomenclature/static/src/js/barcode_parser.js b/addons/barcodes_gs1_nomenclature/static/src/js/barcode_parser.js index b727813e495..14bad1bebdc 100644 --- a/addons/barcodes_gs1_nomenclature/static/src/js/barcode_parser.js +++ b/addons/barcodes_gs1_nomenclature/static/src/js/barcode_parser.js @@ -61,7 +61,10 @@ patch(BarcodeParser.prototype, "barcodes_gs1_nomenclature.BarcodeParser", { const result = { rule: Object.assign({}, rule), ai: match[1], - string_value: match[2] + string_value: match[2], + code: match[2], + base_code: match[2], + type: rule.type }; if (rule.gs1_content_type === 'measure'){ let decimalPosition = 0; // Decimal position begin at the end, 0 means no decimal diff --git a/addons/point_of_sale/static/src/app/barcode/barcode_reader_service.js b/addons/point_of_sale/static/src/app/barcode/barcode_reader_service.js index ff01a4411fe..1a66c24c544 100644 --- a/addons/point_of_sale/static/src/app/barcode/barcode_reader_service.js +++ b/addons/point_of_sale/static/src/app/barcode/barcode_reader_service.js @@ -51,12 +51,10 @@ export class BarcodeReader { 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) { + let parseBarcode = this.parser.parse_barcode(code); + if (Array.isArray(parseBarcode)) { + cbMaps.map((cb) => cb.gs1?.(parseBarcode)); + } else { const cbs = cbMaps.map((cbMap) => cbMap[parseBarcode.type]).filter(Boolean); if (cbs.length === 0) { this.popup.add(ErrorBarcodePopup, { code: this.codeRepr(parseBarcode) }); diff --git a/addons/point_of_sale/static/src/app/screens/product_screen/product_screen.js b/addons/point_of_sale/static/src/app/screens/product_screen/product_screen.js index fd70c933378..4cd500acbaf 100644 --- a/addons/point_of_sale/static/src/app/screens/product_screen/product_screen.js +++ b/addons/point_of_sale/static/src/app/screens/product_screen/product_screen.js @@ -49,6 +49,7 @@ export class ProductScreen extends ControlButtonsMixin(Component) { price: this._barcodeProductAction, client: this._barcodePartnerAction, discount: this._barcodeDiscountAction, + gs1: this._barcodeGS1Action, }); // Call `resset` when the `onMounted` callback in `numberBuffer.use` is done. @@ -132,7 +133,7 @@ export class ProductScreen extends ControlButtonsMixin(Component) { } } } - async _barcodeProductAction(code) { + async _getProductByBarcode(code) { let product = this.pos.db.get_product_by_barcode(code.base_code); if (!product) { // find the barcode in the backend @@ -148,6 +149,13 @@ export class ProductScreen extends ControlButtonsMixin(Component) { return this.popup.add(ErrorBarcodePopup, { code: code.base_code }); } } + return product; + } + async _barcodeProductAction(code) { + const product = await this._getProductByBarcode(code); + if (!product) { + return; + } const options = await product.getAddProductOptions(code); // Do not proceed on adding the product when no options is returned. // This is consistent with clickProduct. @@ -193,6 +201,22 @@ export class ProductScreen extends ControlButtonsMixin(Component) { if (last_orderline) { last_orderline.set_discount(code.value); } + } + /** + * Add a product to the current order using the product identifier and lot number from parsed results. + * This function retrieves the product identifier and lot number from the `parsed_results` parameter. + * It then uses these values to retrieve the product and add it to the current order. + */ + async _barcodeGS1Action(parsed_results) { + const productBarcode = parsed_results.find(element => element.type === 'product'); + const lotBarcode = parsed_results.find(element => element.type === 'lot'); + const product = await this._getProductByBarcode(productBarcode); + if (!product) { + return; + } + const options = await product.getAddProductOptions(lotBarcode); + await this.currentOrder.add_product(product, options); + this.numberBuffer.reset(); } async displayAllControlPopup() { await this.popup.add(ControlButtonPopup, { diff --git a/addons/point_of_sale/static/src/app/store/models.js b/addons/point_of_sale/static/src/app/store/models.js index e0e696e7fe5..e36ac3cc3c5 100644 --- a/addons/point_of_sale/static/src/app/store/models.js +++ b/addons/point_of_sale/static/src/app/store/models.js @@ -112,7 +112,7 @@ export class Product extends PosModel { get isScaleAvailable() { return true; } - async getAddProductOptions(base_code) { + async getAddProductOptions(code) { let price_extra = 0.0; let draftPackLotLines, weight, description, packLotLinesToEdit; let quantity = 1; @@ -158,26 +158,39 @@ export class Product extends PosModel { packLotLinesToEdit = []; } } - const { confirmed, payload } = await this.pos.env.services.popup.add(EditListPopup, { - title: this.pos.env._t("Lot/Serial Number(s) Required"), - name: this.display_name, - isSingleItem: isAllowOnlyOneLot, - array: packLotLinesToEdit, - }); - if (confirmed) { - // Segregate the old and new packlot lines + // if the lot information exists in the barcode, we don't need to ask it from the user. + if (code && code.type === 'lot') { + // consider the old and new packlot lines const modifiedPackLotLines = Object.fromEntries( - payload.newArray.filter((item) => item.id).map((item) => [item.id, item.text]) + packLotLinesToEdit.filter(item => item.id).map(item => [item.id, item.text]) ); - const newPackLotLines = payload.newArray - .filter((item) => !item.id) - .map((item) => ({ lot_name: item.text })); - + const newPackLotLines = [ + { lot_name: code.code }, + ]; draftPackLotLines = { modifiedPackLotLines, newPackLotLines }; } else { - // We don't proceed on adding product. - return; + const { confirmed, payload } = await this.pos.env.services.popup.add(EditListPopup, { + title: this.pos.env._t("Lot/Serial Number(s) Required"), + name: this.display_name, + isSingleItem: isAllowOnlyOneLot, + array: packLotLinesToEdit, + }); + if (confirmed) { + // Segregate the old and new packlot lines + const modifiedPackLotLines = Object.fromEntries( + payload.newArray.filter((item) => item.id).map((item) => [item.id, item.text]) + ); + const newPackLotLines = payload.newArray + .filter((item) => !item.id) + .map((item) => ({ lot_name: item.text })); + + draftPackLotLines = { modifiedPackLotLines, newPackLotLines }; + } else { + // We don't proceed on adding product. + return; + } } + } // Take the weight if necessary. @@ -202,8 +215,8 @@ export class Product extends PosModel { } } - if (base_code && this.pos.db.product_packaging_by_barcode[base_code.code]) { - weight = this.pos.db.product_packaging_by_barcode[base_code.code].qty; + if (code && this.pos.db.product_packaging_by_barcode[code.code]) { + weight = this.pos.db.product_packaging_by_barcode[code.code].qty; } return { draftPackLotLines, quantity, weight, description, price_extra };