Files
analiticaNucleo/nuxt4-app/server/api/metabase/panorama.post.ts
josedario87 f8c53da6fc
All checks were successful
build-and-deploy / build (push) Successful in 43s
build-and-deploy / deploy (push) Successful in 4s
feat: restaurar panorama facturador con nueva arquitectura basada en Metabase
- Crear endpoint /api/metabase/panorama.post.ts que ejecuta las 9 queries en paralelo
- Restaurar y adaptar panorama.vue para usar el nuevo endpoint
- Crear componentes auxiliares: SecosVendidos, TotalesIngresoCompra, TotalesMonetarios, TotalesVerde, MetricBox, RechazosRechazoCard
- Adaptar RechazosSubproductos para recibir data directamente de Metabase
- Toda la transformación de datos ocurre en las queries SQL de Metabase
- Sin uso de stores ni composables de métricas
- Agregar documentación de queries en archivos MD
2025-10-14 10:34:27 -06:00

146 lines
4.7 KiB
TypeScript

/**
* Execute all panorama queries in parallel
* Returns data for the Panorama Facturador page
*/
export default defineEventHandler(async (event) => {
const body = await readBody(event)
const { fecha_desde = null, fecha_hasta = null, incluir_anulados = false } = body
try {
// First, get all cards to find our panorama queries
const allCards = await getMetabaseCards('all')
// Find our panorama queries by name
const queryNames = [
'panorama_totales_financieros_principales',
'panorama_totales_ingreso_compra',
'panorama_totales_monetarios',
'panorama_totales_verde',
'panorama_secos_vendidos',
'panorama_rechazos_subproductos',
'panorama_serie_temporal_diaria',
'panorama_top_clientes',
'panorama_conteo_registros'
]
const cards: Record<string, any> = {}
for (const name of queryNames) {
const card = allCards.find((c: any) => c.name === name)
if (!card) {
console.warn(`[Panorama] Query not found: ${name}`)
} else {
cards[name] = card
}
}
// Build parameters array for Metabase queries
const parameters = [
{
type: 'date/single',
target: ['variable', ['template-tag', 'fecha_desde']],
value: fecha_desde
},
{
type: 'date/single',
target: ['variable', ['template-tag', 'fecha_hasta']],
value: fecha_hasta
},
{
type: 'category',
target: ['variable', ['template-tag', 'incluir_anulados']],
value: incluir_anulados
}
]
// Execute all queries in parallel
const [
financieros,
ingresoCompra,
monetarios,
verde,
secosVendidos,
rechazos,
serieTemporal,
topClientes,
conteos
] = await Promise.all([
cards['panorama_totales_financieros_principales']
? executeCardQuery(cards['panorama_totales_financieros_principales'].id, parameters)
: { data: { rows: [[0, 0, 0]] } },
cards['panorama_totales_ingreso_compra']
? executeCardQuery(cards['panorama_totales_ingreso_compra'].id, parameters)
: { data: { rows: [[]] } },
cards['panorama_totales_monetarios']
? executeCardQuery(cards['panorama_totales_monetarios'].id, parameters)
: { data: { rows: [[]] } },
cards['panorama_totales_verde']
? executeCardQuery(cards['panorama_totales_verde'].id, parameters)
: { data: { rows: [[]] } },
cards['panorama_secos_vendidos']
? executeCardQuery(cards['panorama_secos_vendidos'].id, parameters)
: { data: { rows: [[]] } },
cards['panorama_rechazos_subproductos']
? executeCardQuery(cards['panorama_rechazos_subproductos'].id, parameters)
: { data: { rows: [] } },
cards['panorama_serie_temporal_diaria']
? executeCardQuery(cards['panorama_serie_temporal_diaria'].id, parameters)
: { data: { rows: [] } },
cards['panorama_top_clientes']
? executeCardQuery(cards['panorama_top_clientes'].id, parameters)
: { data: { rows: [] } },
cards['panorama_conteo_registros']
? executeCardQuery(cards['panorama_conteo_registros'].id, parameters)
: { data: { rows: [[0, 0, 0, 0]] } }
])
// 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 {
financieros: transformSingleRow(financieros),
ingresoCompra: transformSingleRow(ingresoCompra),
monetarios: transformSingleRow(monetarios),
verde: transformSingleRow(verde),
secosVendidos: transformSingleRow(secosVendidos),
rechazos: transformMultipleRows(rechazos),
serieTemporal: transformMultipleRows(serieTemporal),
topClientes: transformMultipleRows(topClientes),
conteos: transformSingleRow(conteos)
}
} catch (error: any) {
console.error('[API] Failed to execute panorama queries:', error)
throw createError({
statusCode: error.statusCode || 500,
statusMessage: error.statusMessage || 'Failed to execute panorama queries'
})
}
})