Saltar al contenido principal

Caso Real: Etiquetas Mezclas — Generador PDF con QR

Plataforma .NET para COVAP que digitaliza etiquetas reglamentarias de alimentación animal: subida de TXT, enriquecimiento con Oracle JDE, generación de PDF con iText7 y trazabilidad por QR firmado AES-256/PBKDF2. 9 secciones en plantilla automática, endpoint QR público con defense in depth.

Fran Cobos 11 min de lectura 2099 palabras

Tabla de contenidos

Resumen

Desarrollé Etiquetas Mezclas para COVAP: una plataforma .NET que digitaliza el ciclo de vida de las etiquetas reglamentarias de alimentación animal. Los operadores suben TXT de composición, el sistema los enriquece con Oracle JDE y SQL Server, genera PDFs profesionales con iText7 de 9 secciones y códigos QR firmados criptográficamente con AES-256/PBKDF2. El endpoint QR público está endurecido con defense in depth (rate limiting, circuit breaker, anti path-traversal). Resultado: QR con cache hit < 500ms, panel admin < 1.5s, y trazabilidad total de cada lote.


El problema

COVAP necesitaba digitalizar y automatizar el ciclo de vida de las etiquetas reglamentarias de alimentación animal. El proceso manual era:

  • TXT a mano — los operadores registraban la composición en archivos de texto con formato INI1
  • PDF manual — generación de etiquetas reglamentarias por un proceso que requería intervención humana
  • Sin trazabilidad — no había forma de verificar que una etiqueta correspondía a un lote exacto
  • Dependencia del ERP — Oracle JDE era la fuente de verdad, pero consultarlo en caliente era lento y bloqueante
  • Riesgo de error humano — datos de composición, aditivos, modo de empleo y advertencias legales debían ser 100% correctos

Lo que me pidieron: “Queremos que un operador suba un TXT y obtenga la etiqueta reglamentaria completa en PDF con un QR trazable. Sin depender del ERP en cada consulta, sin errores en los datos, y con seguridad para que nadie pueda falsificar un QR.”


La arquitectura que elegí

┌──────────────────────────────────────────────────────────────────┐
│  OPERADOR                                                        │
│  Subida TXT (formato INI1) → Panel Web ASP.NET                   │
└──────────────────────┬───────────────────────────────────────────┘
                       │
                       ▼
┌──────────────────────────────────────────────────────────────────┐
│  BACKEND: ASP.NET Web Forms + VB.NET                             │
│  .NET Framework 4.6.1/4.8                                       │
│  Patrón AJAX: hidOperacion → Select Case                        │
│  Contrato JSON: {Resultado, Mensaje, Datos}                     │
│  5 endpoints: wfUpload, wfEtiquetas, wfArticulos,              │
│              wfArchivosLocales, wfTxtArticulo                    │
├──────────────────────────────────────────────────────────────────┤
│  DOBLE FUENTE DE DATOS:                                          │
│  Oracle JDE F4101 ──→ Nombre + Días caducidad (7 capas         │
│                      de validación, lotes IN de 50 con caché)    │
│  SQL Server T_Etiquetas ──→ ModoEmpleo, Footer                  │
├──────────────────────────────────────────────────────────────────┤
│  PDF GENERATION: iText7 7.2.5                                    │
│  9 secciones · Plantillas por familia: modelo136, rcd, bigbag   │
├──────────────────────────────────────────────────────────────────┤
│  SEGURIDAD:                                                      │
│  Firma AES-256/CBC + PBKDF2 (65.536 iteraciones)               │
│  Rate limiting 30/min/IP · Circuit breaker · CSRF · Session     │
│  Caché write-through de PDF a 24h en filesystem IIS             │
├──────────────────────────────────────────────────────────────────┤
│  ENDPOINT QR PÚBLICO:                                            │
│  URL firmada → Verificación → Servición de etiqueta             │
│  Caché hit < 500ms (TransmitFile) · MISS < 2s (iText7)         │
└──────────────────────────────────────────────────────────────────┘

Por qué Web Forms + VB.NET (no MVC ni ASP.NET Core)

El proyecto se desarrolló para un entorno corporativo con infraestructura IIS existente y equipo técnico acostumbrado a Web Forms. La elección fue pragmática:

  • Patrón AJAX maduro: hidOperacion → Select Case es un patrón bien establecido en Web Forms que permite contratos JSON limpios sin necesidad de un framework de API separado
  • Master Page + code-behind: permite auditar el flujo de cada página de forma clara y mantenible
  • Configuración multi-entorno: Web.config + URL Rewrite permite cambiar entre entornos sin tocar código
  • IIS nativo: el servidor corporativo ya tenía IIS configurado, lo que simplificó el despliegue

Si hubiera sido un proyecto greenfield en la nube, habría elegido ASP.NET Core. Pero para un servidor corporativo existente con un equipo que conoce el framework, Web Forms fue la decisión correcta.


Generación de PDF con iText7: la etiqueta reglamentaria completa

