Files
analiticaNucleo/nuxt4-app/server/api/metabase/informe.post.ts
josedario87 b235593f80
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 48s
Fix: Solo enviar par\u00e1metros con valores a Metabase para evitar filtros vac\u00edos
Problema: Las queries de Metabase en el endpoint /api/metabase/informe
retornaban 0 filas porque se enviaban par\u00e1metros con arrays vac\u00edos,
lo cual activaba filtros condicionales sin valores.

Soluci\u00f3n: Modificar buildParameters() para solo incluir par\u00e1metros
opcionales (cliente_ids, tipos, estados, ubicaciones, calidades) cuando
tengan valores (arrays no vac\u00edos). Los filtros condicionales [[...]]
de Metabase ahora se omiten correctamente cuando no hay valores.
2025-10-30 15:56:55 -06:00

202 lines
6.8 KiB
TypeScript

import { METABASE_QUERIES } from '../../config/metabase-queries'
/**
* Execute all informe queries in parallel
* Returns data for the Informe de Ingresos page
*/
export default defineEventHandler(async (event) => {
const body = await readBody(event)
const {
fecha_desde = null,
fecha_hasta = null,
incluir_anulados = false,
cliente_ids = [],
tipos = [],
estados = [],
ubicaciones = [],
calidades = [],
granularidad = 'dia'
} = body
try {
// First, get all cards to find our informe queries
const allCards = await getMetabaseCards('all')
// Find our informe queries by name using centralized config
const queryNames = METABASE_QUERIES.informe
const cards: Record<string, any> = {}
for (const [key, name] of Object.entries(queryNames)) {
const card = allCards.find((c: any) => c.name === name)
if (!card) {
console.warn(`[Informe] Query not found: ${name}`)
} else {
cards[key] = card
}
}
// Build parameters array for Metabase queries
// Las queries SQL nativas usan template-tags de tipo 'text' para fechas, no 'date/single'
// IMPORTANTE: Solo incluir parámetros con valores para que Metabase no active filtros opcionales con arrays vacíos
const buildParameters = (includeGranularidad: boolean = false) => {
const params = [
{
type: 'text',
target: ['variable', ['template-tag', 'fecha_desde']],
value: fecha_desde || ''
},
{
type: 'text',
target: ['variable', ['template-tag', 'fecha_hasta']],
value: fecha_hasta || ''
},
{
type: 'boolean',
target: ['variable', ['template-tag', 'incluir_anulados']],
value: incluir_anulados
}
]
// Solo agregar filtros opcionales si tienen valores (no vacíos)
if (cliente_ids && Array.isArray(cliente_ids) && cliente_ids.length > 0) {
params.push({
type: 'number',
target: ['variable', ['template-tag', 'cliente_ids']],
value: cliente_ids
})
}
if (tipos && Array.isArray(tipos) && tipos.length > 0) {
params.push({
type: 'text',
target: ['variable', ['template-tag', 'tipos']],
value: tipos
})
}
if (estados && Array.isArray(estados) && estados.length > 0) {
params.push({
type: 'text',
target: ['variable', ['template-tag', 'estados']],
value: estados
})
}
if (ubicaciones && Array.isArray(ubicaciones) && ubicaciones.length > 0) {
params.push({
type: 'text',
target: ['variable', ['template-tag', 'ubicaciones']],
value: ubicaciones
})
}
if (calidades && Array.isArray(calidades) && calidades.length > 0) {
params.push({
type: 'text',
target: ['variable', ['template-tag', 'calidades']],
value: calidades
})
}
if (includeGranularidad) {
params.push({
type: 'text',
target: ['variable', ['template-tag', 'granularidad']],
value: granularidad
})
}
return params
}
const standardParams = buildParameters(false)
const serieTemporalParams = buildParameters(true)
// Execute all queries in parallel with error handling
const executeWithErrorHandling = async (name: string, cardId: number | undefined, parameters: any[], defaultValue: any) => {
if (!cardId) {
console.warn(`[Informe] No card ID for ${name}`)
return defaultValue
}
try {
console.log(`[Informe] Executing query: ${name} (ID: ${cardId})`)
const result = await executeCardQuery(cardId, parameters)
console.log(`[Informe] Query ${name} returned ${result.data?.rows?.length || 0} rows`)
return result
} catch (error: any) {
console.error(`[Informe] Error executing ${name}:`, error.message)
return defaultValue
}
}
const [
totalesIngresoCompra,
totalesMonetarios,
totalesVerde,
listaIngresos,
listaClientes,
serieTemporal,
opcionesFiltros,
contadores
] = await Promise.all([
executeWithErrorHandling('totales_ingreso_compra', cards.totales_ingreso_compra?.id, standardParams, { data: { rows: [[]], cols: [] } }),
executeWithErrorHandling('totales_monetarios', cards.totales_monetarios?.id, standardParams, { data: { rows: [[]], cols: [] } }),
executeWithErrorHandling('totales_verde', cards.totales_verde?.id, standardParams, { data: { rows: [[]], cols: [] } }),
executeWithErrorHandling('lista_ingresos', cards.lista_ingresos?.id, standardParams, { data: { rows: [], cols: [] } }),
executeWithErrorHandling('lista_clientes', cards.lista_clientes?.id, standardParams, { data: { rows: [], cols: [] } }),
executeWithErrorHandling('serie_temporal', cards.serie_temporal?.id, serieTemporalParams, { data: { rows: [], cols: [] } }),
executeWithErrorHandling('opciones_filtros', cards.opciones_filtros?.id, [], { data: { rows: [[]], cols: [] } }),
executeWithErrorHandling('contadores', cards.contadores?.id, standardParams, { data: { rows: [[]], cols: [] } })
])
// Transform Metabase responses to objects for easier frontend consumption
const transformSingleRow = (result: any) => {
if (!result.data?.rows?.[0] || !result.data?.cols) return {}
const row = result.data.rows[0]
const cols = result.data.cols
const obj: any = {}
cols.forEach((col: any, index: number) => {
obj[col.name] = row[index]
})
return obj
}
const transformMultipleRows = (result: any) => {
if (!result.data?.rows || !result.data?.cols) return []
const cols = result.data.cols
return result.data.rows.map((row: any[]) => {
const obj: any = {}
cols.forEach((col: any, index: number) => {
obj[col.name] = row[index]
})
return obj
})
}
// Return all data in a structured format
return {
totalesIngresoCompra: transformSingleRow(totalesIngresoCompra),
totalesMonetarios: transformSingleRow(totalesMonetarios),
totalesVerde: transformSingleRow(totalesVerde),
listaIngresos: transformMultipleRows(listaIngresos),
listaClientes: transformMultipleRows(listaClientes),
serieTemporal: transformMultipleRows(serieTemporal),
opcionesFiltros: transformSingleRow(opcionesFiltros),
contadores: transformSingleRow(contadores)
}
} catch (error: any) {
console.error('[API] Failed to execute informe queries:', error)
throw createError({
statusCode: error.statusCode || 500,
statusMessage: error.statusMessage || 'Failed to execute informe queries'
})
}
})