From d79d33db36d00649fed6ef3cc4a5e72875e02b33 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Mon, 3 Aug 2026 15:08:28 -0500 Subject: [PATCH] =?UTF-8?q?Facturaci=C3=B3n=20del=20d=C3=ADa:=20finalizado?= =?UTF-8?q?s=20vs=20en=20proceso,=20en=20vez=20de=20extrapolaci=C3=B3n=20l?= =?UTF-8?q?ineal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La proyección estimaba el cierre del día multiplicando el ritmo promedio por las horas que faltaban hasta un cierre fijo a las 17:00. Eso sobreestimaba (el laboratorio concentra las tomas en la mañana por el ayuno), dependía de una hora de cierre escrita a mano, y en fechas pasadas usaba date('G') —la hora actual real— así que proyectaba facturación futura sobre días ya cerrados. Ahora son dos cifras concretas, sin estimar: Facturado = turnos finalizados; En proceso = turnos aún activos, cuyo total_cobrado ya está registrado pero no han cerrado. Ausentes y cancelados quedan fuera de ambos. La gráfica por hora usa la misma separación. Hoy: $22.779.145 en 92 finalizados + $667.000 en 2 en proceso (antes proyectaba $28.740.490). Co-Authored-By: Claude Opus 5 --- modules/turnero/api/get_proyeccion.php | 74 +++++++++++++++++--------- modules/turnero/views/dashboard.php | 11 ++-- 2 files changed, 54 insertions(+), 31 deletions(-) diff --git a/modules/turnero/api/get_proyeccion.php b/modules/turnero/api/get_proyeccion.php index e66c52b..6d91d24 100644 --- a/modules/turnero/api/get_proyeccion.php +++ b/modules/turnero/api/get_proyeccion.php @@ -1,7 +1,13 @@ execute([$hoy]); $sesion = $ses->fetch(PDO::FETCH_ASSOC); if (!$sesion) { - jsonOk(['labels'=>[],'real'=>[],'proyectado'=>[],'total_real'=>0,'total_proyectado'=>0]); + jsonOk(['labels'=>[],'real'=>[],'proyectado'=>[],'total_real'=>0,'total_proyectado'=>0, + 'turnos_finalizados'=>0,'turnos_en_proceso'=>0,'total_en_proceso'=>0]); } $sesId = (int)$sesion['id']; -// Facturación por hora real +const ESTADOS_EN_PROCESO = ['espera', 'en_recepcion', 'en_espera_lugar', 'en_servicio']; + +// Facturación por hora, separada por estado del turno $stmt = $pdo->prepare( "SELECT HOUR(t.creado_at) AS hora, + t.estado, COALESCE(SUM(ts.total_cobrado), 0) AS total, COUNT(DISTINCT t.id) AS turnos FROM turnero_turnos t JOIN turnero_solicitudes ts ON ts.turno_id = t.id WHERE t.sesion_id = ? AND ts.total_cobrado > 0 - GROUP BY hora ORDER BY hora" + GROUP BY hora, t.estado ORDER BY hora" ); $stmt->execute([$sesId]); -$porHora = []; + +$finPorHora = $procPorHora = []; +$totalReal = $totalProceso = 0.0; +$nFin = $nProc = 0; + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) { - $porHora[(int)$r['hora']] = ['total' => (float)$r['total'], 'turnos' => (int)$r['turnos']]; + $h = (int)$r['hora']; + $monto = (float)$r['total']; + $turnos = (int)$r['turnos']; + + if ($r['estado'] === 'finalizado') { + $finPorHora[$h] = ($finPorHora[$h] ?? 0) + $monto; + $totalReal += $monto; + $nFin += $turnos; + } elseif (in_array($r['estado'], ESTADOS_EN_PROCESO, true)) { + $procPorHora[$h] = ($procPorHora[$h] ?? 0) + $monto; + $totalProceso += $monto; + $nProc += $turnos; + } + // ausente / cancelado: se ignoran, no representan ingreso } -$inicioH = $sesion['inicio_at'] ? (int)date('G', strtotime($sesion['inicio_at'])) : 6; -$horaAct = (int)date('G'); -$finDia = 17; // 5pm cierre estimado - -// Calcular tasa por hora basada en horas con datos -$totalReal = array_sum(array_column($porHora, 'total')); -$horasConDatos = max(1, $horaAct - $inicioH); -$tasaPorHora = $totalReal / $horasConDatos; +// Rango de horas a graficar: desde que abrió la sesión hasta la última con datos +$inicioH = $sesion['inicio_at'] ? (int)date('G', strtotime($sesion['inicio_at'])) : 6; +$horas = array_merge(array_keys($finPorHora), array_keys($procPorHora)); +$finH = $horas ? max($horas) : $inicioH; $labels = $real = $proyectado = []; -for ($h = $inicioH; $h <= max($finDia, $horaAct); $h++) { +for ($h = $inicioH; $h <= $finH; $h++) { $labels[] = sprintf('%02d:00', $h); - $real[] = isset($porHora[$h]) ? round($porHora[$h]['total']) : 0; - $proyectado[] = ($h > $horaAct && $h <= $finDia) ? round($tasaPorHora) : null; + $real[] = round($finPorHora[$h] ?? 0); + $proyectado[] = round($procPorHora[$h] ?? 0); } -$horasRestantes = max(0, $finDia - $horaAct); -$totalProyectado = round($totalReal + ($tasaPorHora * $horasRestantes)); - jsonOk([ - 'labels' => $labels, - 'real' => $real, - 'proyectado' => $proyectado, - 'total_real' => $totalReal, - 'total_proyectado' => $totalProyectado, - 'tasa_por_hora' => round($tasaPorHora), + 'labels' => $labels, + 'real' => $real, + 'proyectado' => $proyectado, + 'total_real' => round($totalReal), + 'total_en_proceso' => round($totalProceso), + 'total_proyectado' => round($totalReal + $totalProceso), + 'turnos_finalizados' => $nFin, + 'turnos_en_proceso' => $nProc, ]); diff --git a/modules/turnero/views/dashboard.php b/modules/turnero/views/dashboard.php index 5a87e6e..fe0785e 100644 --- a/modules/turnero/views/dashboard.php +++ b/modules/turnero/views/dashboard.php @@ -256,7 +256,7 @@ try {