La etiqueta reglamentaria de alimentación animal tiene 9 secciones obligatorias que deben aparecer en el PDF generado:

SecciónContenido
CabeceraIdentificación del producto, código, familia
Lote ddmmyyCódigo de lote con fecha
SubtítuloDescripción del artículo
Modo de empleoInstrucciones de uso
AnalíticosComposición nutricional detallada
ComposiciónIngredientes con notas de transgénicos
AditivosCon códigos europeos y dot leaders
Advertencias legalesAvisos reglamentarios
FooterInformación corporativa

Plantillas automáticas por familia

El sistema soporta tres familias de etiquetas con plantillas diferenciadas:

modelo136  → Formato estándar europeo
rcd        → Formato de registro de control
bigbag     → Formato para bolsas grandes

Cada plantilla tiene dimensiones, fuentes y disposición de secciones diferentes. iText7 permite definir cada una como un PdfDocument con Document y Paragraph configurables.

' Generación server-side con iText7
Using pdfDoc As New PdfDocument(New PdfWriter(outputPath))
    Dim doc As New Document(pdfDoc)
    ' Cabecera con logo y código de producto
    doc.Add(New Paragraph(headers).SetFontSize(14).SetBold())
    ' Sección de analíticos con tabla
    doc.Add(New Table(3).AddCell("Proteína").AddCell("..."))
    ' Firma QR como imagen
    doc.Add(New Image(qrImage))
    doc.Close()
End Using

Rendimiento: generación iText7 < 2s para el primer PDF (MISS), < 500ms para PDFs ya cacheados (TransmitFile).


Integración de datos: Oracle JDE + SQL Server

Doble fuente con patrón en dos fases

La clave arquitectónica de este sistema es que no depende del ERP en tiempo real para el flujo QR. El patrón es:

FASE 1 (Subida del TXT):
  TXT subido → Consulta Oracle JDE F4101 (7 capas de validación)
               → Enriquecido con SQL Server T_Etiquetas
               → Persistido en filesystem IIS

FASE 2 (Generación de QR / Lotes históricos):
  TXT enriquecido persistido → Generación de PDF/QR sin ERP
  Reconstrucción por fecha → VERIFICAR / GENERAR_CON_FECHA

Capas de validación Oracle JDE

La consulta a Oracle JD Edwards F4101 pasa por 7 capas de validación:

  1. Formato del artículo — el código existe en F4101
  2. Estado activo — el artículo no está dado de baja
  3. Días de caducidad — se obtiene el campo de fecha de caducidad
  4. Lotes disponibles — se verifican lotes IN en inventario
  5. Caché de lotes — lotes de 50 en memoria para consultas rápidas
  6. Consistencia de datos — el TXT coincide con los datos del ERP
  7. Persistencia — se guarda el enriquecimiento antes de continuar

SQL Server T_Etiquetas

Desde SQL Server se obtienen ModoEmpleo y Footer específicos por artículo. Esto permite que cada producto tenga su modo de empleo y pie de página personalizado sin depender de Oracle.

Resultado: el sistema funciona incluso si Oracle JDE está caído, porque el flujo QR solo lee el TXT ya persistido con los datos enriquecidos.


Seguridad: Defense in depth en el endpoint QR

El endpoint QR público es el componente más crítico desde el punto de vista de seguridad. Está accesible desde internet y sirve etiquetas de productos alimentarios. Un ataque exitoso podría permitir falsificar etiquetas.

Firma criptográfica AES-256/CBC + PBKDF2

Cada URL QR lleva una firma digital que garantiza su autenticidad:

  • Algoritmo: AES-256/CBC + PBKDF2-HMAC-SHA256
  • Iteraciones: 65.536
  • Codificación: Base64 URL-safe
  • Formato fijado: AES/CBC/PKCS5
URL base: https://servidor/qr/ABC123-20260520-001
Firma: AES-256/CBC/PBKDF2(key, iv, data) → Base64 URL-safe
QR: https://servidor/qr/ABC123-20260520-001?sig=<firma>

La app móvil que imprime y valida los QRs de los sacos tiene la clave documentada en código para verificar la firma. Si la firma no coincide, la etiqueta se rechaza automáticamente.

Capas de protección adicionales

CapaMecanismoPropósito
WhitelistValidación de caracteres permitidosAnti path-traversal (../, ..)
Rate limiting30 peticiones/minuto por IPAnti DDoS y brute force
Circuit breakerCorte automático tras erroresProtección ante caídas
CSRFToken de protecciónAnti cross-site request forgery
Session authAutenticación por sesiónPanel de administración
LoggingLogs granularizadosAuditoría de accesos

Problemas reales que encontré (y cómo los resolví)

1. Endpoint QR anónimo accesible desde internet

El endpoint QR es público por diseño (cualquier actor de la cadena debe poder verificar una etiqueta). Esto significa que está expuesto a internet sin autenticación previa.

Solución: defense in depth en capas. Firma AES-256/PBKDF2 en cada URL, whitelist de caracteres que impida path-traversal, rate limiting 30/minuto por IP, circuit breaker y CSRF. El endpoint solo sirve la etiqueta si la firma es válida.

