From 8d5000b837043081a384450dc1b4f258d2f1ebe2 Mon Sep 17 00:00:00 2001
From: pedrambiria <pebr@odoo.com>
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#122257

Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
---
 .../static/src/js/barcode_parser.js           |  5 +-
 .../js/Screens/ProductScreen/ProductScreen.js | 72 ++++++++++++++-----
 .../static/src/js/barcode_reader.js           |  9 ++-
 3 files changed, 62 insertions(+), 24 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 f7bbf51fb46f..ed0fb498dd05 100644
--- a/addons/barcodes_gs1_nomenclature/static/src/js/barcode_parser.js
+++ b/addons/barcodes_gs1_nomenclature/static/src/js/barcode_parser.js
@@ -46,7 +46,10 @@ BarcodeParser.include({
         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/js/Screens/ProductScreen/ProductScreen.js b/addons/point_of_sale/static/src/js/Screens/ProductScreen/ProductScreen.js
index 473de510dd03..672b16b07184 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
@@ -29,6 +29,7 @@ odoo.define('point_of_sale.ProductScreen', function(require) {
                 client: this._barcodePartnerAction,
                 discount: this._barcodeDiscountAction,
                 error: this._barcodeErrorAction,
+                gs1: this._barcodeGS1Action,
             });
             NumberBuffer.use({
                 nonKeyboardInputEvent: 'numpad-click-input',
@@ -61,7 +62,7 @@ odoo.define('point_of_sale.ProductScreen', function(require) {
         get currentOrder() {
             return this.env.pos.get_order();
         }
-        async _getAddProductOptions(product, base_code) {
+        async _getAddProductOptions(product, code) {
             let price_extra = 0.0;
             let draftPackLotLines, weight, description, packLotLinesToEdit;
 
@@ -97,24 +98,36 @@ odoo.define('point_of_sale.ProductScreen', function(require) {
                         packLotLinesToEdit = [];
                     }
                 }
-                const { confirmed, payload } = await this.showPopup('EditListPopup', {
-                    title: this.env._t('Lot/Serial Number(s) Required'),
-                    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.showPopup('EditListPopup', {
+                        title: this.env._t('Lot/Serial Number(s) Required'),
+                        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;
+                    }
                 }
             }
 
@@ -136,8 +149,8 @@ odoo.define('point_of_sale.ProductScreen', function(require) {
                 }
             }
 
-            if (base_code && this.env.pos.db.product_packaging_by_barcode[base_code.code]) {
-                weight = this.env.pos.db.product_packaging_by_barcode[base_code.code].qty;
+            if (code && this.env.pos.db.product_packaging_by_barcode[code.code]) {
+                weight = this.env.pos.db.product_packaging_by_barcode[code.code].qty;
             }
 
             return { draftPackLotLines, quantity: weight, description, price_extra };
@@ -214,7 +227,7 @@ odoo.define('point_of_sale.ProductScreen', function(require) {
                 }
             }
         }
-        async _barcodeProductAction(code) {
+        async _getProductByBarcode(code) {
             let product = this.env.pos.db.get_product_by_barcode(code.base_code);
             if (!product) {
                 // find the barcode in the backend
@@ -244,6 +257,13 @@ odoo.define('point_of_sale.ProductScreen', function(require) {
                     return this._barcodeErrorAction(code);
                 }
             }
+            return product
+        }
+        async _barcodeProductAction(code) {
+            const product = await this._getProductByBarcode(code);
+            if (!product) {
+                return;
+            }
             const options = await this._getAddProductOptions(product, code);
             // Do not proceed on adding the product when no options is returned.
             // This is consistent with _clickProduct.
@@ -289,6 +309,22 @@ odoo.define('point_of_sale.ProductScreen', function(require) {
                 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 this._getAddProductOptions(product, lotBarcode);
+            await this.currentOrder.add_product(product, options);
+            NumberBuffer.reset();
+        }
         // IMPROVEMENT: The following two methods should be in PosScreenComponent?
         // Why? Because once we start declaring barcode actions in different
         // screens, these methods will also be declared over and over.
diff --git a/addons/point_of_sale/static/src/js/barcode_reader.js b/addons/point_of_sale/static/src/js/barcode_reader.js
index 0a9c4b0f9083..6e2ec177f1c4 100644
--- a/addons/point_of_sale/static/src/js/barcode_reader.js
+++ b/addons/point_of_sale/static/src/js/barcode_reader.js
@@ -110,11 +110,10 @@ var BarcodeReader = core.Class.extend({
         const callbacks = Object.keys(this.exclusive_callbacks).length
             ? this.exclusive_callbacks
             : this.action_callbacks;
-        let parsed_results = this.barcode_parser.parse_barcode(code);
-        if (! Array.isArray(parsed_results)) {
-            parsed_results = [parsed_results];
-        }
-        for (const parsed_result of parsed_results) {
+        let parsed_result = this.barcode_parser.parse_barcode(code);
+        if (Array.isArray(parsed_result)) {
+            [...callbacks.gs1].map(cb => cb(parsed_result));
+        } else {
             if (callbacks[parsed_result.type]) {
                 for (const cb of callbacks[parsed_result.type]) {
                     await cb(parsed_result);
-- 
GitLab