Skip to content
Snippets Groups Projects
Commit 8d5000b8 authored by pedrambiria's avatar pedrambiria
Browse files

[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: default avatarJoseph Caburnay (jcb) <jcb@odoo.com>
parent 172cfd19
No related branches found
No related tags found
No related merge requests found
...@@ -46,7 +46,10 @@ BarcodeParser.include({ ...@@ -46,7 +46,10 @@ BarcodeParser.include({
const result = { const result = {
rule: Object.assign({}, rule), rule: Object.assign({}, rule),
ai: match[1], 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'){ if (rule.gs1_content_type === 'measure'){
let decimalPosition = 0; // Decimal position begin at the end, 0 means no decimal let decimalPosition = 0; // Decimal position begin at the end, 0 means no decimal
......
...@@ -29,6 +29,7 @@ odoo.define('point_of_sale.ProductScreen', function(require) { ...@@ -29,6 +29,7 @@ odoo.define('point_of_sale.ProductScreen', function(require) {
client: this._barcodePartnerAction, client: this._barcodePartnerAction,
discount: this._barcodeDiscountAction, discount: this._barcodeDiscountAction,
error: this._barcodeErrorAction, error: this._barcodeErrorAction,
gs1: this._barcodeGS1Action,
}); });
NumberBuffer.use({ NumberBuffer.use({
nonKeyboardInputEvent: 'numpad-click-input', nonKeyboardInputEvent: 'numpad-click-input',
...@@ -61,7 +62,7 @@ odoo.define('point_of_sale.ProductScreen', function(require) { ...@@ -61,7 +62,7 @@ odoo.define('point_of_sale.ProductScreen', function(require) {
get currentOrder() { get currentOrder() {
return this.env.pos.get_order(); return this.env.pos.get_order();
} }
async _getAddProductOptions(product, base_code) { async _getAddProductOptions(product, code) {
let price_extra = 0.0; let price_extra = 0.0;
let draftPackLotLines, weight, description, packLotLinesToEdit; let draftPackLotLines, weight, description, packLotLinesToEdit;
...@@ -97,24 +98,36 @@ odoo.define('point_of_sale.ProductScreen', function(require) { ...@@ -97,24 +98,36 @@ odoo.define('point_of_sale.ProductScreen', function(require) {
packLotLinesToEdit = []; packLotLinesToEdit = [];
} }
} }
const { confirmed, payload } = await this.showPopup('EditListPopup', { // if the lot information exists in the barcode, we don't need to ask it from the user.
title: this.env._t('Lot/Serial Number(s) Required'), if (code && code.type === 'lot') {
isSingleItem: isAllowOnlyOneLot, // consider the old and new packlot lines
array: packLotLinesToEdit,
});
if (confirmed) {
// Segregate the old and new packlot lines
const modifiedPackLotLines = Object.fromEntries( 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 const newPackLotLines = [
.filter(item => !item.id) { lot_name: code.code },
.map(item => ({ lot_name: item.text })); ];
draftPackLotLines = { modifiedPackLotLines, newPackLotLines }; draftPackLotLines = { modifiedPackLotLines, newPackLotLines };
} else { } else {
// We don't proceed on adding product. const { confirmed, payload } = await this.showPopup('EditListPopup', {
return; 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) { ...@@ -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]) { if (code && this.env.pos.db.product_packaging_by_barcode[code.code]) {
weight = this.env.pos.db.product_packaging_by_barcode[base_code.code].qty; weight = this.env.pos.db.product_packaging_by_barcode[code.code].qty;
} }
return { draftPackLotLines, quantity: weight, description, price_extra }; return { draftPackLotLines, quantity: weight, description, price_extra };
...@@ -214,7 +227,7 @@ odoo.define('point_of_sale.ProductScreen', function(require) { ...@@ -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); let product = this.env.pos.db.get_product_by_barcode(code.base_code);
if (!product) { if (!product) {
// find the barcode in the backend // find the barcode in the backend
...@@ -244,6 +257,13 @@ odoo.define('point_of_sale.ProductScreen', function(require) { ...@@ -244,6 +257,13 @@ odoo.define('point_of_sale.ProductScreen', function(require) {
return this._barcodeErrorAction(code); 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); const options = await this._getAddProductOptions(product, code);
// Do not proceed on adding the product when no options is returned. // Do not proceed on adding the product when no options is returned.
// This is consistent with _clickProduct. // This is consistent with _clickProduct.
...@@ -289,6 +309,22 @@ odoo.define('point_of_sale.ProductScreen', function(require) { ...@@ -289,6 +309,22 @@ odoo.define('point_of_sale.ProductScreen', function(require) {
last_orderline.set_discount(code.value); 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? // IMPROVEMENT: The following two methods should be in PosScreenComponent?
// Why? Because once we start declaring barcode actions in different // Why? Because once we start declaring barcode actions in different
// screens, these methods will also be declared over and over. // screens, these methods will also be declared over and over.
......
...@@ -110,11 +110,10 @@ var BarcodeReader = core.Class.extend({ ...@@ -110,11 +110,10 @@ var BarcodeReader = core.Class.extend({
const callbacks = Object.keys(this.exclusive_callbacks).length const callbacks = Object.keys(this.exclusive_callbacks).length
? this.exclusive_callbacks ? this.exclusive_callbacks
: this.action_callbacks; : this.action_callbacks;
let parsed_results = this.barcode_parser.parse_barcode(code); let parsed_result = this.barcode_parser.parse_barcode(code);
if (! Array.isArray(parsed_results)) { if (Array.isArray(parsed_result)) {
parsed_results = [parsed_results]; [...callbacks.gs1].map(cb => cb(parsed_result));
} } else {
for (const parsed_result of parsed_results) {
if (callbacks[parsed_result.type]) { if (callbacks[parsed_result.type]) {
for (const cb of callbacks[parsed_result.type]) { for (const cb of callbacks[parsed_result.type]) {
await cb(parsed_result); await cb(parsed_result);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment