[REF] barcodes,*: convert BarcodeParser to js class
*: barcodes_gs1_nomenclature,point_of_sale In this commit, we are converting the `BarcodeParser` to js class. As a consequence, instead of having 2 options of instantiating the parser -- via nomenclature_id or via nomenclature -- we are removing the first option. Instantiating the parser will now only require the built nomenclature object. This is possible because in all the pathways where the parser is instantiated, the web services are ready, meaning `rpc` and/or `orm` services are ready. As a result, the consumer of the parser can just build the nomenclature object itself by fetching the nomenclature details from the server. A helper static method called `fetchNomenclature` is introduced in the `BarcodeParser` class to aid in building the nomenclature object it needed. Furthermore, the methods that return the required nomenclature and rule fields are converted to static fields which can still be patched (check barcodes_gs1_nomenclature in enterprise). closes odoo/odoo#120228 Related: odoo/enterprise#40582 Signed-off-by: Samuel Degueldre <sad@odoo.com>
This commit is contained in:
@@ -1,51 +1,33 @@
|
||||
/** @odoo-module alias=barcodes.BarcodeParser **/
|
||||
/** @odoo-module **/
|
||||
|
||||
import Class from "web.Class";
|
||||
import rpc from "web.rpc";
|
||||
export class BarcodeParser {
|
||||
static barcodeNomenclatureFields = ["name", "rule_ids", "upc_ean_conv"];
|
||||
static barcodeRuleFields = ["name", "sequence", "type", "encoding", "pattern", "alias"];
|
||||
static async fetchNomenclature(orm, id) {
|
||||
const [nomenclature] = await orm.read(
|
||||
"barcode.nomenclature",
|
||||
[id],
|
||||
this.barcodeNomenclatureFields
|
||||
);
|
||||
let rules = await orm.searchRead(
|
||||
"barcode.rule",
|
||||
[["barcode_nomenclature_id", "=", id]],
|
||||
this.barcodeRuleFields
|
||||
);
|
||||
rules = rules.sort((a, b) => {
|
||||
return a.sequence - b.sequence;
|
||||
});
|
||||
nomenclature.rules = rules;
|
||||
return nomenclature;
|
||||
}
|
||||
|
||||
// The BarcodeParser is used to detect what is the category
|
||||
// of a barcode (product, partner, ...) and extract an encoded value
|
||||
// (like weight, price, etc.)
|
||||
var BarcodeParser = Class.extend({
|
||||
init: function(attributes) {
|
||||
this.nomenclature_id = attributes.nomenclature_id;
|
||||
this.nomenclature = attributes.nomenclature;
|
||||
this.loaded = this.load();
|
||||
},
|
||||
constructor() {
|
||||
this.setup(...arguments);
|
||||
}
|
||||
|
||||
// This loads the barcode nomenclature and barcode rules which are
|
||||
// necessary to parse the barcodes. The BarcodeParser is operational
|
||||
// only when those data have been loaded
|
||||
load: function(){
|
||||
if (!this.nomenclature_id) {
|
||||
return this.nomenclature ? Promise.resolve() : Promise.reject();
|
||||
}
|
||||
var id = this.nomenclature_id[0];
|
||||
return rpc.query({
|
||||
model: 'barcode.nomenclature',
|
||||
method: 'read',
|
||||
args: [[id], this._barcodeNomenclatureFields()],
|
||||
}).then(nomenclatures => {
|
||||
this.nomenclature = nomenclatures[0];
|
||||
var args = [
|
||||
[['barcode_nomenclature_id', '=', this.nomenclature.id]],
|
||||
this._barcodeRuleFields(),
|
||||
];
|
||||
return rpc.query({
|
||||
model: 'barcode.rule',
|
||||
method: 'search_read',
|
||||
args: args,
|
||||
});
|
||||
}).then(rules => {
|
||||
rules = rules.sort(function(a, b){ return a.sequence - b.sequence; });
|
||||
this.nomenclature.rules = rules;
|
||||
});
|
||||
},
|
||||
|
||||
// resolves when the barcode parser is operational.
|
||||
is_loaded: function() {
|
||||
return this.loaded;
|
||||
},
|
||||
setup({ nomenclature }) {
|
||||
this.nomenclature = nomenclature;
|
||||
}
|
||||
|
||||
/**
|
||||
* This algorithm is identical for all fixed length numeric GS1 data structures.
|
||||
@@ -76,7 +58,7 @@ var BarcodeParser = Class.extend({
|
||||
}
|
||||
total = evensum * 3 + oddsum;
|
||||
return (10 - total % 10) % 10;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the barcode string is encoded with the provided encoding.
|
||||
@@ -85,7 +67,7 @@ var BarcodeParser = Class.extend({
|
||||
* @param {String} encoding could be 'any' (no encoding rules), 'ean8', 'upca' or 'ean13'
|
||||
* @returns {boolean}
|
||||
*/
|
||||
check_encoding: function(barcode, encoding) {
|
||||
check_encoding(barcode, encoding) {
|
||||
if (encoding === 'any') {
|
||||
return true;
|
||||
}
|
||||
@@ -96,7 +78,7 @@ var BarcodeParser = Class.extend({
|
||||
};
|
||||
return barcode.length === barcodeSizes[encoding] && /^\d+$/.test(barcode) &&
|
||||
this.get_barcode_check_digit(barcode) === parseInt(barcode[barcode.length - 1]);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes a EAN-13 prefix by padding it with chars zero.
|
||||
@@ -104,11 +86,11 @@ var BarcodeParser = Class.extend({
|
||||
* @param {String} ean
|
||||
* @returns {String}
|
||||
*/
|
||||
sanitize_ean: function(ean){
|
||||
sanitize_ean(ean) {
|
||||
ean = ean.substr(0, 13);
|
||||
ean = "0".repeat(13 - ean.length) + ean;
|
||||
return ean.substr(0, 12) + this.get_barcode_check_digit(ean);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes a UPC-A prefix by padding it with chars zero.
|
||||
@@ -116,9 +98,9 @@ var BarcodeParser = Class.extend({
|
||||
* @param {String} upc
|
||||
* @returns {String}
|
||||
*/
|
||||
sanitize_upc: function(upc) {
|
||||
sanitize_upc(upc) {
|
||||
return this.sanitize_ean(upc).substr(1, 12);
|
||||
},
|
||||
}
|
||||
|
||||
// Checks if barcode matches the pattern
|
||||
// Additionnaly retrieves the optional numerical content in barcode
|
||||
@@ -126,7 +108,7 @@ var BarcodeParser = Class.extend({
|
||||
// - value: the numerical value encoded in the barcode (0 if no value encoded)
|
||||
// - base_code: the barcode in which numerical content is replaced by 0's
|
||||
// - match: boolean
|
||||
match_pattern: function (barcode, pattern, encoding){
|
||||
match_pattern(barcode, pattern, encoding) {
|
||||
var match = {
|
||||
value: 0,
|
||||
base_code: barcode,
|
||||
@@ -182,7 +164,7 @@ var BarcodeParser = Class.extend({
|
||||
match.match = match.base_code.match(base_pattern);
|
||||
|
||||
return match;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to interpret a barcode (string encoding a barcode Code-128)
|
||||
@@ -194,7 +176,7 @@ var BarcodeParser = Class.extend({
|
||||
* - value: if the barcode encodes a numerical value, it will be put there
|
||||
* - base_code: the barcode with all the encoding parts set to zero; the one put on the product in the backend
|
||||
*/
|
||||
parse_barcode: function(barcode){
|
||||
parse_barcode(barcode) {
|
||||
var parsed_result = {
|
||||
encoding: '',
|
||||
type:'error',
|
||||
@@ -250,30 +232,5 @@ var BarcodeParser = Class.extend({
|
||||
}
|
||||
}
|
||||
return parsed_result;
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
_barcodeNomenclatureFields: function () {
|
||||
return [
|
||||
'name',
|
||||
'rule_ids',
|
||||
'upc_ean_conv',
|
||||
];
|
||||
},
|
||||
|
||||
_barcodeRuleFields: function () {
|
||||
return [
|
||||
'name',
|
||||
'sequence',
|
||||
'type',
|
||||
'encoding',
|
||||
'pattern',
|
||||
'alias',
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
export default BarcodeParser;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,29 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import BarcodeParser from "barcodes.BarcodeParser";
|
||||
import { BarcodeParser } from "@barcodes/js/barcode_parser";
|
||||
|
||||
|
||||
QUnit.module('Barcodes', {}, function () {
|
||||
QUnit.module('Barcode Parser', {
|
||||
beforeEach: function () {
|
||||
this.data = {
|
||||
'barcode.nomenclature': {
|
||||
fields: {
|
||||
name: {type: 'char', string: 'Barcode Nomenclature'},
|
||||
rule_ids: {type: 'one2many', relation: 'barcode.rule'},
|
||||
upc_ean_conv: {type: 'selection', string: 'UPC/EAN Conversion'},
|
||||
},
|
||||
records: [
|
||||
{id: 1, name: "normal", upc_ean_conv: "always"},
|
||||
],
|
||||
},
|
||||
'barcode.rule': {
|
||||
fields: {
|
||||
name: {type: 'char', string: 'Barcode Nomenclature'},
|
||||
barcode_nomenclature_id: {type: 'many2one', relation: 'barcode.nomenclature'},
|
||||
sequence: {type: 'integer', string: 'Sequence'},
|
||||
encoding: {type: 'selection', string: 'Encoding'},
|
||||
type: {type: 'selection', string: 'Type'},
|
||||
pattern: {type: 'Char', string: 'Pattern'},
|
||||
alias: {type: 'Char', string: 'Alias'},
|
||||
},
|
||||
records: [
|
||||
{id: 1, name: "Product Barcodes", barcode_nomenclature_id: 1, sequence: 90, type: 'product', encoding: 'any', pattern: ".*"},
|
||||
],
|
||||
}
|
||||
};
|
||||
}
|
||||
}, function () {
|
||||
QUnit.test('Test check digit', async function (assert) {
|
||||
QUnit.module('Barcode Parser', function () {
|
||||
QUnit.test('Test check digit', function (assert) {
|
||||
assert.expect(6);
|
||||
const barcodeNomenclature = new BarcodeParser({'nomenclature_id': 1});
|
||||
await barcodeNomenclature.loaded;
|
||||
const nomenclature = {
|
||||
id: 1,
|
||||
name: "normal",
|
||||
upc_ean_conv: "always",
|
||||
rules: [
|
||||
{
|
||||
id: 1,
|
||||
name: "Product Barcodes",
|
||||
barcode_nomenclature_id: 1,
|
||||
sequence: 90,
|
||||
type: "product",
|
||||
encoding: "any",
|
||||
pattern: ".*",
|
||||
},
|
||||
],
|
||||
};
|
||||
const barcodeNomenclature = new BarcodeParser({ nomenclature });
|
||||
|
||||
let ean8 = "87111125";
|
||||
assert.equal(barcodeNomenclature.get_barcode_check_digit(ean8), ean8[ean8.length - 1]);
|
||||
|
||||
@@ -1,10 +1,26 @@
|
||||
/** @odoo-module alias=barcodes_gs1_nomenclature.barcode_parser **/
|
||||
/** @odoo-module **/
|
||||
|
||||
import BarcodeParser from "barcodes.BarcodeParser";
|
||||
const FNC1_CHAR = String.fromCharCode(29);
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { BarcodeParser } from "@barcodes/js/barcode_parser";
|
||||
import {_lt} from "web.core";
|
||||
|
||||
BarcodeParser.include({
|
||||
export const FNC1_CHAR = String.fromCharCode(29);
|
||||
|
||||
patch(BarcodeParser, "barcodes_gs1_nomenclature.BarcodeParser static", {
|
||||
barcodeNomenclatureFields: [
|
||||
...BarcodeParser.barcodeNomenclatureFields,
|
||||
"is_gs1_nomenclature",
|
||||
"gs1_separator_fnc1",
|
||||
],
|
||||
barcodeRuleFields: [
|
||||
...BarcodeParser.barcodeRuleFields,
|
||||
"gs1_content_type",
|
||||
"gs1_decimal_usage",
|
||||
"associated_uom_id",
|
||||
],
|
||||
});
|
||||
|
||||
patch(BarcodeParser.prototype, "barcodes_gs1_nomenclature.BarcodeParser", {
|
||||
/**
|
||||
* Convert YYMMDD GS1 date into a Date object
|
||||
*
|
||||
@@ -123,31 +139,4 @@ BarcodeParser.include({
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_barcodeNomenclatureFields: function () {
|
||||
const fieldNames = this._super(...arguments);
|
||||
fieldNames.push('is_gs1_nomenclature', 'gs1_separator_fnc1');
|
||||
return fieldNames;
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_barcodeRuleFields: function () {
|
||||
const fieldNames = this._super(...arguments);
|
||||
fieldNames.push('gs1_content_type', 'gs1_decimal_usage', 'associated_uom_id');
|
||||
return fieldNames;
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
BarcodeParser,
|
||||
FNC1_CHAR,
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ import { session } from "@web/session";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { barcodeService } from '@barcodes/barcode_service';
|
||||
|
||||
import { FNC1_CHAR } from "barcodes_gs1_nomenclature.barcode_parser";
|
||||
import { FNC1_CHAR } from "@barcodes_gs1_nomenclature/js/barcode_parser";
|
||||
|
||||
|
||||
patch(barcodeService, 'barcodes_gs1_nomenclature', {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import BarcodeParser from "barcodes.BarcodeParser";
|
||||
import { BarcodeParser } from "@barcodes/js/barcode_parser";
|
||||
import { barcodeService } from "@barcodes/barcode_service";
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ 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";
|
||||
import { BarcodeParser } from "@barcodes/js/barcode_parser";
|
||||
|
||||
export class BarcodeReader {
|
||||
static serviceDependencies = ["popup", "hardware_proxy"];
|
||||
@@ -103,14 +103,17 @@ export class BarcodeReader {
|
||||
}
|
||||
|
||||
export const barcodeReaderService = {
|
||||
dependencies: [...BarcodeReader.serviceDependencies, "popup", "barcode"],
|
||||
dependencies: [...BarcodeReader.serviceDependencies, "popup", "barcode", "orm"],
|
||||
async start(env, deps) {
|
||||
const { popup, barcode } = deps;
|
||||
const { popup, barcode, orm } = deps;
|
||||
let barcodeReader = null;
|
||||
|
||||
if (session.nomenclature_id) {
|
||||
const parser = new BarcodeParser({ nomenclature_id: [session.nomenclature_id] });
|
||||
await parser.is_loaded();
|
||||
const nomenclature = await BarcodeParser.fetchNomenclature(
|
||||
orm,
|
||||
session.nomenclature_id
|
||||
);
|
||||
const parser = new BarcodeParser({ nomenclature });
|
||||
barcodeReader = new BarcodeReader(parser, deps);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user