$fechaHasta) $fechaDesde = $fechaHasta; $estadoFiltro = trim($_GET['estado'] ?? ''); $lugarId = (int)($_GET['lugar_id'] ?? 0); $prioridadId = (int)($_GET['prioridad_id'] ?? 0); $pacienteId = (int)($_GET['paciente_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 ($pacienteId > 0) { $where[] = 'ts2.paciente_id = ?'; $binds[] = $pacienteId; } 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, rd.nombre AS recepcion_desk_nombre, ur.full_name AS atendido_recepcion_nombre, ul.full_name AS atendido_lugar_nombre, ts2.observaciones AS notas, 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_lugares rd ON rd.id = t.recepcion_desk_id LEFT JOIN admin_users ur ON ur.id = t.atendido_recepcion_por LEFT JOIN admin_users ul ON ul.id = t.atendido_lugar_por 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); // ── Enriquecer con exámenes y consentimientos ────────────────── foreach ($turnos as &$turno) { // Exámenes $stmtExam = $pdo->prepare( "SELECT DISTINCT et.nombre, et.codigo FROM turnero_examen_items tei JOIN exam_tipos et ON et.id = tei.exam_tipo_id WHERE tei.solicitud_id = ( SELECT id FROM turnero_solicitudes WHERE turno_id = ? LIMIT 1 ) ORDER BY et.nombre ASC" ); $stmtExam->execute([(int)$turno['id']]); $turno['examenes'] = $stmtExam->fetchAll(PDO::FETCH_ASSOC); // Consentimientos $stmtCons = $pdo->prepare( "SELECT tc.id, f.nombre, tc.estado, tc.firmado_at FROM turnero_consentimientos tc LEFT JOIN lab_formularios f ON f.id = tc.formulario_id WHERE tc.turno_id = ? ORDER BY tc.estado DESC, tc.firmado_at DESC" ); $stmtCons->execute([(int)$turno['id']]); $turno['consentimientos'] = $stmtCons->fetchAll(PDO::FETCH_ASSOC); // Comentarios $stmtCom = $pdo->prepare( "SELECT usuario_nombre, comentario, tipo, creado_at FROM turnero_comentarios WHERE turno_id = ? ORDER BY creado_at DESC LIMIT 3" ); $stmtCom->execute([(int)$turno['id']]); $turno['comentarios'] = $stmtCom->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, ]);