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
This commit is contained in:
145
nuxt4-app/server/api/metabase/panorama.post.ts
Normal file
145
nuxt4-app/server/api/metabase/panorama.post.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* 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'
|
||||
})
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user