Capitulo 4: GS1 Application Identifiers

Por: Artiko
gs1application-identifiersaielement-stringgs1-128lotevencimientoserie

Que es un Application Identifier

Un Application Identifier (AI) es un prefijo de 2 a 4 digitos que precede a un campo de datos y define:

  1. Que tipo de dato es (GTIN, lote, fecha, peso, etc.)
  2. Cuantos caracteres tiene el dato (longitud fija o variable)
  3. Que formato usa (numerico, alfanumerico, fecha)

Sin AIs, un escaner leeria una cadena de caracteres sin saber que significa cada parte. Los AIs son el “diccionario” que permite interpretar la informacion.

Tabla de AIs principales

AINombreFormatoLong.Fija/VarEjemplo
(00)SSCCn1818Fija(00)306141410000000012
(01)GTINn1414Fija(01)09521234543213
(02)GTIN de items contenidosn1414Fija(02)09521234543213
(10)Numero de lotean..201-20Variable(10)LOTE-AB1
(11)Fecha de produccionn6 (YYMMDD)6Fija(11)260101
(17)Fecha de vencimienton6 (YYMMDD)6Fija(17)261231
(21)Numero de seriean..201-20Variable(21)SN001
(30)Cantidadn..81-8Variable(30)48
(310n)Peso neto en kgn66Fija(3102)001500
(410)GLN destino (Ship To)n1313Fija(410)0614141073467
(414)GLN de ubicacion fisican1313Fija(414)0614141073467
(420)Codigo postal destinoan..201-20Variable(420)28001

Notas sobre el formato

Que es un Element String

Un Element String es la concatenacion de uno o mas pares AI+dato. Es lo que realmente se codifica dentro de un codigo de barras GS1-128 o DataMatrix.

Ejemplo completo

(01)09521234543213(17)261231(10)LOTE-AB1(21)SN001

Desglose:

AIDatoSignificado
(01)09521234543213GTIN del producto
(17)261231Vence el 31 de diciembre de 2026
(10)LOTE-AB1Lote de produccion
(21)SN001Numero de serie unico

Este Element String identifica una unidad especifica de un producto: sabemos que es, cuando vence, de que lote viene y cual es su numero de serie individual.

Separadores: el problema de los AIs de longitud variable

Los AIs de longitud fija (como 01, 00, 17) no necesitan separador porque el parser sabe exactamente cuantos caracteres leer. Pero los AIs de longitud variable (como 10, 21, 420) necesitan indicar donde terminan.

FNC1 en GS1-128

En codigos de barras GS1-128, se usa un caracter especial llamado FNC1 (Function Code 1) como separador. No es un caracter visible: es una instruccion para el escaner.

GS (Group Separator) en transmision de datos

Cuando los datos se transmiten como texto (desde un escaner via USB/serial), el FNC1 se reemplaza por el caracter ASCII GS (Group Separator, \x1D, codigo 29).

Regla clave

El separador FNC1/GS se coloca al final de un AI de longitud variable, excepto si es el ultimo AI del Element String.

Ejemplo con separadores (usando {GS} para representar el caracter):

(01)09521234543213(10)LOTE-AB1{GS}(21)SN001{GS}(17)261231

En la practica, para evitar separadores innecesarios, se colocan los AIs de longitud fija al final:

(01)09521234543213(10)LOTE-AB1{GS}(21)SN001(17)261231

Aqui (17) tiene longitud fija, asi que al estar al final no necesita separador, y (21) al no ser el ultimo AI variable, si lo necesitaria… pero si lo ponemos justo antes de un AI fijo, solo necesitamos un GS despues de (10).

Parsear un Element String

// Definicion de AIs conocidos con su longitud
const AI_DEFS: Record<string, { length?: number; label: string }> = {
  '00': { length: 18, label: 'SSCC' },
  '01': { length: 14, label: 'GTIN' },
  '02': { length: 14, label: 'GTIN contenido' },
  '10': { label: 'Lote' },
  '11': { length: 6, label: 'Fecha produccion' },
  '17': { length: 6, label: 'Fecha vencimiento' },
  '21': { label: 'Serie' },
  '30': { label: 'Cantidad' },
  '410': { length: 13, label: 'GLN destino' },
  '414': { length: 13, label: 'GLN ubicacion' },
  '420': { label: 'CP destino' },
};

interface ParsedAI {
  ai: string;
  label: string;
  value: string;
}

function parseElementString(raw: string): ParsedAI[] {
  const GS = '\x1D';
  const results: ParsedAI[] = [];
  let pos = 0;

  // Limpiar parentesis si existen (formato HRI)
  const clean = raw.replace(/[()]/g, '');

  while (pos < clean.length) {
    if (clean[pos] === GS) { pos++; continue; }

    // Intentar AIs de 3, luego 2 digitos
    const candidates = [3, 2];
    let matched = false;

    for (const len of candidates) {
      const aiCandidate = clean.substring(pos, pos + len);
      const def = AI_DEFS[aiCandidate];

      if (def) {
        pos += len;
        let value: string;

        if (def.length) {
          value = clean.substring(pos, pos + def.length);
          pos += def.length;
        } else {
          const gsIndex = clean.indexOf(GS, pos);
          const end = gsIndex === -1 ? clean.length : gsIndex;
          value = clean.substring(pos, end);
          pos = end;
        }

        results.push({ ai: aiCandidate, label: def.label, value });
        matched = true;
        break;
      }
    }

    if (!matched) {
      throw new Error(`AI desconocido en posicion ${pos}: ${clean.substring(pos, pos + 4)}`);
    }
  }

  return results;
}

// Ejemplo
const parsed = parseElementString('(01)09521234543213(17)261231(10)LOTE-AB1(21)SN001');
console.log(parsed);
// [
//   { ai: '01', label: 'GTIN', value: '09521234543213' },
//   { ai: '17', label: 'Fecha vencimiento', value: '261231' },
//   { ai: '10', label: 'Lote', value: 'LOTE-AB1' },
//   { ai: '21', label: 'Serie', value: 'SN001' }
// ]

Ahora que entendemos como se estructuran los datos dentro de los codigos, en el proximo capitulo veremos el GS1-128: el codigo de barras logistico que empaqueta todo esto en un simbolo escaneable.