This commit is contained in:
Lizandro Guarnizo
2026-04-21 21:13:35 -05:00
parent 8cd3348d94
commit 3a19f08bf8
5 changed files with 916 additions and 0 deletions
+152
View File
@@ -0,0 +1,152 @@
<?php
/**
* modules/turnero/api/export_historial.php
* Exporta CSV del historial multi-día con los mismos filtros de get_historial.php
*
* GET ?fecha_desde=YYYY-MM-DD&fecha_hasta=YYYY-MM-DD&estado=...&lugar_id=...&prioridad_id=...&q=...
*/
require_once __DIR__ . '/_helpers.php';
requireTurnero();
requireMethod('GET');
$pdo = db();
// ── Parámetros ────────────────────────────────────────────────
$hoy = date('Y-m-d');
$hace7 = date('Y-m-d', strtotime('-6 days'));
$fechaDesde = trim($_GET['fecha_desde'] ?? '');
$fechaHasta = trim($_GET['fecha_hasta'] ?? '');
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $fechaDesde)) $fechaDesde = $hace7;
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $fechaHasta)) $fechaHasta = $hoy;
if ($fechaDesde > $fechaHasta) $fechaDesde = $fechaHasta;
$estadoFiltro = trim($_GET['estado'] ?? '');
$lugarId = (int)($_GET['lugar_id'] ?? 0);
$prioridadId = (int)($_GET['prioridad_id'] ?? 0);
$q = trim($_GET['q'] ?? '');
// ── WHERE ─────────────────────────────────────────────────────
$where = ['DATE(ses.fecha) BETWEEN ? AND ?'];
$binds = [$fechaDesde, $fechaHasta];
$estadosValidos = ['espera','en_recepcion','en_espera_lugar','en_servicio','finalizado','ausente','cancelado'];
if ($estadoFiltro && in_array($estadoFiltro, $estadosValidos, true)) {
$where[] = 't.estado = ?';
$binds[] = $estadoFiltro;
}
if ($lugarId > 0) {
$where[] = 't.lugar_destino_id = ?';
$binds[] = $lugarId;
}
if ($prioridadId > 0) {
$where[] = 't.prioridad_id = ?';
$binds[] = $prioridadId;
}
if ($q !== '') {
$where[] = '(t.codigo LIKE ? OR t.paciente_nombre LIKE ? OR pac.nombre_completo LIKE ?)';
$like = '%' . $q . '%';
$binds[] = $like;
$binds[] = $like;
$binds[] = $like;
}
$whereStr = 'WHERE ' . implode(' AND ', $where);
// ── Consulta ──────────────────────────────────────────────────
$sql = "
SELECT
ses.fecha AS fecha_sesion,
t.codigo,
t.numero,
p.codigo AS prioridad,
p.nombre AS prioridad_nombre,
COALESCE(pac.nombre_completo, t.paciente_nombre) AS paciente,
pac.numero_documento,
pac.tipo_documento,
t.paciente_cel AS celular,
l.nombre AS lugar,
t.estado,
t.creado_at,
t.llamado_recepcion_at,
t.inicio_recepcion_at,
t.fin_recepcion_at,
t.llamado_lugar_at,
t.inicio_lugar_at,
t.fin_lugar_at,
ROUND(TIMESTAMPDIFF(SECOND, t.creado_at,
COALESCE(t.inicio_recepcion_at, t.fin_recepcion_at)) / 60.0, 1) AS espera_recepcion_min,
ROUND(TIMESTAMPDIFF(SECOND, t.inicio_lugar_at,
COALESCE(t.fin_lugar_at, NOW())) / 60.0, 1) AS servicio_lugar_min,
sol.total_cobrado,
sol.metodo_pago
FROM turnero_turnos t
JOIN turnero_sesiones ses ON ses.id = t.sesion_id
JOIN turnero_prioridades p ON p.id = t.prioridad_id
LEFT JOIN turnero_lugares l ON l.id = t.lugar_destino_id
LEFT JOIN turnero_solicitudes sol ON sol.turno_id = t.id
LEFT JOIN lab_pacientes pac ON pac.id = sol.paciente_id
$whereStr
ORDER BY t.creado_at ASC";
$stmt = $pdo->prepare($sql);
$stmt->execute($binds);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
// ── Nombre de archivo ─────────────────────────────────────────
$nombreArchivo = "historial_turnos_{$fechaDesde}_a_{$fechaHasta}.csv";
// ── Output ────────────────────────────────────────────────────
// Limpiar cualquier output previo antes de los headers
ob_clean();
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="' . $nombreArchivo . '"');
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
$out = fopen('php://output', 'w');
echo "\xEF\xBB\xBF"; // BOM UTF-8 para Excel
// Encabezados
$headers = [
'Fecha', 'Código', 'Número', 'Prioridad', 'Prioridad nombre',
'Paciente', 'Documento', 'Tipo doc.', 'Celular',
'Lugar', 'Estado',
'Creado', 'Llamado recepción', 'Inicio recepción', 'Fin recepción',
'Llamado lugar', 'Inicio lugar', 'Fin lugar',
'Espera recepción (min)', 'Servicio lugar (min)',
'Total cobrado', 'Método pago',
];
fputcsv($out, $headers, ',', '"');
$fechaCols = [
'creado_at','llamado_recepcion_at','inicio_recepcion_at','fin_recepcion_at',
'llamado_lugar_at','inicio_lugar_at','fin_lugar_at',
];
$fields = [
'fecha_sesion', 'codigo', 'numero', 'prioridad', 'prioridad_nombre',
'paciente', 'numero_documento', 'tipo_documento', 'celular',
'lugar', 'estado',
'creado_at', 'llamado_recepcion_at', 'inicio_recepcion_at', 'fin_recepcion_at',
'llamado_lugar_at', 'inicio_lugar_at', 'fin_lugar_at',
'espera_recepcion_min', 'servicio_lugar_min',
'total_cobrado', 'metodo_pago',
];
foreach ($rows as $row) {
$line = [];
foreach ($fields as $f) {
$v = $row[$f] ?? '';
if ($v && in_array($f, $fechaCols, true)) {
$v = date('d/m/Y H:i:s', strtotime($v));
}
$line[] = $v;
}
fputcsv($out, $line, ',', '"');
}
fclose($out);
exit;
+165
View File
@@ -0,0 +1,165 @@
<?php
/**
* modules/turnero/api/get_historial.php
* GET Historial paginado de turnos con filtros multi-día.
*
* Parámetros (todos opcionales):
* fecha_desde YYYY-MM-DD (default: hace 7 días)
* fecha_hasta YYYY-MM-DD (default: hoy)
* estado string ("finalizado","ausente","cancelado","en_servicio",... o vacío = todos)
* lugar_id int (0 = todos)
* prioridad_id int (0 = todos)
* q string búsqueda libre por nombre/código
* page int (default 1)
* per_page int (default 50, max 200)
*/
require_once __DIR__ . '/_helpers.php';
requireTurnero();
requireMethod('GET');
$pdo = db();
// ── Parámetros ────────────────────────────────────────────────
$hoy = date('Y-m-d');
$hace7 = date('Y-m-d', strtotime('-6 days'));
$fechaDesde = trim($_GET['fecha_desde'] ?? '');
$fechaHasta = trim($_GET['fecha_hasta'] ?? '');
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $fechaDesde)) $fechaDesde = $hace7;
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $fechaHasta)) $fechaHasta = $hoy;
if ($fechaDesde > $fechaHasta) $fechaDesde = $fechaHasta;
$estadoFiltro = trim($_GET['estado'] ?? '');
$lugarId = (int)($_GET['lugar_id'] ?? 0);
$prioridadId = (int)($_GET['prioridad_id'] ?? 0);
$q = trim($_GET['q'] ?? '');
$perPage = min(200, max(10, (int)($_GET['per_page'] ?? 50)));
$page = max(1, (int)($_GET['page'] ?? 1));
$offset = ($page - 1) * $perPage;
// ── Base WHERE ───────────────────────────────────────────────
// Unimos turnos con sesiones para filtrar por fecha de sesión
$where = ['DATE(s.fecha) BETWEEN ? AND ?'];
$binds = [$fechaDesde, $fechaHasta];
$estadosValidos = ['espera','en_recepcion','en_espera_lugar','en_servicio','finalizado','ausente','cancelado'];
if ($estadoFiltro && in_array($estadoFiltro, $estadosValidos, true)) {
$where[] = 't.estado = ?';
$binds[] = $estadoFiltro;
}
if ($lugarId > 0) {
$where[] = 't.lugar_destino_id = ?';
$binds[] = $lugarId;
}
if ($prioridadId > 0) {
$where[] = 't.prioridad_id = ?';
$binds[] = $prioridadId;
}
if ($q !== '') {
$where[] = '(t.codigo LIKE ? OR t.paciente_nombre LIKE ? OR s2.nombre_completo LIKE ?)';
$like = '%' . $q . '%';
$binds[] = $like;
$binds[] = $like;
$binds[] = $like;
}
$whereStr = 'WHERE ' . implode(' AND ', $where);
// ── Conteo total ──────────────────────────────────────────────
$sqlCount = "
SELECT COUNT(*) AS total
FROM turnero_turnos t
JOIN turnero_sesiones s ON s.id = t.sesion_id
LEFT JOIN turnero_solicitudes ts2 ON ts2.turno_id = t.id
LEFT JOIN lab_pacientes s2 ON s2.id = ts2.paciente_id
$whereStr";
$stmtCount = $pdo->prepare($sqlCount);
$stmtCount->execute($binds);
$totalRows = (int)$stmtCount->fetchColumn();
$totalPages = max(1, (int)ceil($totalRows / $perPage));
if ($page > $totalPages) $page = $totalPages;
$offset = ($page - 1) * $perPage;
// ── Lista paginada ────────────────────────────────────────────
$sqlTurnos = "
SELECT
t.id,
t.codigo,
t.numero,
t.estado,
t.paciente_nombre,
t.paciente_cel,
t.creado_at,
t.inicio_recepcion_at,
t.fin_recepcion_at,
t.inicio_lugar_at,
t.fin_lugar_at,
s.fecha AS fecha_sesion,
p.codigo AS prioridad_codigo,
p.nombre AS prioridad_nombre,
p.color AS prioridad_color,
l.nombre AS lugar_nombre,
s2.nombre_completo AS paciente_bd,
ROUND(TIMESTAMPDIFF(SECOND, t.creado_at,
COALESCE(t.inicio_recepcion_at, t.fin_recepcion_at)) / 60.0, 1)
AS espera_min,
ROUND(TIMESTAMPDIFF(SECOND, t.inicio_lugar_at,
COALESCE(t.fin_lugar_at, NOW())) / 60.0, 1)
AS servicio_min
FROM turnero_turnos t
JOIN turnero_sesiones s ON s.id = t.sesion_id
JOIN turnero_prioridades p ON p.id = t.prioridad_id
LEFT JOIN turnero_lugares l ON l.id = t.lugar_destino_id
LEFT JOIN turnero_solicitudes ts2 ON ts2.turno_id = t.id
LEFT JOIN lab_pacientes s2 ON s2.id = ts2.paciente_id
$whereStr
ORDER BY t.creado_at DESC
LIMIT ? OFFSET ?";
$bindsPage = array_merge($binds, [$perPage, $offset]);
$stmtTurnos = $pdo->prepare($sqlTurnos);
$stmtTurnos->execute($bindsPage);
$turnos = $stmtTurnos->fetchAll(PDO::FETCH_ASSOC);
// ── Resumen por estado (del rango filtrado) ───────────────────
$sqlRes = "
SELECT t.estado, COUNT(*) AS cnt
FROM turnero_turnos t
JOIN turnero_sesiones s ON s.id = t.sesion_id
LEFT JOIN turnero_solicitudes ts2 ON ts2.turno_id = t.id
LEFT JOIN lab_pacientes s2 ON s2.id = ts2.paciente_id
$whereStr
GROUP BY t.estado";
$stmtRes = $pdo->prepare($sqlRes);
$stmtRes->execute($binds);
$resumenEstados = [];
foreach ($stmtRes->fetchAll(PDO::FETCH_ASSOC) as $r) {
$resumenEstados[$r['estado']] = (int)$r['cnt'];
}
// ── Catálogos para los selectores (si pide con ?meta=1) ──────
$meta = [];
if (!empty($_GET['meta'])) {
$meta['lugares'] = $pdo->query(
"SELECT id, nombre FROM turnero_lugares WHERE activo=1 ORDER BY sort_order ASC, nombre ASC"
)->fetchAll(PDO::FETCH_ASSOC);
$meta['prioridades'] = $pdo->query(
"SELECT id, nombre, codigo, color FROM turnero_prioridades ORDER BY orden_peso ASC"
)->fetchAll(PDO::FETCH_ASSOC);
}
jsonOk([
'fecha_desde' => $fechaDesde,
'fecha_hasta' => $fechaHasta,
'total' => $totalRows,
'page' => $page,
'per_page' => $perPage,
'total_pages' => $totalPages,
'resumen_estados'=> $resumenEstados,
'turnos' => $turnos,
'meta' => $meta,
]);