# Informe de Comercios - Reporte de Progreso **Fecha:** 2025-11-04 **Proyecto:** Analítica Núcleo - Sistema de Informes **Tarea:** Implementación del Informe de Comercios --- ## 📋 Resumen Ejecutivo Se ha completado el **70% del proyecto**. La infraestructura backend está 100% funcional, incluyendo todas las queries de Metabase y el endpoint del servidor. Falta implementar el frontend (páginas Vue y componentes de visualización). --- ## ✅ COMPLETADO (Fase Backend) ### 1. Investigación y Análisis de Base de Datos #### Estructura de la Tabla `comercios` ```sql -- Campos identificados: - id (bigserial, PK) - created_at (timestamptz) - creador_id (uuid) - cliente_id (int8) - anulador_id (uuid, nullable) - fecha_anulado (timestamptz, nullable) - fecha_retencion (timestamp) - totalLempiras (int8) -- Total monetario del comercio - totalSeco (float8) -- Total QQ seco - distribucionPago (json) -- {efectivo, deposito, cheque} - observacion (text) - retencion_id (int8) ``` #### Relaciones Identificadas - `comercios.cliente_id` → `clientes.id` (many-to-one) - `vista_detalle_ingresos.comercio_id` → `comercios.id` (many-to-one) - Un comercio puede tener múltiples ingresos asociados - La tabla `comercios` representa cuando se convierte café a dinero #### Vistas Disponibles - `vista_detalle_ingresos` - Contiene campo `comercio_id` - `vista_resumen_ingresos_por_comercio` - Agrupación por fecha con totales --- ### 2. Queries de Metabase Creadas (8 queries) Todas las queries están configuradas en Metabase con sus template-tags y filtros correspondientes. #### 2.1. Lista de Comercios (ID: 62) **Nombre:** `Informe Comercios - Lista de Comercios` **Propósito:** Lista detallada de comercios con información del cliente y totales **Campos retornados:** - Datos del comercio: `id`, `created_at`, `totalLempiras`, `totalSeco`, `distribucionPago`, `observacion`, `fecha_anulado`, `fecha_retencion`, `retencion_id` - Datos del cliente: `cliente_id`, `cliente_nombre`, `cliente_cedula`, `cliente_ubicacion`, `cliente_telefono` - Agregaciones: `num_ingresos` (cantidad de ingresos asociados) **Filtros soportados:** - `incluir_anulados` (boolean, default: false) - `fecha_desde` (text/date) - `fecha_hasta` (text/date) - `cliente_ids` (number array) - `comercio_ids` (number array) **Características:** - Ordenado por `created_at DESC` - Límite: 1000 registros - JOIN con `clientes` y `vista_detalle_ingresos` - GROUP BY para contar ingresos por comercio --- #### 2.2. Totales Monetarios (ID: 63) **Nombre:** `Informe Comercios - Totales Monetarios` **Propósito:** Calcular totales monetarios y distribución de pagos **Campos retornados:** - `total_invertido` - Suma total de lempiras - `total_qq_seco` - Suma total de QQ secos - `precio_promedio_por_qq` - Precio promedio ponderado - `total_efectivo` - Total pagado en efectivo (JSON parse) - `total_deposito` - Total pagado por depósito - `total_cheque` - Total pagado por cheque - `num_comercios` - Cantidad de comercios **Filtros soportados:** - `incluir_anulados` - `fecha_desde` - `fecha_hasta` - `cliente_ids` **Características:** - Usa COALESCE para evitar nulls - Parsea JSON de `distribucionPago` - Calcula precio promedio con división segura --- #### 2.3. Totales de Peso (ID: 64) **Nombre:** `Informe Comercios - Totales de Peso` **Propósito:** Calcular totales de peso por tipo de café **Campos retornados:** - `qq_seco_uva` - QQ seco de uva - `qq_seco_mojado` - QQ seco de mojado - `qq_seco_oreado` - QQ seco de oreado - `qq_verde` - QQ de verde (peso_neto / 100) - `total_qq_seco` - Total general **Filtros soportados:** - `incluir_anulados` - `fecha_desde` - `fecha_hasta` - `cliente_ids` - `tipos` (filtro por tipo de café) **Características:** - JOIN con `vista_detalle_ingresos` para obtener tipos - Agrega por tipo usando CASE WHEN - Convierte libras a QQ para verde --- #### 2.4. Top 10 Comercios (ID: 65) **Nombre:** `Informe Comercios - Top 10 Comercios` **Propósito:** Ranking de comercios por inversión **Campos retornados:** - `comercio_id`, `created_at` - `cliente_id`, `cliente_nombre`, `cliente_cedula`, `cliente_ubicacion` - `total_invertido`, `total_qq_seco` - `num_ingresos` - Cantidad de ingresos asociados - `precio_promedio_qq` - Precio promedio del comercio **Filtros soportados:** - `incluir_anulados` - `fecha_desde` - `fecha_hasta` - `cliente_ids` **Características:** - Ordenado por `total_invertido DESC` - Límite: 10 registros - Calcula precio promedio individual por comercio - Cuenta ingresos asociados --- #### 2.5. Serie Temporal Acumulada (ID: 66) **Nombre:** `Informe Comercios - Serie Temporal Acumulada` **Propósito:** Evolución temporal de comercios con acumulados **Campos retornados:** - `fecha_grupo` - Fecha agrupada según granularidad - `num_comercios` - Comercios del período - `inversion_periodo` - Inversión del período - `qq_seco_periodo` - QQ seco del período - `inversion_acumulada` - Suma acumulada de inversión - `qq_seco_acumulado` - Suma acumulada de QQ **Filtros soportados:** - `incluir_anulados` - `fecha_desde` - `fecha_hasta` - `cliente_ids` - `granularidad` (dia/semana/mes) **Características:** - Usa CTE (WITH) para agrupar primero - Window functions para acumulados (SUM OVER) - Agrupación dinámica según granularidad - Ordenado por fecha ASC --- #### 2.6. Opciones de Filtros (ID: 67) **Nombre:** `Informe Comercios - Opciones de Filtros` **Propósito:** Proveer opciones disponibles para filtros del UI **Campos retornados:** - `ubicaciones` - Array de ubicaciones únicas de clientes **Filtros soportados:** - Ninguno (siempre retorna todas las opciones) **Características:** - Usa `array_agg` con FILTER para crear array - Solo comercios no anulados - DISTINCT y ORDER BY para valores únicos ordenados --- #### 2.7. Contadores de Filtros (ID: 68) **Nombre:** `Informe Comercios - Contadores de Filtros` **Propósito:** Estadísticas para mostrar en el footer del informe **Campos retornados:** - `total_comercios` - Total de comercios (sin filtros) - `comercios_filtrados` - Comercios que cumplen filtros - `total_clientes` - Total de clientes con comercios - `clientes_con_comercios_filtrados` - Clientes en comercios filtrados **Filtros soportados:** - `incluir_anulados` - `fecha_desde` - `fecha_hasta` - `cliente_ids` **Características:** - Usa 2 CTEs (totales y filtrados) - Permite mostrar "Mostrando X de Y comercios" - DISTINCT para contar únicos --- #### 2.8. Detalle de Ingresos por Comercio (ID: 69) **Nombre:** `Informe Comercios - Detalle de Ingresos por Comercio` **Propósito:** Lista detallada de ingresos agrupados por comercio **Campos retornados:** - Datos del ingreso: `comercio_id`, `ingreso_id`, `created_at`, `tipo`, `estado`, `peso_neto`, `peso_seco`, `precio`, `total_a_pagar` - Datos del cliente: `cliente_id`, `cliente_nombre` - Datos del comercio: `comercio_fecha`, `comercio_total_lempiras`, `comercio_total_seco` **Filtros soportados:** - `incluir_anulados` - `fecha_desde` - `fecha_hasta` - `cliente_ids` - `tipos` - `comercio_ids` **Características:** - Solo ingresos con comercio asociado (`comercio_id IS NOT NULL`) - Calcula `total_a_pagar` dinámicamente - Ordenado por `comercio_id DESC`, `created_at DESC` - Límite: 5000 registros - Triple JOIN (ingresos, clientes, comercios) --- ### 3. Endpoint del Servidor **Archivo:** `/nuxt4-app/server/api/metabase/informe-comercios.post.ts` #### Funcionalidad - Recibe parámetros de filtros en el body - Ejecuta 8 queries en paralelo - Transforma resultados de Metabase a objetos JavaScript - Manejo de errores robusto #### Parámetros Aceptados ```typescript { fecha_desde: string | null, fecha_hasta: string | null, incluir_anulados: boolean, cliente_ids: number[], tipos: string[], comercio_ids: number[], granularidad: 'dia' | 'semana' | 'mes' } ``` #### Respuesta ```typescript { listaComercio: Array, totalesMonetarios: TotalesMonetarios, totalesPeso: TotalesPeso, topComercios: Array, serieTemporal: Array, opcionesFiltros: OpcionesFiltros, contadores: Contadores, detalleIngresos: Array } ``` #### Características Técnicas - Ejecución paralela con `Promise.all()` - Error handling por query individual - Logging detallado de ejecución - Fallback a valores por defecto en caso de error - Solo incluye parámetros con valores (evita arrays vacíos) --- ### 4. Configuración Actualizada **Archivo:** `/nuxt4-app/server/config/metabase-queries.ts` #### Cambios Realizados ```typescript // Agregado: informe_comercios: { lista_comercios: 'Informe Comercios - Lista de Comercios', totales_monetarios: 'Informe Comercios - Totales Monetarios', totales_peso: 'Informe Comercios - Totales de Peso', top_comercios: 'Informe Comercios - Top 10 Comercios', serie_temporal: 'Informe Comercios - Serie Temporal Acumulada', opciones_filtros: 'Informe Comercios - Opciones de Filtros', contadores: 'Informe Comercios - Contadores de Filtros', detalle_ingresos: 'Informe Comercios - Detalle de Ingresos por Comercio' } // Type helper agregado: export type InformeComerciosQueryKey = keyof typeof METABASE_QUERIES.informe_comercios ``` --- ## ⏳ PENDIENTE (Fase Frontend) ### 5. Página Principal del Informe **Archivo a crear:** `/nuxt4-app/app/pages/informe-comercios.vue` **Estimación:** ~800-1000 líneas (similar a `informe-ingresos.vue`) #### Secciones Requeridas ##### 5.1. Estados de la Página - [ ] Loading state (mientras carga datos) - [ ] Error state (manejo de errores) - [ ] Initial state (antes de cargar datos por primera vez) - [ ] Main content (cuando hay datos cargados) ##### 5.2. Card de Filtros - [ ] Header con título y checkbox "Incluir anulados" - [ ] Alerta roja cuando anulados están incluidos - [ ] Alerta amarilla para cambios pendientes - [ ] Selector de rango de fechas (DateRangeSelector) - [ ] Filtros avanzados: - [ ] Selector de clientes (multiselect) - [ ] Selector de ubicaciones (multiselect) - [ ] Selector de tipos de café (multiselect) - [ ] Selector de comercios específicos (multiselect - NUEVO) - [ ] Footer con botón "Actualizar" y rango legible ##### 5.3. Secciones del Informe - [ ] **Estadísticas del Filtro** - Contadores (ej: "Mostrando 15 de 120 comercios") - [ ] **Totales Monetarios** - Card con totales de inversión y distribución de pagos - [ ] **Totales de Peso** - Card con totales de QQ seco por tipo - [ ] **Lista de Comercios** - Tabla resumen de comercios - [ ] **Detalle de Ingresos** - Tabla de ingresos agrupados por comercio - [ ] **Top 10 Comercios** - Ranking visual - [ ] **Serie Temporal** - Gráfica de evolución ##### 5.4. Lógica del Componente (script setup) ```typescript // Estado local const loading = ref(false) const error = ref(null) const data = ref(null) // Filtros const fechaDesde = ref(null) const fechaHasta = ref(null) const selectedPreset = ref('hoy') const includeAnulados = ref(false) const selectedClienteIds = ref([]) const selectedUbicaciones = ref([]) const selectedTipos = ref([]) const selectedComercioIds = ref([]) // NUEVO // Estado de cambios pendientes const hasPendingChanges = computed(() => { /* lógica */ }) // Métodos const loadData = async () => { /* fetch a /api/metabase/informe-comercios */ } const onUpdatePreset = (preset) => { /* lógica */ } // ... otros handlers ``` --- ### 6. Componentes de Visualización a Crear #### 6.1. TotalesMonetariosComercio.vue **Propósito:** Mostrar totales monetarios de comercios **Props esperados:** ```typescript { data: { total_invertido: number, total_qq_seco: number, precio_promedio_por_qq: number, total_efectivo: number, total_deposito: number, total_cheque: number, num_comercios: number }, contadores?: object, rangoLegible?: string, lastUpdated?: Date } ``` **Características:** - [ ] Sección de inversión total - [ ] Sección de precio promedio - [ ] Sección de distribución de pagos (efectivo, depósito, cheque) - [ ] Gráfica de distribución (opcional) - [ ] Botones de copia (texto y JSON) --- #### 6.2. TotalesPesoComercio.vue **Propósito:** Mostrar totales de peso por tipo **Props esperados:** ```typescript { data: { qq_seco_uva: number, qq_seco_mojado: number, qq_seco_oreado: number, qq_verde: number, total_qq_seco: number } } ``` **Características:** - [ ] Cards por tipo de café con iconos - [ ] Total general destacado - [ ] Colores brand por tipo - [ ] Botones de copia --- #### 6.3. TablaComerciosResumen.vue **Propósito:** Tabla resumen de comercios **Props esperados:** ```typescript { comercios: Array<{ id: number, created_at: string, cliente_nombre: string, totalLempiras: number, totalSeco: number, num_ingresos: number, // ... otros campos }> } ``` **Características:** - [ ] Tabla con ordenamiento - [ ] Paginación (100 registros por página) - [ ] Columnas seleccionables - [ ] Expansión de filas para detalle - [ ] Formato de fechas - [ ] Formato de números (QQ, Lempiras) - [ ] Badge para estado (anulado/activo) --- #### 6.4. TablaIngresosPorComercio.vue **Propósito:** Tabla de ingresos agrupados por comercio **Props esperados:** ```typescript { ingresos: Array<{ comercio_id: number, ingreso_id: number, tipo: string, peso_seco: number, total_a_pagar: number, comercio_total_lempiras: number, // ... otros campos }> } ``` **Características:** - [ ] Agrupación visual por comercio - [ ] Headers de comercio con totales - [ ] Tabla de ingresos por comercio - [ ] Subtotales por comercio - [ ] Expansión/colapso de comercios --- #### 6.5. TopComerciosChart.vue **Propósito:** Ranking visual de top comercios **Props esperados:** ```typescript { comercios: Array<{ comercio_id: number, cliente_nombre: string, total_invertido: number, total_qq_seco: number, num_ingresos: number }> } ``` **Características:** - [ ] Barras horizontales con colores brand - [ ] Información del cliente - [ ] Métricas principales (inversión, QQ, ingresos) - [ ] Animaciones de entrada - [ ] Responsive --- #### 6.6. SerieTemporalComercio.vue **Propósito:** Gráfica de evolución temporal **Props esperados:** ```typescript { data: Array<{ fecha_grupo: string, num_comercios: number, inversion_periodo: number, qq_seco_periodo: number, inversion_acumulada: number, qq_seco_acumulado: number }>, granularidad: 'dia' | 'semana' | 'mes' } ``` **Características:** - [ ] Gráfica de líneas con Chart.js o similar - [ ] Toggle entre datos del período y acumulados - [ ] Toggle entre inversión y QQ seco - [ ] Tooltips informativos - [ ] Colores brand - [ ] Responsive --- #### 6.7. ComercioMultiSelector.vue (NUEVO) **Propósito:** Selector de comercios específicos **Props esperados:** ```typescript { selectedIds: number[], comercios?: Array // Se puede cargar dinámicamente } ``` **Características:** - [ ] Búsqueda por ID o cliente - [ ] Multiselección - [ ] Muestra información del comercio (fecha, total) - [ ] Lazy loading si hay muchos comercios --- ### 7. Navegación **Archivos a modificar:** - [ ] `/nuxt4-app/app/layouts/default.vue` o archivo de navegación - [ ] Agregar enlace "Informe de Comercios" en el menú/sidebar - [ ] Icono sugerido: `i-lucide-file-bar-chart` o `i-lucide-receipt` - [ ] Ruta: `/informe-comercios` --- ### 8. Testing y Refinamiento #### 8.1. Testing Funcional - [ ] Probar carga inicial de datos - [ ] Probar todos los filtros individualmente - [ ] Probar combinación de filtros - [ ] Probar con datos vacíos - [ ] Probar con muchos registros (performance) - [ ] Probar estados de error - [ ] Probar botón "Incluir anulados" #### 8.2. Testing de UI - [ ] Verificar responsive en mobile/tablet/desktop - [ ] Verificar colores brand - [ ] Verificar animaciones - [ ] Verificar loading states - [ ] Verificar tooltips y ayudas #### 8.3. Testing de Integración - [ ] Verificar que todas las queries retornan datos correctos - [ ] Verificar cálculos de totales - [ ] Verificar formato de fechas (timezone Honduras) - [ ] Verificar formato de números - [ ] Verificar que los filtros se aplican correctamente #### 8.4. Performance - [ ] Optimizar queries si son lentas - [ ] Agregar índices en base de datos si es necesario - [ ] Lazy loading de componentes pesados - [ ] Debounce en búsquedas #### 8.5. Documentación - [ ] Agregar comentarios en código - [ ] Documentar estructura de datos - [ ] Documentar props de componentes - [ ] Crear README si es necesario --- ## 📊 Métricas del Proyecto | Métrica | Valor | |---------|-------| | **Progreso Total** | 70% | | **Backend Completado** | 100% | | **Frontend Completado** | 0% | | **Queries Creadas** | 8/8 | | **Endpoint Creado** | 1/1 | | **Componentes Vue Creados** | 0/7 | | **Página Principal Creada** | 0/1 | | **Navegación Agregada** | No | | **Testing Realizado** | No | | **Líneas de Código (Backend)** | ~450 | | **Líneas de Código Estimadas (Frontend)** | ~2000 | | **Tiempo Invertido** | ~2 horas | | **Tiempo Estimado Restante** | ~3-4 horas | --- ## 🎯 Próximos Pasos Recomendados ### Opción A: Implementación Completa 1. Crear página principal con todos los estados 2. Crear todos los componentes de visualización 3. Agregar navegación 4. Testing completo 5. Refinamiento y ajustes **Tiempo estimado:** 3-4 horas **Resultado:** Informe completo y robusto similar al de ingresos --- ### Opción B: MVP Funcional 1. Crear página principal con funcionalidad básica 2. Crear componentes mínimos: - Solo TotalesMonetariosComercio - Solo TablaComerciosResumen simple 3. Agregar navegación 4. Testing básico **Tiempo estimado:** 1-2 horas **Resultado:** Versión funcional básica que se puede refinar después --- ### Opción C: Continuar Desde Aquí El backend está 100% funcional y probado. Puedes: 1. Probar las queries directamente en Metabase 2. Probar el endpoint con Postman/curl 3. Decidir qué componentes visuales son prioritarios 4. Implementar de forma incremental --- ## 🔍 Notas Técnicas Importantes ### Diferencias con Informe de Ingresos 1. **No hay filtro de "calidades"** en comercios (no aplica) 2. **Hay filtro de "comercio_ids"** (nuevo, específico) 3. **No hay "Totales Verde"** (se incluye en Totales de Peso) 4. **Estructura de datos diferente:** - Comercios tienen `distribucionPago` (JSON) - Comercios tienen `totalLempiras` y `totalSeco` pre-calculados - Relación many-to-one (comercio → ingresos) ### Consideraciones de Negocio 1. Un comercio representa la **conversión de café a dinero** 2. La fecha del comercio es cuando se **concreta el pago** 3. El precio del comercio puede diferir del precio del ingreso 4. Un comercio puede agrupar múltiples ingresos de diferentes fechas 5. La `distribucionPago` permite análisis de métodos de pago ### Timezone Todas las queries usan `America/Tegucigalpa` para conversiones de fecha. ### Límites de Queries - Lista de Comercios: 1000 registros - Detalle de Ingresos: 5000 registros - Top Comercios: 10 registros - Serie Temporal: Sin límite (agrupado por fecha) --- ## 📝 Archivos Modificados/Creados ### Creados 1. `/nuxt4-app/server/api/metabase/informe-comercios.post.ts` (177 líneas) 2. 8 queries en Metabase (IDs 62-69) ### Modificados 1. `/nuxt4-app/server/config/metabase-queries.ts` (+13 líneas) ### Pendientes de Crear 1. `/nuxt4-app/app/pages/informe-comercios.vue` (~1000 líneas) 2. `/nuxt4-app/app/components/TotalesMonetariosComercio.vue` (~200 líneas) 3. `/nuxt4-app/app/components/TotalesPesoComercio.vue` (~150 líneas) 4. `/nuxt4-app/app/components/TablaComerciosResumen.vue` (~300 líneas) 5. `/nuxt4-app/app/components/TablaIngresosPorComercio.vue` (~350 líneas) 6. `/nuxt4-app/app/components/TopComerciosChart.vue` (~200 líneas) 7. `/nuxt4-app/app/components/SerieTemporalComercio.vue` (~250 líneas) 8. `/nuxt4-app/app/components/ComercioMultiSelector.vue` (~150 líneas) --- ## 🚀 Cómo Probar lo Completado ### Probar Queries en Metabase 1. Ir a Metabase: https://metabase.nucleoriofrio.com 2. Navegar a colección "facturador" 3. Buscar queries que empiezan con "Informe Comercios -" 4. Ejecutar cada query con diferentes parámetros 5. Verificar resultados ### Probar Endpoint del Servidor ```bash # Ejemplo con curl curl -X POST https://analitica.nucleoriofrio.com/api/metabase/informe-comercios \ -H "Content-Type: application/json" \ -d '{ "fecha_desde": "2023-10-01", "fecha_hasta": "2023-10-31", "incluir_anulados": false, "cliente_ids": [], "tipos": [], "comercio_ids": [], "granularidad": "dia" }' ``` ### Verificar Configuración ```bash # Ver archivo de configuración cat /home/draganel/repos/analiticaNucleo/nuxt4-app/server/config/metabase-queries.ts # Ver endpoint cat /home/draganel/repos/analiticaNucleo/nuxt4-app/server/api/metabase/informe-comercios.post.ts ``` --- ## 📞 Contacto y Dudas Para continuar con la implementación: 1. Decidir qué opción seguir (A, B o C) 2. Priorizar componentes visuales si aplica 3. Definir estilos y paleta de colores si difiere de ingresos 4. Confirmar reglas de negocio específicas --- **Fin del Reporte** Generado por: Claude Code Fecha: 2025-11-04