# Plan de Trazabilidad de Lotes - Seguidor de Lotes ## Estado Actual del Proyecto 🌐 **ProducciΓ³n**: https://lotes.nucleoriofrio.com ### βœ… Completado - [x] Modelo de base de datos (grafo DAG) - [x] Scripts de inicializaciΓ³n y seed con datos de ejemplo - [x] API REST completa (11 endpoints) - [x] FunciΓ³n recursiva de trazabilidad SQL - [x] AutenticaciΓ³n con Authentik Proxy - [x] CI/CD con Gitea Actions - [x] Despliegue automΓ‘tico con Docker Compose - [x] Persistencia de datos entre deploys - [x] ConfiguraciΓ³n automΓ‘tica de autenticaciΓ³n PostgreSQL - [x] Botones de prueba de API en frontend ### 🎯 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]/ β”‚ β”‚ β”‚ β”‚ └── 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:** - `uva` - `despulpado_primera` - `despulpado_segunda` - `despulpado_rechazos` - `oreado` - `presecado` - `reposo` - `secado` #### 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Γ© uva - `despulpado` - Despulpado del cafΓ© - `oreado` - Proceso de oreado - `presecado` - Presecado - `reposo` - Reposo - `secado` - Secado final - `traslado` - Movimiento de lote - `mezcla` - Mezcla de lotes *Operaciones de ajuste/correcciΓ³n:* - `ajuste_merma` - CorrecciΓ³n por pΓ©rdida de peso - `ajuste_cantidad` - CorrecciΓ³n de cantidad registrada - `ajuste_tipo` - CorrecciΓ³n de tipo de lote - `correccion_asignacion` - CorrecciΓ³n de lote mal asignado - `fusion_manual` - FusiΓ³n manual de lotes - `division_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 ```bash GET /api/lotes # Con filtros GET /api/lotes?tipo=secado&limit=10 ``` **Respuesta:** ```json { "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 ```bash GET /api/lotes/{id}/trazabilidad ``` **Respuesta:** ```json { "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) ```bash 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:** ```json { "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" } ] } } ``` --- ## Componentes Frontend ### Componentes de Lotes 1. **LotesTable.vue** - Tabla con listado de lotes - Filtros por tipo - Acciones: Ver, Editar, Ver Trazabilidad 2. **LoteForm.vue** - Formulario para crear/editar lotes - ValidaciΓ³n de campos - Soporte para metadata JSON 3. **LoteCard.vue** - Vista de detalle de un lote - InformaciΓ³n completa del lote - Acciones rΓ‘pidas 4. **TrazabilidadTree.vue** - VisualizaciΓ³n del historial completo - Árbol indentado por profundidad - EstadΓ­sticas de trazabilidad ### Componentes de Operaciones 1. **OperacionesTable.vue** - Tabla con listado de operaciones - Filtros por tipo - Acciones: Ver detalle 2. **OperacionForm.vue** - Formulario multi-paso: 1. Seleccionar tipo de operaciΓ³n 2. Seleccionar lotes input 3. Definir lotes output - CreaciΓ³n transaccional --- ## Consultas SQL Importantes ### Obtener trazabilidad completa (funciΓ³n recursiva) ```sql 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 ```sql 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 ```env # 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 1. **Push a main/master** β†’ Gitea Actions se ejecuta automΓ‘ticamente 2. **Build**: Construye imagen Docker 3. **Push**: Sube imagen al registry de Gitea 4. **Deploy**: Ejecuta `docker-compose up -d` en el servidor ### Primer Inicio Al iniciar PostgreSQL por primera vez, ejecutarΓ‘ automΓ‘ticamente: 1. `01_schema.sql` - Crea tablas, Γ­ndices, funciones 2. `02_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: ```bash docker logs seguidorDeLotes-postgres ``` ### Los datos de ejemplo no se cargan Eliminar el volumen y recrear: ```bash docker-compose down -v docker-compose up -d ``` ### Error de conexiΓ³n a PostgreSQL Verificar que el contenedor postgres estΓ© saludable: ```bash 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.