document.addEventListener('DOMContentLoaded', () => {
const tipoReporte = document.getElementById('tipo-reporte');
const parametros = document.getElementById('parametros');
const generarBtn = document.getElementById('generar-reporte');
const exportarBtn = document.getElementById('exportar-pdf');
const reporteContainer = document.getElementById('reporte-container');
const reporteContenido = document.getElementById('reporte-contenido');
const spinner = document.getElementById('spinner');
const errorMessage = document.getElementById('error-message');
// Variable global para almacenar el período actual
let periodoActual = {
tipo: 'diario',
fecha: new Date().toISOString().split('T')[0],
nombreArchivo: 'reporte'
};
// ========== FUNCIONES AUXILIARES ==========
function formatoMoneda(valor) {
return new Intl.NumberFormat('es-AR', {
style: 'currency',
currency: 'ARS'
}).format(valor);
}
function obtenerSemanaActual() {
const hoy = new Date();
const diaSemana = hoy.getDay();
const lunes = new Date(hoy);
const diferenciaDias = diaSemana === 0 ? -6 : 1 - diaSemana;
lunes.setDate(hoy.getDate() + diferenciaDias);
const fechaReferencia = new Date(lunes.getFullYear(), 0, 4);
const diaSemanaRef = fechaReferencia.getDay();
const lunesRef = new Date(fechaReferencia);
lunesRef.setDate(fechaReferencia.getDate() - (diaSemanaRef === 0 ? 6 : diaSemanaRef - 1));
const diffMs = lunes - lunesRef;
const diffDias = Math.floor(diffMs / (24 * 60 * 60 * 1000));
const numeroSemana = Math.floor(diffDias / 7) + 1;
const ano = lunes.getFullYear();
return `${ano}-W${String(numeroSemana).padStart(2, '0')}`;
}
function mostrarSpinner() {
spinner.style.display = 'flex';
}
function ocultarSpinner() {
spinner.style.display = 'none';
}
function mostrarError(mensaje) {
errorMessage.textContent = mensaje;
errorMessage.style.display = 'block';
}
function ocultarError() {
errorMessage.style.display = 'none';
}
function generarNombresYMetadatos(tipo, data) {
let nombreArchivo = 'reporte';
if (tipo === 'diario') {
const fecha = new Date(data.periodo + 'T00:00:00');
const fechaCorta = fecha.getDate().toString().padStart(2, '0') + '-' +
(fecha.getMonth() + 1).toString().padStart(2, '0') + '-' +
fecha.getFullYear().toString().slice(-2);
nombreArchivo = 'reporte_' + fechaCorta;
} else if (tipo === 'semanal') {
const inicio = new Date(data.fecha_inicio + 'T00:00:00');
const fin = new Date(data.fecha_fin + 'T00:00:00');
const diaInicio = inicio.getDate().toString().padStart(2, '0');
const diaFin = fin.getDate().toString().padStart(2, '0');
const mesNombre = inicio.toLocaleDateString('es-ES', { month: 'long' });
nombreArchivo = `reporte_semana_${diaInicio}-${diaFin}_${mesNombre}`;
} else if (tipo === 'mensual') {
const mesesNombres = ['', 'enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'];
let nombreMes = 'mensual';
let anio = new Date().getFullYear();
if (data.nombre_mes) {
nombreMes = data.nombre_mes;
anio = data.nombre_anio || anio;
} else if (data.fecha_inicio) {
const f = new Date(data.fecha_inicio + 'T00:00:00');
if (!isNaN(f.getTime())) {
nombreMes = f.toLocaleDateString('es-ES', { month: 'long' });
anio = f.getFullYear();
}
} else if (data.periodo && /^\d{4}-\d{2}$/.test(String(data.periodo))) {
const parts = String(data.periodo).split('-');
nombreMes = mesesNombres[parseInt(parts[1] || '0')] || nombreMes;
anio = parts[0] || anio;
}
nombreArchivo = 'reporte_' + nombreMes + '_' + anio;
} else if (tipo === 'anual') {
const anio = data.periodo || data.anio || (data.fecha_inicio ? (new Date(data.fecha_inicio + 'T00:00:00')).getFullYear() : new Date().getFullYear());
nombreArchivo = 'reporte_' + anio;
}
periodoActual.tipo = tipo;
periodoActual.nombreArchivo = nombreArchivo;
}
function renderizarDiario(data) {
const fecha = new Date(data.periodo + 'T00:00:00');
const opciones = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
const fechaFormato = fecha.toLocaleDateString('es-ES', opciones);
const fechaCorta = fecha.getDate().toString().padStart(2, '0') + '-' +
(fecha.getMonth() + 1).toString().padStart(2, '0') + '-' +
fecha.getFullYear().toString().slice(-2);
let html = `
📅 Reporte Diario ${fechaCorta} - Cierre de Caja
Reporte ${fechaFormato}
| Hora |
Tipo de Operación |
Descripción / Detalle |
Monto ($) |
Usuario |
`;
if (data.transacciones && data.transacciones.length > 0) {
data.transacciones.forEach(tx => {
const monto = parseFloat(tx.monto);
const claseNegativo = monto < 0 ? 'negativo' : '';
html += `
| ${tx.hora || '--:--'} |
${tx.tipo_operacion} |
${tx.descripcion || '--'} |
${formatoMoneda(monto)} |
${tx.usuario_registro || 'Sistema'} |
`;
});
} else {
html += `| Sin transacciones registradas |
`;
}
html += `
Resumen del Día
Ingresos Reservas:
${formatoMoneda(data.resumen.ingresos_reservas)}
Ingresos Cantina:
${formatoMoneda(data.resumen.ingresos_cantina)}
Total Ingresos:
${formatoMoneda(data.resumen.total_ingresos)}
Egresos Totales:
-${formatoMoneda(data.resumen.egresos)}
💰 SALDO NETO:
${formatoMoneda(data.resumen.saldo_neto)}
`;
reporteContenido.innerHTML = html;
}
function renderizarSemanal(data) {
const inicio = new Date(data.fecha_inicio + 'T00:00:00');
const fin = new Date(data.fecha_fin + 'T00:00:00');
const opciones = { month: 'short', day: 'numeric' };
const inicioCorta = inicio.getDate().toString().padStart(2, '0') + '-' +
(inicio.getMonth() + 1).toString().padStart(2, '0');
const finCorta = fin.getDate().toString().padStart(2, '0') + '-' +
(fin.getMonth() + 1).toString().padStart(2, '0') + '-' +
fin.getFullYear().toString().slice(-2);
let html = `
📆 Reporte Semanal ${inicioCorta} al ${finCorta} - Flujo de Caja
Reporte Semana del ${inicio.toLocaleDateString('es-ES', opciones)} al ${fin.toLocaleDateString('es-ES', opciones)}
| Día de la Semana |
Ingresos Reservas ($) |
Ingresos Cantina ($) |
Costo Mercadería ($) |
Saldo Neto del Día ($) |
`;
if (data.dias && data.dias.length > 0) {
data.dias.forEach(dia => {
const nombreDia = new Date(dia.dia + 'T00:00:00').toLocaleDateString('es-ES', { weekday: 'long' });
const saldoNeto = dia.saldo_neto;
const claseNegativo = saldoNeto < 0 ? 'negativo' : '';
html += `
| ${nombreDia.charAt(0).toUpperCase() + nombreDia.slice(1)} (${dia.dia}) |
${formatoMoneda(dia.ingresos_reservas)} |
${formatoMoneda(dia.ingresos_cantina)} |
${formatoMoneda(dia.costo_mercaderia)} |
${formatoMoneda(saldoNeto)} |
`;
});
}
html += `
| Resumen de la Semana |
| Ingresos Reservas Total: |
${formatoMoneda(data.resumen.ingresos_reservas_totales)} |
| Ingresos Cantina Total: |
${formatoMoneda(data.resumen.ingresos_cantina_totales)} |
| Costo Mercadería Total: |
${formatoMoneda(data.resumen.costo_mercaderia_total)} |
| 💰 Saldo Neto Semana: |
${formatoMoneda(data.resumen.saldo_neto_semana)} |
% Ocupación Promedio
Turnos Ocupados:
${data.resumen.turnos_ocupados} / ${data.resumen.turnos_disponibles}
Porcentaje Ocupación:
${data.resumen.ocupacion_promedio}%
`;
reporteContenido.innerHTML = html;
}
function renderizarMensual(data) {
const fechaInicio = new Date(data.fecha_inicio + 'T00:00:00');
const mesCorta = (fechaInicio.getMonth() + 1).toString().padStart(2, '0') + '-' +
fechaInicio.getFullYear().toString().slice(-2);
const nombreMes = data.nombre_mes || fechaInicio.toLocaleDateString('es-ES', { month: 'long' });
let html = `
📊 Reporte Mensual ${mesCorta} - Rentabilidad
Reporte de ${nombreMes.charAt(0).toUpperCase() + nombreMes.slice(1)}
Ingreso por Categoría
| Concepto / Categoría |
Operaciones |
Monto ($) |
`;
if (data.categorias && data.categorias.length > 0) {
data.categorias.forEach(cat => {
html += `
| ${cat.categoria} |
${cat.operaciones} |
${formatoMoneda(cat.monto)} |
`;
});
}
html += `
Gastos Fijos
| Nombre del Gasto |
Monto Gasto ($) |
`;
if (data.gastos_fijos && data.gastos_fijos.length > 0) {
data.gastos_fijos.forEach(gasto => {
html += `
| ${gasto.descripcion} |
${formatoMoneda(gasto.monto)} |
`;
});
} else {
html += `
| Sin gastos registrados |
`;
}
html += `
Estado de Resultados
Ingresos Totales:
${formatoMoneda(data.resumen.ingresos_totales)}
Costo de Mercadería:
-${formatoMoneda(data.resumen.costo_mercaderia)}
Ganancia Bruta:
${formatoMoneda(data.resumen.ganancia_bruta)}
Gastos Fijos:
-${formatoMoneda(data.resumen.gastos_fijos_total)}
💰 GANANCIA NETA:
${formatoMoneda(data.resumen.ganancia_neta)}
`;
reporteContenido.innerHTML = html;
}
function renderizarAnual(data) {
let notaAnoActual = '';
if (data.es_ano_actual) {
notaAnoActual = `
ℹ️ Nota: Este es el año en curso. El reporte muestra datos hasta el ${data.mes_actual}° mes (mes actual).
`;
}
let html = `
📈 Reporte Anual ${data.periodo} - Visión Estratégica
Reporte Año ${data.periodo}
${notaAnoActual}
| Mes |
Ingreso Operativo ($) |
Costo Bruto ($) |
Costo Operativo ($) |
Ganancia Bruta ($) |
Ganancia Neta ($) |
`;
if (data.meses && data.meses.length > 0) {
data.meses.forEach(mes => {
const gananciaNeta = parseFloat(mes.ganancia_neta);
const claseNegativo = gananciaNeta < 0 ? 'negativo' : '';
html += `
| ${mes.mes} |
${formatoMoneda(mes.ingreso_operativo)} |
${formatoMoneda(mes.costo_bruto)} |
${formatoMoneda(mes.costo_operativo)} |
${formatoMoneda(mes.ganancia_bruta)} |
${formatoMoneda(gananciaNeta)} |
`;
});
}
html += `
Totales Anuales
Ingreso Operativo Total:
${formatoMoneda(data.totales.ingreso_operativo_total)}
Costo Bruto Total:
-${formatoMoneda(data.totales.costo_bruto_total)}
Costo Operativo Total:
-${formatoMoneda(data.totales.costo_operativo_total)}
Ganancia Bruta Total:
${formatoMoneda(data.totales.ganancia_bruta_total)}
💰 GANANCIA NETA ANUAL:
${formatoMoneda(data.totales.ganancia_neta_total)}
`;
reporteContenido.innerHTML = html;
}
// ========== INICIALIZACIÓN ==========
// Establecer valores por defecto
const hoy = new Date();
document.getElementById('fecha-diario').valueAsDate = hoy;
const semanaActual = obtenerSemanaActual();
document.getElementById('semana-input').value = semanaActual;
const mesActual = hoy.toISOString().slice(0, 7);
document.getElementById('mes-input').value = mesActual;
document.getElementById('anio-input').value = hoy.getFullYear();
// Cambiar parámetros según tipo de reporte
tipoReporte.addEventListener('change', () => {
document.querySelectorAll('.param-section').forEach(el => el.style.display = 'none');
const tipo = tipoReporte.value;
if (tipo === 'diario') {
document.getElementById('param-diario').style.display = 'block';
} else if (tipo === 'semanal') {
document.getElementById('param-semanal').style.display = 'block';
} else if (tipo === 'mensual') {
document.getElementById('param-mensual').style.display = 'block';
} else if (tipo === 'anual') {
document.getElementById('param-anual').style.display = 'block';
}
});
// Generar reporte
generarBtn.addEventListener('click', async () => {
const tipo = tipoReporte.value;
let url = '../php/Obtener_Reporte.php?tipo_reporte=' + tipo;
// Agregar parámetros según el tipo
if (tipo === 'diario') {
const fecha = document.getElementById('fecha-diario').value;
if (!fecha) {
mostrarError('Por favor selecciona una fecha');
return;
}
url += '&fecha=' + fecha;
} else if (tipo === 'semanal') {
const semana = document.getElementById('semana-input').value;
if (!semana) {
mostrarError('Por favor selecciona una semana');
return;
}
url += '&semana=' + semana;
} else if (tipo === 'mensual') {
const mes = document.getElementById('mes-input').value;
if (!mes) {
mostrarError('Por favor selecciona un mes');
return;
}
url += '&mes=' + mes;
} else if (tipo === 'anual') {
const anio = document.getElementById('anio-input').value;
if (!anio) {
mostrarError('Por favor selecciona un año');
return;
}
url += '&anio=' + anio;
}
ocultarError();
mostrarSpinner();
try {
const response = await fetch(url);
const data = await response.json();
if (data.error) {
mostrarError(data.error);
ocultarSpinner();
return;
}
ocultarSpinner();
// Generar nombres de archivo
generarNombresYMetadatos(tipo, data);
// Renderizar según tipo
if (tipo === 'diario') {
renderizarDiario(data);
} else if (tipo === 'semanal') {
renderizarSemanal(data);
} else if (tipo === 'mensual') {
renderizarMensual(data);
} else if (tipo === 'anual') {
renderizarAnual(data);
}
reporteContainer.style.display = 'block';
exportarBtn.style.display = 'inline-block';
} catch (error) {
mostrarError('Error al cargar el reporte: ' + error.message);
ocultarSpinner();
}
});
// Exportar a PDF
exportarBtn.addEventListener('click', () => {
const elemento = document.getElementById('reporte-contenido');
const nombreArchivo = periodoActual.nombreArchivo;
const opciones = {
margin: 10,
filename: nombreArchivo + '.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2, useCORS: true },
jsPDF: { orientation: 'portrait', unit: 'mm', format: 'a4', compress: true }
};
html2pdf()
.set(opciones)
.from(elemento)
.save(nombreArchivo + '.pdf');
});
});