# 📊 QUERIES DE METABASE PARA COMPARATIVA DE COSECHAS ## 🎯 FILOSOFÍA **Metabase calcula TODO. Vue solo renderiza.** Esta página trabaja con **`vista_resumen_ingresos`** (datos agregados por día), NO con `vista_detalle_ingresos`. --- ## 📋 PARÁMETROS GLOBALES Todas las queries aceptan estos parámetros: | Parámetro | Tipo | Default | Descripción | |-----------|------|---------|-------------| | `cosechas_ids` | Array[String] | `[]` | Array de IDs de cosechas: 'cosecha-20-21', 'cosecha-21-22', etc. | | `incluir_anulados` | Boolean | `false` | Si `false`, excluye registros anulados | **IMPORTANTE:** En lugar de fechas libres, se usan IDs de cosechas que se mapean a rangos predefinidos en el backend. --- ## 📅 DEFINICIÓN DE COSECHAS (HARDCODED) Estas definiciones están hardcoded en el código pero necesitan estar también en las queries de Metabase: ```sql -- Mapping de cosechas a rangos de fechas CASE WHEN 'cosecha-20-21' = ANY({{cosechas_ids}}) AND fecha >= '2020-09-08' AND fecha <= '2021-09-07' THEN 'cosecha-20-21' WHEN 'cosecha-21-22' = ANY({{cosechas_ids}}) AND fecha >= '2021-09-08' AND fecha <= '2022-09-07' THEN 'cosecha-21-22' WHEN 'cosecha-22-23' = ANY({{cosechas_ids}}) AND fecha >= '2022-09-08' AND fecha <= '2023-09-07' THEN 'cosecha-22-23' WHEN 'cosecha-23-24' = ANY({{cosechas_ids}}) AND fecha >= '2023-09-08' AND fecha <= '2024-09-07' THEN 'cosecha-23-24' WHEN 'cosecha-24-25' = ANY({{cosechas_ids}}) AND fecha >= '2024-09-08' AND fecha <= '2025-09-07' THEN 'cosecha-24-25' WHEN 'cosecha-25-26' = ANY({{cosechas_ids}}) AND fecha >= '2025-09-08' THEN 'cosecha-25-26' ELSE NULL END as cosecha_id ``` --- ## 🎨 QUERIES POR COMPONENTE ### Query 1: `comparativa_datos_diarios_completos` **Componente:** `CosechasHeatmap.vue`, `CosechasEvolucion.vue` **Devuelve:** Múltiples filas (una por día de cada cosecha seleccionada) ```sql WITH cosechas_mapping AS ( SELECT fecha, -- Identificar a qué cosecha pertenece cada fecha CASE WHEN fecha >= '2020-09-08' AND fecha <= '2021-09-07' THEN 'cosecha-20-21' WHEN fecha >= '2021-09-08' AND fecha <= '2022-09-07' THEN 'cosecha-21-22' WHEN fecha >= '2022-09-08' AND fecha <= '2023-09-07' THEN 'cosecha-22-23' WHEN fecha >= '2023-09-08' AND fecha <= '2024-09-07' THEN 'cosecha-23-24' WHEN fecha >= '2024-09-08' AND fecha <= '2025-09-07' THEN 'cosecha-24-25' WHEN fecha >= '2025-09-08' THEN 'cosecha-25-26' ELSE NULL END as cosecha_id, -- Calcular día relativo dentro de cada cosecha (empieza en 1) CASE WHEN fecha >= '2020-09-08' AND fecha <= '2021-09-07' THEN (fecha - DATE '2020-09-08') + 1 WHEN fecha >= '2021-09-08' AND fecha <= '2022-09-07' THEN (fecha - DATE '2021-09-08') + 1 WHEN fecha >= '2022-09-08' AND fecha <= '2023-09-07' THEN (fecha - DATE '2022-09-08') + 1 WHEN fecha >= '2023-09-08' AND fecha <= '2024-09-07' THEN (fecha - DATE '2023-09-08') + 1 WHEN fecha >= '2024-09-08' AND fecha <= '2025-09-07' THEN (fecha - DATE '2024-09-08') + 1 WHEN fecha >= '2025-09-08' THEN (fecha - DATE '2025-09-08') + 1 ELSE NULL END as dia_relativo, -- Métricas del día COALESCE(total_peso_seco, 0) as total_peso_seco, COALESCE(peso_neto_uva, 0) as peso_neto_uva, COALESCE(peso_neto_verde, 0) as peso_neto_verde, COALESCE(sacos_total_dia, 0) as sacos_total_dia, COALESCE(total_lempiras_uva, 0) as total_lempiras_uva, COALESCE(total_lempiras_verde, 0) as total_lempiras_verde, COALESCE(total_lempiras_mojado, 0) as total_lempiras_mojado, COALESCE(total_lempiras_oreado, 0) as total_lempiras_oreado, COALESCE(total_lempiras_mojado, 0) + COALESCE(total_lempiras_oreado, 0) as total_lempiras_mojado_oreado FROM vista_resumen_ingresos WHERE ({{incluir_anulados}} OR (estado != 'anulado' AND fecha_anulado IS NULL)) -- Filtrar solo las fechas que pertenecen a cosechas seleccionadas AND ( (fecha >= '2020-09-08' AND fecha <= '2021-09-07' AND 'cosecha-20-21' = ANY({{cosechas_ids}})) OR (fecha >= '2021-09-08' AND fecha <= '2022-09-07' AND 'cosecha-21-22' = ANY({{cosechas_ids}})) OR (fecha >= '2022-09-08' AND fecha <= '2023-09-07' AND 'cosecha-22-23' = ANY({{cosechas_ids}})) OR (fecha >= '2023-09-08' AND fecha <= '2024-09-07' AND 'cosecha-23-24' = ANY({{cosechas_ids}})) OR (fecha >= '2024-09-08' AND fecha <= '2025-09-07' AND 'cosecha-24-25' = ANY({{cosechas_ids}})) OR (fecha >= '2025-09-08' AND 'cosecha-25-26' = ANY({{cosechas_ids}})) ) ) SELECT fecha, cosecha_id, dia_relativo, total_peso_seco, peso_neto_uva, peso_neto_verde, sacos_total_dia, total_lempiras_uva, total_lempiras_verde, total_lempiras_mojado, total_lempiras_oreado, total_lempiras_mojado_oreado FROM cosechas_mapping WHERE cosecha_id IS NOT NULL ORDER BY cosecha_id, fecha; ``` **Respuesta esperada:** ```json [ { "fecha": "2025-01-15", "cosecha_id": "cosecha-24-25", "dia_relativo": 130, "total_peso_seco": 45.50, "peso_neto_uva": 2250.00, "peso_neto_verde": 500.00, "sacos_total_dia": 50, "total_lempiras_uva": 11475.00, "total_lempiras_verde": 5000.00, "total_lempiras_mojado": 8000.00, "total_lempiras_oreado": 6000.00, "total_lempiras_mojado_oreado": 14000.00 } ] ``` --- ### Query 2: `comparativa_totales_por_cosecha` **Componente:** `CosechasTotales.vue` **Devuelve:** Múltiples filas (una por cosecha seleccionada con totales agregados) ```sql WITH cosechas_mapping AS ( SELECT CASE WHEN fecha >= '2020-09-08' AND fecha <= '2021-09-07' THEN 'cosecha-20-21' WHEN fecha >= '2021-09-08' AND fecha <= '2022-09-07' THEN 'cosecha-21-22' WHEN fecha >= '2022-09-08' AND fecha <= '2023-09-07' THEN 'cosecha-22-23' WHEN fecha >= '2023-09-08' AND fecha <= '2024-09-07' THEN 'cosecha-23-24' WHEN fecha >= '2024-09-08' AND fecha <= '2025-09-07' THEN 'cosecha-24-25' WHEN fecha >= '2025-09-08' THEN 'cosecha-25-26' ELSE NULL END as cosecha_id, total_peso_seco, peso_neto_uva, peso_neto_verde, sacos_total_dia, total_lempiras_uva, total_lempiras_verde, total_lempiras_mojado, total_lempiras_oreado FROM vista_resumen_ingresos WHERE ({{incluir_anulados}} OR (estado != 'anulado' AND fecha_anulado IS NULL)) AND ( (fecha >= '2020-09-08' AND fecha <= '2021-09-07' AND 'cosecha-20-21' = ANY({{cosechas_ids}})) OR (fecha >= '2021-09-08' AND fecha <= '2022-09-07' AND 'cosecha-21-22' = ANY({{cosechas_ids}})) OR (fecha >= '2022-09-08' AND fecha <= '2023-09-07' AND 'cosecha-22-23' = ANY({{cosechas_ids}})) OR (fecha >= '2023-09-08' AND fecha <= '2024-09-07' AND 'cosecha-23-24' = ANY({{cosechas_ids}})) OR (fecha >= '2024-09-08' AND fecha <= '2025-09-07' AND 'cosecha-24-25' = ANY({{cosechas_ids}})) OR (fecha >= '2025-09-08' AND 'cosecha-25-26' = ANY({{cosechas_ids}})) ) ) SELECT cosecha_id, -- Totales agregados COUNT(*) as num_dias, COALESCE(SUM(total_peso_seco), 0) as peso_total, COALESCE(SUM(peso_neto_uva), 0) as peso_uva_total, COALESCE(SUM(peso_neto_verde), 0) as peso_verde_total, COALESCE(SUM(sacos_total_dia), 0) as sacos_total, -- Inversiones totales COALESCE(SUM(total_lempiras_uva), 0) as inversion_uva_total, COALESCE(SUM(total_lempiras_verde), 0) as inversion_verde_total, COALESCE(SUM(total_lempiras_mojado), 0) as inversion_mojado_total, COALESCE(SUM(total_lempiras_oreado), 0) as inversion_oreado_total, COALESCE(SUM(total_lempiras_uva), 0) + COALESCE(SUM(total_lempiras_verde), 0) + COALESCE(SUM(total_lempiras_mojado), 0) + COALESCE(SUM(total_lempiras_oreado), 0) as inversion_total, -- Promedios diarios AVG(total_peso_seco) as peso_promedio_diario, AVG(sacos_total_dia) as sacos_promedio_diario FROM cosechas_mapping WHERE cosecha_id IS NOT NULL GROUP BY cosecha_id ORDER BY cosecha_id; ``` **Respuesta esperada:** ```json [ { "cosecha_id": "cosecha-23-24", "num_dias": 365, "peso_total": 16425.50, "peso_uva_total": 10000.00, "peso_verde_total": 3000.00, "sacos_total": 18250, "inversion_uva_total": 4500000.00, "inversion_verde_total": 300000.00, "inversion_mojado_total": 800000.00, "inversion_oreado_total": 600000.00, "inversion_total": 6200000.00, "peso_promedio_diario": 45.00, "sacos_promedio_diario": 50.00 }, { "cosecha_id": "cosecha-24-25", "num_dias": 130, "peso_total": 5850.00, "peso_uva_total": 3500.00, "peso_verde_total": 1000.00, "sacos_total": 6500, "inversion_uva_total": 1575000.00, "inversion_verde_total": 100000.00, "inversion_mojado_total": 280000.00, "inversion_oreado_total": 210000.00, "inversion_total": 2165000.00, "peso_promedio_diario": 45.00, "sacos_promedio_diario": 50.00 } ] ``` --- ### Query 3: `comparativa_datos_acumulados_por_dia` **Componente:** `CosechasEvolucion.vue` (modo acumulado) **Devuelve:** Múltiples filas (una por día con valores acumulados) ```sql WITH cosechas_mapping AS ( SELECT fecha, CASE WHEN fecha >= '2020-09-08' AND fecha <= '2021-09-07' THEN 'cosecha-20-21' WHEN fecha >= '2021-09-08' AND fecha <= '2022-09-07' THEN 'cosecha-21-22' WHEN fecha >= '2022-09-08' AND fecha <= '2023-09-07' THEN 'cosecha-22-23' WHEN fecha >= '2023-09-08' AND fecha <= '2024-09-07' THEN 'cosecha-23-24' WHEN fecha >= '2024-09-08' AND fecha <= '2025-09-07' THEN 'cosecha-24-25' WHEN fecha >= '2025-09-08' THEN 'cosecha-25-26' ELSE NULL END as cosecha_id, CASE WHEN fecha >= '2020-09-08' AND fecha <= '2021-09-07' THEN (fecha - DATE '2020-09-08') + 1 WHEN fecha >= '2021-09-08' AND fecha <= '2022-09-07' THEN (fecha - DATE '2021-09-08') + 1 WHEN fecha >= '2022-09-08' AND fecha <= '2023-09-07' THEN (fecha - DATE '2022-09-08') + 1 WHEN fecha >= '2023-09-08' AND fecha <= '2024-09-07' THEN (fecha - DATE '2023-09-08') + 1 WHEN fecha >= '2024-09-08' AND fecha <= '2025-09-07' THEN (fecha - DATE '2024-09-08') + 1 WHEN fecha >= '2025-09-08' THEN (fecha - DATE '2025-09-08') + 1 ELSE NULL END as dia_relativo, total_peso_seco FROM vista_resumen_ingresos WHERE ({{incluir_anulados}} OR (estado != 'anulado' AND fecha_anulado IS NULL)) AND ( (fecha >= '2020-09-08' AND fecha <= '2021-09-07' AND 'cosecha-20-21' = ANY({{cosechas_ids}})) OR (fecha >= '2021-09-08' AND fecha <= '2022-09-07' AND 'cosecha-21-22' = ANY({{cosechas_ids}})) OR (fecha >= '2022-09-08' AND fecha <= '2023-09-07' AND 'cosecha-22-23' = ANY({{cosechas_ids}})) OR (fecha >= '2023-09-08' AND fecha <= '2024-09-07' AND 'cosecha-23-24' = ANY({{cosechas_ids}})) OR (fecha >= '2024-09-08' AND fecha <= '2025-09-07' AND 'cosecha-24-25' = ANY({{cosechas_ids}})) OR (fecha >= '2025-09-08' AND 'cosecha-25-26' = ANY({{cosechas_ids}})) ) ) SELECT fecha, cosecha_id, dia_relativo, total_peso_seco as peso_dia, -- Acumulado hasta este día (dentro de cada cosecha) SUM(total_peso_seco) OVER ( PARTITION BY cosecha_id ORDER BY fecha ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) as peso_acumulado FROM cosechas_mapping WHERE cosecha_id IS NOT NULL ORDER BY cosecha_id, fecha; ``` **Respuesta esperada:** ```json [ { "fecha": "2024-09-08", "cosecha_id": "cosecha-24-25", "dia_relativo": 1, "peso_dia": 45.50, "peso_acumulado": 45.50 }, { "fecha": "2024-09-09", "cosecha_id": "cosecha-24-25", "dia_relativo": 2, "peso_dia": 50.00, "peso_acumulado": 95.50 } ] ``` --- ### Query 4: `comparativa_metadata_cosechas` **Propósito:** Obtener información de disponibilidad de cada cosecha (cuántos días tiene datos) **Devuelve:** Múltiples filas (una por cosecha) ```sql WITH cosechas_definidas AS ( SELECT unnest(ARRAY[ 'cosecha-20-21', 'cosecha-21-22', 'cosecha-22-23', 'cosecha-23-24', 'cosecha-24-25', 'cosecha-25-26' ]) as cosecha_id ), cosechas_mapping AS ( SELECT CASE WHEN fecha >= '2020-09-08' AND fecha <= '2021-09-07' THEN 'cosecha-20-21' WHEN fecha >= '2021-09-08' AND fecha <= '2022-09-07' THEN 'cosecha-21-22' WHEN fecha >= '2022-09-08' AND fecha <= '2023-09-07' THEN 'cosecha-22-23' WHEN fecha >= '2023-09-08' AND fecha <= '2024-09-07' THEN 'cosecha-23-24' WHEN fecha >= '2024-09-08' AND fecha <= '2025-09-07' THEN 'cosecha-24-25' WHEN fecha >= '2025-09-08' THEN 'cosecha-25-26' ELSE NULL END as cosecha_id, fecha FROM vista_resumen_ingresos WHERE ({{incluir_anulados}} OR (estado != 'anulado' AND fecha_anulado IS NULL)) ) SELECT d.cosecha_id, COALESCE(COUNT(m.fecha), 0) as num_registros, MIN(m.fecha) as primera_fecha, MAX(m.fecha) as ultima_fecha, -- Verificar si tiene datos hasta hoy CASE WHEN MAX(m.fecha) >= CURRENT_DATE THEN true ELSE false END as tiene_datos_hasta_hoy FROM cosechas_definidas d LEFT JOIN cosechas_mapping m ON d.cosecha_id = m.cosecha_id GROUP BY d.cosecha_id ORDER BY d.cosecha_id; ``` **Respuesta esperada:** ```json [ { "cosecha_id": "cosecha-20-21", "num_registros": 200, "primera_fecha": "2020-09-08", "ultima_fecha": "2021-09-07", "tiene_datos_hasta_hoy": false }, { "cosecha_id": "cosecha-21-22", "num_registros": 0, "primera_fecha": null, "ultima_fecha": null, "tiene_datos_hasta_hoy": false }, { "cosecha_id": "cosecha-22-23", "num_registros": 300, "primera_fecha": "2022-09-08", "ultima_fecha": "2023-09-07", "tiene_datos_hasta_hoy": false }, { "cosecha_id": "cosecha-23-24", "num_registros": 365, "primera_fecha": "2023-09-08", "ultima_fecha": "2024-09-07", "tiene_datos_hasta_hoy": false }, { "cosecha_id": "cosecha-24-25", "num_registros": 130, "primera_fecha": "2024-09-08", "ultima_fecha": "2025-01-15", "tiene_datos_hasta_hoy": true }, { "cosecha_id": "cosecha-25-26", "num_registros": 0, "primera_fecha": null, "ultima_fecha": null, "tiene_datos_hasta_hoy": false } ] ``` --- ## 📊 RESUMEN DE QUERIES | # | Nombre | Componente | Tipo de respuesta | |---|--------|------------|-------------------| | 1 | `comparativa_datos_diarios_completos` | Heatmap, Evolución | Múltiples filas (días) | | 2 | `comparativa_totales_por_cosecha` | Totales (barras) | Múltiples filas (cosechas) | | 3 | `comparativa_datos_acumulados_por_dia` | Evolución (acumulado) | Múltiples filas (días) | | 4 | `comparativa_metadata_cosechas` | Selector de cosechas | Múltiples filas (cosechas) | **Total: 4 Queries** --- ## 🔧 CONFIGURACIÓN DE PARÁMETROS EN METABASE ### `cosechas_ids` - **Tipo:** Text (se parsea como array) - **Widget Type:** Text - **Default:** `[]` - **Required:** No - **SQL Type:** `TEXT[]` - **Valores posibles:** `['cosecha-20-21', 'cosecha-21-22', 'cosecha-22-23', 'cosecha-23-24', 'cosecha-24-25', 'cosecha-25-26']` ### `incluir_anulados` - **Tipo:** Boolean - **Widget Type:** Boolean - **Default:** `false` - **Required:** No --- ## 🚀 INTEGRACIÓN EN VUE ```typescript // comparativa-cosechas.vue const cosechasSeleccionadas = ref(['cosecha-24-25', 'cosecha-25-26']) const incluirAnulados = ref(false) const params = computed(() => ({ cosechas_ids: cosechasSeleccionadas.value, incluir_anulados: incluirAnulados.value })) async function loadAllData() { const [ datosDiarios, totalesCosechas, datosAcumulados, metadataCosechas ] = await Promise.all([ $fetch('/api/metabase/question/201', { query: params.value }), $fetch('/api/metabase/question/202', { query: params.value }), $fetch('/api/metabase/question/203', { query: params.value }), $fetch('/api/metabase/question/204', { query: params.value }) ]) // Asignar directamente datos.value = { diarios: datosDiarios.data.rows, totales: totalesCosechas.data.rows, acumulados: datosAcumulados.data.rows, metadata: metadataCosechas.data.rows } } // Transformar para componentes const resumenIngresos = computed(() => { // Convertir datos.value.diarios al formato que esperan los componentes return datos.value.diarios.map(row => ({ fecha: row.fecha, cosecha_id: row.cosecha_id, dia_relativo: row.dia_relativo, total_peso_seco: row.total_peso_seco, peso_neto_uva: row.peso_neto_uva, peso_neto_verde: row.peso_neto_verde, sacos_total_dia: row.sacos_total_dia, total_lempiras_uva: row.total_lempiras_uva, total_lempiras_verde: row.total_lempiras_verde, total_lempiras_mojado: row.total_lempiras_mojado, total_lempiras_oreado: row.total_lempiras_oreado, total_lempiras_mojado_oreado: row.total_lempiras_mojado_oreado })) }) ``` --- ## ⚠️ NOTAS IMPORTANTES ### 1. Fuente de Datos Diferente Esta página usa **`vista_resumen_ingresos`**, NO `vista_detalle_ingresos`: - **Granularidad:** Por DÍA (un registro = un día completo con totales) - **Estructura:** Ya viene agregada con totales diarios - **Performance:** Mucho más rápida porque trabaja con menos registros ### 2. Cosechas Hardcoded Los rangos de cosechas están hardcoded: - **Cosecha X-Y:** Del 8 de septiembre del año X al 7 de septiembre del año Y - **Última cosecha:** Del 8 de septiembre hasta "hoy" Si se agregan nuevas cosechas, hay que actualizar: - Las queries de Metabase - La definición en el código Vue (`cosechasDefiniciones`) ### 3. Día Relativo Cada cosecha tiene su propio "día relativo" que empieza en 1: - **Día 1:** 8 de septiembre (inicio de cosecha) - **Día 365:** 7 de septiembre del año siguiente (fin de cosecha) Esto permite comparar "día a día" entre cosechas diferentes. ### 4. Métricas Computadas La métrica `total_lempiras_mojado_oreado` es computada: ```sql COALESCE(total_lempiras_mojado, 0) + COALESCE(total_lempiras_oreado, 0) ``` ### 5. Performance - Query 1 puede devolver hasta ~2000 filas (365 días × 6 cosechas max) - Query 3 usa window functions, requiere índice en `fecha` - Recomendado: Crear índices en `vista_resumen_ingresos(fecha, estado)` ### 6. Heatmap y Evolución Ambos componentes usan la **misma Query 1**, pero procesan los datos diferente: - **Heatmap:** Muestra matriz de días × cosechas con colores - **Evolución:** Muestra gráficos de líneas temporales Por eso Query 1 debe devolver TODOS los campos necesarios. --- ## 📝 PRÓXIMOS PASOS 1. ✅ Crear estas 4 queries en Metabase 2. ✅ Configurar parámetros (especialmente `cosechas_ids` como array) 3. ✅ Probar con diferentes combinaciones de cosechas 4. ✅ Obtener los Question IDs 5. ✅ Adaptar `comparativa-cosechas.vue` para usar las nuevas queries 6. ✅ Crear función de transformación de datos para los componentes 7. ✅ Probar performance con datasets completos (365 días × N cosechas) 8. ✅ Verificar que el heatmap y evolución funcionen correctamente --- **Documento creado:** 2025-10-14 **Autor:** Claude Code **Proyecto:** Analítica Núcleo - Comparativa de Cosechas