284 lines
7.8 KiB
JavaScript
284 lines
7.8 KiB
JavaScript
// 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 = '<i class="fas fa-chart-line me-2"></i> Balance Anual';
|
|
cargarBalance(anio);
|
|
|
|
} else {
|
|
document.getElementById('tituloInforme').innerHTML = '<i class="fas fa-money-bill-wave me-2"></i> 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); |