Files
Complejo_Cap1tan/php/Obtener_Reporte.php
T
MarcosFlorSalcedo 8ec55e40fc 1ra Versión
2026-07-14 18:24:27 -03:00

1206 lines
52 KiB
PHP

<?php
require_once 'conexion.php';
session_start();
header('Content-Type: application/json');
$tipo_reporte = $_GET['tipo_reporte'] ?? 'financiero';
$tipo = $_GET['tipo'] ?? '';
$fecha = $_GET['fecha'] ?? date('Y-m-d');
$semana = $_GET['semana'] ?? '';
$mes = $_GET['mes'] ?? '';
$anio = $_GET['anio'] ?? '';
$tipo_cancha = $_GET['tipo_cancha'] ?? '';
$id_usuario_logueado = $_SESSION['id_usuario'] ?? null;
// Función para obtener gastos fijos de un período
function obtenerGastosFijos($conexion, $fecha_inicio, $fecha_fin) {
$sql = "
SELECT * FROM gastos
WHERE Periodo_Desde <= '$fecha_fin'
AND (Periodo_Hasta IS NULL OR Periodo_Hasta >= '$fecha_inicio')
ORDER BY Periodo_Desde DESC
LIMIT 1
";
$result = $conexion->query($sql);
$total_gastos = 0;
$gastos_detalles = [];
if ($result && $result->num_rows > 0) {
$row = $result->fetch_assoc();
// Sumar todos los valores numéricos excepto ID_Gasto, Periodo_Desde, Periodo_Hasta
foreach ($row as $columna => $valor) {
if (!in_array($columna, ['ID_Gasto', 'Periodo_Desde', 'Periodo_Hasta']) && is_numeric($valor) && floatval($valor) > 0) {
$total_gastos += floatval($valor);
$gastos_detalles[] = [
'descripcion' => ucfirst($columna),
'monto' => floatval($valor)
];
}
}
}
return ['total' => $total_gastos, 'detalles' => $gastos_detalles];
}
function obtenerHorasDisponiblesPorPeriodo($conexion, $fecha_inicio, $fecha_fin) {
$total_horas = 0;
$fecha = new DateTime($fecha_inicio);
$fecha_fin_dt = new DateTime($fecha_fin);
while ($fecha <= $fecha_fin_dt) {
$diaSemana = intval($fecha->format('N'));
$diaTexto = $diaSemana >= 1 && $diaSemana <= 5 ? 'Lunes a Viernes' : ($diaSemana === 6 ? 'Sábado' : 'Domingo');
$sqlHorario = "SELECT Hora_Apertura, Hora_Cierre FROM horarios WHERE Dia = '$diaTexto' LIMIT 1";
$resultHorario = $conexion->query($sqlHorario);
if ($resultHorario && $rowHorario = $resultHorario->fetch_assoc()) {
$horaApertura = $rowHorario['Hora_Apertura'];
$horaCierre = $rowHorario['Hora_Cierre'];
if ($horaApertura && $horaCierre) {
$horasDia = (strtotime($horaCierre) - strtotime($horaApertura)) / 3600;
if ($horasDia > 0) {
$total_horas += $horasDia;
}
}
}
$fecha->modify('+1 day');
}
return $total_horas;
}
try {
// ===== REPORTE FINANCIERO =====
if ($tipo_reporte === 'financiero') {
// ===== REPORTE DIARIO =====
if ($tipo === 'diario') {
// Filtro opcional: si hay usuario logueado, mostrar solo sus operaciones
$filtro_venta = $id_usuario_logueado ? "AND v.ID_Usuario = $id_usuario_logueado" : "";
$filtro_reserva = $id_usuario_logueado ? "AND r.ID_Usuario = $id_usuario_logueado" : "";
$filtro_orden = $id_usuario_logueado ? "AND oc.ID_Usuario = $id_usuario_logueado" : "";
$sql = "
SELECT
DATE(v.Fecha) as fecha,
TIME(v.Fecha) as hora,
'Venta Cantina' as tipo_operacion,
GROUP_CONCAT(CONCAT(vp.Cantidad, 'x ', p.Descripcion) SEPARATOR ', ') as descripcion,
COALESCE(SUM(vp.Cantidad * p.Precio_Venta), 0) as monto,
COALESCE(CONCAT(u.Nombre, ' ', u.Apellido), 'Sistema') as usuario_registro
FROM ventas v
LEFT JOIN ventas_productos vp ON v.ID_Venta = vp.ID_Venta
LEFT JOIN productos p ON vp.ID_Producto = p.ID_Producto
LEFT JOIN usuarios u ON v.ID_Usuario = u.ID_Usuario
WHERE DATE(v.Fecha) = '$fecha' $filtro_venta
GROUP BY v.ID_Venta, v.Fecha, v.ID_Usuario
UNION ALL
SELECT
DATE(r.Fecha_Hora) as fecha,
TIME(r.Fecha_Hora) as hora,
CONCAT('Reserva ', CASE c.Tipo WHEN 'F' THEN 'Fútbol' WHEN 'P' THEN 'Pádel' WHEN 'Q' THEN 'Quincho' ELSE 'Otro' END) as tipo_operacion,
CONCAT(cl.Nombre, ' - Cancha ', r.Numero, ' - ', TIME(r.Fecha_Hora), ' hs') as descripcion,
r.Monto as monto,
COALESCE(CONCAT(u.Nombre, ' ', u.Apellido), 'Sistema') as usuario_registro
FROM reservas r
JOIN canchas c ON r.ID_Cancha = c.ID_Cancha
JOIN clientes cl ON r.ID_Cliente = cl.ID_Cliente
LEFT JOIN usuarios u ON r.ID_Usuario = u.ID_Usuario
WHERE DATE(r.Fecha_Hora) = '$fecha' $filtro_reserva
UNION ALL
SELECT
DATE(oc.Fecha) as fecha,
TIME(oc.Fecha) as hora,
'Orden de Compra' as tipo_operacion,
CONCAT('Proveedor: ', pr.Nombre) as descripcion,
-COALESCE(SUM(op.Cantidad * p.Precio_Compra), 0) as monto,
COALESCE(CONCAT(u.Nombre, ' ', u.Apellido), 'Sistema') as usuario_registro
FROM ordenes_compra oc
JOIN ordenes_productos op ON oc.ID_Orden = op.ID_Orden
JOIN productos p ON op.ID_Producto = p.ID_Producto
JOIN proveedores pr ON oc.ID_Proveedor = pr.ID_Proveedor
LEFT JOIN usuarios u ON oc.ID_Usuario = u.ID_Usuario
WHERE oc.Estado = 1 AND DATE(oc.Fecha) = '$fecha' $filtro_orden
GROUP BY oc.ID_Orden, oc.Fecha, oc.ID_Usuario
ORDER BY hora ASC
";
$result = $conexion->query($sql);
if (!$result) {
throw new Exception("Error: " . $conexion->error);
}
$transacciones = [];
$ingresos_reservas = 0;
$ingresos_cantina = 0;
$egresos = 0;
while ($row = $result->fetch_assoc()) {
$transacciones[] = $row;
$tipo_op = $row['tipo_operacion'];
if (strpos($tipo_op, 'Reserva') !== false) {
$ingresos_reservas += floatval($row['monto']);
} elseif ($tipo_op === 'Venta Cantina') {
$ingresos_cantina += floatval($row['monto']);
} else {
$egresos += abs(floatval($row['monto']));
}
}
echo json_encode([
'tipo_reporte' => 'diario',
'periodo' => $fecha,
'transacciones' => $transacciones,
'resumen' => [
'ingresos_reservas' => $ingresos_reservas,
'ingresos_cantina' => $ingresos_cantina,
'total_ingresos' => $ingresos_reservas + $ingresos_cantina,
'egresos' => $egresos,
'saldo_neto' => ($ingresos_reservas + $ingresos_cantina) - $egresos
]
]);
}
// ===== REPORTE SEMANAL =====
elseif ($tipo === 'semanal') {
// Obtener el rango de fechas de la semana
if (!$semana) {
$semana = date('Y-W');
}
// Parsear la semana ISO (formato YYYY-Www)
$semanaPartes = explode('-W', $semana);
if (count($semanaPartes) !== 2) {
throw new Exception("Formato de semana inválido");
}
$anio_s = intval($semanaPartes[0]);
$semana_s = intval($semanaPartes[1]);
// Calcular el lunes de la semana ISO especificada
// Crear una fecha para el jueves de la primera semana del año (para cálculo ISO correcto)
$fecha_base = new DateTime($anio_s . '-01-04'); // 4 de enero siempre está en semana 1
// Retroceder al lunes de esa semana
$dia_semana = $fecha_base->format('N'); // 1=lunes, 7=domingo
$fecha_base->modify('-' . ($dia_semana - 1) . ' days');
// Sumar las semanas necesarias
$fecha_base->modify('+' . ($semana_s - 1) . ' weeks');
$fecha_inicio = $fecha_base->format('Y-m-d');
$fecha_fin = $fecha_base->modify('+6 days')->format('Y-m-d');
// Resetear fecha_base para los cálculos posteriores
$fecha_base = new DateTime($fecha_inicio);
$sql = "
SELECT
DATE(r.Fecha_Hora) as dia,
DAYNAME(r.Fecha_Hora) as nombre_dia,
CASE c.Tipo WHEN 'F' THEN 'Fútbol' WHEN 'P' THEN 'Pádel' WHEN 'Q' THEN 'Quincho' ELSE 'Otro' END as tipo_cancha,
COUNT(r.ID_Reserva) as cantidad_reservas,
COALESCE(SUM(r.Monto), 0) as ingreso_reservas
FROM reservas r
JOIN canchas c ON r.ID_Cancha = c.ID_Cancha
WHERE DATE(r.Fecha_Hora) BETWEEN '$fecha_inicio' AND '$fecha_fin'
GROUP BY DATE(r.Fecha_Hora), c.Tipo
ORDER BY DATE(r.Fecha_Hora), tipo_cancha
";
$result = $conexion->query($sql);
$reservas_por_dia = [];
$dias_map = [
'Monday' => 'Lunes',
'Tuesday' => 'Martes',
'Wednesday' => 'Miércoles',
'Thursday' => 'Jueves',
'Friday' => 'Viernes',
'Saturday' => 'Sábado',
'Sunday' => 'Domingo'
];
while ($row = $result->fetch_assoc()) {
$dia = $row['dia'];
if (!isset($reservas_por_dia[$dia])) {
$reservas_por_dia[$dia] = [
'dia' => $dia,
'nombre_dia' => $row['nombre_dia'],
'ingresos_reservas' => 0,
'detalles_cancha' => []
];
}
$reservas_por_dia[$dia]['ingresos_reservas'] += floatval($row['ingreso_reservas']);
$reservas_por_dia[$dia]['detalles_cancha'][] = [
'tipo' => $row['tipo_cancha'],
'cantidad' => $row['cantidad_reservas'],
'ingreso' => $row['ingreso_reservas']
];
}
// Ingresos Cantina por día
$sql_cantina = "
SELECT
DATE(v.Fecha) as dia,
COALESCE(SUM(vp.Cantidad * p.Precio_Venta), 0) as ingreso_cantina,
COALESCE(SUM((vp.Cantidad * p.Precio_Venta) - (vp.Cantidad * p.Precio_Compra)), 0) as ganancia
FROM ventas v
JOIN ventas_productos vp ON v.ID_Venta = vp.ID_Venta
JOIN productos p ON vp.ID_Producto = p.ID_Producto
WHERE DATE(v.Fecha) BETWEEN '$fecha_inicio' AND '$fecha_fin'
GROUP BY DATE(v.Fecha)
";
$result_cantina = $conexion->query($sql_cantina);
$cantina_por_dia = [];
while ($row = $result_cantina->fetch_assoc()) {
$cantina_por_dia[$row['dia']] = floatval($row['ingreso_cantina']);
}
// Costo de Mercadería por día
$sql_compras = "
SELECT
DATE(oc.Fecha) as dia,
COALESCE(SUM(op.Cantidad * p.Precio_Compra), 0) as costo_mercaderia
FROM ordenes_compra oc
JOIN ordenes_productos op ON oc.ID_Orden = op.ID_Orden
JOIN productos p ON op.ID_Producto = p.ID_Producto
WHERE oc.Estado = 1
AND DATE(oc.Fecha) BETWEEN '$fecha_inicio' AND '$fecha_fin'
GROUP BY DATE(oc.Fecha)
";
$result_compras = $conexion->query($sql_compras);
$compras_por_dia = [];
while ($row = $result_compras->fetch_assoc()) {
$compras_por_dia[$row['dia']] = floatval($row['costo_mercaderia']);
}
// Gastos fijos para el período
$gastos_fijos_data = obtenerGastosFijos($conexion, $fecha_inicio, $fecha_fin);
$gastos_totales = $gastos_fijos_data['total'];
// Ocupación
$sql_ocupacion = "
SELECT
COALESCE(SUM(CASE WHEN r.Estado != 2 THEN c.Duracion ELSE 0 END), 0) as minutos_reservados
, COALESCE(SUM(CASE WHEN r.Estado != 2 THEN 1 ELSE 0 END), 0) as turnos_ocupados
FROM reservas r
JOIN canchas c ON r.ID_Cancha = c.ID_Cancha
WHERE DATE(r.Fecha_Hora) BETWEEN '$fecha_inicio' AND '$fecha_fin'
";
$sql_disponibilidad = "
SELECT
COUNT(*) as cantidad_canchas
FROM canchas
";
$result_ocu = $conexion->query($sql_ocupacion);
$result_disp = $conexion->query($sql_disponibilidad);
$minutos_reservados = 0;
$turnos_disponibles = 1; // para evitar división por cero
$horas_disponibles_por_cancha = obtenerHorasDisponiblesPorPeriodo($conexion, $fecha_inicio, $fecha_fin);
if ($result_ocu) {
$row = $result_ocu->fetch_assoc();
$minutos_reservados = intval($row['minutos_reservados'] ?? 0);
$turnos_ocupados = intval($row['turnos_ocupados'] ?? 0);
}
if ($result_disp) {
$row = $result_disp->fetch_assoc();
$cantidad_canchas = intval($row['cantidad_canchas'] ?? 1);
$turnos_disponibles = $cantidad_canchas * $horas_disponibles_por_cancha;
}
$horas_reservadas = $minutos_reservados / 60;
$ocupacion = ($turnos_disponibles > 0) ? round(($horas_reservadas / $turnos_disponibles) * 100, 2) : 0;
// Consolidar datos por día
$dias_map_semana = [
'Monday' => 'Lunes',
'Tuesday' => 'Martes',
'Wednesday' => 'Miércoles',
'Thursday' => 'Jueves',
'Friday' => 'Viernes',
'Saturday' => 'Sábado',
'Sunday' => 'Domingo'
];
$semana_datos = [];
for ($i = 0; $i < 7; $i++) {
$dia_actual = date('Y-m-d', strtotime($fecha_inicio . ' +' . $i . ' days'));
$ingresos_res = isset($reservas_por_dia[$dia_actual]) ? floatval($reservas_por_dia[$dia_actual]['ingresos_reservas']) : 0;
$ingresos_cant = isset($cantina_por_dia[$dia_actual]) ? floatval($cantina_por_dia[$dia_actual]) : 0;
$costo_merc = isset($compras_por_dia[$dia_actual]) ? floatval($compras_por_dia[$dia_actual]) : 0;
$total_ingresos = $ingresos_res + $ingresos_cant;
$saldo_neto = $total_ingresos - $costo_merc;
$nombre_dia_ingles = date('l', strtotime($dia_actual));
$nombre_dia_es = isset($dias_map_semana[$nombre_dia_ingles]) ? $dias_map_semana[$nombre_dia_ingles] : $nombre_dia_ingles;
$semana_datos[] = [
'dia' => $dia_actual,
'nombre_dia' => $nombre_dia_es,
'ingresos_reservas' => $ingresos_res,
'ingresos_cantina' => $ingresos_cant,
'costo_mercaderia' => $costo_merc,
'total_ingresos' => $total_ingresos,
'saldo_neto' => $saldo_neto
];
}
echo json_encode([
'tipo_reporte' => 'semanal',
'periodo' => $semana,
'fecha_inicio' => $fecha_inicio,
'fecha_fin' => $fecha_fin,
'dias' => $semana_datos,
'resumen' => [
'ingresos_reservas_totales' => array_sum(array_column($semana_datos, 'ingresos_reservas')),
'ingresos_cantina_totales' => array_sum(array_column($semana_datos, 'ingresos_cantina')),
'costo_mercaderia_total' => array_sum(array_column($semana_datos, 'costo_mercaderia')),
'total_ingresos' => array_sum(array_column($semana_datos, 'total_ingresos')),
'saldo_neto_semana' => array_sum(array_column($semana_datos, 'saldo_neto')),
'ocupacion_promedio' => $ocupacion,
'turnos_ocupados' => $turnos_ocupados,
'turnos_disponibles' => $turnos_disponibles,
'gastos_fijos_estimados' => $gastos_totales / 4 // Aproximado a una semana
]
]);
}
// ===== REPORTE MENSUAL =====
elseif ($tipo === 'mensual') {
if (!$mes) {
$mes = date('Y-m');
}
list($anio_m, $mes_m) = explode('-', $mes);
$fecha_inicio = $anio_m . '-' . $mes_m . '-01';
$fecha_fin = date('Y-m-d', strtotime($fecha_inicio . ' +1 month -1 day'));
// Ingresos por categoría
$categorias = ['Fútbol', 'Pádel', 'Quincho', 'Cantina'];
$datos_categorias = [];
// Reservas (Fútbol, Pádel, Quincho)
$sql_futbol = "
SELECT
'Fútbol' as categoria,
COUNT(r.ID_Reserva) as operaciones,
COALESCE(SUM(r.Monto), 0) as monto
FROM reservas r
JOIN canchas c ON r.ID_Cancha = c.ID_Cancha
WHERE c.Tipo = 'F' AND DATE(r.Fecha_Hora) BETWEEN '$fecha_inicio' AND '$fecha_fin'
";
$sql_padel = "
SELECT
'Pádel' as categoria,
COUNT(r.ID_Reserva) as operaciones,
COALESCE(SUM(r.Monto), 0) as monto
FROM reservas r
JOIN canchas c ON r.ID_Cancha = c.ID_Cancha
WHERE c.Tipo = 'P' AND DATE(r.Fecha_Hora) BETWEEN '$fecha_inicio' AND '$fecha_fin'
";
$sql_quincho = "
SELECT
'Quincho' as categoria,
COUNT(r.ID_Reserva) as operaciones,
COALESCE(SUM(r.Monto), 0) as monto
FROM reservas r
JOIN canchas c ON r.ID_Cancha = c.ID_Cancha
WHERE c.Tipo = 'Q' AND DATE(r.Fecha_Hora) BETWEEN '$fecha_inicio' AND '$fecha_fin'
";
$sql_cantina = "
SELECT
'Cantina' as categoria,
COUNT(DISTINCT v.ID_Venta) as operaciones,
COALESCE(SUM(vp.Cantidad * p.Precio_Venta), 0) as monto
FROM ventas v
JOIN ventas_productos vp ON v.ID_Venta = vp.ID_Venta
JOIN productos p ON vp.ID_Producto = p.ID_Producto
WHERE DATE(v.Fecha) BETWEEN '$fecha_inicio' AND '$fecha_fin'
";
foreach ([$sql_futbol, $sql_padel, $sql_quincho, $sql_cantina] as $sql) {
$result = $conexion->query($sql);
if ($result && $result->num_rows > 0) {
$row = $result->fetch_assoc();
$datos_categorias[] = [
'categoria' => $row['categoria'],
'operaciones' => intval($row['operaciones']),
'monto' => floatval($row['monto'])
];
} else {
$datos_categorias[] = [
'categoria' => $row['categoria'] ?? '',
'operaciones' => 0,
'monto' => 0
];
}
}
// Costo de Mercadería
$sql_costo = "
SELECT
'Costo de Mercadería' as concepto,
COALESCE(SUM(op.Cantidad * p.Precio_Compra), 0) as monto
FROM ordenes_compra oc
JOIN ordenes_productos op ON oc.ID_Orden = op.ID_Orden
JOIN productos p ON op.ID_Producto = p.ID_Producto
WHERE oc.Estado = 1
AND DATE(oc.Fecha) BETWEEN '$fecha_inicio' AND '$fecha_fin'
";
$result_costo = $conexion->query($sql_costo);
$costo_mercaderia = 0;
if ($result_costo && $result_costo->num_rows > 0) {
$row = $result_costo->fetch_assoc();
$costo_mercaderia = floatval($row['monto']);
}
// Gastos fijos
$gastos_fijos_data = obtenerGastosFijos($conexion, $fecha_inicio, $fecha_fin);
$gastos_fijos = [];
foreach ($gastos_fijos_data['detalles'] as $gasto) {
$gastos_fijos[] = [
'descripcion' => $gasto['descripcion'],
'monto' => $gasto['monto']
];
}
$total_gastos_fijos = $gastos_fijos_data['total'];
// Calcular ganancias
$ingresos_totales = array_sum(array_column($datos_categorias, 'monto'));
$ganancia_bruta = $ingresos_totales - $costo_mercaderia;
$ganancia_neta = $ganancia_bruta - $total_gastos_fijos;
// Obtener el nombre del mes en español
$meses_nombres = [
'01' => 'enero', '02' => 'febrero', '03' => 'marzo', '04' => 'abril',
'05' => 'mayo', '06' => 'junio', '07' => 'julio', '08' => 'agosto',
'09' => 'septiembre', '10' => 'octubre', '11' => 'noviembre', '12' => 'diciembre'
];
$nombre_mes = $meses_nombres[$mes_m];
echo json_encode([
'tipo_reporte' => 'mensual',
'periodo' => $mes,
'fecha_inicio' => $fecha_inicio,
'fecha_fin' => $fecha_fin,
'nombre_mes' => $nombre_mes,
'categorias' => $datos_categorias,
'gastos_fijos' => $gastos_fijos,
'resumen' => [
'ingresos_totales' => $ingresos_totales,
'costo_mercaderia' => $costo_mercaderia,
'gastos_fijos_total' => $total_gastos_fijos,
'ganancia_bruta' => $ganancia_bruta,
'ganancia_neta' => $ganancia_neta
]
]);
}
// ===== REPORTE ANUAL =====
elseif ($tipo === 'anual') {
if (!$anio) {
$anio = date('Y');
}
$fecha_inicio = $anio . '-01-01';
$fecha_fin = $anio . '-12-31';
// Determinar si es el año actual
$ano_actual = intval(date('Y'));
$es_ano_actual = ($anio == $ano_actual);
$mes_actual = intval(date('m'));
$mes_limite = $es_ano_actual ? $mes_actual : 12;
$meses = [
'01' => 'Enero', '02' => 'Febrero', '03' => 'Marzo', '04' => 'Abril',
'05' => 'Mayo', '06' => 'Junio', '07' => 'Julio', '08' => 'Agosto',
'09' => 'Septiembre', '10' => 'Octubre', '11' => 'Noviembre', '12' => 'Diciembre'
];
$datos_anuales = [];
for ($mes_num = 1; $mes_num <= $mes_limite; $mes_num++) {
$mes_str = str_pad($mes_num, 2, '0', STR_PAD_LEFT);
$mes_inicio = $anio . '-' . $mes_str . '-01';
$mes_fin = date('Y-m-d', strtotime($mes_inicio . ' +1 month -1 day'));
// Si es el mes actual, solo contar hasta hoy
if ($es_ano_actual && $mes_num == $mes_actual) {
$mes_fin = date('Y-m-d');
}
// Ingreso Operativo
$sql_ingreso = "
SELECT
COALESCE(SUM(r.Monto), 0) as reservas,
(SELECT COALESCE(SUM(vp.Cantidad * p.Precio_Venta), 0)
FROM ventas v
JOIN ventas_productos vp ON v.ID_Venta = vp.ID_Venta
JOIN productos p ON vp.ID_Producto = p.ID_Producto
WHERE DATE(v.Fecha) BETWEEN '$mes_inicio' AND '$mes_fin') as ventas
FROM reservas r
WHERE DATE(r.Fecha_Hora) BETWEEN '$mes_inicio' AND '$mes_fin'
";
$result_ingreso = $conexion->query($sql_ingreso);
$ingreso_operativo = 0;
if ($result_ingreso) {
$row = $result_ingreso->fetch_assoc();
$ingreso_operativo = floatval($row['reservas']) + floatval($row['ventas']);
}
// Costo Bruto (Mercadería)
$sql_costo_bruto = "
SELECT COALESCE(SUM(op.Cantidad * p.Precio_Compra), 0) as mercaderia
FROM ordenes_compra oc
JOIN ordenes_productos op ON oc.ID_Orden = op.ID_Orden
JOIN productos p ON op.ID_Producto = p.ID_Producto
WHERE oc.Estado = 1
AND DATE(oc.Fecha) BETWEEN '$mes_inicio' AND '$mes_fin'
";
$result_costo_bruto = $conexion->query($sql_costo_bruto);
$costo_bruto = 0;
if ($result_costo_bruto) {
$row = $result_costo_bruto->fetch_assoc();
$costo_bruto = floatval($row['mercaderia']);
}
// Obtener gastos fijos dinámicamente
$gastos_fijos_data = obtenerGastosFijos($conexion, $mes_inicio, $mes_fin);
$costo_gastos_fijos = $gastos_fijos_data['total'];
$costo_operativo_total = $costo_bruto + $costo_gastos_fijos;
$ganancia_bruta = $ingreso_operativo - $costo_bruto;
$ganancia_neta = $ingreso_operativo - $costo_operativo_total;
$datos_anuales[] = [
'mes' => $meses[$mes_str],
'ingreso_operativo' => $ingreso_operativo,
'costo_bruto' => $costo_bruto,
'costo_operativo' => $costo_operativo_total,
'ganancia_bruta' => $ganancia_bruta,
'ganancia_neta' => $ganancia_neta
];
}
// Calcular totales anuales
$ingreso_operativo_total = array_sum(array_column($datos_anuales, 'ingreso_operativo'));
$costo_bruto_total = array_sum(array_column($datos_anuales, 'costo_bruto'));
$costo_operativo_total = array_sum(array_column($datos_anuales, 'costo_operativo'));
$ganancia_bruta_total = array_sum(array_column($datos_anuales, 'ganancia_bruta'));
$ganancia_neta_total = array_sum(array_column($datos_anuales, 'ganancia_neta'));
echo json_encode([
'tipo_reporte' => 'anual',
'periodo' => $anio,
'es_ano_actual' => $es_ano_actual,
'mes_actual' => $mes_actual,
'meses' => $datos_anuales,
'totales' => [
'ingreso_operativo_total' => $ingreso_operativo_total,
'costo_bruto_total' => $costo_bruto_total,
'costo_operativo_total' => $costo_operativo_total,
'ganancia_bruta_total' => $ganancia_bruta_total,
'ganancia_neta_total' => $ganancia_neta_total
]
]);
}
} // FIN REPORTE FINANCIERO
// ===== REPORTE VENTAS CANTINA =====
elseif ($tipo_reporte === 'ventas_cantina') {
$fecha_inicio = '';
$fecha_fin = '';
if ($tipo === 'diario') {
$fecha_inicio = $fecha;
$fecha_fin = $fecha;
} elseif ($tipo === 'semanal') {
if (!$semana) $semana = date('Y-W');
$semanaPartes = explode('-W', $semana);
$anio_s = intval($semanaPartes[0]);
$semana_s = intval($semanaPartes[1]);
$fecha_base = new DateTime($anio_s . '-01-04');
$dia_semana = $fecha_base->format('N');
$fecha_base->modify('-' . ($dia_semana - 1) . ' days');
$fecha_base->modify('+' . ($semana_s - 1) . ' weeks');
$fecha_inicio = $fecha_base->format('Y-m-d');
$fecha_fin = $fecha_base->modify('+6 days')->format('Y-m-d');
} elseif ($tipo === 'mensual') {
if (!$mes) $mes = date('Y-m');
list($anio_m, $mes_m) = explode('-', $mes);
$fecha_inicio = $anio_m . '-' . $mes_m . '-01';
$fecha_fin = date('Y-m-d', strtotime($fecha_inicio . ' +1 month -1 day'));
} elseif ($tipo === 'anual') {
if (!$anio) $anio = date('Y');
$fecha_inicio = $anio . '-01-01';
$fecha_fin = $anio . '-12-31';
}
// Obtener productos de cantina con ventas
$sql_productos = "
SELECT
p.ID_Producto,
p.Descripcion as producto,
'Cantina' as categoria,
SUM(vp.Cantidad) as cantidad_vendida,
SUM(vp.Cantidad * p.Precio_Venta) as ingreso_generado,
COALESCE(SUM(vp.Cantidad * (p.Precio_Venta - p.Precio_Compra)), 0) as ganancia
FROM productos p
LEFT JOIN ventas_productos vp ON p.ID_Producto = vp.ID_Producto
LEFT JOIN ventas v ON vp.ID_Venta = v.ID_Venta
WHERE DATE(v.Fecha) BETWEEN '$fecha_inicio' AND '$fecha_fin'
GROUP BY p.ID_Producto, p.Descripcion
ORDER BY cantidad_vendida DESC
";
$result_productos = $conexion->query($sql_productos);
if (!$result_productos) {
throw new Exception("Error en query productos: " . $conexion->error);
}
$productos = [];
$total_ingreso = 0;
$total_cantidad = 0;
while ($row = $result_productos->fetch_assoc()) {
$ingreso = floatval($row['ingreso_generado']) ?? 0;
$productos[] = [
'producto' => $row['producto'],
'categoria' => $row['categoria'],
'cantidad_vendida' => intval($row['cantidad_vendida']) ?? 0,
'ingreso_generado' => $ingreso,
'ganancia' => floatval($row['ganancia']) ?? 0
];
$total_ingreso += $ingreso;
$total_cantidad += intval($row['cantidad_vendida']) ?? 0;
}
// Calcular porcentajes sobre el total de unidades vendidas
foreach ($productos as &$producto) {
$producto['porcentaje_total'] = $total_cantidad > 0 ? round(($producto['cantidad_vendida'] / $total_cantidad) * 100, 2) : 0;
}
unset($producto);
echo json_encode([
'tipo_reporte' => 'ventas_cantina',
'periodo' => $tipo,
'fecha_inicio' => $fecha_inicio,
'fecha_fin' => $fecha_fin,
'productos' => $productos,
'resumen' => [
'total_productos_vendidos' => count($productos),
'total_cantidad' => $total_cantidad,
'total_ingreso' => $total_ingreso,
'ganancia_total' => array_sum(array_column($productos, 'ganancia'))
]
]);
}
// ===== REPORTE OCUPACIÓN CANCHAS =====
elseif ($tipo_reporte === 'ocupacion_canchas') {
$fecha_inicio = '';
$fecha_fin = '';
if ($tipo === 'diario') {
$fecha_inicio = $fecha;
$fecha_fin = $fecha;
} elseif ($tipo === 'semanal') {
if (!$semana) $semana = date('Y-W');
$semanaPartes = explode('-W', $semana);
$anio_s = intval($semanaPartes[0]);
$semana_s = intval($semanaPartes[1]);
$fecha_base = new DateTime($anio_s . '-01-04');
$dia_semana = $fecha_base->format('N');
$fecha_base->modify('-' . ($dia_semana - 1) . ' days');
$fecha_base->modify('+' . ($semana_s - 1) . ' weeks');
$fecha_inicio = $fecha_base->format('Y-m-d');
$fecha_fin = $fecha_base->modify('+6 days')->format('Y-m-d');
} elseif ($tipo === 'mensual') {
if (!$mes) $mes = date('Y-m');
list($anio_m, $mes_m) = explode('-', $mes);
$fecha_inicio = $anio_m . '-' . $mes_m . '-01';
$fecha_fin = date('Y-m-d', strtotime($fecha_inicio . ' +1 month -1 day'));
} elseif ($tipo === 'anual') {
if (!$anio) $anio = date('Y');
$fecha_inicio = $anio . '-01-01';
$fecha_fin = $anio . '-12-31';
}
$total_dias_periodo = (strtotime($fecha_fin) - strtotime($fecha_inicio)) / (60 * 60 * 24) + 1;
// Filtro por tipo de cancha si está especificado
$tipo_cancha_filtro = $tipo_cancha ?? '';
$filtro_cancha = '';
if ($tipo_cancha_filtro) {
$filtro_cancha = "AND c.Tipo = '$tipo_cancha_filtro'";
}
// 1. Reservas por día
$sql_por_dia = "
SELECT
DATE(r.Fecha_Hora) as dia,
DAYNAME(r.Fecha_Hora) as nombre_dia,
COUNT(r.ID_Reserva) as cantidad_reservas,
COUNT(CASE WHEN r.Estado = 2 THEN 1 END) as canceladas
FROM reservas r
JOIN canchas c ON r.ID_Cancha = c.ID_Cancha
WHERE DATE(r.Fecha_Hora) BETWEEN '$fecha_inicio' AND '$fecha_fin'
$filtro_cancha
GROUP BY DATE(r.Fecha_Hora)
ORDER BY dia ASC
";
$result_dia = $conexion->query($sql_por_dia);
if (!$result_dia) {
throw new Exception("Error en query por día: " . $conexion->error);
}
$reservas_por_dia = [];
$total_reservas = 0;
$total_canceladas = 0;
$dias_map = [
'Monday' => 'Lunes',
'Tuesday' => 'Martes',
'Wednesday' => 'Miércoles',
'Thursday' => 'Jueves',
'Friday' => 'Viernes',
'Saturday' => 'Sábado',
'Sunday' => 'Domingo'
];
while ($row = $result_dia->fetch_assoc()) {
$nombre_dia_original = $row['nombre_dia'];
$nombre_dia_es = isset($dias_map[$nombre_dia_original]) ? $dias_map[$nombre_dia_original] : $nombre_dia_original;
$reservas_por_dia[] = [
'dia' => $row['dia'],
'nombre_dia' => $nombre_dia_es,
'cantidad_reservas' => intval($row['cantidad_reservas']),
'canceladas' => intval($row['canceladas'])
];
$total_reservas += intval($row['cantidad_reservas']);
$total_canceladas += intval($row['canceladas']);
}
// 2. Reservas por horario - Considerar duración de cada tipo de cancha
// Primero obtener todos los tipos de canchas con su duración
$sql_tipos_canchas = "
SELECT DISTINCT
CASE c.Tipo WHEN 'F' THEN 'Fútbol' WHEN 'P' THEN 'Pádel' WHEN 'Q' THEN 'Quincho' ELSE 'Otro' END as tipo_nombre,
c.Tipo as tipo_codigo,
c.Duracion as duracion_minutos
FROM canchas c
WHERE 1=1 $filtro_cancha
ORDER BY c.Tipo
";
$result_tipos = $conexion->query($sql_tipos_canchas);
if (!$result_tipos) {
throw new Exception("Error al obtener tipos de canchas: " . $conexion->error);
}
$tipos_canchas = [];
while ($row = $result_tipos->fetch_assoc()) {
$tipos_canchas[] = [
'tipo_nombre' => $row['tipo_nombre'],
'tipo_codigo' => $row['tipo_codigo'],
'duracion_minutos' => intval($row['duracion_minutos'])
];
}
// Si no hay tipos de canchas, retornar estructura vacía
if (empty($tipos_canchas)) {
$tipos_canchas[] = [
'tipo_nombre' => 'Fútbol',
'tipo_codigo' => 'F',
'duracion_minutos' => 60
];
}
// Generar horarios disponibles por tipo de cancha
$horarios_por_tipo = [];
foreach ($tipos_canchas as $tipo_cancha_info) {
$tipo_codigo = $tipo_cancha_info['tipo_codigo'];
$duracion_min = $tipo_cancha_info['duracion_minutos'];
$horarios_unicos_tipo = [];
if ($tipo_codigo === 'Q') {
$horarios_unicos_tipo['reserva_quincho'] = [
'key' => 'reserva_quincho',
'horario' => 'Reserva del Quincho',
'inicio' => '',
'fin' => '',
'es_quincho' => true
];
} else {
$fecha_temp = new DateTime($fecha_inicio);
$fecha_fin_dt = new DateTime($fecha_fin);
while ($fecha_temp <= $fecha_fin_dt) {
$diaSemana = intval($fecha_temp->format('N'));
$diaTexto = $diaSemana >= 1 && $diaSemana <= 5 ? 'Lunes a Viernes' : ($diaSemana === 6 ? 'Sábado' : 'Domingo');
$sqlHorario = "SELECT Hora_Apertura, Hora_Cierre FROM horarios WHERE Dia = '$diaTexto' LIMIT 1";
$resultHorario = $conexion->query($sqlHorario);
if ($resultHorario && $rowHorario = $resultHorario->fetch_assoc()) {
$horaApertura = $rowHorario['Hora_Apertura'];
$horaCierre = $rowHorario['Hora_Cierre'];
if ($horaApertura && $horaCierre) {
$start_hour = intval(substr($horaApertura, 0, 2));
$start_min = intval(substr($horaApertura, 3, 2));
$end_hour = intval(substr($horaCierre, 0, 2));
$end_min = intval(substr($horaCierre, 3, 2));
// Convertir a minutos desde medianoche
$apertura_minutos = $start_hour * 60 + $start_min;
$cierre_minutos = $end_hour * 60 + $end_min;
// Generar horarios considerando la duración real de la cancha
$tiempo_actual = $apertura_minutos;
while ($tiempo_actual + $duracion_min <= $cierre_minutos) {
$hora_inicio = intval($tiempo_actual / 60);
$minuto_inicio = $tiempo_actual % 60;
$hora_fin = $tiempo_actual + $duracion_min;
$hora_fin_h = intval($hora_fin / 60);
$minuto_fin = $hora_fin % 60;
$inicio_texto = sprintf('%02d:%02d', $hora_inicio, $minuto_inicio);
$fin_texto = sprintf('%02d:%02d', $hora_fin_h, $minuto_fin);
$horario_formato = $inicio_texto . ' - ' . $fin_texto;
$horarios_unicos_tipo[$inicio_texto] = [
'key' => $inicio_texto,
'horario' => $horario_formato,
'inicio' => $inicio_texto,
'fin' => $fin_texto,
'es_quincho' => false
];
$tiempo_actual += $duracion_min;
}
}
}
$fecha_temp->modify('+1 day');
}
}
// Guardar horarios por tipo
ksort($horarios_unicos_tipo);
$horarios_por_tipo[$tipo_cancha_info['tipo_nombre']] = array_values($horarios_unicos_tipo);
}
// Obtener reservas por horario y tipo de cancha
$sql_por_horario = "
SELECT
CASE c.Tipo WHEN 'F' THEN 'Fútbol' WHEN 'P' THEN 'Pádel' WHEN 'Q' THEN 'Quincho' ELSE 'Otro' END as tipo_nombre,
CASE WHEN c.Tipo = 'Q' THEN 'reserva_quincho' ELSE TIME_FORMAT(r.Fecha_Hora, '%H:%i') END as horario_key,
CASE WHEN c.Tipo = 'Q' THEN 'Reserva del Quincho' ELSE TIME_FORMAT(r.Fecha_Hora, '%H:%i') END as horario,
COUNT(r.ID_Reserva) as cantidad_reservas,
COUNT(CASE WHEN r.Estado = 2 THEN 1 END) as canceladas
FROM reservas r
JOIN canchas c ON r.ID_Cancha = c.ID_Cancha
WHERE DATE(r.Fecha_Hora) BETWEEN '$fecha_inicio' AND '$fecha_fin'
$filtro_cancha
GROUP BY CASE c.Tipo WHEN 'F' THEN 'Fútbol' WHEN 'P' THEN 'Pádel' WHEN 'Q' THEN 'Quincho' ELSE 'Otro' END,
CASE WHEN c.Tipo = 'Q' THEN 'reserva_quincho' ELSE TIME_FORMAT(r.Fecha_Hora, '%H:%i') END
ORDER BY tipo_nombre, horario ASC
";
$result_horario = $conexion->query($sql_por_horario);
if (!$result_horario) {
throw new Exception("Error en query por horario: " . $conexion->error);
}
// Obtener conteo de reservas de Quincho por horario (agregado para mostrar en tablas de Fútbol/Pádel)
$sql_quincho_por_horario = "
SELECT
TIME_FORMAT(r.Fecha_Hora, '%H:%i') as horario_key,
COUNT(r.ID_Reserva) as cantidad
FROM reservas r
JOIN canchas c ON r.ID_Cancha = c.ID_Cancha
WHERE c.Tipo = 'Q' AND DATE(r.Fecha_Hora) BETWEEN '$fecha_inicio' AND '$fecha_fin'
GROUP BY horario_key
";
$result_quincho_hor = $conexion->query($sql_quincho_por_horario);
$quincho_por_horario = [];
if ($result_quincho_hor) {
while ($q = $result_quincho_hor->fetch_assoc()) {
$quincho_por_horario[$q['horario_key']] = intval($q['cantidad']);
}
}
// Obtener conteo de reservas de quincho asociadas a cada tipo de cancha (emparejando por Fecha_Hora e ID_Cliente)
$sql_quincho_asociada = "
SELECT
TIME_FORMAT(q.Fecha_Hora, '%H:%i') as horario_key,
CASE WHEN c2.Tipo = 'F' THEN 'Fútbol' WHEN c2.Tipo = 'P' THEN 'Pádel' ELSE 'Otro' END as asociado_tipo,
COUNT(q.ID_Reserva) as cantidad
FROM reservas q
JOIN canchas cq ON q.ID_Cancha = cq.ID_Cancha AND cq.Tipo = 'Q'
JOIN reservas r ON q.Fecha_Hora = r.Fecha_Hora AND q.ID_Cliente = r.ID_Cliente
JOIN canchas c2 ON r.ID_Cancha = c2.ID_Cancha
WHERE DATE(q.Fecha_Hora) BETWEEN '$fecha_inicio' AND '$fecha_fin' AND c2.Tipo IN ('F','P')
GROUP BY horario_key, asociado_tipo
";
$result_quincho_asoc = $conexion->query($sql_quincho_asociada);
$quincho_asociada = [];
if ($result_quincho_asoc) {
while ($qa = $result_quincho_asoc->fetch_assoc()) {
$tipo_asoc = $qa['asociado_tipo'];
$hk = $qa['horario_key'];
if (!isset($quincho_asociada[$tipo_asoc])) $quincho_asociada[$tipo_asoc] = [];
$quincho_asociada[$tipo_asoc][$hk] = intval($qa['cantidad']);
}
}
$reservas_por_horario_temp = [];
while ($row = $result_horario->fetch_assoc()) {
$tipo = $row['tipo_nombre'];
$horario_key = $row['horario_key'];
if (!isset($reservas_por_horario_temp[$tipo])) {
$reservas_por_horario_temp[$tipo] = [];
}
$reservas_por_horario_temp[$tipo][$horario_key] = [
'horario' => $row['horario'],
'inicio' => $row['horario_key'],
'fin' => '',
'cantidad_reservas' => intval($row['cantidad_reservas']),
'canceladas' => intval($row['canceladas'])
];
// Añadir conteo de reservas de quincho vinculadas por horario.
// Si existe asociación por tipo (Fútbol/Pádel), usarla; si no, usar conteo general de quincho por horario.
$reservas_por_horario_temp[$tipo][$horario_key]['reservas_quincho'] = 0;
if (isset($quincho_asociada[$tipo][$horario_key])) {
$reservas_por_horario_temp[$tipo][$horario_key]['reservas_quincho'] = intval($quincho_asociada[$tipo][$horario_key]);
} else {
$reservas_por_horario_temp[$tipo][$horario_key]['reservas_quincho'] = intval($quincho_por_horario[$horario_key] ?? 0);
}
}
// Combinar todos los horarios disponibles con las reservas por tipo
$reservas_por_horario = [];
foreach ($horarios_por_tipo as $tipo_cancha => $horarios_disponibles) {
$reservas_por_horario[$tipo_cancha] = [];
foreach ($horarios_disponibles as $horario) {
$key = $horario['key'];
if (isset($reservas_por_horario_temp[$tipo_cancha][$key])) {
$item = $reservas_por_horario_temp[$tipo_cancha][$key];
$item['horario'] = $horario['horario'];
$item['inicio'] = $horario['inicio'];
$item['fin'] = $horario['fin'];
$item['es_quincho'] = $horario['es_quincho'] ?? false;
$reservas_por_horario[$tipo_cancha][] = $item;
} else {
// Horario sin reservas
$reservas_por_horario[$tipo_cancha][] = [
'horario' => $horario['horario'],
'inicio' => $horario['inicio'],
'fin' => $horario['fin'],
'cantidad_reservas' => 0,
'canceladas' => 0,
'es_quincho' => $horario['es_quincho'] ?? false,
'reservas_quincho' => intval(isset($quincho_asociada[$tipo_cancha][$key]) ? $quincho_asociada[$tipo_cancha][$key] : ($quincho_por_horario[$horario['inicio']] ?? 0))
];
}
}
}
// 3. Ocupación por cancha
$sql_ocupacion_cancha = "
SELECT
c.ID_Cancha,
CASE c.Tipo WHEN 'F' THEN 'Fútbol' WHEN 'P' THEN 'Pádel' WHEN 'Q' THEN 'Quincho' ELSE 'Otro' END as tipo,
c.Duracion as duracion_minutos,
COUNT(r.ID_Reserva) as cantidad_reservas,
COUNT(CASE WHEN r.Estado != 2 THEN 1 END) as activas,
COUNT(CASE WHEN r.Estado = 2 THEN 1 END) as canceladas
FROM canchas c
LEFT JOIN reservas r ON c.ID_Cancha = r.ID_Cancha
AND DATE(r.Fecha_Hora) BETWEEN '$fecha_inicio' AND '$fecha_fin'
WHERE 1=1 $filtro_cancha
GROUP BY c.ID_Cancha, c.Tipo
ORDER BY c.Tipo, c.ID_Cancha ASC
";
$result_cancha = $conexion->query($sql_ocupacion_cancha);
if (!$result_cancha) {
throw new Exception("Error en query ocupación cancha: " . $conexion->error);
}
$ocupacion_cancha = [];
$horas_disponibles_en_periodo = obtenerHorasDisponiblesPorPeriodo($conexion, $fecha_inicio, $fecha_fin);
$minutos_disponibles_por_cancha = $horas_disponibles_en_periodo * 60;
while ($row = $result_cancha->fetch_assoc()) {
$minutos_reservados = intval($row['activas']) * intval($row['duracion_minutos']);
$ocupacion = 0;
if ($minutos_disponibles_por_cancha > 0) {
$ocupacion = round(($minutos_reservados / $minutos_disponibles_por_cancha) * 100, 2);
}
$ocupacion_cancha[] = [
'numero' => intval($row['ID_Cancha']),
'tipo' => $row['tipo'],
'duracion_minutos' => intval($row['duracion_minutos']),
'cantidad_reservas' => intval($row['cantidad_reservas']),
'activas' => intval($row['activas']),
'canceladas' => intval($row['canceladas']),
'porcentaje_ocupacion' => $ocupacion
];
}
// Para el período diario, no mostrar % de ocupación para el Quincho
if ($tipo === 'diario') {
foreach ($ocupacion_cancha as &$cancha_tmp) {
if (isset($cancha_tmp['tipo']) && $cancha_tmp['tipo'] === 'Quincho') {
$cancha_tmp['porcentaje_ocupacion'] = null;
}
}
unset($cancha_tmp);
}
// Calcular horarios pico y bajos por tipo de cancha (considerando horarios sin reservas)
$horarios_pico_por_tipo = [];
$horarios_bajos_por_tipo = [];
$horarios_sin_reservas_por_tipo = [];
foreach ($reservas_por_horario as $tipo_cancha => $horarios) {
$horarios_pico_por_tipo[$tipo_cancha] = [];
$horarios_bajos_por_tipo[$tipo_cancha] = [];
$horarios_sin_reservas_por_tipo[$tipo_cancha] = [];
if (!empty($horarios)) {
// Separar horarios con reservas de los sin reservas
$horarios_con_reservas = array_filter($horarios, function($h) {
return $h['cantidad_reservas'] > 0;
});
$horarios_sin_reservas_temp = array_filter($horarios, function($h) {
return $h['cantidad_reservas'] == 0;
});
// Extraer solo los horarios sin reservas
$horarios_sin_reservas_por_tipo[$tipo_cancha] = array_values(array_map(function($h) { return $h['horario']; }, $horarios_sin_reservas_temp));
if ($tipo === 'diario') {
// Para diario: Horarios Con Reservas = todos los que tienen reservas
$horarios_pico_por_tipo[$tipo_cancha] = array_values(array_map(function($h) { return $h['horario']; }, $horarios_con_reservas));
// Horarios sin reservas ya está definido arriba
} else {
// Para otros períodos: calcular promedio y separar en mayor y menor demanda
if (!empty($horarios_con_reservas)) {
$promedio_reservas = array_sum(array_column($horarios_con_reservas, 'cantidad_reservas')) / count($horarios_con_reservas);
foreach ($horarios_con_reservas as $horario) {
if ($horario['cantidad_reservas'] >= $promedio_reservas) {
$horarios_pico_por_tipo[$tipo_cancha][] = $horario['horario'];
} else {
$horarios_bajos_por_tipo[$tipo_cancha][] = $horario['horario'];
}
}
// Incluir horarios sin reservas en "bajos"
$horarios_bajos_por_tipo[$tipo_cancha] = array_merge($horarios_bajos_por_tipo[$tipo_cancha], $horarios_sin_reservas_por_tipo[$tipo_cancha]);
sort($horarios_bajos_por_tipo[$tipo_cancha]);
}
}
}
}
// Para compatibilidad, crear arrays planos con todos los horarios (solo si hay datos)
$horarios_pico = [];
$horarios_bajos = [];
$horarios_sin_reservas = [];
if (!empty($horarios_pico_por_tipo)) {
foreach ($horarios_pico_por_tipo as $horarios_tipo) {
$horarios_pico = array_merge($horarios_pico, $horarios_tipo);
}
}
if (!empty($horarios_bajos_por_tipo)) {
foreach ($horarios_bajos_por_tipo as $horarios_tipo) {
$horarios_bajos = array_merge($horarios_bajos, $horarios_tipo);
}
}
if (!empty($horarios_sin_reservas_por_tipo)) {
foreach ($horarios_sin_reservas_por_tipo as $horarios_tipo) {
$horarios_sin_reservas = array_merge($horarios_sin_reservas, $horarios_tipo);
}
}
// Ocupación general del período (reservado horas / horas disponibles)
$ocupacion_general = 0;
$total_cancha_horas_disponibles = count($ocupacion_cancha) * $horas_disponibles_en_periodo;
$minutos_reservados_total = 0;
foreach ($ocupacion_cancha as $cancha) {
$minutos_reservados_total += intval($cancha['activas']) * intval($cancha['duracion_minutos']);
}
if ($total_cancha_horas_disponibles > 0) {
$ocupacion_general = round(($minutos_reservados_total / ($total_cancha_horas_disponibles * 60)) * 100, 2);
}
echo json_encode([
'tipo_reporte' => 'ocupacion_canchas',
'periodo' => $tipo,
'fecha_inicio' => $fecha_inicio,
'fecha_fin' => $fecha_fin,
'tipo_cancha_filtro' => $tipo_cancha_filtro ?: 'Todas',
'reservas_por_dia' => $reservas_por_dia,
'reservas_por_horario' => $reservas_por_horario,
'reservas_por_horario_por_tipo' => [
'horarios_pico_por_tipo' => $horarios_pico_por_tipo,
'horarios_bajos_por_tipo' => $horarios_bajos_por_tipo,
'horarios_sin_reservas_por_tipo' => $horarios_sin_reservas_por_tipo
],
'ocupacion_cancha' => $ocupacion_cancha,
'horarios_pico' => $horarios_pico,
'horarios_bajos' => $horarios_bajos,
'horarios_sin_reservas' => $horarios_sin_reservas,
'resumen' => [
'total_reservas' => $total_reservas,
'total_canceladas' => $total_canceladas,
'reservas_activas' => $total_reservas - $total_canceladas,
'ocupacion_promedio' => $ocupacion_general,
'dias_en_periodo' => intval($total_dias_periodo),
'total_horarios' => count($reservas_por_horario),
'total_canchas' => count($ocupacion_cancha)
]
]);
}
else {
echo json_encode(['error' => 'Tipo de reporte no válido']);
}
} catch (Exception $e) {
echo json_encode(['error' => $e->getMessage()]);
}
?>