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