diff --git a/modules/turnero/api/firmar_profesional_consentimiento.php b/modules/turnero/api/firmar_profesional_consentimiento.php index ffc0386..5bf30d7 100644 --- a/modules/turnero/api/firmar_profesional_consentimiento.php +++ b/modules/turnero/api/firmar_profesional_consentimiento.php @@ -19,9 +19,8 @@ $svg = $body['svg'] ?? ''; $soloPro = !empty($body['solo_profesional']); // Siempre mergeamos datos_respuestas (incluye _pro_nombre/_pro_cedula) $datosResp = null; -if (isset($body['datos_respuestas']) && is_array($body['datos_respuestas']) && !empty($body['datos_respuestas'])) { - $datosResp = json_encode($body['datos_respuestas'], JSON_UNESCAPED_UNICODE); -} +$datosArr = (isset($body['datos_respuestas']) && is_array($body['datos_respuestas'])) + ? $body['datos_respuestas'] : []; if (!$turnoId) jsonError('turno_id requerido.'); if (!$formularioId) jsonError('formulario_id requerido.'); @@ -32,6 +31,32 @@ if (!preg_match('/^data:image\/(svg\+xml|png|jpeg|webp);base64,/i', $svg)) { $pdo = db(); +// Quién firma se resuelve aquí y no se toma del navegador. El cliente manda el +// nombre que cargó al abrir la página, y si el personal cambia de turno sin +// recargar, la firma quedaba a nombre de quien abrió el formulario en vez de +// quien la está haciendo. La sesión del servidor es la única fuente confiable. +// (guardar_toma.php ya lo hacía así para cada toma; esto alinea la firma final.) +$__uid = adminId(); +if ($__uid) { + $stmtPro = $pdo->prepare( + "SELECT au.full_name, au.cedula AS au_cedula, + le.nombre_completo AS pro_nombre, le.numero_documento AS pro_documento + FROM admin_users au + LEFT JOIN lab_enfermeras le ON le.id = au.enfermera_id + WHERE au.id = ? LIMIT 1" + ); + $stmtPro->execute([$__uid]); + $pro = $stmtPro->fetch(PDO::FETCH_ASSOC) ?: []; + $proNombre = ($pro['pro_nombre'] ?? null) ?: ($pro['full_name'] ?? null); + $proCedula = ($pro['au_cedula'] ?? null) ?: ($pro['pro_documento'] ?? null); + if ($proNombre) $datosArr['_pro_nombre'] = $proNombre; + if ($proCedula) $datosArr['_pro_cedula'] = $proCedula; + $datosArr['_pro_id'] = $__uid; +} +if (!empty($datosArr)) { + $datosResp = json_encode($datosArr, JSON_UNESCAPED_UNICODE); +} + $stmt = $pdo->prepare( "SELECT tc.id, tc.estado, t.sesion_id FROM turnero_consentimientos tc diff --git a/modules/turnero/api/get_traza_turno.php b/modules/turnero/api/get_traza_turno.php index 97ce3a3..db688b1 100644 --- a/modules/turnero/api/get_traza_turno.php +++ b/modules/turnero/api/get_traza_turno.php @@ -60,16 +60,67 @@ $add($turno['fin_lugar_at'], 'fin', 'Finaliza la atención'); // ── 2. Documentos ───────────────────────────────────────────────────────── $q = $pdo->prepare( "SELECT tc.enviado_at, tc.firmado_at, tc.firmado_profesional_at, tc.estado, - f.nombre AS formulario + tc.firma_svg, tc.firma_profesional_svg, tc.datos_respuestas, + f.nombre AS formulario, f.es_toma_progresiva FROM turnero_consentimientos tc JOIN lab_formularios f ON f.id = tc.formulario_id WHERE tc.turno_id = ?" ); $q->execute([$turnoId]); foreach ($q->fetchAll(PDO::FETCH_ASSOC) as $c) { - $add($c['enviado_at'], 'doc', 'Enviado: ' . $c['formulario']); - $add($c['firmado_at'], 'doc_ok', 'Firmado por el paciente: ' . $c['formulario']); - $add($c['firmado_profesional_at'], 'doc_ok', 'Firmado por el profesional: ' . $c['formulario']); + $add($c['enviado_at'], 'doc', 'Enviado: ' . $c['formulario']); + + // No todos los formularios los firma el paciente: el F-LAB-08 solo lo firma + // el profesional y el F-LAB-28 lleva una firma por toma. Se mira qué firma + // hay guardada en vez de suponer que firmado_at es del paciente, que era lo + // que antes hacía decir «firmado por el paciente» en documentos que el + // paciente nunca firmó. + $firmoPaciente = !empty($c['firma_svg']); + $firmoProfesional = !empty($c['firma_profesional_svg']); + + if ($firmoPaciente) { + $add($c['firmado_at'], 'doc_ok', 'Firmado por el paciente: ' . $c['formulario']); + } + if ($firmoProfesional) { + $add($c['firmado_profesional_at'] ?: $c['firmado_at'], 'doc_ok', + 'Firmado por el profesional: ' . $c['formulario']); + } + + // Protocolo prolongado: cada toma lleva su hora y su firmante, que es el + // dato que de verdad interesa. La firma general no dice quién pinchó. + if (!empty($c['es_toma_progresiva'])) { + $dr = json_decode($c['datos_respuestas'] ?? '{}', true) ?: []; + $tomas = []; + foreach ($dr as $clave => $valor) { + // Las firmas de toma terminan en _f y traen su hora en _h + if (!preg_match('/^(_tm[a-z0-9]+)_f$/', $clave, $m)) continue; + if (!is_string($valor) || strlen($valor) < 10) continue; + $base = $m[1]; + $tomas[] = [ + 'hora' => $dr[$base . '_h'] ?? null, + // _tm00 → «Minuto 0», _tm150 → «Minuto 150» + 'etiqueta' => 'Minuto ' . (int) substr($base, 3), + 'firmante' => $dr[$clave . '_pro_nombre'] ?? null, + ]; + } + // Se ordenan por hora para que la línea de tiempo quede coherente + usort($tomas, fn($a, $b) => strcmp((string)$a['hora'], (string)$b['hora'])); + foreach ($tomas as $t) { + if (!$t['hora']) continue; + // La hora viene como "HH:MM"; se ancla al día del turno + $cuando = substr($turno['creado_at'], 0, 10) . ' ' . $t['hora'] . ':00'; + $add($cuando, 'muestra', + 'Toma tomada y firmada (' . $t['etiqueta'] . ')', + $t['firmante']); + } + } + + if (!$firmoPaciente && !$firmoProfesional && $c['firmado_at'] && empty($c['es_toma_progresiva'])) { + $add($c['firmado_at'], 'doc_ok', 'Completado: ' . $c['formulario']); + } + if (!empty($c['es_toma_progresiva']) && $c['firmado_at']) { + $add($c['firmado_at'], 'fin', 'Protocolo cerrado: ' . $c['formulario']); + } } // ── 3. Comentarios del personal ─────────────────────────────────────────── @@ -153,6 +204,14 @@ foreach ($q->fetchAll(PDO::FETCH_ASSOC) as $a) { $d['formulario_nombre'] ?? null); break; + case 'llamar_turno': + $add($a['created_at'], 'llamado', 'Llamó al paciente', $a['admin_nombre']); + break; + + case 'rellamar': + $add($a['created_at'], 'llamado', 'Volvió a llamar al paciente', $a['admin_nombre']); + break; + case 'vincular_paciente': $add($a['created_at'], 'admin', 'Paciente vinculado: ' . ($d['paciente_nombre'] ?? '#' . ($d['paciente_id'] ?? '?')), @@ -199,7 +258,6 @@ jsonOk([ // Lo que el sistema sigue sin registrar. Se declara para que la ausencia de // un evento no se lea como prueba de que no ocurrió. 'sinRastro' => [ - 'rellamados', - 'quién guardó cada toma de un protocolo', + 'el llamado original de los turnos anteriores a hoy, que el rellamado sobrescribía', ], ]); diff --git a/modules/turnero/api/llamar_turno.php b/modules/turnero/api/llamar_turno.php index 5ad006d..01b7cdb 100644 --- a/modules/turnero/api/llamar_turno.php +++ b/modules/turnero/api/llamar_turno.php @@ -10,6 +10,7 @@ */ require_once __DIR__ . '/_helpers.php'; +require_once __DIR__ . '/../../../classes/lab/ActividadAdmin.php'; requireMethod('POST'); requireTurnero(); @@ -144,6 +145,20 @@ try { $pdo->commit(); notificarSSE((int) $turnoActualizado['sesion_id']); + // Quién llamó no se guardaba en ninguna parte: el turno solo conserva la hora + // del último llamado, así que en la bitácora aparecía «Llamado a X» sin autor. + try { + (new ActividadAdmin())->registrar( + adminId(), 'turnero', 'llamar_turno', $turnoId, + [ + 'turno_id' => $turnoId, + 'turno_codigo' => $turnoActualizado['codigo'] ?? null, + ] + ); + } catch (\Throwable $e) { + error_log('[llamar_turno] auditoría: ' . $e->getMessage()); + } + jsonOk(['turno' => $turnoActualizado], "Turno {$turnoActualizado['codigo']} llamado"); } catch (\Throwable $e) { diff --git a/modules/turnero/api/rellamar.php b/modules/turnero/api/rellamar.php index fa52d27..adb8180 100644 --- a/modules/turnero/api/rellamar.php +++ b/modules/turnero/api/rellamar.php @@ -9,6 +9,7 @@ */ require_once __DIR__ . '/_helpers.php'; +require_once __DIR__ . '/../../../classes/lab/ActividadAdmin.php'; requireMethod('POST'); requireTurnero(); @@ -48,4 +49,15 @@ $stmtT = $pdo->prepare( $stmtT->execute([$turnoId]); $turno = $stmtT->fetch(PDO::FETCH_ASSOC); +// El rellamado pisa llamado_recepcion_at, así que sin esto no queda rastro de +// cuántas veces se llamó al paciente ni quién lo hizo. +try { + (new ActividadAdmin())->registrar( + adminId(), 'turnero', 'rellamar', $turnoId, + ['turno_id' => $turnoId, 'turno_codigo' => $turno['codigo'] ?? null] + ); +} catch (\Throwable $e) { + error_log('[rellamar] auditoría: ' . $e->getMessage()); +} + jsonOk(['turno' => $turno]);