Files
whatsapp/modules/turnero/api/get_proyeccion.php
Lizandro GuarnizoandClaude Opus 5 d79d33db36 Facturación del día: finalizados vs en proceso, en vez de extrapolación lineal
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>
2026-08-03 15:08:28 -05:00

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,
]);