diff --git a/addons/barcodes/static/src/js/barcode_parser.js b/addons/barcodes/static/src/js/barcode_parser.js index 0b98785cd8b..e11b2fc7bcd 100644 --- a/addons/barcodes/static/src/js/barcode_parser.js +++ b/addons/barcodes/static/src/js/barcode_parser.js @@ -158,9 +158,9 @@ export class BarcodeParser { match.base_code = base_code.join(''); } - if (base_pattern[0] !== '^') { - base_pattern = "^" + base_pattern; - } + base_pattern = base_pattern.split('|') + .map(part => part.startsWith('^') ? part : '^' + part) + .join('|'); match.match = match.base_code.match(base_pattern); return match; diff --git a/addons/point_of_sale/static/tests/tours/BarcodeScanning.tour.js b/addons/point_of_sale/static/tests/tours/BarcodeScanning.tour.js new file mode 100644 index 00000000000..fbef6db6105 --- /dev/null +++ b/addons/point_of_sale/static/tests/tours/BarcodeScanning.tour.js @@ -0,0 +1,41 @@ +/** @odoo-module */ + +import { ProductScreen } from "@point_of_sale/../tests/tours/helpers/ProductScreenTourMethods"; +import { getSteps, startSteps } from "@point_of_sale/../tests/tours/helpers/utils"; +import { registry } from "@web/core/registry"; + +registry + .category("web_tour.tours") + .add("BarcodeScanningTour", { + test: true, + url: "/pos/ui", + steps: () => { + startSteps(); + + // The following step is to make sure that the Chrome widget initialization ends + // If we try to use the barcode parser before its initiation, we will have + // some inconsistent JS errors: + // TypeError: Cannot read properties of undefined (reading 'parse_barcode') + ProductScreen.do.confirmOpeningPopup(); + + // Add a product with its barcode + ProductScreen.do.scan_barcode("0123456789"); + ProductScreen.check.selectedOrderlineHas('Monitor Stand'); + ProductScreen.do.scan_barcode("0123456789"); + ProductScreen.check.selectedOrderlineHas('Monitor Stand', 2); + + // Test "Prices product" EAN-13 `23.....{NNNDD}` barcode pattern + ProductScreen.do.scan_ean13_barcode("2301000000006"); + ProductScreen.check.selectedOrderlineHas('Magnetic Board', 1, "0.00"); + ProductScreen.do.scan_ean13_barcode("2301000123453"); + ProductScreen.check.selectedOrderlineHas('Magnetic Board', 1, "123.45"); + + // Test "Weighted product" EAN-13 `21.....{NNDDD}` barcode pattern + ProductScreen.do.scan_ean13_barcode("2100002000000"); + ProductScreen.check.selectedOrderlineHas('Wall Shelf Unit', 0, "0.00"); + ProductScreen.do.scan_ean13_barcode("2100002080000"); + ProductScreen.check.selectedOrderlineHas('Wall Shelf Unit', 8); + + return getSteps(); + } + }); diff --git a/addons/point_of_sale/static/tests/tours/helpers/ProductScreenTourMethods.js b/addons/point_of_sale/static/tests/tours/helpers/ProductScreenTourMethods.js index 744503d7e6f..e12afd2ac46 100644 --- a/addons/point_of_sale/static/tests/tours/helpers/ProductScreenTourMethods.js +++ b/addons/point_of_sale/static/tests/tours/helpers/ProductScreenTourMethods.js @@ -277,6 +277,29 @@ class Do { }, ]; } + scan_barcode(barcode) { + return [ + { + content: `PoS model scan barcode '${barcode}'`, + trigger: '.pos', // The element here does not really matter as long as it is present + run: () => { window.posmodel.env.services.barcode_reader.scan(barcode); }, + }, + ]; + } + scan_ean13_barcode(barcode) { + return [ + { + content: `PoS model scan EAN13 barcode '${barcode}'`, + trigger: '.pos', // The element here does not really matter as long as it is present + run: () => { + const barcode_reader = window.posmodel.env.services.barcode_reader; + barcode_reader.scan( + barcode_reader.parser.sanitize_ean(barcode) + ); + }, + }, + ]; + } goBackToMainScreen() { return [ { diff --git a/addons/point_of_sale/tests/test_frontend.py b/addons/point_of_sale/tests/test_frontend.py index ceca57ce456..002b2148dfb 100644 --- a/addons/point_of_sale/tests/test_frontend.py +++ b/addons/point_of_sale/tests/test_frontend.py @@ -113,6 +113,7 @@ class TestPointOfSaleHttpCommon(AccountTestInvoicingHttpCommon): 'available_in_pos': True, 'list_price': 1.98, 'taxes_id': False, + 'barcode': '2100002000003', }) cls.small_shelf = env['product.product'].create({ 'name': 'Small Shelf', @@ -125,12 +126,14 @@ class TestPointOfSaleHttpCommon(AccountTestInvoicingHttpCommon): 'available_in_pos': True, 'list_price': 1.98, 'taxes_id': False, + 'barcode': '2301000000006', }) cls.monitor_stand = env['product.product'].create({ 'name': 'Monitor Stand', 'available_in_pos': True, 'list_price': 3.19, 'taxes_id': False, + 'barcode': '0123456789', # No pattern in barcode nomenclature }) cls.desk_pad = env['product.product'].create({ 'name': 'Desk Pad', @@ -151,6 +154,7 @@ class TestPointOfSaleHttpCommon(AccountTestInvoicingHttpCommon): 'available_in_pos': True, 'list_price': 5.10, 'taxes_id': False, + 'barcode': '2300001000008', }) configurable_chair = env['product.product'].create({ 'name': 'Configurable Chair', @@ -856,6 +860,15 @@ class TestUi(TestPointOfSaleHttpCommon): self.main_pos_config.with_user(self.pos_user).open_ui() self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'ReceiptScreenDiscountWithPricelistTour', login="pos_user") + def test_07_pos_barcodes_scan(self): + barcode_rule = self.env.ref("point_of_sale.barcode_rule_client") + barcode_rule.pattern = barcode_rule.pattern + "|234" + # should in theory be changed in the JS code to `|^234` + # If not, it will fail as it will mistakenly match with the product barcode "0123456789" + + self.main_pos_config.with_user(self.pos_user).open_ui() + self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'BarcodeScanningTour', login="pos_user") + def test_08_show_tax_excluded(self): # define a tax included tax record tax = self.env['account.tax'].create({