// 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);