diff --git a/nuxt4-app/app/composables/useIngresosMetrics.ts b/nuxt4-app/app/composables/useIngresosMetrics.ts deleted file mode 100644 index 8ac04e2..0000000 --- a/nuxt4-app/app/composables/useIngresosMetrics.ts +++ /dev/null @@ -1,418 +0,0 @@ -/** - * ⚠️ 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' - -export interface IngresoRecord { - cliente_id: number - estado: 'pagado' | 'pendiente' - tipo: 'uva' | 'oreado' | 'mojado' | 'verde' - peso_seco: number - peso_neto: number - precio: number - created_at?: string - [key: string]: any // Para permitir otras propiedades dinámicas -} - -export interface IngresosMetrics { - // Totales de ingreso y compra - totalQqSecoIngresado: ComputedRef - totalQqSecoComprado: ComputedRef - totalLbUvaIngresada: ComputedRef - totalQqSecoUvaIngresado: ComputedRef - totalQqSecoMojadoIngresado: ComputedRef - totalQqSecoOreadoIngresado: ComputedRef - totalQqSecoMojadoOreadoIngresado: ComputedRef - totalLbUvaPagada: ComputedRef - totalQqSecoUvaPagado: ComputedRef - totalQqSecoMojadoPagado: ComputedRef - totalQqSecoOreadoPagado: ComputedRef - totalQqSecoMojadoOreadoPagado: ComputedRef - precioPromedioUvaPorQqLb: ComputedRef - precioPromedioUvaPorQq: ComputedRef - precioPromedioOreadoPorQq: ComputedRef - precioPromedioMojadoPorQq: ComputedRef - precioPromedioQqSeco: ComputedRef - - // Inversión - inversionUva: ComputedRef - inversionOreado: ComputedRef - inversionMojado: ComputedRef - totalInvertido: ComputedRef - - // Inventarios en depósito - totalQqSecoDeposito: ComputedRef - totalQqMojadoDeposito: ComputedRef - totalQqOreadoDeposito: ComputedRef - totalLbUvaDeposito: ComputedRef - - // Inversión restante - inversionRestanteOreado: ComputedRef - inversionRestanteMojado: ComputedRef - inversionRestanteUva: ComputedRef - inversionRestanteEsperada: ComputedRef - - // Totales netos de verde - totalLbNetoVerde: ComputedRef - precioPromedioVerdePagado: ComputedRef - totalLbNetoVerdeDeposito: ComputedRef - inversionVerdeHastaFecha: ComputedRef - inversionRestanteVerde: ComputedRef - totalLbNetoCompradoVerde: ComputedRef - - // Secos vendidos y pérdidas (placeholder) - totalQqSecoPorVender: ComputedRef - precioVentaPromedioPorQq: ComputedRef - precioCompraPromedioPorQq: ComputedRef - margenGananciaPorQq: ComputedRef -} - -export function useIngresosMetrics(ingresos: ComputedRef) { - // Función auxiliar para calcular total a pagar - const calcularTotalAPagar = (ingreso: IngresoRecord): number => { - if (ingreso.tipo === 'verde' || ingreso.tipo === 'uva') { - return ingreso.precio * ingreso.peso_neto - } - if (ingreso.tipo === 'oreado' || ingreso.tipo === 'mojado') { - return (ingreso.precio / 2) * ingreso.peso_seco - } - return 0 - } - - // TOTALES DE INGRESO Y COMPRA - const totalQqSecoIngresado = computed(() => { - return ingresos.value - .filter(i => i.estado === 'pagado' || i.estado === 'pendiente') - .reduce((sum, i) => sum + (i.peso_seco || 0), 0) - }) - - const totalQqSecoComprado = computed(() => { - return ingresos.value - .filter(i => i.estado === 'pagado') - .reduce((sum, i) => sum + (i.peso_seco || 0), 0) - }) - - // Total lb uva ingresada (peso_neto) - pagado y pendiente - const totalLbUvaIngresada = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'uva' && (i.estado === 'pagado' || i.estado === 'pendiente')) - .reduce((sum, i) => sum + (i.peso_neto || 0), 0) - }) - - // Total qq seco uva ingresado (peso_seco) - pagado y pendiente - const totalQqSecoUvaIngresado = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'uva' && (i.estado === 'pagado' || i.estado === 'pendiente')) - .reduce((sum, i) => sum + (i.peso_seco || 0), 0) - }) - - // Total qq seco mojado ingresado (peso_seco) - pagado y pendiente - const totalQqSecoMojadoIngresado = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'mojado' && (i.estado === 'pagado' || i.estado === 'pendiente')) - .reduce((sum, i) => sum + (i.peso_seco || 0), 0) - }) - - // Total qq seco oreado ingresado (peso_seco) - pagado y pendiente - const totalQqSecoOreadoIngresado = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'oreado' && (i.estado === 'pagado' || i.estado === 'pendiente')) - .reduce((sum, i) => sum + (i.peso_seco || 0), 0) - }) - - // Total qq seco mojado/oreado ingresado (peso_seco) - pagado y pendiente - const totalQqSecoMojadoOreadoIngresado = computed(() => { - return ingresos.value - .filter(i => (i.tipo === 'mojado' || i.tipo === 'oreado') && (i.estado === 'pagado' || i.estado === 'pendiente')) - .reduce((sum, i) => sum + (i.peso_seco || 0), 0) - }) - - // Total lb uva pagada (peso_neto) - solo pagado - const totalLbUvaPagada = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'uva' && i.estado === 'pagado') - .reduce((sum, i) => sum + (i.peso_neto || 0), 0) - }) - - // Total qq seco uva pagado (peso_seco) - solo pagado - const totalQqSecoUvaPagado = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'uva' && i.estado === 'pagado') - .reduce((sum, i) => sum + (i.peso_seco || 0), 0) - }) - - // Total qq seco mojado pagado (peso_seco) - solo pagado - const totalQqSecoMojadoPagado = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'mojado' && i.estado === 'pagado') - .reduce((sum, i) => sum + (i.peso_seco || 0), 0) - }) - - // Total qq seco oreado pagado (peso_seco) - solo pagado - const totalQqSecoOreadoPagado = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'oreado' && i.estado === 'pagado') - .reduce((sum, i) => sum + (i.peso_seco || 0), 0) - }) - - // Total qq seco mojado/oreado pagado (peso_seco) - solo pagado - const totalQqSecoMojadoOreadoPagado = computed(() => { - return ingresos.value - .filter(i => (i.tipo === 'mojado' || i.tipo === 'oreado') && i.estado === 'pagado') - .reduce((sum, i) => sum + (i.peso_seco || 0), 0) - }) - - const precioPromedioUvaPorQqLb = computed(() => { - const uvasPagadas = ingresos.value.filter(i => i.tipo === 'uva' && i.estado === 'pagado') - const sumaPesoNeto = uvasPagadas.reduce((sum, i) => sum + (i.peso_neto || 0), 0) - - if (sumaPesoNeto === 0) return 0 - - const sumaProducto = uvasPagadas.reduce((sum, i) => - sum + (i.peso_neto || 0) * (i.precio || 0), 0 - ) - - return sumaProducto / sumaPesoNeto - }) - - // Precio promedio uva por qq seco (500 lb uva = 1 qq seco) - const precioPromedioUvaPorQq = computed(() => { - return precioPromedioUvaPorQqLb.value * 500 - }) - - const precioPromedioOreadoPorQq = computed(() => { - const oreadosPagados = ingresos.value.filter(i => i.tipo === 'oreado' && i.estado === 'pagado') - const sumaPesoSeco = oreadosPagados.reduce((sum, i) => sum + (i.peso_seco || 0), 0) - - if (sumaPesoSeco === 0) return 0 - - const sumaProducto = oreadosPagados.reduce((sum, i) => - sum + (i.peso_seco || 0) * (i.precio || 0), 0 - ) - - return (sumaProducto / sumaPesoSeco) / 2 - }) - - const precioPromedioMojadoPorQq = computed(() => { - const mojadosPagados = ingresos.value.filter(i => i.tipo === 'mojado' && i.estado === 'pagado') - const sumaPesoSeco = mojadosPagados.reduce((sum, i) => sum + (i.peso_seco || 0), 0) - - if (sumaPesoSeco === 0) return 0 - - const sumaProducto = mojadosPagados.reduce((sum, i) => - sum + (i.peso_seco || 0) * (i.precio || 0), 0 - ) - - return (sumaProducto / sumaPesoSeco) / 2 - }) - - // Precio promedio ponderado de qq seco (combinando todos los tipos) - const precioPromedioQqSeco = computed(() => { - const todosPagados = ingresos.value.filter(i => i.estado === 'pagado') - const sumaPesoSeco = todosPagados.reduce((sum, i) => sum + (i.peso_seco || 0), 0) - - if (sumaPesoSeco === 0) return 0 - - const sumaTotal = todosPagados.reduce((sum, i) => sum + calcularTotalAPagar(i), 0) - - return sumaTotal / sumaPesoSeco - }) - - // INVERSIÓN - const inversionUva = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'uva' && i.estado === 'pagado') - .reduce((sum, i) => sum + calcularTotalAPagar(i), 0) - }) - - const inversionOreado = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'oreado' && i.estado === 'pagado') - .reduce((sum, i) => sum + calcularTotalAPagar(i), 0) - }) - - const inversionMojado = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'mojado' && i.estado === 'pagado') - .reduce((sum, i) => sum + calcularTotalAPagar(i), 0) - }) - - const totalInvertido = computed(() => { - return inversionUva.value + inversionOreado.value + inversionMojado.value - }) - - // INVENTARIOS EN DEPÓSITO - const totalQqSecoDeposito = computed(() => { - return ingresos.value - .filter(i => i.estado === 'pendiente') - .reduce((sum, i) => sum + (i.peso_seco || 0), 0) - }) - - const totalQqMojadoDeposito = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'mojado' && i.estado === 'pendiente') - .reduce((sum, i) => sum + (i.peso_seco || 0), 0) - }) - - const totalQqOreadoDeposito = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'oreado' && i.estado === 'pendiente') - .reduce((sum, i) => sum + (i.peso_seco || 0), 0) - }) - - const totalLbUvaDeposito = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'uva' && i.estado === 'pendiente') - .reduce((sum, i) => sum + (i.peso_neto || 0), 0) - }) - - // INVERSIÓN RESTANTE - const inversionRestanteOreado = computed(() => { - return precioPromedioOreadoPorQq.value * totalQqOreadoDeposito.value - }) - - const inversionRestanteMojado = computed(() => { - return precioPromedioMojadoPorQq.value * totalQqMojadoDeposito.value - }) - - const inversionRestanteUva = computed(() => { - return precioPromedioUvaPorQqLb.value * totalLbUvaDeposito.value - }) - - const inversionRestanteEsperada = computed(() => { - return inversionRestanteOreado.value + inversionRestanteMojado.value + inversionRestanteUva.value - }) - - // TOTALES NETOS DE VERDE - const totalLbNetoVerde = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'verde') - .reduce((sum, i) => sum + (i.peso_neto || 0), 0) - }) - - const precioPromedioVerdePagado = computed(() => { - const verdesPagados = ingresos.value.filter(i => i.tipo === 'verde' && i.estado === 'pagado') - const sumaPesoNeto = verdesPagados.reduce((sum, i) => sum + (i.peso_neto || 0), 0) - - if (sumaPesoNeto === 0) return 0 - - const sumaProducto = verdesPagados.reduce((sum, i) => - sum + (i.peso_neto || 0) * (i.precio || 0), 0 - ) - - return sumaProducto / sumaPesoNeto - }) - - const totalLbNetoVerdeDeposito = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'verde' && i.estado === 'pendiente') - .reduce((sum, i) => sum + (i.peso_neto || 0), 0) - }) - - const inversionVerdeHastaFecha = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'verde' && i.estado === 'pagado') - .reduce((sum, i) => sum + calcularTotalAPagar(i), 0) - }) - - const inversionRestanteVerde = computed(() => { - return precioPromedioVerdePagado.value * totalLbNetoVerdeDeposito.value - }) - - const totalLbNetoCompradoVerde = computed(() => { - return ingresos.value - .filter(i => i.tipo === 'verde' && i.estado === 'pagado') - .reduce((sum, i) => sum + (i.peso_neto || 0), 0) - }) - - // SECOS VENDIDOS Y PÉRDIDAS (placeholder - necesitan datos de ventas) - const totalQqSecoPorVender = computed(() => totalQqSecoDeposito.value) - const precioVentaPromedioPorQq = computed(() => 0) // Necesita datos de ventas - const precioCompraPromedioPorQq = computed(() => { - const totalPagado = ingresos.value.filter(i => i.estado === 'pagado') - const sumaPesoSeco = totalPagado.reduce((sum, i) => sum + (i.peso_seco || 0), 0) - - if (sumaPesoSeco === 0) return 0 - - const sumaTotal = totalPagado.reduce((sum, i) => sum + calcularTotalAPagar(i), 0) - - return sumaTotal / sumaPesoSeco - }) - const margenGananciaPorQq = computed(() => - precioVentaPromedioPorQq.value - precioCompraPromedioPorQq.value - ) - - return { - // Totales de ingreso y compra - totalQqSecoIngresado, - totalQqSecoComprado, - totalLbUvaIngresada, - totalQqSecoUvaIngresado, - totalQqSecoMojadoIngresado, - totalQqSecoOreadoIngresado, - totalQqSecoMojadoOreadoIngresado, - totalLbUvaPagada, - totalQqSecoUvaPagado, - totalQqSecoMojadoPagado, - totalQqSecoOreadoPagado, - totalQqSecoMojadoOreadoPagado, - precioPromedioUvaPorQqLb, - precioPromedioUvaPorQq, - precioPromedioOreadoPorQq, - precioPromedioMojadoPorQq, - precioPromedioQqSeco, - - // Inversión - inversionUva, - inversionOreado, - inversionMojado, - totalInvertido, - - // Inventarios en depósito - totalQqSecoDeposito, - totalQqMojadoDeposito, - totalQqOreadoDeposito, - totalLbUvaDeposito, - - // Inversión restante - inversionRestanteOreado, - inversionRestanteMojado, - inversionRestanteUva, - inversionRestanteEsperada, - - // Totales netos de verde - totalLbNetoVerde, - precioPromedioVerdePagado, - totalLbNetoVerdeDeposito, - inversionVerdeHastaFecha, - inversionRestanteVerde, - totalLbNetoCompradoVerde, - - // Secos vendidos y pérdidas - totalQqSecoPorVender, - precioVentaPromedioPorQq, - precioCompraPromedioPorQq, - margenGananciaPorQq - } -} \ No newline at end of file diff --git a/nuxt4-app/app/composables/useRechazosMetrics.ts b/nuxt4-app/app/composables/useRechazosMetrics.ts deleted file mode 100644 index 40054f9..0000000 --- a/nuxt4-app/app/composables/useRechazosMetrics.ts +++ /dev/null @@ -1,95 +0,0 @@ -/** - * ⚠️ 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' - -export interface RechazoRecord { - tipo: 'chibolita' | 'perico' | 'vano' | 'picadillo' | 'magalla' | 'pinta' - cantidad: number // libras para chibolita, perico, pinta; galones para vano, picadillo, magalla - precio_unitario: number - total_cobrado: number -} - -export interface RechazoMetrics { - totalCantidad: number - precioPromedio: number - totalCobrado: number -} - -export interface RechazosMetrics { - chibolita: ComputedRef - perico: ComputedRef - vano: ComputedRef - picadillo: ComputedRef - magalla: ComputedRef - pinta: ComputedRef - totalRechazos: ComputedRef -} - -export function useRechazosMetrics(rechazos: ComputedRef) { - const calcularMetricasPorTipo = (tipo: RechazoRecord['tipo']): ComputedRef => { - return computed(() => { - const registros = rechazos.value.filter(r => r.tipo === tipo) - - const totalCantidad = registros.reduce((sum, r) => sum + (r.cantidad || 0), 0) - // const totalCobrado = registros.reduce((sum, r) => sum + (r.total_cobrado || 0), 0) DESACTIVADO HASTA NORMALIZAR LOS DATOS DE LA TABLA ORIGINAL, MENCIONAR SI TE TOPAS CON ESTO UN MES DESPUES ESTAMOS EN 1 OCTUBRE 2025 - // const precioPromedio = totalCantidad > 0 ? totalCobrado / totalCantidad : 0 DESACTIVADO HASTA NORMALIZAR LOS DATOS DE LA TABLA ORIGINAL, MENCIONAR SI TE TOPAS CON ESTO UN MES DESPUES ESTAMOS EN 1 OCTUBRE 2025 - const precioPromedio = 10 - const totalCobrado = 100 - - return { - totalCantidad, - precioPromedio, - totalCobrado - } - }) - } - - const chibolita = calcularMetricasPorTipo('chibolita') - const perico = calcularMetricasPorTipo('perico') - const vano = calcularMetricasPorTipo('vano') - const picadillo = calcularMetricasPorTipo('picadillo') - const magalla = calcularMetricasPorTipo('magalla') - const pinta = calcularMetricasPorTipo('pinta') - - const totalRechazos = computed(() => { - return chibolita.value.totalCobrado + - perico.value.totalCobrado + - vano.value.totalCobrado + - picadillo.value.totalCobrado + - magalla.value.totalCobrado + - pinta.value.totalCobrado - }) - - return { - chibolita, - perico, - vano, - picadillo, - magalla, - pinta, - totalRechazos - } -} \ No newline at end of file