Odoo Community Association

Barcode GS1

Beta License: AGPL-3 OCA/barcode-interface Translate me on Weblate Try me on Runboat

This module adds GS1 barcode parsing to the Barcode suite.

It registers a GS1 parser into the barcode_scanner base — through the barcode_parsers registry, without patching the base. Scanned GS1-128 barcodes, whether written with parenthesised application identifiers ((01)...(10)...) or as raw FNC1-separated data, are decoded into structured fields: GTIN (AI 01), batch/lot (10), serial (21), production/pack/best-before/ expiry dates (11/13/15/16/17), count (30/37), net weight and other measures (310n–360n), amounts payable (390n–393n), SSCC (00) and location GLNs (410/413/414).

What each application identifier means is read from Odoo’s own GS1 nomenclature (barcode.nomenclature and its rules, as shipped by the standard Barcode - GS1 Nomenclature module), so a rule added or adapted in Settings applies to the scanner too. Identifiers the nomenclature does not define fall back to the ones built into this module, so an unknown AI in the middle of a barcode never cuts the rest of the scan short.

Numeric identifiers — GTIN, SSCC, GLN — are only accepted when their check digit matches, so a misread is reported instead of quietly resolving to the wrong product.

Every screen then receives the parsed data alongside the raw scan, so a single GS1 scan can identify the product and fill lot, expiry and quantity at once. Non-GS1 codes fall through to the base EAN13 parser, so the module is safe to install next to the rest of the suite.

Table of contents

Configuration

No configuration is needed: once installed, GS1 barcodes are parsed wherever the app reads a scan. Two things can be adjusted.

Application identifiers

The application identifiers are read from a GS1 barcode.nomenclature: the one set on the company when it is a GS1 nomenclature, otherwise the first GS1 nomenclature found (the Default GS1 Nomenclature Odoo ships). Its GS1-128 rules are fetched once, when the barcode app opens.

To teach the scanner a new application identifier, add a rule under Inventory → Configuration → Barcode Nomenclatures — no code change. A rule whose Type is one the app understands (product, lot, quantity, weighted product, priced product, package, package type, expiration date, best before date, pack date, location, destination location) lands on the matching field of the parsed scan (see Usage); the value of any other rule is still available in ais.

Identifiers the nomenclature does not define keep working through the ones built into the module, and if the nomenclature cannot be read at all the module falls back to them entirely.

Scanners that cannot send FNC1

GS1 ends a variable-length value with FNC1 unless it is the last element of the barcode, and a keyboard-wedge scanner often sends nothing in its place. Odoo has a field for exactly that — FNC1 Separator on the nomenclature — so configure the scanner to send one of those characters (# out of the box) and the scan is read as printed.

Usage

Install barcode_gs1 alongside barcode_scanner (and a feature module such as barcode_stock). Once installed, GS1 barcodes are parsed automatically wherever the app reads a scan.

A scanned GS1 barcode produces a parsed object such as:

{
    type: "gs1",
    value: "<product code>",  // GTIN (AI 01), in the form products store
    gtin: "<GTIN>",           // the GTIN exactly as scanned (14 digits)
    productCodes: [...],      // every form the GTIN may be stored as
    lot: "<batch/lot>",       // AI 10 (falls back to the serial, AI 21)
    serial: "<serial>",       // AI 21
    expiry: "YYYY-MM-DD",     // AI 17, or 15/16 when no 17 is present
    useDate: "YYYY-MM-DD",    // AI 15 / 16 (best before, sell by)
    packDate: "YYYY-MM-DD",   // AI 13
    productionDate: "...",    // AI 11
    qty: <number>,            // AI 30 / 37; null when the label states no count
    count: <number>,          // AI 30 / 37 (pieces), kept next to the weight
    weight: <number>,         // AI 31xx–36xx, the measure of the goods
    weightUom: {id, name},    // the unit that measure is in, per the rule
    measures: [{ai, value, uom}, ...], // every measure, gross weight included
    price: <number>,          // AI 390n–393n (amount payable)
    currency: "<ISO 4217>",   // the numeric code AI 391n / 393n carries
    sscc: "<SSCC>",           // AI 00 (logistic unit)
    location: "<GLN>",        // AI 414
    locationDest: "<GLN>",    // AI 410 / 413
    ais: {"01": "...", "10": "...", ...},
    errors: [...],            // what could not be read, e.g. a bad check digit
}

Both parenthesised barcodes — (01)09501101020917(10)LOT123(17)261231 — and raw FNC1-separated scans are supported.

GS1 always carries the GTIN zero-padded to 14 digits, while a product stores the short code printed on the item. value therefore holds the padding-free form Odoo stores (13 digits, as sanitize_ean produces), which is what the screens match the product against; productCodes lists the other lengths a barcode may have been stored as, longest-standing form first.

Variable-length values

GS1 ends a variable-length value (a lot, a serial…) with FNC1, except when it is the last element of the barcode, where none is needed. The parser reads it that way: up to the next separator, or to the end of the barcode (see Configuration for scanners that cannot send FNC1).

Only when that reading is impossible — a value longer than its rule allows — was a separator clearly dropped. The value then ends at the next element that reads cleanly: a specific numeric identifier (never an alphanumeric one, nor a catch-all range) with a value, a real date or a right check digit. Anything short of that is ambiguous — 21 5830192 is the serial 5830192, or the serial 58 followed by a count of 192 — and is read as GS1 says, as one value.

Misreads are refused

A GTIN, SSCC or GLN is only accepted when its check digit (GS1 modulo 10) matches. A misread identifier is left empty and reported in errors instead of resolving to the wrong product or pallet.

In the warehouse app

What the warehouse app does with it (with barcode_stock installed):

  • the quantity picked is the piece count the label states (AI 30/37). A label without one picks a single unit, or the pack’s quantity when its GTIN is a packaging barcode. A measure such as a net weight is decoded but not taken as the quantity: that depends on the unit the product is stocked in;
  • the lot or serial is looked up for the scanned product and preselected in the move wizard; on a receipt an unknown lot opens the create-lot flow with the name and, when product_expiry is installed, the expiry date prefilled;
  • on a delivery or an internal transfer, a lot that is not available is reported instead of silently picking another one.

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed feedback.

Do not contact contributors directly about support or help with technical issues.

Credits

Authors

  • Binhex

Maintainers

This module is maintained by the OCA.

Odoo Community Association

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

Current maintainer:

szalatyzuzanna

This module is part of the OCA/barcode-interface project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.