Carpeta js
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
// 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);
|
||||
Reference in New Issue
Block a user