# 馃搳 QUERIES DE METABASE PARA PANORAMA FACTURADOR ## 馃幆 FILOSOF脥A **Metabase calcula TODO. Vue solo renderiza.** Cada query devuelve **exactamente los valores que un componente necesita mostrar**, ya calculados y listos para usar. Sin composables de m茅tricas, sin c谩lculos en el frontend. --- ## 馃搵 PAR脕METROS GLOBALES Todas las queries aceptan estos par谩metros: | Par谩metro | Tipo | Default | Descripci贸n | |-----------|------|---------|-------------| | `fecha_desde` | Date | `null` | Fecha inicio (filtra por `created_at`) | | `fecha_hasta` | Date | `null` | Fecha fin (filtra por `created_at`) | | `incluir_anulados` | Boolean | `false` | Si `false`, excluye `estado='anulado'` o `fecha_anulado IS NOT NULL` | **Condici贸n SQL est谩ndar:** ```sql WHERE ({{incluir_anulados}} OR (estado != 'anulado' AND fecha_anulado IS NULL)) AND ({{fecha_desde}} IS NULL OR created_at >= {{fecha_desde}}) AND ({{fecha_hasta}} IS NULL OR created_at <= {{fecha_hasta}}) ``` --- ## 馃帹 QUERIES POR COMPONENTE ### Query 1: `panorama_totales_financieros_principales` **Componente:** Card principal de "Totales Financieros" **Devuelve:** 1 fila con 3 valores ```sql SELECT -- Total Invertido en Caf茅 (pagado: uva + mojado + oreado) COALESCE(SUM( CASE WHEN estado = 'pagado' AND tipo IN ('verde', 'uva') THEN precio * peso_neto WHEN estado = 'pagado' AND tipo IN ('oreado', 'mojado') THEN (precio / 2) * peso_seco ELSE 0 END ), 0) as total_invertido_cafe, -- Total Rechazos (traer de otra tabla) COALESCE(( SELECT SUM(total_cobrado) FROM rechazos WHERE ({{incluir_anulados}} OR (estado != 'anulado' AND fecha_anulado IS NULL)) AND ({{fecha_desde}} IS NULL OR created_at >= {{fecha_desde}}) AND ({{fecha_hasta}} IS NULL OR created_at <= {{fecha_hasta}}) ), 0) as total_rechazos, -- Balance Neto (invertido - rechazos) COALESCE(SUM( CASE WHEN estado = 'pagado' AND tipo IN ('verde', 'uva') THEN precio * peso_neto WHEN estado = 'pagado' AND tipo IN ('oreado', 'mojado') THEN (precio / 2) * peso_seco ELSE 0 END ), 0) - COALESCE(( SELECT SUM(total_cobrado) FROM rechazos WHERE ({{incluir_anulados}} OR (estado != 'anulado' AND fecha_anulado IS NULL)) AND ({{fecha_desde}} IS NULL OR created_at >= {{fecha_desde}}) AND ({{fecha_hasta}} IS NULL OR created_at <= {{fecha_hasta}}) ), 0) as balance_neto FROM vista_detalle_ingresos WHERE ({{incluir_anulados}} OR (estado != 'anulado' AND fecha_anulado IS NULL)) AND ({{fecha_desde}} IS NULL OR created_at >= {{fecha_desde}}) AND ({{fecha_hasta}} IS NULL OR created_at <= {{fecha_hasta}}); ``` **Respuesta esperada:** ```json { "total_invertido_cafe": 4567890.12, "total_rechazos": 44750.00, "balance_neto": 4523140.12 } ``` --- ### Query 2: `panorama_totales_ingreso_compra` **Componente:** `TotalesIngresoCompra.vue` **Devuelve:** 1 fila con TODOS los valores del componente ```sql SELECT -- === TOTALES GENERALES (Pagado + Pendiente) === -- Uva COALESCE(SUM(CASE WHEN tipo = 'uva' THEN peso_neto ELSE 0 END), 0) as total_lb_uva_ingresada, COALESCE(SUM(CASE WHEN tipo = 'uva' THEN peso_seco ELSE 0 END), 0) as total_qq_seco_uva_ingresado, -- Mojado COALESCE(SUM(CASE WHEN tipo = 'mojado' THEN peso_seco ELSE 0 END), 0) as total_qq_seco_mojado_ingresado, -- Oreado COALESCE(SUM(CASE WHEN tipo = 'oreado' THEN peso_seco ELSE 0 END), 0) as total_qq_seco_oreado_ingresado, -- Total general COALESCE(SUM(peso_seco), 0) as total_qq_seco_ingresado, -- === SOLO PAGADOS === -- Uva pagada COALESCE(SUM(CASE WHEN tipo = 'uva' AND estado = 'pagado' THEN peso_neto ELSE 0 END), 0) as total_lb_uva_pagada, COALESCE(SUM(CASE WHEN tipo = 'uva' AND estado = 'pagado' THEN peso_seco ELSE 0 END), 0) as total_qq_seco_uva_pagado, -- Mojado pagado COALESCE(SUM(CASE WHEN tipo = 'mojado' AND estado = 'pagado' THEN peso_seco ELSE 0 END), 0) as total_qq_seco_mojado_pagado, -- Oreado pagado COALESCE(SUM(CASE WHEN tipo = 'oreado' AND estado = 'pagado' THEN peso_seco ELSE 0 END), 0) as total_qq_seco_oreado_pagado, -- Total pagado COALESCE(SUM(CASE WHEN estado = 'pagado' THEN peso_seco ELSE 0 END), 0) as total_qq_seco_comprado, -- === INVENTARIO EN DEP脫SITO (Pendiente) === -- Uva en dep贸sito COALESCE(SUM(CASE WHEN tipo = 'uva' AND estado = 'pendiente' THEN peso_neto ELSE 0 END), 0) as total_lb_uva_deposito, -- Mojado en dep贸sito COALESCE(SUM(CASE WHEN tipo = 'mojado' AND estado = 'pendiente' THEN peso_seco ELSE 0 END), 0) as total_qq_mojado_deposito, -- Oreado en dep贸sito COALESCE(SUM(CASE WHEN tipo = 'oreado' AND estado = 'pendiente' THEN peso_seco ELSE 0 END), 0) as total_qq_oreado_deposito, -- Total en dep贸sito COALESCE(SUM(CASE WHEN estado = 'pendiente' THEN peso_seco ELSE 0 END), 0) as total_qq_seco_deposito FROM vista_detalle_ingresos WHERE ({{incluir_anulados}} OR (estado != 'anulado' AND fecha_anulado IS NULL)) AND ({{fecha_desde}} IS NULL OR created_at >= {{fecha_desde}}) AND ({{fecha_hasta}} IS NULL OR created_at <= {{fecha_hasta}}); ``` **Respuesta esperada:** ```json { "total_lb_uva_ingresada": 123456.78, "total_qq_seco_uva_ingresado": 246.91, "total_qq_seco_mojado_ingresado": 500.00, "total_qq_seco_oreado_ingresado": 300.00, "total_qq_seco_ingresado": 1046.91, "total_lb_uva_pagada": 100000.00, "total_qq_seco_uva_pagado": 200.00, "total_qq_seco_mojado_pagado": 450.00, "total_qq_seco_oreado_pagado": 250.00, "total_qq_seco_comprado": 900.00, "total_lb_uva_deposito": 23456.78, "total_qq_mojado_deposito": 50.00, "total_qq_oreado_deposito": 50.00, "total_qq_seco_deposito": 146.91 } ``` --- ### Query 3: `panorama_totales_monetarios` **Componente:** `TotalesMonetarios.vue` **Devuelve:** 1 fila con TODOS los valores ```sql SELECT -- === INVERSI脫N HASTA LA FECHA === COALESCE(SUM( CASE WHEN estado = 'pagado' AND tipo = 'uva' THEN precio * peso_neto ELSE 0 END ), 0) as inversion_uva, COALESCE(SUM( CASE WHEN estado = 'pagado' AND tipo = 'mojado' THEN (precio / 2) * peso_seco ELSE 0 END ), 0) as inversion_mojado, COALESCE(SUM( CASE WHEN estado = 'pagado' AND tipo = 'oreado' THEN (precio / 2) * peso_seco ELSE 0 END ), 0) as inversion_oreado, COALESCE(SUM( CASE WHEN estado = 'pagado' AND tipo IN ('uva', 'mojado', 'oreado') THEN CASE WHEN tipo = 'uva' THEN precio * peso_neto ELSE (precio / 2) * peso_seco END ELSE 0 END ), 0) as total_invertido, -- === PRECIOS PROMEDIO PONDERADOS === -- Precio promedio uva por lb CASE WHEN SUM(CASE WHEN tipo = 'uva' AND estado = 'pagado' THEN peso_neto ELSE 0 END) > 0 THEN SUM(CASE WHEN tipo = 'uva' AND estado = 'pagado' THEN precio * peso_neto ELSE 0 END) / SUM(CASE WHEN tipo = 'uva' AND estado = 'pagado' THEN peso_neto ELSE 0 END) ELSE 0 END as precio_promedio_uva_por_lb, -- Precio promedio uva por qq (lb * 500) CASE WHEN SUM(CASE WHEN tipo = 'uva' AND estado = 'pagado' THEN peso_neto ELSE 0 END) > 0 THEN (SUM(CASE WHEN tipo = 'uva' AND estado = 'pagado' THEN precio * peso_neto ELSE 0 END) / SUM(CASE WHEN tipo = 'uva' AND estado = 'pagado' THEN peso_neto ELSE 0 END)) * 500 ELSE 0 END as precio_promedio_uva_por_qq, -- Precio promedio mojado por qq CASE WHEN SUM(CASE WHEN tipo = 'mojado' AND estado = 'pagado' THEN peso_seco ELSE 0 END) > 0 THEN SUM(CASE WHEN tipo = 'mojado' AND estado = 'pagado' THEN (precio / 2) * peso_seco ELSE 0 END) / SUM(CASE WHEN tipo = 'mojado' AND estado = 'pagado' THEN peso_seco ELSE 0 END) ELSE 0 END as precio_promedio_mojado_por_qq, -- Precio promedio oreado por qq CASE WHEN SUM(CASE WHEN tipo = 'oreado' AND estado = 'pagado' THEN peso_seco ELSE 0 END) > 0 THEN SUM(CASE WHEN tipo = 'oreado' AND estado = 'pagado' THEN (precio / 2) * peso_seco ELSE 0 END) / SUM(CASE WHEN tipo = 'oreado' AND estado = 'pagado' THEN peso_seco ELSE 0 END) ELSE 0 END as precio_promedio_oreado_por_qq, -- Precio promedio global qq seco CASE WHEN SUM(CASE WHEN estado = 'pagado' THEN peso_seco ELSE 0 END) > 0 THEN SUM( CASE WHEN estado = 'pagado' AND tipo IN ('verde', 'uva') THEN precio * peso_neto WHEN estado = 'pagado' AND tipo IN ('oreado', 'mojado') THEN (precio / 2) * peso_seco ELSE 0 END ) / SUM(CASE WHEN estado = 'pagado' THEN peso_seco ELSE 0 END) ELSE 0 END as precio_promedio_qq_seco, -- === INVERSI脫N RESTANTE A REALIZAR === -- Inversi贸n restante uva (precio_promedio_uva_lb * lb_deposito) (CASE WHEN SUM(CASE WHEN tipo = 'uva' AND estado = 'pagado' THEN peso_neto ELSE 0 END) > 0 THEN SUM(CASE WHEN tipo = 'uva' AND estado = 'pagado' THEN precio * peso_neto ELSE 0 END) / SUM(CASE WHEN tipo = 'uva' AND estado = 'pagado' THEN peso_neto ELSE 0 END) ELSE 0 END) * COALESCE(SUM(CASE WHEN tipo = 'uva' AND estado = 'pendiente' THEN peso_neto ELSE 0 END), 0) as inversion_restante_uva, -- Inversi贸n restante mojado (precio_promedio_mojado_qq * qq_deposito) (CASE WHEN SUM(CASE WHEN tipo = 'mojado' AND estado = 'pagado' THEN peso_seco ELSE 0 END) > 0 THEN SUM(CASE WHEN tipo = 'mojado' AND estado = 'pagado' THEN (precio / 2) * peso_seco ELSE 0 END) / SUM(CASE WHEN tipo = 'mojado' AND estado = 'pagado' THEN peso_seco ELSE 0 END) ELSE 0 END) * COALESCE(SUM(CASE WHEN tipo = 'mojado' AND estado = 'pendiente' THEN peso_seco ELSE 0 END), 0) as inversion_restante_mojado, -- Inversi贸n restante oreado (precio_promedio_oreado_qq * qq_deposito) (CASE WHEN SUM(CASE WHEN tipo = 'oreado' AND estado = 'pagado' THEN peso_seco ELSE 0 END) > 0 THEN SUM(CASE WHEN tipo = 'oreado' AND estado = 'pagado' THEN (precio / 2) * peso_seco ELSE 0 END) / SUM(CASE WHEN tipo = 'oreado' AND estado = 'pagado' THEN peso_seco ELSE 0 END) ELSE 0 END) * COALESCE(SUM(CASE WHEN tipo = 'oreado' AND estado = 'pendiente' THEN peso_seco ELSE 0 END), 0) as inversion_restante_oreado, -- Inversi贸n restante esperada (suma de las 3 anteriores) (CASE WHEN SUM(CASE WHEN tipo = 'uva' AND estado = 'pagado' THEN peso_neto ELSE 0 END) > 0 THEN SUM(CASE WHEN tipo = 'uva' AND estado = 'pagado' THEN precio * peso_neto ELSE 0 END) / SUM(CASE WHEN tipo = 'uva' AND estado = 'pagado' THEN peso_neto ELSE 0 END) ELSE 0 END) * COALESCE(SUM(CASE WHEN tipo = 'uva' AND estado = 'pendiente' THEN peso_neto ELSE 0 END), 0) + (CASE WHEN SUM(CASE WHEN tipo = 'mojado' AND estado = 'pagado' THEN peso_seco ELSE 0 END) > 0 THEN SUM(CASE WHEN tipo = 'mojado' AND estado = 'pagado' THEN (precio / 2) * peso_seco ELSE 0 END) / SUM(CASE WHEN tipo = 'mojado' AND estado = 'pagado' THEN peso_seco ELSE 0 END) ELSE 0 END) * COALESCE(SUM(CASE WHEN tipo = 'mojado' AND estado = 'pendiente' THEN peso_seco ELSE 0 END), 0) + (CASE WHEN SUM(CASE WHEN tipo = 'oreado' AND estado = 'pagado' THEN peso_seco ELSE 0 END) > 0 THEN SUM(CASE WHEN tipo = 'oreado' AND estado = 'pagado' THEN (precio / 2) * peso_seco ELSE 0 END) / SUM(CASE WHEN tipo = 'oreado' AND estado = 'pagado' THEN peso_seco ELSE 0 END) ELSE 0 END) * COALESCE(SUM(CASE WHEN tipo = 'oreado' AND estado = 'pendiente' THEN peso_seco ELSE 0 END), 0) as inversion_restante_esperada FROM vista_detalle_ingresos WHERE ({{incluir_anulados}} OR (estado != 'anulado' AND fecha_anulado IS NULL)) AND ({{fecha_desde}} IS NULL OR created_at >= {{fecha_desde}}) AND ({{fecha_hasta}} IS NULL OR created_at <= {{fecha_hasta}}); ``` **Respuesta esperada:** ```json { "inversion_uva": 1234567.89, "inversion_mojado": 567890.12, "inversion_oreado": 345678.90, "total_invertido": 2148136.91, "precio_promedio_uva_por_lb": 4.50, "precio_promedio_uva_por_qq": 2250.00, "precio_promedio_mojado_por_qq": 2500.00, "precio_promedio_oreado_por_qq": 2300.00, "precio_promedio_qq_seco": 2400.00, "inversion_restante_uva": 105555.51, "inversion_restante_mojado": 125000.00, "inversion_restante_oreado": 115000.00, "inversion_restante_esperada": 345555.51 } ``` --- ### Query 4: `panorama_totales_verde` **Componente:** `TotalesVerde.vue` **Devuelve:** 1 fila con TODOS los valores ```sql SELECT -- Total lb neto de verde (pagado + pendiente) COALESCE(SUM(CASE WHEN tipo = 'verde' THEN peso_neto ELSE 0 END), 0) as total_lb_neto_verde, -- Precio promedio verde pagado (por lb) CASE WHEN SUM(CASE WHEN tipo = 'verde' AND estado = 'pagado' THEN peso_neto ELSE 0 END) > 0 THEN SUM(CASE WHEN tipo = 'verde' AND estado = 'pagado' THEN precio * peso_neto ELSE 0 END) / SUM(CASE WHEN tipo = 'verde' AND estado = 'pagado' THEN peso_neto ELSE 0 END) ELSE 0 END as precio_promedio_verde_pagado, -- Total lb neto verde en dep贸sito (pendiente) COALESCE(SUM(CASE WHEN tipo = 'verde' AND estado = 'pendiente' THEN peso_neto ELSE 0 END), 0) as total_lb_neto_verde_deposito, -- Inversi贸n verde hasta la fecha (pagado) COALESCE(SUM( CASE WHEN tipo = 'verde' AND estado = 'pagado' THEN precio * peso_neto ELSE 0 END ), 0) as inversion_verde_hasta_fecha, -- Inversi贸n restante verde (precio_promedio * lb_deposito) (CASE WHEN SUM(CASE WHEN tipo = 'verde' AND estado = 'pagado' THEN peso_neto ELSE 0 END) > 0 THEN SUM(CASE WHEN tipo = 'verde' AND estado = 'pagado' THEN precio * peso_neto ELSE 0 END) / SUM(CASE WHEN tipo = 'verde' AND estado = 'pagado' THEN peso_neto ELSE 0 END) ELSE 0 END) * COALESCE(SUM(CASE WHEN tipo = 'verde' AND estado = 'pendiente' THEN peso_neto ELSE 0 END), 0) as inversion_restante_verde, -- Total lb neto comprado verde (solo pagado) COALESCE(SUM(CASE WHEN tipo = 'verde' AND estado = 'pagado' THEN peso_neto ELSE 0 END), 0) as total_lb_neto_comprado_verde FROM vista_detalle_ingresos WHERE ({{incluir_anulados}} OR (estado != 'anulado' AND fecha_anulado IS NULL)) AND ({{fecha_desde}} IS NULL OR created_at >= {{fecha_desde}}) AND ({{fecha_hasta}} IS NULL OR created_at <= {{fecha_hasta}}); ``` **Respuesta esperada:** ```json { "total_lb_neto_verde": 50000.00, "precio_promedio_verde_pagado": 10.00, "total_lb_neto_verde_deposito": 5000.00, "inversion_verde_hasta_fecha": 450000.00, "inversion_restante_verde": 50000.00, "total_lb_neto_comprado_verde": 45000.00 } ``` --- ### Query 5: `panorama_secos_vendidos` **Componente:** `SecosVendidos.vue` **Devuelve:** 1 fila con TODOS los valores ```sql SELECT -- Total qq seco por vender (en dep贸sito) COALESCE(SUM(CASE WHEN estado = 'pendiente' THEN peso_seco ELSE 0 END), 0) as total_qq_seco_por_vender, -- Precio de venta promedio por qq (TODO: necesita tabla de ventas, por ahora 0) 0 as precio_venta_promedio_por_qq, -- Precio de compra promedio por qq CASE WHEN SUM(CASE WHEN estado = 'pagado' THEN peso_seco ELSE 0 END) > 0 THEN SUM( CASE WHEN estado = 'pagado' AND tipo IN ('verde', 'uva') THEN precio * peso_neto WHEN estado = 'pagado' AND tipo IN ('oreado', 'mojado') THEN (precio / 2) * peso_seco ELSE 0 END ) / SUM(CASE WHEN estado = 'pagado' THEN peso_seco ELSE 0 END) ELSE 0 END as precio_compra_promedio_por_qq, -- Margen de ganancia por qq (venta - compra, por ahora ser谩 negativo) 0 - CASE WHEN SUM(CASE WHEN estado = 'pagado' THEN peso_seco ELSE 0 END) > 0 THEN SUM( CASE WHEN estado = 'pagado' AND tipo IN ('verde', 'uva') THEN precio * peso_neto WHEN estado = 'pagado' AND tipo IN ('oreado', 'mojado') THEN (precio / 2) * peso_seco ELSE 0 END ) / SUM(CASE WHEN estado = 'pagado' THEN peso_seco ELSE 0 END) ELSE 0 END as margen_ganancia_por_qq FROM vista_detalle_ingresos WHERE ({{incluir_anulados}} OR (estado != 'anulado' AND fecha_anulado IS NULL)) AND ({{fecha_desde}} IS NULL OR created_at >= {{fecha_desde}}) AND ({{fecha_hasta}} IS NULL OR created_at <= {{fecha_hasta}}); ``` **Respuesta esperada:** ```json { "total_qq_seco_por_vender": 146.91, "precio_venta_promedio_por_qq": 0, "precio_compra_promedio_por_qq": 2400.00, "margen_ganancia_por_qq": -2400.00 } ``` --- ### Query 6: `panorama_rechazos_subproductos` **Componente:** `RechazosSubproductos.vue` **Devuelve:** 6 filas (una por cada tipo de rechazo) ```sql SELECT tipo, COUNT(*) as num_registros, COALESCE(SUM(cantidad), 0) as total_cantidad, COALESCE(SUM(total_cobrado), 0) as total_cobrado, -- Precio promedio CASE WHEN SUM(cantidad) > 0 THEN SUM(total_cobrado) / SUM(cantidad) ELSE 0 END as precio_promedio FROM rechazos WHERE ({{incluir_anulados}} OR (estado != 'anulado' AND fecha_anulado IS NULL)) AND ({{fecha_desde}} IS NULL OR created_at >= {{fecha_desde}}) AND ({{fecha_hasta}} IS NULL OR created_at <= {{fecha_hasta}}) GROUP BY tipo ORDER BY tipo; ``` **Respuesta esperada:** ```json [ { "tipo": "chibolita", "num_registros": 25, "total_cantidad": 1250.00, "total_cobrado": 12500.00, "precio_promedio": 10.00 }, { "tipo": "magalla", "num_registros": 10, "total_cantidad": 50.00, "total_cobrado": 2500.00, "precio_promedio": 50.00 }, { "tipo": "perico", "num_registros": 30, "total_cantidad": 1500.00, "total_cobrado": 15000.00, "precio_promedio": 10.00 }, { "tipo": "picadillo", "num_registros": 15, "total_cantidad": 75.00, "total_cobrado": 3750.00, "precio_promedio": 50.00 }, { "tipo": "pinta", "num_registros": 12, "total_cantidad": 600.00, "total_cobrado": 6000.00, "precio_promedio": 10.00 }, { "tipo": "vano", "num_registros": 20, "total_cantidad": 100.00, "total_cobrado": 5000.00, "precio_promedio": 50.00 } ] ``` --- ## 馃搱 QUERIES ADICIONALES (GR脕FICAS Y SERIES) ### Query 7: `panorama_serie_temporal_diaria` **Componente:** `GraficaSerieIngresos.vue`, `GraficaSerieInversion.vue` **Devuelve:** M煤ltiples filas (una por d铆a/tipo/estado) ```sql SELECT DATE(created_at) as fecha, tipo, estado, COUNT(*) as num_ingresos, COALESCE(SUM(peso_seco), 0) as total_peso_seco_dia, COALESCE(SUM(peso_neto), 0) as total_peso_neto_dia, COALESCE(SUM( CASE WHEN tipo IN ('verde', 'uva') THEN precio * peso_neto WHEN tipo IN ('oreado', 'mojado') THEN (precio / 2) * peso_seco ELSE 0 END ), 0) as total_inversion_dia FROM vista_detalle_ingresos WHERE ({{incluir_anulados}} OR (estado != 'anulado' AND fecha_anulado IS NULL)) AND ({{fecha_desde}} IS NULL OR created_at >= {{fecha_desde}}) AND ({{fecha_hasta}} IS NULL OR created_at <= {{fecha_hasta}}) GROUP BY DATE(created_at), tipo, estado ORDER BY fecha, tipo, estado; ``` --- ### Query 8: `panorama_top_clientes` **Componente:** `TopClientes.vue` **Devuelve:** M煤ltiples filas (top 20 clientes por inversi贸n) ```sql SELECT cliente_id, COUNT(*) as num_ingresos, COALESCE(SUM(peso_seco), 0) as total_qq_seco, COALESCE(SUM(peso_neto), 0) as total_lb_neto, COALESCE(SUM( CASE WHEN tipo IN ('verde', 'uva') THEN precio * peso_neto WHEN tipo IN ('oreado', 'mojado') THEN (precio / 2) * peso_seco ELSE 0 END ), 0) as total_pagado FROM vista_detalle_ingresos WHERE estado = 'pagado' AND ({{incluir_anulados}} OR (estado != 'anulado' AND fecha_anulado IS NULL)) AND ({{fecha_desde}} IS NULL OR created_at >= {{fecha_desde}}) AND ({{fecha_hasta}} IS NULL OR created_at <= {{fecha_hasta}}) GROUP BY cliente_id ORDER BY total_pagado DESC LIMIT 20; ``` --- ### Query 9: `panorama_conteo_registros` **Prop贸sito:** Para el footer que dice "Registros considerados: Ingresos X/Y 路 Rechazos A/B" **Devuelve:** 1 fila con conteos ```sql SELECT -- Ingresos filtrados (SELECT COUNT(*) FROM vista_detalle_ingresos WHERE ({{incluir_anulados}} OR (estado != 'anulado' AND fecha_anulado IS NULL)) AND ({{fecha_desde}} IS NULL OR created_at >= {{fecha_desde}}) AND ({{fecha_hasta}} IS NULL OR created_at <= {{fecha_hasta}}) ) as ingresos_filtrados, -- Total ingresos (sin filtros de fecha/anulados) (SELECT COUNT(*) FROM vista_detalle_ingresos) as ingresos_total, -- Rechazos filtrados (SELECT COUNT(*) FROM rechazos WHERE ({{incluir_anulados}} OR (estado != 'anulado' AND fecha_anulado IS NULL)) AND ({{fecha_desde}} IS NULL OR created_at >= {{fecha_desde}}) AND ({{fecha_hasta}} IS NULL OR created_at <= {{fecha_hasta}}) ) as rechazos_filtrados, -- Total rechazos (SELECT COUNT(*) FROM rechazos) as rechazos_total; ``` **Respuesta esperada:** ```json { "ingresos_filtrados": 230, "ingresos_total": 350, "rechazos_filtrados": 112, "rechazos_total": 150 } ``` --- ## 馃搳 RESUMEN DE QUERIES | # | Nombre | Componente | Tipo de respuesta | |---|--------|------------|-------------------| | 1 | `panorama_totales_financieros_principales` | Card principal | 1 fila, 3 campos | | 2 | `panorama_totales_ingreso_compra` | TotalesIngresoCompra | 1 fila, 14 campos | | 3 | `panorama_totales_monetarios` | TotalesMonetarios | 1 fila, 14 campos | | 4 | `panorama_totales_verde` | TotalesVerde | 1 fila, 6 campos | | 5 | `panorama_secos_vendidos` | SecosVendidos | 1 fila, 4 campos | | 6 | `panorama_rechazos_subproductos` | RechazosSubproductos | 6 filas (por tipo) | | 7 | `panorama_serie_temporal_diaria` | Gr谩ficas | M煤ltiples filas | | 8 | `panorama_top_clientes` | TopClientes | 20 filas max | | 9 | `panorama_conteo_registros` | Footer de filtros | 1 fila, 4 campos | **Total: 9 Queries** --- ## 馃殌 INTEGRACI脫N EN VUE ```typescript // panorama.vue const metrics = ref({ financieros: null, ingresoCompra: null, monetarios: null, verde: null, secosVendidos: null, rechazos: [], serieTemporal: [], topClientes: [], conteos: null }) async function loadAllData() { const params = { fecha_desde: fechaDesde.value, fecha_hasta: fechaHasta.value, incluir_anulados: includeAnulados.value } const [ financieros, ingresoCompra, monetarios, verde, secosVendidos, rechazos, serieTemporal, topClientes, conteos ] = await Promise.all([ $fetch('/api/metabase/question/1', { query: params }), $fetch('/api/metabase/question/2', { query: params }), $fetch('/api/metabase/question/3', { query: params }), $fetch('/api/metabase/question/4', { query: params }), $fetch('/api/metabase/question/5', { query: params }), $fetch('/api/metabase/question/6', { query: params }), $fetch('/api/metabase/question/7', { query: params }), $fetch('/api/metabase/question/8', { query: params }), $fetch('/api/metabase/question/9', { query: params }) ]) // Asignar directamente, SIN procesamiento metrics.value.financieros = financieros.data.rows[0] metrics.value.ingresoCompra = ingresoCompra.data.rows[0] metrics.value.monetarios = monetarios.data.rows[0] metrics.value.verde = verde.data.rows[0] metrics.value.secosVendidos = secosVendidos.data.rows[0] metrics.value.rechazos = rechazos.data.rows metrics.value.serieTemporal = serieTemporal.data.rows metrics.value.topClientes = topClientes.data.rows metrics.value.conteos = conteos.data.rows[0] } ``` ```vue ``` --- ## 鈿狅笍 NOTAS IMPORTANTES 1. **Sin composables de m茅tricas**: Los composables `useIngresosMetrics` y `useRechazosMetrics` se eliminan completamente. 2. **Metabase hace TODO**: C谩lculos, agregaciones, promedios ponderados, todo en SQL. 3. **Vue solo renderiza**: Los componentes reciben props con los valores ya calculados. 4. **Estructura de respuesta de Metabase**: ```javascript { data: { rows: [...], // Array de filas cols: [...], // Metadata de columnas rows_truncated: 1000 } } ``` 5. **Optimizaci贸n**: Las 9 queries se ejecutan en **paralelo** con `Promise.all()`. 6. **Filtros reactivos**: Cuando cambian los filtros, se vuelven a ejecutar las queries. --- **Documento creado:** 2025-10-14 **Autor:** Claude Code **Proyecto:** Anal铆tica N煤cleo - Panorama Facturador