2. Dependencia de ERP externo sin exponer credenciales

Oracle JDE era la fuente de datos principal, pero consultarlo en cada petición de QR significaba depender de un sistema externo que puede caerse o ser lento. Además, no quería exponer credenciales del ERP en el código del endpoint público.

Solución: patrón en dos fases. El ERP solo se consulta al subir el TXT. El flujo QR lee el TXT ya enriquecido y persistido en el filesystem de IIS. Sin joins en caliente, sin credenciales expuestas, sin bloqueos.

3. Blank page bug por cierre prematuro de la respuesta HTTP

En la generación de PDFs, Response.Close() cerraba la conexión antes de que el navegador recibiera el PDF completo, generando páginas en blanco.

Solución: sustitución de Response.Close() por CompleteRequest() + logging defensivo. Esto asegura que el pipeline IIS procese la respuesta completa antes de cerrarla.

4. Servir etiquetas históricas sin depender del ERP en caliente

Un cliente necesitaba reimprimir una etiqueta de un lote de hace 6 meses, pero el ERP no tenía el histórico accesible en tiempo real.

Solución: generación idempotente CODIGO-LOTE + caché en filesystem IIS + operaciones VERIFICAR y GENERAR_CON_FECHA. El sistema reconstruye la etiqueta a partir del TXT enriquecido persistido, soportando formatos antiguos (CODIGO-fecha.txt y CODIGO.txt).

5. Interoperabilidad criptográfica con la app móvil

La app Android que imprime los QRs en los sacos necesita validar las firmas. La interoperabilidad entre .NET (AES/CBC/PKCS5) y Android tiene particularidades que hay que documentar y fijar.

Solución: formato fijado AES/CBC/PKCS5 + PBKDF2-HMAC-SHA256 con 65.536 iteraciones y Base64 URL-safe. Documentado en código en ambas plataformas. La clave simétrica se gestiona de forma que ambas partes pueden verificar la firma sin exposición.


Despliegue en producción

Despliegue en el servidor corporativo de COVAP:

  • IIS como servidor web (configuración existente de la empresa)
  • Caché write-through de PDF a 24h en filesystem de IIS — el primer PDF se genera con iText7, los siguientes se sirven desde caché
  • Logs granularizados por módulo (oracle, parsing, pdf, cache, security, descarga)
  • Web.config + URL Rewrite para configuración multi-entorno (desarrollo, preproducción, producción)
  • Master Page + code-behind para auditoría clara del flujo

Números del proyecto

MétricaValor
Endpoints API (Web Forms)5
Secciones en etiqueta PDF9
Plantillas por familia3 (modelo136, rcd, bigbag)
Capas de validación Oracle JDE7
Secciones de seguridad6 (firma, whitelist, rate limit, circuit breaker, CSRF, session)
Iteraciones PBKDF265.536
Formatos TXT soportados2 (CODIGO-fecha.txt, CODIGO.txt)
Tiempo de desarrollo2 meses
Lighthouse Performance90/100
Accesibilidad93/100
SEO92/100
Best Practices98/100
QR con cache HIT< 500ms
QR con generación (MISS)< 2s
Panel admin (intranet)< 1.5s

Lo que aprendí

  1. Defense in depth no es opcional — un endpoint público que sirve datos de productos alimentarios necesita capas de seguridad. La firma criptográfica + rate limiting + circuit breaker es la combinación correcta.
  2. El patrón de dos fases es oro — separar la consulta al ERP de la generación de QRs elimina dependencias en caliente y hace el sistema resiliente a caídas externas.
  3. iText7 es potente pero exige atención — los cierres prematuros de respuesta (Response.Close()) causan bugs silenciosos. CompleteRequest() es la forma correcta.
  4. La cachaé write-through salva la vida — servir PDFs desde filesystem a 24h hace que el endpoint QR sea extremadamente rápido para el caso habitual (reimprimir etiquetas existentes).
  5. La criptografía necesita formato fijo — AES/CBC/PKCS5 + PBKDF2 con 65.536 iteraciones y Base64 URL-safe documentado en código es la única forma de garantizar interoperabilidad entre .NET y Android sin sorpresas.
  6. Web Forms sigue siendo viable — para proyectos en entorno corporativo con IIS existente y equipo experto, Web Forms con patrón AJAX y contratos JSON es una opción que entrega resultados sólidos.

¿Necesitas algo similar?

Si tu empresa necesita automatizar la generación de documentos reglamentarios, implementar trazabilidad con códigos QR firmados, o cualquier proyecto .NET con integración de ERP y seguridad reforzada, hablemos.

Otros casos reales:

Fran Cobos

Fran Cobos

Desarrollador Full Stack especializado en IA aplicada, automatización y desarrollo web. Escribo sobre herramientas, tutoriales y casos reales para programadores.

¿Necesitas desarrollo a medida?

Apps web, IA, módulos ERP — cuéntame tu proyecto.