Estado actual, estándar GS1, análisis competitivo y mockup interactivo
Creado: 14 abril 2026 | Última revisión: 14 abril 2026 | Fuente: repo qiiub (branch main)
3
Entidades con barcode
3
Unique indexes
7
Barcode types
6
Gaps
Resumen Ejecutivo
QIIUB tiene el modelo de datos correcto: barcode primario en ProductVariant, aliases en ProductBarcode, clasificación por tipo, indexes para lookup en < 1ms.
Falta la lógica de negocio: auto-detección de tipo (función pura en código, no en DB), validación Mod10, y endpoint de búsqueda por barcode.
Cualquier merchant (supermercado grande o tienda pequeña) siempre tiene mezcla de formatos: UPC/EAN del fabricante + códigos internos + PLU. El sistema debe auto-detectar, nunca forzar un formato único.
Interactivo 1. Mockup: Data Entry de Producto
Haga clic en un ejemplo para ver la auto-detección y validación en tiempo real.
Nunca. Se busca por VALOR de barcode, no por tipo.
Implicación: La columna ProductBarcode.BarcodeType que existe hoy en el schema se elimina en la próxima migración. Ni ProductVariant ni ProductBarcode necesitan ese campo. El tipo se muestra en la UI llamando a DetectType() sobre el valor almacenado.
Contexto 2. Realidad del Merchant Pequeño / Supermercado
Un merchant que "solo usa el barcode del fabricante" en la práctica siempre tiene mezcla:
Categoría de producto
% típico
Tipo de barcode
Ejemplos
Empacados del fabricante
70-85%
UPC-A / EAN-13
Coca-Cola, Colgate, Bimbo
Sin barcode estándar
10-25%
Interno (prefijo 2) o Custom
Pan local, tortillas, granel, importados, reempacados
Frutas y verduras
3-8%
PLU (4-5 dígitos)
4011 = plátano, 4065 = pimiento
Peso variable (báscula)
2-5%
Interno (prefijo 2 + precio)
2-12345-00399-5 = queso $3.99
Productos que NUNCA traen UPC
Producto
Por qué no tiene UPC
Qué hace la tienda
Pan de panadería local
Productor sin registro GS1
Código interno + etiqueta propia
Tortillas artesanales
Productor informal
Código interno o venta por nombre
Tornillos sueltos (ferretería)
Se venden por unidad de bolsa grande
Código interno por tipo/tamaño
Importados sin GS1
Común en Latam
Código interno
Comida preparada (deli)
Producido en tienda
Código interno con precio embebido
Servicios (flete, instalación)
No es producto físico
Alfanumérico: FLETE-001
Conclusión: No se puede forzar un formato único por merchant. Auto-detectar por cada barcode individual y validar según el tipo detectado.
Referencia 3. Cómo lo Hacen los Competidores
Característica
Square
Shopify POS
Lightspeed
Clover
QIIUB (meta)
SKU y Barcode separados
Si
Si
Si
Si
Si
Ambos opcionales
Si
Si
SKU auto
Si
SKU req.
Múltiples barcodes / item
No
No
Si
No
Si
Auto-detección formato
No
No
Si
No
Diseñado
Validación check digit
No
Warning
Bloquea
No
Warning
Barcode interno (gen.)
No
No
Si
Parcial
Diseñado
Soporte PLU
No
No
Si
No
Propuesto
Offline barcode scan
Limitado
No
Si
Limitado
Si (SQLite)
Patrón de la industria: La mayoría no valida check digits (confían en el escáner físico). La validación importa cuando el barcode se digita manualmente. Lightspeed es el benchmark; QIIUB apunta a igualarlo.
Recomendación: Warning (como Shopify), no error bloqueante. Override manual para importaciones y casos legítimos.
Referencia 4. Glosario de Términos
Todos estos términos están documentados en docs/GLOSSARY.md. Referencia rápida inline:
GS1Global Standards One
Organización sin fines de lucro que administra los estándares globales de códigos de barras. Fundada en 2005 (fusión de EAN International + UCC). Opera en ~115 países.
GTINGlobal Trade Item Number
Término paraguas de GS1 que abarca UPC-A (12 dígitos), EAN-13 (13), EAN-8 (8). Todo UPC es un GTIN, pero no todo GTIN es un UPC.
UPC-AUniversal Product Code
Barcode de 12 dígitos numéricos usado en retail USA/Canadá. El último dígito es un check digit Modulo 10.
EAN-13European Article Number
Barcode de 13 dígitos numéricos. Estándar internacional fuera de USA. Compatible con UPC-A (un UPC-A es un EAN-13 con prefijo 0).
EAN-8
Versión compacta de EAN para productos pequeños. 8 dígitos numéricos con check digit Modulo 10.
ISBNInternational Standard Book Number
Identificador de libros. ISBN-13 usa formato EAN-13 con prefijo 978 o 979. Check digit Modulo 10.
PLUPrice Look-Up Code
Código de 4-5 dígitos para frutas y verduras. Estándar IFPS (International Federation for Produce Standards). Sin check digit. Ej: 4011 = plátano.
Check Digit
Último dígito de un UPC/EAN calculado con algoritmo Modulo 10. Detecta errores de digitación: si cambias un dígito, el check digit no cuadra.
Modulo 10(Mod 10 / Luhn variant)
Algoritmo GS1: suma ponderada alternando ×1 y ×3 de derecha a izquierda, check = (10 - suma%10) % 10.
Prefijo 2GS1 In-Store
GS1 reserva prefijo 2 (UPC-A) y 20-29 (EAN-13) para uso interno de tienda. Estos códigos los inventa el merchant, no son globalmente únicos.
IFPSInternational Federation for Produce Standards
Organización que administra los códigos PLU para frutas y verduras a nivel mundial.
SKUStock Keeping Unit
Código interno del merchant para identificar un producto. No es estándar global — cada tienda define los suyos. En QIIUB: ProductVariant.Sku.
Referencia 5. Estándar GS1: Por qué Prefijo "2"
El primer dígito de un UPC-A indica su propósito. Esto es un estándar global de GS1, no una convención arbitraria:
0UPC regular
1UPC regular
2Uso interno
3Pharma (NDC)
4Interno (no-food)
5Cupones
6UPC regular
7UPC regular
8Reservado
9Reservado
Por qué "2" y no otro
Cero colisiones — un barcode prefijo 2 nunca choca con un código de fabricante real
Sin registro — el merchant no paga a GS1 para generarlos
No son globales — dos tiendas pueden usar 2100001 para productos distintos
Ejemplo: peso variable en báscula
Barcode generado por la báscula del supermercado:212345003995
│ │ │ └─ Check digit
│ │ └─ Precio: $3.99
│ └─ Código interno del producto (queso gouda)
└─ Prefijo 2 = "este código lo generé yo"
Prefijos EAN-13 relevantes (Latam)
Prefijo
Asignado a
20-29
Uso interno de tienda (reservado por GS1)
00-09
USA / Canadá
740
Guatemala
741
El Salvador
742
Honduras
743
Nicaragua
744
Costa Rica
745
Panamá
750
México
770
Colombia
978-979
ISBN (libros)
No implementada 6. Lógica de Auto-Detección (función pura, no DB)
// BarcodeHelper.DetectType() — enum en código, NO columna en DB// Orden: más específico → más general. El usuario NUNCA elige tipo manualmente.if (4-5 dígitos numéricos) → PLU// frutas/verduraselse if (13 dig, 978|979) → ISBN// libroselse if (13 dig, prefijo 20-29) → Internal// generado por tienda (EAN)else if (12 dig, prefijo 2 o 4) → Internal// generado por tienda (UPC)else if (8 dígitos numéricos) → EAN-8// validar Mod10else if (12 dígitos numéricos) → UPC-A// validar Mod10else if (13 dígitos numéricos) → EAN-13// validar Mod10else → Custom// sin validación
Tipo
Validar Mod10
Razón
UPC-A, EAN-13, EAN-8, ISBN
Si (warning)
Estándar GS1 — detecta errores de digitación
Internal (prefijo 2)
No
Generado por tienda, formato libre
PLU
No
4-5 dígitos IFPS, sin check digit
Custom
No
Formato libre
No existe 7. Validación Check Digit (Modulo 10)
// Ejemplo: UPC-A "012345678905"// Posiciones desde la derecha (sin check digit), pesos alternados 3,1,3,1...// 0×1 + 1×3 + 2×1 + 3×3 + 4×1 + 5×3 + 6×1 + 7×3 + 8×1 + 9×3 + 0×1 = 85// Check = (10 - 85%10) % 10 = 5 ✓public static boolIsValidCheckDigit(string barcode)
{
if (!barcode.All(char.IsDigit)) return false;
if (barcode.Length is not (8or12or13)) return true;
var sum = 0;
for (var i = 0; i < barcode.Length - 1; i++)
{
var w = ((barcode.Length - 1 - i) % 2 == 0) ? 1 : 3;
sum += (barcode[i] - '0') * w;
}
var expected = (10 - (sum % 10)) % 10;
return (barcode[^1] - '0') == expected;
}
PK Id, MerchantId FK ProductVariantId UQ Barcode varchar(50) IsPrimary bit BarcodeType eliminado (se calcula)
Item Code vs Barcode
Campo
Entidad
Propósito
Ejemplo
Sku
ProductVariant
Código interno del merchant
CAM-BLK-XL
Barcode
ProductVariant
Barcode primario (inline)
012345678905
Barcode
ProductBarcode
Aliases adicionales (N por variante)
4006381333931
Categorías (multi-asignación):Product.CategoryId es la categoría primaria (usada por DailySales y reportes). La tabla junction ProductCategory(MerchantId, ProductId, CategoryId, SortOrder) contiene todas las asignaciones — incluyendo la primaria — para merchandising (“New Arrivals”), cross-selling y browsing del portal web. Ver ADR-0044.
Implementado 9. Indexes & Performance
SQL Server (API cloud)
Index
Propósito
UQ_..._Sku
Lookup por SKU
UQ_..._Barcode (Variant)
Barcode primario
UQ_..._Barcode (ProdBarcode)
Aliases
SQLite (POS offline)
Index
Rendimiento
ProductBarcode.Barcode
< 0.1 ms
ProductVariant.Sku
< 0.1 ms
FTS5 Product.Name
1-5 ms
Parcial 10. Flujo de Búsqueda
POS Terminal (offline — todo local)
Cashier escanea
→
SQLite
→
Variant.Barcode
→
ProductBarcode
→
Resultado
Portal Web / Mobile
Usuario busca
→
Endpoint NO existe
→
GET /products/by-barcode/{code}
11. Gap Analysis
1
BarcodeHelper.DetectType() + enum BarcodeType
Función pura de auto-detección + enum C# (7 valores). No se almacena en DB — se calcula del valor. Eliminar columna ProductBarcode.BarcodeType existente.
2
Check digit Modulo 10
No hay validación GS1. Warning (no bloqueante) recomendado. Solo para tipos detectados como UPC-A, EAN-13, EAN-8, ISBN.
3
Setting catalog.barcode.policy
barcode_as_sku (supermercados) vs sku_and_barcode (tiendas con código propio).
4
Endpoint GET /products/by-barcode/{code}
No existe. Necesario para portal web y app mobile.
MerchantSetting catalog.barcode.policy + lógica barcode→SKU auto
Medio
4
Endpoint GET /products/by-barcode/{code}
Medio
5
CRUD endpoints ProductBarcode (alias)
Medio
6
POS client SQLite (indexes + FTS5 + scan offline)
Alto
Pasos 1-2: quick wins (< 1 día). No hay seed data ni ref table — el tipo se calcula. Paso 3: wiring con settings cascade. Pasos 4-5: endpoints estándar con tests. Paso 6: parte del POS client.