Refactorizar Panorama Facturador: implementar filosofía "Metabase calcula TODO, Vue solo renderiza"
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 5m41s
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 5m41s
Cambios principales: - Refactorizar todos los componentes de panorama para recibir datos directos de Metabase * TotalesMonetarios.vue: cambiar de props.metrics a props.data * TotalesIngresoCompra.vue: cambiar de props.metrics a props.data * TotalesVerde.vue: cambiar de props.metrics a props.data * SecosVendidos.vue: cambiar de props.metrics a props.data - Eliminar fechas hardcodeadas en panorama.post.ts * Pasar valores null directamente a Metabase para usar sus defaults - Marcar composables obsoletos para Panorama Facturador * useIngresosMetrics.ts: agregar advertencia de no uso en Panorama * useRechazosMetrics.ts: agregar advertencia de no uso en Panorama Resultado: Todos los cálculos (agregaciones, promedios ponderados) se hacen en Metabase mediante SQL. Los componentes Vue solo renderizan valores ya calculados.
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xs uppercase tracking-wide opacity-80 mb-1">Total qq Secos por Vender</span>
|
||||
<div class="flex items-baseline gap-2">
|
||||
<span class="text-2xl font-bold">{{ formatNumber(metrics.totalQqSecoPorVender.value) }}</span>
|
||||
<span class="text-2xl font-bold">{{ formatNumber(data.total_qq_seco_por_vender) }}</span>
|
||||
<span class="text-sm font-bold opacity-70">qq</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -18,7 +18,7 @@
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xs uppercase tracking-wide opacity-80 mb-1">Precio de Venta Promedio por qq</span>
|
||||
<div class="flex items-baseline gap-2">
|
||||
<span class="text-2xl font-bold">{{ formatCurrency(metrics.precioVentaPromedioPorQq.value) }}</span>
|
||||
<span class="text-2xl font-bold">{{ formatCurrency(data.precio_venta_promedio_por_qq) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -26,15 +26,15 @@
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xs uppercase tracking-wide opacity-80 mb-1">Precio de Compra Promedio por qq</span>
|
||||
<div class="flex items-baseline gap-2">
|
||||
<span class="text-2xl font-bold">{{ formatCurrency(metrics.precioCompraPromedioPorQq.value) }}</span>
|
||||
<span class="text-2xl font-bold">{{ formatCurrency(data.precio_compra_promedio_por_qq) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 rounded-lg border transition-all bg-[#1c140c] text-purple-600" :class="metrics.margenGananciaPorQq.value > 0 ? 'border-purple-600/40' : 'border-red-600/40'">
|
||||
<div class="p-4 rounded-lg border transition-all bg-[#1c140c] text-purple-600" :class="data.margen_ganancia_por_qq > 0 ? 'border-purple-600/40' : 'border-red-600/40'">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xs uppercase tracking-wide opacity-80 mb-1">Margen de Ganancia por qq</span>
|
||||
<div class="flex items-baseline gap-2">
|
||||
<span class="text-2xl font-bold">{{ formatCurrency(metrics.margenGananciaPorQq.value) }}</span>
|
||||
<span class="text-2xl font-bold">{{ formatCurrency(data.margen_ganancia_por_qq) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -43,10 +43,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { IngresosMetrics } from '~/composables/useIngresosMetrics'
|
||||
|
||||
// Props reciben datos directos de Metabase - SIN composables
|
||||
defineProps<{
|
||||
metrics: IngresosMetrics
|
||||
data: {
|
||||
total_qq_seco_por_vender: number
|
||||
precio_venta_promedio_por_qq: number
|
||||
precio_compra_promedio_por_qq: number
|
||||
margen_ganancia_por_qq: number
|
||||
}
|
||||
}>()
|
||||
|
||||
const formatNumber = (value: number) => {
|
||||
|
||||
@@ -23,25 +23,25 @@
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-2xl font-bold">{{ formatUvaValueNumber(metrics.totalLbUvaIngresada.value, metrics.totalQqSecoUvaIngresado.value) }}</span>
|
||||
<span class="text-2xl font-bold">{{ formatUvaValueNumber(data.total_lb_uva_ingresada, data.total_qq_seco_uva_ingresado) }}</span>
|
||||
<span class="text-sm font-bold opacity-70 ml-2">{{ formatUvaValueUnit() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<MetricCard
|
||||
label="Total qq Seco Mojado"
|
||||
:value="formatNumber(metrics.totalQqSecoMojadoIngresado.value)"
|
||||
:value="formatNumber(data.total_qq_seco_mojado_ingresado)"
|
||||
unit="qq"
|
||||
variant="info"
|
||||
/>
|
||||
<MetricCard
|
||||
label="Total qq Seco Oreado"
|
||||
:value="formatNumber(metrics.totalQqSecoOreadoIngresado.value)"
|
||||
:value="formatNumber(data.total_qq_seco_oreado_ingresado)"
|
||||
unit="qq"
|
||||
variant="warning"
|
||||
/>
|
||||
<MetricCard
|
||||
label="Total qq Seco Ingresado"
|
||||
:value="formatNumber(metrics.totalQqSecoIngresado.value)"
|
||||
:value="formatNumber(data.total_qq_seco_ingresado)"
|
||||
unit="qq"
|
||||
variant="primary"
|
||||
/>
|
||||
@@ -66,25 +66,25 @@
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-2xl font-bold">{{ formatUvaValueNumber(metrics.totalLbUvaPagada.value, metrics.totalQqSecoUvaPagado.value) }}</span>
|
||||
<span class="text-2xl font-bold">{{ formatUvaValueNumber(data.total_lb_uva_pagada, data.total_qq_seco_uva_pagado) }}</span>
|
||||
<span class="text-sm font-bold opacity-70 ml-2">{{ formatUvaValueUnit() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<MetricCard
|
||||
label="Total qq Seco Mojado Pagado"
|
||||
:value="formatNumber(metrics.totalQqSecoMojadoPagado.value)"
|
||||
:value="formatNumber(data.total_qq_seco_mojado_pagado)"
|
||||
unit="qq"
|
||||
variant="info"
|
||||
/>
|
||||
<MetricCard
|
||||
label="Total qq Seco Oreado Pagado"
|
||||
:value="formatNumber(metrics.totalQqSecoOreadoPagado.value)"
|
||||
:value="formatNumber(data.total_qq_seco_oreado_pagado)"
|
||||
unit="qq"
|
||||
variant="warning"
|
||||
/>
|
||||
<MetricCard
|
||||
label="Total qq Seco Comprado"
|
||||
:value="formatNumber(metrics.totalQqSecoComprado.value)"
|
||||
:value="formatNumber(data.total_qq_seco_comprado)"
|
||||
unit="qq"
|
||||
variant="primary"
|
||||
/>
|
||||
@@ -109,25 +109,25 @@
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-2xl font-bold">{{ formatUvaValueNumber(metrics.totalLbUvaDeposito.value, 0) }}</span>
|
||||
<span class="text-2xl font-bold">{{ formatUvaValueNumber(data.total_lb_uva_deposito, 0) }}</span>
|
||||
<span class="text-sm font-bold opacity-70 ml-2">{{ formatUvaValueUnit() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<MetricCard
|
||||
label="Total qq Seco Mojado en Depósito"
|
||||
:value="formatNumber(metrics.totalQqMojadoDeposito.value)"
|
||||
:value="formatNumber(data.total_qq_mojado_deposito)"
|
||||
unit="qq"
|
||||
variant="info"
|
||||
/>
|
||||
<MetricCard
|
||||
label="Total qq Seco Oreado en Depósito"
|
||||
:value="formatNumber(metrics.totalQqOreadoDeposito.value)"
|
||||
:value="formatNumber(data.total_qq_oreado_deposito)"
|
||||
unit="qq"
|
||||
variant="warning"
|
||||
/>
|
||||
<MetricCard
|
||||
label="Total qq Seco en Depósito"
|
||||
:value="formatNumber(metrics.totalQqSecoDeposito.value)"
|
||||
:value="formatNumber(data.total_qq_seco_deposito)"
|
||||
unit="qq"
|
||||
variant="primary"
|
||||
/>
|
||||
@@ -139,10 +139,25 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import type { IngresosMetrics } from '~/composables/useIngresosMetrics'
|
||||
|
||||
defineProps<{
|
||||
metrics: IngresosMetrics
|
||||
// Props reciben datos directos de Metabase - SIN composables
|
||||
const props = defineProps<{
|
||||
data: {
|
||||
total_lb_uva_ingresada: number
|
||||
total_qq_seco_uva_ingresado: number
|
||||
total_qq_seco_mojado_ingresado: number
|
||||
total_qq_seco_oreado_ingresado: number
|
||||
total_qq_seco_ingresado: number
|
||||
total_lb_uva_pagada: number
|
||||
total_qq_seco_uva_pagado: number
|
||||
total_qq_seco_mojado_pagado: number
|
||||
total_qq_seco_oreado_pagado: number
|
||||
total_qq_seco_comprado: number
|
||||
total_lb_uva_deposito: number
|
||||
total_qq_mojado_deposito: number
|
||||
total_qq_oreado_deposito: number
|
||||
total_qq_seco_deposito: number
|
||||
}
|
||||
}>()
|
||||
|
||||
type UnitDisplay = 'lb' | 'qq' | 'both'
|
||||
|
||||
@@ -13,22 +13,22 @@
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<MetricCard
|
||||
label="Inversión en Uva"
|
||||
:value="formatCurrency(metrics.inversionUva.value)"
|
||||
:value="formatCurrency(data.inversion_uva)"
|
||||
variant="danger"
|
||||
/>
|
||||
<MetricCard
|
||||
label="Inversión en Mojado"
|
||||
:value="formatCurrency(metrics.inversionMojado.value)"
|
||||
:value="formatCurrency(data.inversion_mojado)"
|
||||
variant="info"
|
||||
/>
|
||||
<MetricCard
|
||||
label="Inversión en Oreado"
|
||||
:value="formatCurrency(metrics.inversionOreado.value)"
|
||||
:value="formatCurrency(data.inversion_oreado)"
|
||||
variant="warning"
|
||||
/>
|
||||
<MetricCard
|
||||
label="Total Invertido"
|
||||
:value="formatCurrency(metrics.totalInvertido.value)"
|
||||
:value="formatCurrency(data.total_invertido)"
|
||||
variant="primary"
|
||||
/>
|
||||
</div>
|
||||
@@ -58,19 +58,19 @@
|
||||
</div>
|
||||
<MetricCard
|
||||
label="Precio Promedio Ponderado Mojado"
|
||||
:value="formatNumber(metrics.precioPromedioMojadoPorQq.value)"
|
||||
:value="formatNumber(data.precio_promedio_mojado_por_qq)"
|
||||
unit="L./qq"
|
||||
variant="info"
|
||||
/>
|
||||
<MetricCard
|
||||
label="Precio Promedio Ponderado Oreado"
|
||||
:value="formatNumber(metrics.precioPromedioOreadoPorQq.value)"
|
||||
:value="formatNumber(data.precio_promedio_oreado_por_qq)"
|
||||
unit="L./qq"
|
||||
variant="warning"
|
||||
/>
|
||||
<MetricCard
|
||||
label="Precio Promedio Ponderado qq Seco"
|
||||
:value="formatNumber(metrics.precioPromedioQqSeco.value)"
|
||||
:value="formatNumber(data.precio_promedio_qq_seco)"
|
||||
unit="L./qq"
|
||||
variant="primary"
|
||||
/>
|
||||
@@ -85,22 +85,22 @@
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<MetricCard
|
||||
label="Inversión Restante Uva"
|
||||
:value="formatCurrency(metrics.inversionRestanteUva.value)"
|
||||
:value="formatCurrency(data.inversion_restante_uva)"
|
||||
variant="danger"
|
||||
/>
|
||||
<MetricCard
|
||||
label="Inversión Restante Mojado"
|
||||
:value="formatCurrency(metrics.inversionRestanteMojado.value)"
|
||||
:value="formatCurrency(data.inversion_restante_mojado)"
|
||||
variant="info"
|
||||
/>
|
||||
<MetricCard
|
||||
label="Inversión Restante Oreado"
|
||||
:value="formatCurrency(metrics.inversionRestanteOreado.value)"
|
||||
:value="formatCurrency(data.inversion_restante_oreado)"
|
||||
variant="warning"
|
||||
/>
|
||||
<MetricCard
|
||||
label="Inversión Restante Esperada"
|
||||
:value="formatCurrency(metrics.inversionRestanteEsperada.value)"
|
||||
:value="formatCurrency(data.inversion_restante_esperada)"
|
||||
variant="primary"
|
||||
/>
|
||||
</div>
|
||||
@@ -111,10 +111,24 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import type { IngresosMetrics } from '~/composables/useIngresosMetrics'
|
||||
|
||||
// Props reciben datos directos de Metabase - SIN composables
|
||||
const props = defineProps<{
|
||||
metrics: IngresosMetrics
|
||||
data: {
|
||||
inversion_uva: number
|
||||
inversion_mojado: number
|
||||
inversion_oreado: number
|
||||
total_invertido: number
|
||||
precio_promedio_uva_por_lb: number
|
||||
precio_promedio_uva_por_qq: number
|
||||
precio_promedio_mojado_por_qq: number
|
||||
precio_promedio_oreado_por_qq: number
|
||||
precio_promedio_qq_seco: number
|
||||
inversion_restante_uva: number
|
||||
inversion_restante_mojado: number
|
||||
inversion_restante_oreado: number
|
||||
inversion_restante_esperada: number
|
||||
}
|
||||
}>()
|
||||
|
||||
type UnitDisplay = 'lb' | 'qq' | 'both'
|
||||
@@ -158,11 +172,11 @@ const formatCurrency = (value: number) => {
|
||||
function formatPrecioUvaNumber(): string {
|
||||
switch (unitDisplay.value) {
|
||||
case 'lb':
|
||||
return formatNumber(props.metrics.precioPromedioUvaPorQqLb.value)
|
||||
return formatNumber(props.data.precio_promedio_uva_por_lb)
|
||||
case 'qq':
|
||||
return formatNumber(props.metrics.precioPromedioUvaPorQq.value)
|
||||
return formatNumber(props.data.precio_promedio_uva_por_qq)
|
||||
case 'both':
|
||||
return `${formatNumber(props.metrics.precioPromedioUvaPorQqLb.value)} / ${formatNumber(props.metrics.precioPromedioUvaPorQq.value)}`
|
||||
return `${formatNumber(props.data.precio_promedio_uva_por_lb)} / ${formatNumber(props.data.precio_promedio_uva_por_qq)}`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xs uppercase tracking-wide opacity-80 mb-1">Total Lb Neto de Verde</span>
|
||||
<div class="flex items-baseline gap-2">
|
||||
<span class="text-2xl font-bold">{{ formatNumber(metrics.totalLbNetoVerde.value) }}</span>
|
||||
<span class="text-2xl font-bold">{{ formatNumber(data.total_lb_neto_verde) }}</span>
|
||||
<span class="text-sm font-bold opacity-70">lb</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -18,7 +18,7 @@
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xs uppercase tracking-wide opacity-80 mb-1">Precio Promedio Ponderado Pagado</span>
|
||||
<div class="flex items-baseline gap-2">
|
||||
<span class="text-2xl font-bold">{{ formatNumber(metrics.precioPromedioVerdePagado.value) }}</span>
|
||||
<span class="text-2xl font-bold">{{ formatNumber(data.precio_promedio_verde_pagado) }}</span>
|
||||
<span class="text-sm font-bold opacity-70">L./lb</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -27,7 +27,7 @@
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xs uppercase tracking-wide opacity-80 mb-1">Total Lb Neto de Verde en Depósito</span>
|
||||
<div class="flex items-baseline gap-2">
|
||||
<span class="text-2xl font-bold">{{ formatNumber(metrics.totalLbNetoVerdeDeposito.value) }}</span>
|
||||
<span class="text-2xl font-bold">{{ formatNumber(data.total_lb_neto_verde_deposito) }}</span>
|
||||
<span class="text-sm font-bold opacity-70">lb</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -36,7 +36,7 @@
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xs uppercase tracking-wide opacity-80 mb-1">Inversión en Verde Hasta la Fecha</span>
|
||||
<div class="flex items-baseline gap-2">
|
||||
<span class="text-2xl font-bold">{{ formatCurrency(metrics.inversionVerdeHastaFecha.value) }}</span>
|
||||
<span class="text-2xl font-bold">{{ formatCurrency(data.inversion_verde_hasta_fecha) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -44,7 +44,7 @@
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xs uppercase tracking-wide opacity-80 mb-1">Inversión Restante a Realizar en Verde</span>
|
||||
<div class="flex items-baseline gap-2">
|
||||
<span class="text-2xl font-bold">{{ formatCurrency(metrics.inversionRestanteVerde.value) }}</span>
|
||||
<span class="text-2xl font-bold">{{ formatCurrency(data.inversion_restante_verde) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -52,7 +52,7 @@
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xs uppercase tracking-wide opacity-80 mb-1">Total Lb Neto Comprado de Verde</span>
|
||||
<div class="flex items-baseline gap-2">
|
||||
<span class="text-2xl font-bold">{{ formatNumber(metrics.totalLbNetoCompradoVerde.value) }}</span>
|
||||
<span class="text-2xl font-bold">{{ formatNumber(data.total_lb_neto_comprado_verde) }}</span>
|
||||
<span class="text-sm font-bold opacity-70">lb</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -62,10 +62,16 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { IngresosMetrics } from '~/composables/useIngresosMetrics'
|
||||
|
||||
// Props reciben datos directos de Metabase - SIN composables
|
||||
defineProps<{
|
||||
metrics: IngresosMetrics
|
||||
data: {
|
||||
total_lb_neto_verde: number
|
||||
precio_promedio_verde_pagado: number
|
||||
total_lb_neto_verde_deposito: number
|
||||
inversion_verde_hasta_fecha: number
|
||||
inversion_restante_verde: number
|
||||
total_lb_neto_comprado_verde: number
|
||||
}
|
||||
}>()
|
||||
|
||||
const formatNumber = (value: number) => {
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
/**
|
||||
* ⚠️ ADVERTENCIA - COMPOSABLE OBSOLETO PARA PANORAMA FACTURADOR ⚠️
|
||||
*
|
||||
* Este composable NO debe usarse en el Panorama Facturador.
|
||||
*
|
||||
* FILOSOFÍA DE LA APLICACIÓN:
|
||||
* "Metabase calcula TODO. Vue solo renderiza."
|
||||
*
|
||||
* Los cálculos de métricas, agregaciones y promedios ponderados DEBEN
|
||||
* hacerse en Metabase mediante SQL. Los componentes de Vue solo deben
|
||||
* recibir datos ya calculados y renderizarlos.
|
||||
*
|
||||
* Este composable existe para compatibilidad con otras páginas legacy,
|
||||
* pero NO debe usarse en nuevas funcionalidades del Panorama.
|
||||
*
|
||||
* Para el Panorama Facturador, consulta:
|
||||
* - METABASE_QUERIES_PANORAMA.md
|
||||
* - server/api/metabase/panorama.post.ts
|
||||
* - pages/panorama.vue
|
||||
*
|
||||
* Última actualización: 2025-10-27
|
||||
*/
|
||||
|
||||
import { computed } from 'vue'
|
||||
import type { ComputedRef } from 'vue'
|
||||
|
||||
|
||||
@@ -1,3 +1,27 @@
|
||||
/**
|
||||
* ⚠️ ADVERTENCIA - COMPOSABLE OBSOLETO PARA PANORAMA FACTURADOR ⚠️
|
||||
*
|
||||
* Este composable NO debe usarse en el Panorama Facturador.
|
||||
*
|
||||
* FILOSOFÍA DE LA APLICACIÓN:
|
||||
* "Metabase calcula TODO. Vue solo renderiza."
|
||||
*
|
||||
* Los cálculos de métricas, agregaciones y promedios DEBEN hacerse
|
||||
* en Metabase mediante SQL. Los componentes de Vue solo deben recibir
|
||||
* datos ya calculados y renderizarlos.
|
||||
*
|
||||
* Este composable existe para compatibilidad con otras páginas legacy,
|
||||
* pero NO debe usarse en nuevas funcionalidades del Panorama.
|
||||
*
|
||||
* Para el Panorama Facturador, consulta:
|
||||
* - METABASE_QUERIES_PANORAMA.md (Query #6: panorama_rechazos_subproductos)
|
||||
* - server/api/metabase/panorama.post.ts
|
||||
* - pages/panorama.vue
|
||||
* - components/rechazos/RechazosSubproductos.vue
|
||||
*
|
||||
* Última actualización: 2025-10-27
|
||||
*/
|
||||
|
||||
import { computed } from 'vue'
|
||||
import type { ComputedRef } from 'vue'
|
||||
|
||||
|
||||
@@ -36,18 +36,17 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
|
||||
// Build parameters array for Metabase queries
|
||||
// IMPORTANTE: Si los parámetros son null, usar los valores por defecto de Metabase
|
||||
// En este caso, las queries usan los defaults: fecha_desde="2025-09-10", fecha_hasta="2025-10-14"
|
||||
// Los valores null se pasan directamente a Metabase para que use sus propios defaults
|
||||
const parameters = [
|
||||
{
|
||||
type: 'date/single',
|
||||
target: ['variable', ['template-tag', 'fecha_desde']],
|
||||
value: fecha_desde || '2025-09-10' // Usar default si es null
|
||||
value: fecha_desde
|
||||
},
|
||||
{
|
||||
type: 'date/single',
|
||||
target: ['variable', ['template-tag', 'fecha_hasta']],
|
||||
value: fecha_hasta || '2025-10-14' // Usar default si es null
|
||||
value: fecha_hasta
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
|
||||
Reference in New Issue
Block a user