- Agregar sección de botones de debug (temporales) en README - Documentar endpoints /api/debug/reset-database y /api/debug/seed-database - Incluir advertencias y flujos de uso recomendados - Marcar claramente como TEMPORALES con instrucciones de no eliminar - Actualizar PLAN con endpoints de debug en sección Completado
18 KiB
Plan de Trazabilidad de Lotes - Seguidor de Lotes
Estado Actual del Proyecto
🌐 Producción: https://lotes.nucleoriofrio.com
✅ Completado
- Modelo de base de datos (grafo DAG)
- Scripts de inicialización y seed con datos de ejemplo
- API REST completa (11 endpoints + 2 debug temporales)
- Función recursiva de trazabilidad SQL
- Autenticación con Authentik Proxy
- CI/CD con Gitea Actions
- Despliegue automático con Docker Compose
- Persistencia de datos entre deploys
- Configuración automática de autenticación PostgreSQL
- Botones de prueba de API en frontend
- Botones de debug temporales para gestión de BD (reset y seed)
- ⚠️ Marcados como TEMPORALES - NO ELIMINAR sin consultar
POST /api/debug/reset-database- DROP de todas las tablasPOST /api/debug/seed-database- Carga datos de ejemplo- UI con advertencias notorias en rojo
🎯 Objetivo Actual: Visualización del Grafo
Meta: Implementar un componente visual que muestre el grafo de trazabilidad de forma interactiva.
Requisitos:
- Visualizar el flujo completo de un lote desde origen hasta estado actual
- Mostrar nodos (lotes) y aristas (operaciones)
- Interactividad: zoom, pan, click en nodos
- Metadatos al hacer hover
- Indicadores visuales por tipo de operación
Tecnologías a considerar:
- D3.js (máximo control, curva de aprendizaje)
- vis.js (redes y grafos, fácil de usar)
- Cytoscape.js (grafos complejos, buena performance)
- ELK.js (layout automático de grafos)
Descripción General
El Sistema de Trazabilidad de Lotes rastrea el flujo completo del café desde el ingreso de uva hasta el secado final. Implementa un modelo de grafo (DAG) que permite representar operaciones complejas:
- División: Un lote se divide en varios (ej: despulpado → primera, segunda, rechazos)
- Combinación: Varios lotes se mezclan en uno (ej: varios reposos → un secado)
- Transformación: Un lote cambia de estado (ej: oreado → presecado)
- Ajustes: Correcciones de merma, cantidad o tipo sin alterar el historial
Arquitectura del Sistema
Stack Tecnológico
- Frontend: Nuxt 4 + Nuxt UI + Vue 3
- Backend: Nuxt Server API Routes
- Base de Datos: PostgreSQL 16
- Autenticación: Authentik Proxy Outpost
- Despliegue: Docker + Traefik + Gitea Actions
Estructura del Proyecto
/home/draganel/repos/seguidorDeLotes/
├── nuxt4/
│ ├── app/
│ │ ├── components/
│ │ │ ├── lotes/
│ │ │ │ ├── LotesTable.vue
│ │ │ │ ├── LoteForm.vue
│ │ │ │ ├── LoteCard.vue
│ │ │ │ └── TrazabilidadTree.vue
│ │ │ └── operaciones/
│ │ │ ├── OperacionesTable.vue
│ │ │ └── OperacionForm.vue
│ │ ├── composables/
│ │ │ └── useLotes.ts
│ │ └── app.vue
│ ├── server/
│ │ ├── api/
│ │ │ ├── lotes/
│ │ │ │ ├── index.get.ts
│ │ │ │ ├── index.post.ts
│ │ │ │ ├── [id].get.ts
│ │ │ │ ├── [id].patch.ts
│ │ │ │ ├── [id].delete.ts
│ │ │ │ └── [id]/
│ │ │ ├── debug/ # ⚠️ TEMPORALES
│ │ │ │ ├── reset-database.post.ts
│ │ │ │ └── seed-database.post.ts
│ │ │ │ └── trazabilidad.get.ts
│ │ │ └── operaciones/
│ │ │ ├── index.get.ts
│ │ │ ├── index.post.ts
│ │ │ └── [id].get.ts
│ │ ├── utils/
│ │ │ ├── db.ts
│ │ │ └── queries.ts
│ │ └── database/
│ │ ├── 01_schema.sql
│ │ ├── 02_seed.sql
│ │ └── README.md
│ └── package.json
├── docker-compose.yml
└── PLAN_TRAZABILIDAD.md (este archivo)
Modelo de Datos
Concepto Central: Grafo de Lotes y Operaciones
El sistema representa la trazabilidad como un grafo dirigido acíclico (DAG):
- Nodos = Lotes: Estados físicos del café
- Aristas = Operaciones: Eventos que transforman lotes
Tablas Principales
1. lotes
Representa cualquier estado físico del café.
| Campo | Tipo | Descripción |
|---|---|---|
id |
UUID | Identificador único |
codigo |
TEXT | Código legible (ej: UVA-001, SEC-042) |
tipo |
TEXT | Tipo de lote (uva, despulpado_primera, oreado, etc.) |
fecha_creado |
TIMESTAMPTZ | Fecha de creación |
lugar_id |
INTEGER | Lugar donde se encuentra (opcional) |
cantidad_kg |
NUMERIC | Cantidad en kilogramos |
meta |
JSONB | Datos adicionales (humedad, notas, etc.) |
Tipos de lote válidos:
uvadespulpado_primeradespulpado_segundadespulpado_rechazosoreadopresecadorepososecado
2. operaciones
Representa eventos donde lotes se transforman, combinan o dividen.
| Campo | Tipo | Descripción |
|---|---|---|
id |
UUID | Identificador único |
tipo |
TEXT | Tipo de operación (despulpado, oreado, etc.) |
fecha |
TIMESTAMPTZ | Fecha de la operación |
lugar_id |
INTEGER | Lugar donde ocurrió (opcional) |
meta |
JSONB | Datos adicionales |
Tipos de operación válidos:
Operaciones de proceso normal:
ingreso- Ingreso de café uvadespulpado- Despulpado del caféoreado- Proceso de oreadopresecado- Presecadoreposo- Repososecado- Secado finaltraslado- Movimiento de lotemezcla- Mezcla de lotes
Operaciones de ajuste/corrección:
ajuste_merma- Corrección por pérdida de pesoajuste_cantidad- Corrección de cantidad registradaajuste_tipo- Corrección de tipo de lotecorreccion_asignacion- Corrección de lote mal asignadofusion_manual- Fusión manual de lotesdivision_manual- División manual de lotes
3. operacion_lotes
Relación muchos a muchos entre operaciones y lotes.
| Campo | Tipo | Descripción |
|---|---|---|
operacion_id |
UUID | ID de la operación |
lote_id |
UUID | ID del lote |
rol |
TEXT | 'input' o 'output' |
cantidad_kg |
NUMERIC | Cantidad específica utilizada/producida |
Diagrama del Grafo de Ejemplo
Flujo completo incluido en los datos de ejemplo (02_seed.sql):
┌───────────────────────────────┐
│ INGRESO UVA │
│ OP1: ingreso_uva │
└─────────────┬─────────────────┘
│
▼
[L_UVA1] 2086 kg
│
│
┌─────────────┴──────────────────┐
│ DESPULPADO │
│ OP2: despulpado │
└───┬─────────┬─────────┬────────┘
│ │ │
▼ ▼ ▼
[L_PRIM1] [L_SEG1] [L_RECH1]
1500 kg 400 kg 150 kg
│
│
┌────────────┴───────────────────────────┐
│ OREADO │
│ OP3: oreado (registro mal) │
└─────────────────────┬───────────────────┘
│
▼
[L_ORE1] 1500 kg
│
│
┌────────────────────────┴────────────────────────────┐
│ AJUSTE DE MERMA │
│ OP4: ajuste_merma (1500 → 1480 kg) │
└───────────────────────────┬──────────────────────────┘
│
▼
[L_ORE1A] 1480 kg
│
│
┌───────────────────────┴───────────────────────────┐
│ AJUSTE DE TIPO │
│ OP5: ajuste_tipo (oreado → presecado) │
└──────────────────────────┬─────────────────────────┘
│
▼
[L_PRE1] 1480 kg
│
│
┌────────────────────┴────────────────────────┐
│ REPOSO │
│ OP6: reposo │
└───────────────────┬──────────────────────────┘
│
▼
[L_REP1] 1480 kg
│
│ (+ [L_REP2] 520 kg)
│ │
┌──────────────────────────┴──────┴────────────────────┐
│ SECADO │
│ OP7: secado (mezcla) │
└───────────────────────┬───────────────────────────────┘
│
▼
[L_SEC1] 2000 kg
Ejemplos de Uso de la API
1. Listar todos los lotes
GET /api/lotes
# Con filtros
GET /api/lotes?tipo=secado&limit=10
Respuesta:
{
"success": true,
"data": [
{
"id": "uuid-here",
"codigo": "SEC-001",
"tipo": "secado",
"fecha_creado": "2025-11-21T10:00:00Z",
"cantidad_kg": 2000,
"meta": { "humedad_final": 11.5 }
}
],
"count": 1
}
2. Obtener trazabilidad completa de un lote
GET /api/lotes/{id}/trazabilidad
Respuesta:
{
"success": true,
"data": {
"historial": [
{
"lote_id": "uuid-sec-001",
"codigo": "SEC-001",
"tipo": "secado",
"cantidad_kg": 2000,
"operacion_id": "uuid-op-secado",
"operacion_tipo": "secado",
"profundidad": 0
},
{
"lote_id": "uuid-rep-001",
"codigo": "REP-001",
"tipo": "reposo",
"cantidad_kg": 1480,
"operacion_id": "uuid-op-reposo",
"operacion_tipo": "reposo",
"profundidad": 1
}
// ... más ancestros
],
"estadisticas": {
"total_ancestros": 7,
"profundidad_maxima": 6,
"kg_iniciales": 2086
}
}
}
3. Crear una nueva operación (ejemplo: despulpado)
POST /api/operaciones
Content-Type: application/json
{
"tipo": "despulpado",
"inputs": [
{ "lote_id": "uuid-uva-001", "cantidad_kg": 2086 }
],
"outputs": [
{ "codigo": "PRIM-001", "tipo": "despulpado_primera", "cantidad_kg": 1500 },
{ "codigo": "SEG-001", "tipo": "despulpado_segunda", "cantidad_kg": 400 },
{ "codigo": "RECH-001", "tipo": "despulpado_rechazos", "cantidad_kg": 150 }
],
"meta": {
"pila": 2,
"operador": "Juan Pérez"
}
}
Respuesta:
{
"success": true,
"data": {
"operacion": {
"id": "uuid-operacion",
"tipo": "despulpado",
"fecha": "2025-11-21T10:00:00Z",
"meta": { "pila": 2, "operador": "Juan Pérez" }
},
"lotes_creados": [
{ "id": "uuid-prim", "codigo": "PRIM-001", "tipo": "despulpado_primera" },
{ "id": "uuid-seg", "codigo": "SEG-001", "tipo": "despulpado_segunda" },
{ "id": "uuid-rech", "codigo": "RECH-001", "tipo": "despulpado_rechazos" }
]
}
}
4. Endpoints de Debug (⚠️ Temporales)
⚠️ ADVERTENCIA: Estos endpoints son temporales y están marcados para no eliminar sin consultar.
Reset Database
POST /api/debug/reset-database
Descripción: Elimina completamente todas las tablas (DROP TABLE) para reinicializar la BD.
Respuesta:
{
"success": true,
"message": "Base de datos reseteada. Tablas eliminadas completamente. El próximo deploy las recreará con datos de ejemplo."
}
Flujo recomendado:
- Llamar a este endpoint desde el frontend (botón rojo)
- Hacer push para triggear workflow
- El workflow detecta que no hay tablas y las recrea con seed
Seed Database
POST /api/debug/seed-database
Descripción: Ejecuta el script de seed (02_seed.sql) para cargar datos de ejemplo.
Respuesta:
{
"success": true,
"message": "Datos de ejemplo cargados: 10 lotes, 7 operaciones, 16 relaciones"
}
Uso típico:
- Después de resetear la BD manualmente
- Para pruebas de desarrollo
- Para restaurar datos de ejemplo rápidamente
⚠️ Nota importante: Estos endpoints incluyen comentarios muy visibles en el código fuente:
/**
* ⚠️ ⚠️ ⚠️ ENDPOINT DE DEBUG - TEMPORAL ⚠️ ⚠️ ⚠️
*
* ⚠️ NO ELIMINAR SIN CONSULTAR A DARIO/DRAGANEL/NUCLEO000 ⚠️
*/
Componentes Frontend
Componentes de Lotes
-
LotesTable.vue
- Tabla con listado de lotes
- Filtros por tipo
- Acciones: Ver, Editar, Ver Trazabilidad
-
LoteForm.vue
- Formulario para crear/editar lotes
- Validación de campos
- Soporte para metadata JSON
-
LoteCard.vue
- Vista de detalle de un lote
- Información completa del lote
- Acciones rápidas
-
TrazabilidadTree.vue
- Visualización del historial completo
- Árbol indentado por profundidad
- Estadísticas de trazabilidad
Componentes de Operaciones
-
OperacionesTable.vue
- Tabla con listado de operaciones
- Filtros por tipo
- Acciones: Ver detalle
-
OperacionForm.vue
- Formulario multi-paso:
- Seleccionar tipo de operación
- Seleccionar lotes input
- Definir lotes output
- Creación transaccional
- Formulario multi-paso:
Consultas SQL Importantes
Obtener trazabilidad completa (función recursiva)
SELECT * FROM get_trazabilidad('uuid-del-lote-final');
Esta función CTE recursiva camina el grafo hacia atrás desde el lote final hasta los ingresos iniciales.
Ver lotes con su operación de origen
SELECT * FROM vista_lotes_con_origen
ORDER BY fecha_creado DESC;
Despliegue
Requisitos
- Docker y Docker Compose
- Acceso al servidor con Traefik y Authentik configurados
- Gitea con Actions habilitado
Variables de Entorno
# PostgreSQL
POSTGRES_USER=seguidor
POSTGRES_PASSWORD=seguidor_password
POSTGRES_DB=seguidor_lotes
# Aplicación
APP_NAME=seguidorDeLotes
APP_DOMAIN=lotes.nucleoriofrio.com
NUXT_PUBLIC_APP_URL=https://lotes.nucleoriofrio.com
# Registry
REG=gitea.nucleoriofrio.com
REPO_OWNER=nucleo000
Proceso de Deploy
- Push a main/master → Gitea Actions se ejecuta automáticamente
- Build: Construye imagen Docker
- Push: Sube imagen al registry de Gitea
- Deploy: Ejecuta
docker-compose up -den el servidor
Primer Inicio
Al iniciar PostgreSQL por primera vez, ejecutará automáticamente:
01_schema.sql- Crea tablas, índices, funciones02_seed.sql- Inserta datos de ejemplo
Próximos Pasos
Fase 2: Visualización Avanzada
- Integrar librería de grafos (Cytoscape.js o D3.js)
- Vista de grafo interactivo
- Zoom, pan y selección de nodos
- Colores por tipo de lote
Fase 3: Reportes y Análisis
- Reporte de trazabilidad en PDF
- Estadísticas por período
- Gráficos de volumen procesado
- Análisis de mermas
Fase 4: Características Avanzadas
- Gestión de lugares (patios, pilas, bodegas)
- QR codes para lotes
- Escáner móvil
- Notificaciones de eventos
- Integración con básculas
Troubleshooting
La base de datos no se inicializa
Verificar logs del contenedor postgres:
docker logs seguidorDeLotes-postgres
Los datos de ejemplo no se cargan
Eliminar el volumen y recrear:
docker-compose down -v
docker-compose up -d
Error de conexión a PostgreSQL
Verificar que el contenedor postgres esté saludable:
docker ps
# Buscar "healthy" en la columna STATUS
Contacto y Soporte
- Proyecto: Nucleo Rio Frio
- Desarrollador: Dario (draganel)
- Repositorio: https://gitea.nucleoriofrio.com/nucleo000/seguidorDeLotes
Licencia
Este proyecto es propiedad de Nucleo Rio Frio y está desarrollado para uso interno.