diff --git a/api/lab/get_formularios.php b/api/lab/get_formularios.php index 4921304..de1ec52 100644 --- a/api/lab/get_formularios.php +++ b/api/lab/get_formularios.php @@ -19,6 +19,7 @@ try { if (!empty($_GET['fecha_desde'])) $filtros['fecha_desde'] = $_GET['fecha_desde']; if (!empty($_GET['fecha_hasta'])) $filtros['fecha_hasta'] = $_GET['fecha_hasta']; if (!empty($_GET['paciente'])) $filtros['paciente'] = trim($_GET['paciente']); + if (!empty($_GET['origen'])) $filtros['origen'] = $_GET['origen']; // Enfermero solo ve los suyos if (userRole() === 'enfermero') { $filtros['enviado_por'] = adminId(); diff --git a/classes/lab/Formulario.php b/classes/lab/Formulario.php index 2d4f533..43c064e 100644 --- a/classes/lab/Formulario.php +++ b/classes/lab/Formulario.php @@ -174,40 +174,84 @@ class Formulario { * Listar envíos (admin/enfermero). */ public function listarEnvios(array $filtros = [], int $pagina = 1, int $porPagina = 20): array { - $where = ['1=1']; - $params = []; - if (!empty($filtros['formulario_id'])) { $where[] = 'e.formulario_id = ?'; $params[] = $filtros['formulario_id']; } - if (!empty($filtros['paciente_id'])) { $where[] = 'e.paciente_id = ?'; $params[] = $filtros['paciente_id']; } - if (!empty($filtros['enviado_por'])) { $where[] = 'e.enviado_por = ?'; $params[] = $filtros['enviado_por']; } - if (!empty($filtros['estado'])) { $where[] = 'e.estado = ?'; $params[] = $filtros['estado']; } - if (!empty($filtros['fecha_desde'])) { $where[] = 'DATE(e.created_at) >= ?'; $params[] = $filtros['fecha_desde']; } - if (!empty($filtros['fecha_hasta'])) { $where[] = 'DATE(e.created_at) <= ?'; $params[] = $filtros['fecha_hasta']; } - if (!empty($filtros['paciente'])) { $where[] = 'p.nombre_completo LIKE ?'; $params[] = '%' . $filtros['paciente'] . '%'; } - $w = implode(' AND ', $where); + $origen = $filtros['origen'] ?? ''; // 'turnero' | 'otros' | '' (todos) $offset = ($pagina - 1) * $porPagina; + // ── Rama 1: envíos normales (domicilios, envío manual, etc.) ────────── + $whereA = ['1=1']; + $paramsA = []; + if (!empty($filtros['formulario_id'])) { $whereA[] = 'e.formulario_id = ?'; $paramsA[] = $filtros['formulario_id']; } + if (!empty($filtros['paciente_id'])) { $whereA[] = 'e.paciente_id = ?'; $paramsA[] = $filtros['paciente_id']; } + if (!empty($filtros['enviado_por'])) { $whereA[] = 'e.enviado_por = ?'; $paramsA[] = $filtros['enviado_por']; } + if (!empty($filtros['estado'])) { $whereA[] = 'e.estado = ?'; $paramsA[] = $filtros['estado']; } + if (!empty($filtros['fecha_desde'])) { $whereA[] = 'DATE(e.created_at) >= ?'; $paramsA[] = $filtros['fecha_desde']; } + if (!empty($filtros['fecha_hasta'])) { $whereA[] = 'DATE(e.created_at) <= ?'; $paramsA[] = $filtros['fecha_hasta']; } + if (!empty($filtros['paciente'])) { $whereA[] = 'p.nombre_completo LIKE ?'; $paramsA[] = '%' . $filtros['paciente'] . '%'; } + $wA = implode(' AND ', $whereA); + + $sqlA = "SELECT e.id, e.formulario_id, e.estado, e.created_at AS fecha, + f.nombre AS form_nombre, f.categoria, + p.nombre_completo AS paciente_nombre, + u.full_name AS enviado_por_nombre, + 'otros' AS origen, e.token, NULL AS turno_codigo + FROM lab_form_envios e + JOIN lab_formularios f ON f.id = e.formulario_id + LEFT JOIN lab_pacientes p ON p.id = e.paciente_id + LEFT JOIN admin_users u ON u.id = e.enviado_por + WHERE $wA"; + + // ── Rama 2: consentimientos del turnero ─────────────────────────────── + $whereB = ['1=1']; + $paramsB = []; + if (!empty($filtros['formulario_id'])) { $whereB[] = 'tc.formulario_id = ?'; $paramsB[] = $filtros['formulario_id']; } + if (!empty($filtros['estado'])) { + // Mapear estados: firmado=firmado, pendiente=pendiente/enviado/visto + if ($filtros['estado'] === 'firmado') { + $whereB[] = "tc.estado = 'firmado'"; + } elseif ($filtros['estado'] === 'pendiente') { + $whereB[] = "tc.estado IN ('pendiente','enviado','visto')"; + } else { + $whereB[] = '1=0'; // estados como 'completado'/'expirado' no aplican al turnero + } + } + if (!empty($filtros['fecha_desde'])) { $whereB[] = 'DATE(tc.enviado_at) >= ?'; $paramsB[] = $filtros['fecha_desde']; } + if (!empty($filtros['fecha_hasta'])) { $whereB[] = 'DATE(tc.enviado_at) <= ?'; $paramsB[] = $filtros['fecha_hasta']; } + if (!empty($filtros['paciente'])) { $whereB[] = 'p.nombre_completo LIKE ?'; $paramsB[] = '%' . $filtros['paciente'] . '%'; } + if (!empty($filtros['enviado_por'])) { $whereB[] = '1=0'; } // turnero no tiene enviado_por + $wB = implode(' AND ', $whereB); + + $sqlB = "SELECT tc.id, tc.formulario_id, tc.estado, COALESCE(tc.enviado_at, tc.firmado_at) AS fecha, + f.nombre AS form_nombre, f.categoria, + p.nombre_completo AS paciente_nombre, + 'Turnero' AS enviado_por_nombre, + 'turnero' AS origen, tc.token, t.codigo AS turno_codigo + FROM turnero_consentimientos tc + JOIN lab_formularios f ON f.id = tc.formulario_id + JOIN turnero_turnos t ON t.id = tc.turno_id + LEFT JOIN turnero_solicitudes ts ON ts.turno_id = tc.turno_id + LEFT JOIN lab_pacientes p ON p.id = ts.paciente_id + WHERE $wB"; + + // ── Combinar según filtro origen ────────────────────────────────────── + if ($origen === 'turnero') { + $sql = $sqlB; + $params = $paramsB; + } elseif ($origen === 'otros') { + $sql = $sqlA; + $params = $paramsA; + } else { + $sql = "($sqlA) UNION ALL ($sqlB)"; + $params = array_merge($paramsA, $paramsB); + } + $total = $this->db->fetch( - "SELECT COUNT(*) AS n - FROM lab_form_envios e - JOIN lab_formularios f ON f.id = e.formulario_id - LEFT JOIN lab_pacientes p ON p.id = e.paciente_id - LEFT JOIN admin_users u ON u.id = e.enviado_por - WHERE $w", - $params + "SELECT COUNT(*) AS n FROM ($sql) AS _u", $params )['n'] ?? 0; - $rows = $this->db->fetchAll(" - SELECT e.*, f.nombre AS form_nombre, f.categoria, f.esquema, - p.nombre_completo AS paciente_nombre, - u.full_name AS enviado_por_nombre - FROM lab_form_envios e - JOIN lab_formularios f ON f.id = e.formulario_id - LEFT JOIN lab_pacientes p ON p.id = e.paciente_id - LEFT JOIN admin_users u ON u.id = e.enviado_por - WHERE $w - ORDER BY e.created_at DESC - LIMIT ? OFFSET ? - ", array_merge($params, [$porPagina, $offset])); + $rows = $this->db->fetchAll( + "SELECT * FROM ($sql) AS _u ORDER BY fecha DESC LIMIT ? OFFSET ?", + array_merge($params, [$porPagina, $offset]) + ); return [ 'data' => $rows, diff --git a/lab_formularios.php b/lab_formularios.php index 77b9f08..1f9ac75 100644 --- a/lab_formularios.php +++ b/lab_formularios.php @@ -121,6 +121,14 @@ require_once __DIR__ . '/shared/components/sidebar.php'; +
+ + +
Formulario Paciente + Origen Estado - Enviado - Expira + Fecha Acciones - Cargando… + Cargando…
@@ -496,6 +504,7 @@ const envios = { paciente: $('env-f-paciente').value.trim() || '', formulario_id: $('env-f-formulario').value || '', estado: $('env-f-estado').value || '', + origen: $('env-f-origen').value || '', }); }, @@ -514,7 +523,7 @@ const envios = { this._render(); } catch(e) { const tb = $('tbody-envios'); - if (tb) tb.innerHTML = 'Error al cargar envíos'; + if (tb) tb.innerHTML = 'Error al cargar envíos'; } finally { if (spinner) spinner.style.display = 'none'; if (wrap) wrap.style.display = ''; @@ -532,6 +541,7 @@ const envios = { $('env-f-paciente').value = ''; $('env-f-formulario').value = ''; $('env-f-estado').value = ''; + $('env-f-origen').value = ''; this.cargar(1); }, @@ -595,29 +605,39 @@ const envios = { _render() { const tb = $('tbody-envios'); if (!this._paginaData.length) { - tb.innerHTML = 'Sin envíos para los filtros seleccionados'; + tb.innerHTML = 'Sin envíos para los filtros seleccionados'; $('env-pag-info').textContent = ''; $('env-paginacion').innerHTML = ''; return; } const inicio = (this._pagina - 1) * POR_PAGINA; tb.innerHTML = this._paginaData.map(e => { - const info = ESTADO_ENV[e.estado] || {}; + const info = ESTADO_ENV[e.estado] || {}; + const esTurnero = e.origen === 'turnero'; + const origenBadge = esTurnero + ? `Turnero${e.turno_codigo ? ' #'+esc(e.turno_codigo):''}` + : `Otros`; + const viewHref = esTurnero + ? `ver_formulario_enviado.php?token=${esc(e.token)}` + : `ver_formulario_enviado.php?id=${e.id}`; + const fecha = (e.fecha||'').slice(0,16).replace('T',' '); return ` ${esc(e.form_nombre)}
${esc(e.categoria)} ${esc(e.paciente_nombre || '—')} + ${origenBadge} ${info.icon} ${e.estado} - ${esc((e.created_at||'').slice(0,16).replace('T',' '))} - ${esc((e.expira_en||'').slice(0,10))} + ${esc(fecha)} ${['completado','firmado'].includes(e.estado) - ? ` - ` + ? (esTurnero + ? `` + : ` + `) : ''} `; diff --git a/migrations/20260616_turnero_firma_profesional.sql b/migrations/20260616_turnero_firma_profesional.sql new file mode 100644 index 0000000..cbc34a5 --- /dev/null +++ b/migrations/20260616_turnero_firma_profesional.sql @@ -0,0 +1,6 @@ +-- Agrega columna para firma del enfermero en consentimientos del turnero +ALTER TABLE turnero_consentimientos + ADD COLUMN IF NOT EXISTS firma_profesional_svg MEDIUMTEXT DEFAULT NULL + COMMENT 'Firma del enfermero/profesional en formato SVG o PNG base64', + ADD COLUMN IF NOT EXISTS firmado_profesional_at DATETIME DEFAULT NULL + COMMENT 'Fecha y hora en que el profesional firmó'; diff --git a/modules/turnero/api/firmar_profesional_consentimiento.php b/modules/turnero/api/firmar_profesional_consentimiento.php new file mode 100644 index 0000000..f73eed9 --- /dev/null +++ b/modules/turnero/api/firmar_profesional_consentimiento.php @@ -0,0 +1,50 @@ +prepare( + "SELECT tc.id, tc.estado, t.sesion_id + FROM turnero_consentimientos tc + JOIN turnero_turnos t ON t.id = tc.turno_id + WHERE tc.turno_id = ? AND tc.formulario_id = ?" +); +$stmt->execute([$turnoId, $formularioId]); +$tc = $stmt->fetch(PDO::FETCH_ASSOC); + +if (!$tc) jsonError('Consentimiento no encontrado.', 404); +if ($tc['estado'] !== 'firmado') jsonError('El paciente debe firmar primero antes de que el profesional pueda firmar.'); + +$stmt = $pdo->prepare( + "UPDATE turnero_consentimientos + SET firma_profesional_svg = ?, firmado_profesional_at = NOW() + WHERE turno_id = ? AND formulario_id = ?" +); +$stmt->execute([$svg, $turnoId, $formularioId]); + +notificarSSE((int)$tc['sesion_id']); + +jsonOk(['mensaje' => 'Firma del profesional guardada correctamente.']); diff --git a/modules/turnero/api/get_consentimientos.php b/modules/turnero/api/get_consentimientos.php index f7f2616..a04b6c1 100644 --- a/modules/turnero/api/get_consentimientos.php +++ b/modules/turnero/api/get_consentimientos.php @@ -35,6 +35,8 @@ $stmt = $pdo->prepare( tc.estado, tc.enviado_at, tc.firmado_at, + (tc.firma_profesional_svg IS NOT NULL) AS tiene_firma_profesional, + tc.firmado_profesional_at, f.nombre AS formulario_nombre FROM turnero_consentimientos tc LEFT JOIN lab_formularios f ON f.id = tc.formulario_id diff --git a/modules/turnero/views/recepcion.php b/modules/turnero/views/recepcion.php index d878654..5cacc7e 100644 --- a/modules/turnero/views/recepcion.php +++ b/modules/turnero/views/recepcion.php @@ -168,6 +168,9 @@ $adminNombre = $_SESSION['admin_user']['full_name'] ?? $_SESSION['admin_user'][' border-radius: 99px; border: 1px solid currentColor; opacity: .85; } .consent-acciones { display: flex; gap: .3rem; flex-shrink: 0; } .consent-acciones .btn { font-size: .72rem; padding: 2px 8px; border-radius: 7px; } + .consent-prof-row { display:flex; align-items:center; gap:.4rem; font-size:.75rem; + margin-top:.25rem; padding-top:.25rem; border-top:1px solid rgba(0,0,0,.07); } + .consent-prof-row .badge { font-size:.65rem; } /* ── Barra de acciones ── */ .ficha-acciones { @@ -425,6 +428,35 @@ require_once __DIR__ . '/../../../shared/components/sidebar.php'; + + +
@@ -875,10 +907,11 @@ function renderConsentimientos(lista) { const token = escHtml(c.token || ''); const nom = escHtml(c.formulario_nombre || 'Consentimiento'); const fId = c.formulario_id; + const nomJs = (c.formulario_nombre || 'Consentimiento').replace(/'/g, "\\'"); - // Botón de firma (solo si no está firmado/rechazado) + // Botón de firma paciente (solo si no está firmado/rechazado) const btnFirmar = (!ya) - ? `` : ''; @@ -893,15 +926,103 @@ function renderConsentimientos(lista) { WhatsApp `; - return `