// Variable global para almacenar la instancia del gráfico, Chart.js no permite reutilizar el canvas sin destruir el anterior let grafico; // FUNCIÓN PARA ACTUALIZAR KPIS function actualizarKpis(kpis) { // KPI: INGRESOS // Formateo a moneda argentina con separador de miles document.getElementById('kpiIngresos').innerText = '$' + kpis.totalIngresos.toLocaleString('es-AR'); // KPI: EGRESOS document.getElementById('kpiEgresos').innerText = '$' + kpis.totalCostos.toLocaleString('es-AR'); // KPI: RESULTADO NETO const resultadoEl = document.getElementById('kpiResultado'); resultadoEl.innerText = '$' + kpis.resultado.toLocaleString('es-AR'); // Se ajusta el color dinámicamente, verde si hay ganancia y rojo si hay pérdida resultadoEl.classList.remove('text-success', 'text-danger'); resultadoEl.classList.add( kpis.resultado >= 0 ? 'text-success' : 'text-danger' ); // KPI: MEJOR MES (calculado desde backend) document.getElementById('kpiMejorMes').innerText = kpis.mejorMes; } // FUNCIÓN PARA CARGAR BALANCE async function cargarBalance(anio) { // Llamada al backend (controller PHP), se pasa el año como parámetro const response = await fetch(`index.php?opt=generar_balance_anual&anio=${anio}`); // Convertimos la respuesta a JSON const responseJson = await response.json(); // TRANSFORMACIÓN DE DATOS // El backend devuelve los meses como números, acá se convierten a nombres abreviados const meses = responseJson.meses.map(m => { const nombres = ["Ene","Feb","Mar","Abr","May","Jun","Jul","Ago","Sep","Oct","Nov","Dic"]; return nombres[m - 1]; }); // Parseo de datos, convertimos todos los valores a números (por seguridad) const ventas = responseJson.ventas.map(v => parseFloat(v)); const servicios = responseJson.servicios.map(s => parseFloat(s)); const costos = responseJson.costos.map(c => parseFloat(c)); const utilidad = responseJson.utilidad.map(u => parseFloat(u)); // Obtenemos el contexto del canvas const ctx = document.getElementById('graficoBalance').getContext('2d'); // Destruir gráfico anterior if (grafico) { grafico.destroy(); } // CREAR NUEVO GRÁFICO grafico = new Chart(ctx, { type: 'bar', // Tipo base (gráfico de barras) data: { labels: meses, // Etiquetas (meses) datasets: [ // Dataset VENTAS { label: 'Ventas', data: ventas }, // Dataset SERVICIOS { label: 'Servicios', data: servicios }, // Dataset COSTOS { label: 'Costos', data: costos }, // Dataset UTILIDAD (linea) { label: 'Utilidad', data: utilidad, type: 'line' } ] }, options: { // Hace que el gráfico se adapte automáticamente al tamanio del contenedor responsive: true, // Permite mostrar todos los valores al pasar el mouse interaction: { mode: 'index', // Muestra todos los datos intersect: false // No hace falta tocar la línea (con estar cerca ya muestra los datos) }, plugins: { // Tooltip personalizado tooltip: { callbacks: { label: function(context) { // CONTEXT: objeto que da Chart.js que contiene info del punto actual return context.dataset.label + ': $' + context.parsed.y.toLocaleString('es-AR'); } } } }, scales: { // Eje Y (valores) y: { beginAtZero: true, // Fuerza a que el eje empiece desde cero // Formateo de valores como moneda ticks: { callback: function(value) { return '$' + value.toLocaleString('es-AR'); } } } } } }); // Actualizar KPIs actualizarKpis(responseJson.kpis); } // FUNCIÓN CARGAR DINERO A COBRAR async function cargarACobrar(anio) { // Llamada al backend const response = await fetch(`index.php?opt=generar_cuentas_por_cobrar&anio=${anio}`); const responseJson = await response.json(); // Transformación de meses const meses = responseJson.meses.map(m => { const nombres = ["Ene","Feb","Mar","Abr","May","Jun","Jul","Ago","Sep","Oct","Nov","Dic"]; return nombres[m - 1]; }); // Datos (totales a cobrar por mes) const totales = responseJson.totales.map(t => parseFloat(t)); const ctx = document.getElementById('graficoBalance').getContext('2d'); // Destruimos gráfico anterior if (grafico) { grafico.destroy(); } // Creamos nuevo gráfico grafico = new Chart(ctx, { type: 'bar', // Del tipo barra data: { labels: meses, datasets: [ { label: 'Dinero a Cobrar', data: totales } ] }, options: { responsive: true, interaction: { mode: 'index', intersect: false }, plugins: { tooltip: { callbacks: { label: function(context) { return context.dataset.label + ': $' + context.parsed.y.toLocaleString('es-AR'); } } } }, scales: { y: { beginAtZero: true, ticks: { callback: function(value) { return '$' + value.toLocaleString('es-AR'); } } } } } }); // KPIs SIMPLIFICADOS // Solo mostramos el total a cobrar document.getElementById('kpiIngresos').innerText = '$' + responseJson.kpis.totalACobrar.toLocaleString('es-AR'); // En este contexto los demas KPIs no aplican document.getElementById('kpiEgresos').innerText = '-'; document.getElementById('kpiResultado').innerText = '-'; document.getElementById('kpiMejorMes').innerText = '-'; } // EVENTO: CAMBIO DE ANIO document.getElementById('anio').addEventListener('change', function() { // Se obtiene el tipo de informe actual const tipo = document.getElementById('tipoInforme').value; // Segú el tipo, se llama a la función correspondiente if (tipo === 'balance') { cargarBalance(this.value); } else { cargarACobrar(this.value); } }); // EVENTO: CAMBIO DE TIPO DE INFORME document.getElementById('tipoInforme').addEventListener('change', function() { const anio = document.getElementById('anio').value; if (this.value === 'balance') { // Cambia el título dinámicamente document.getElementById('tituloInforme').innerHTML = ' Balance Anual'; cargarBalance(anio); } else { document.getElementById('tituloInforme').innerHTML = ' Dinero a Cobrar'; cargarACobrar(anio); } }); // INICIALIZACIÓN // Se obtiene el año actual automáticamente const anioInicial = new Date().getFullYear(); // Se carga el balance al inciar la vista cargarBalance(anioInicial);