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 <noreply@anthropic.com>
88 lines
3.1 KiB
PHP
88 lines
3.1 KiB
PHP
<?php
|
|
/**
|
|
* GET get_proyeccion.php
|
|
* Facturación del día por hora, separando lo ya cerrado de lo que sigue en curso.
|
|
*
|
|
* Facturado → turnos finalizados (plata efectivamente cobrada)
|
|
* En proceso → turnos aún activos (espera / recepción / servicio); su
|
|
* total_cobrado ya está registrado pero el turno no ha cerrado.
|
|
*
|
|
* Los turnos ausentes o cancelados quedan fuera de ambos: no se van a cobrar.
|
|
*/
|
|
require_once __DIR__ . '/_helpers.php';
|
|
requireTurnero();
|
|
requireMethod('GET');
|
|
|
|
$pdo = db();
|
|
$hoy = trim($_GET['fecha'] ?? date('Y-m-d'));
|
|
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $hoy)) $hoy = date('Y-m-d');
|
|
|
|
$ses = $pdo->prepare("SELECT id, inicio_at FROM turnero_sesiones WHERE fecha = ? LIMIT 1");
|
|
$ses->execute([$hoy]);
|
|
$sesion = $ses->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!$sesion) {
|
|
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'];
|
|
|
|
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, t.estado ORDER BY hora"
|
|
);
|
|
$stmt->execute([$sesId]);
|
|
|
|
$finPorHora = $procPorHora = [];
|
|
$totalReal = $totalProceso = 0.0;
|
|
$nFin = $nProc = 0;
|
|
|
|
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) {
|
|
$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
|
|
}
|
|
|
|
// 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 <= $finH; $h++) {
|
|
$labels[] = sprintf('%02d:00', $h);
|
|
$real[] = round($finPorHora[$h] ?? 0);
|
|
$proyectado[] = round($procPorHora[$h] ?? 0);
|
|
}
|
|
|
|
jsonOk([
|
|
'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,
|
|
]);
|