diff --git a/migrations/20260611_turnero_consentimiento_principal.sql b/migrations/20260611_turnero_consentimiento_principal.sql new file mode 100644 index 0000000..d046b2e --- /dev/null +++ b/migrations/20260611_turnero_consentimiento_principal.sql @@ -0,0 +1,10 @@ +-- ============================================================ +-- Migration: 20260611_turnero_consentimiento_principal +-- Añade flag is_principal a lab_formularios para marcar +-- el consentimiento principal de recepción (no ligado a exámenes). +-- ============================================================ + +ALTER TABLE lab_formularios + ADD COLUMN is_principal TINYINT(1) NOT NULL DEFAULT 0 + COMMENT '1 = consentimiento principal de recepción, se auto-crea al tomar turno' + AFTER tipo; diff --git a/migrations/20260611_turnero_paciente_id.sql b/migrations/20260611_turnero_paciente_id.sql new file mode 100644 index 0000000..8960cd3 --- /dev/null +++ b/migrations/20260611_turnero_paciente_id.sql @@ -0,0 +1,13 @@ +-- ============================================================ +-- Migration: 20260611_turnero_paciente_id +-- Añade paciente_id (FK lab_pacientes) a turnero_turnos +-- para que el paciente quede vinculado desde el kiosko. +-- ============================================================ + +ALTER TABLE turnero_turnos + ADD COLUMN paciente_id INT(11) DEFAULT NULL + COMMENT 'FK lab_pacientes.id (vinculado desde kiosko o recepción)' + AFTER paciente_cel; + +ALTER TABLE turnero_turnos + ADD INDEX idx_paciente_id (paciente_id); diff --git a/modules/turnero/api/create_turno.php b/modules/turnero/api/create_turno.php index 13eba54..6556b5c 100644 --- a/modules/turnero/api/create_turno.php +++ b/modules/turnero/api/create_turno.php @@ -24,9 +24,9 @@ if (!in_array($prioCodigo, ['A', 'B', 'C', 'D', 'E', 'F'], true)) { jsonError('prioridad_codigo inválido. Use A, B, C, D, E o F.'); } -$pacienteNombre = substr(trim($datos['paciente_nombre'] ?? ''), 0, 150); -$pacienteCel = preg_replace('/[^0-9+\- ]/', '', $datos['paciente_cel'] ?? ''); -$pacienteCel = substr($pacienteCel, 0, 20); +$pacienteDoc = substr(trim($datos['paciente_nombre'] ?? ''), 0, 150); +$pacienteCel = preg_replace('/[^0-9+\- ]/', '', $datos['paciente_cel'] ?? ''); +$pacienteCel = substr($pacienteCel, 0, 20); // ── Lógica principal (dentro de transacción para evitar race conditions) ── $pdo = db(); @@ -49,6 +49,27 @@ try { $prioridadId = (int) $prioridad['id']; + // Buscar paciente por número de documento en lab_pacientes + $pacienteNombre = $pacienteDoc; + $pacienteId = null; + $pacienteTel = $pacienteCel; // fallback: celular del kiosko + if ($pacienteDoc) { + try { + $stmtP = $pdo->prepare( + 'SELECT id, nombre_completo, telefono FROM lab_pacientes WHERE numero_documento = ? AND is_active = 1 LIMIT 1' + ); + $stmtP->execute([$pacienteDoc]); + $pacRow = $stmtP->fetch(PDO::FETCH_ASSOC); + if ($pacRow) { + $pacienteNombre = $pacRow['nombre_completo']; + $pacienteId = (int) $pacRow['id']; + $pacienteTel = $pacRow['telefono'] ?: $pacienteCel; + } + } catch (\Throwable $e) { + $pacienteNombre = $pacienteDoc; + } + } + // Correlativo independiente por prioridad (A-001, A-002... B-001, B-002... etc.) $numero = siguienteNumeroPorPrioridad($sesionId, $prioridadId); @@ -57,10 +78,10 @@ try { $stmt = $pdo->prepare( 'INSERT INTO turnero_turnos - (sesion_id, numero, codigo, prioridad_id, paciente_nombre, paciente_cel, + (sesion_id, numero, codigo, prioridad_id, paciente_nombre, paciente_cel, paciente_id, estado, creado_at) VALUES - (?, ?, ?, ?, ?, ?, "espera", NOW())' + (?, ?, ?, ?, ?, ?, ?, "espera", NOW())' ); $stmt->execute([ $sesionId, @@ -69,6 +90,7 @@ try { $prioridadId, $pacienteNombre ?: null, $pacienteCel ?: null, + $pacienteId, ]); $turnoId = (int) $pdo->lastInsertId(); @@ -76,6 +98,69 @@ try { notificarSSE($sesionId); + // ── Auto-crear consentimiento principal + enviar WhatsApp ── + try { + $stmtPc = $pdo->prepare( + "SELECT id, nombre FROM lab_formularios WHERE is_principal = 1 AND tipo = 'consentimiento' AND is_active = 1 LIMIT 1" + ); + $stmtPc->execute(); + $principalForm = $stmtPc->fetch(PDO::FETCH_ASSOC); + if ($principalForm) { + $tokenPrincipal = sprintf( + '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', + mt_rand(0, 0xffff), mt_rand(0, 0xffff), + mt_rand(0, 0xffff), + mt_rand(0, 0x0fff) | 0x4000, + mt_rand(0, 0x3fff) | 0x8000, + mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) + ); + $stmtIns = $pdo->prepare( + "INSERT IGNORE INTO turnero_consentimientos (turno_id, formulario_id, token, estado) + VALUES (?, ?, ?, 'pendiente')" + ); + $stmtIns->execute([$turnoId, (int)$principalForm['id'], $tokenPrincipal]); + + // Enviar WhatsApp si tenemos celular + if ($pacienteTel) { + $cel = preg_replace('/[\s\-\.]/', '', $pacienteTel); + if (!str_starts_with($cel, '+')) { + $cel = '+57' . ltrim($cel, '0'); + } + $enlaceFirma = (defined('BASE_URL') ? rtrim(BASE_URL, '/') : '') + . '/ver_formulario_enviado.php?token=' . urlencode($tokenPrincipal); + $nombrePacWA = $pacienteNombre ?: 'Paciente'; + require_once __DIR__ . '/../../../services/WhatsAppService.php'; + try { + $wa = new WhatsAppService(); + $wa->sendTemplateMessage( + $cel, + 'consentimiento_turno', + 'es', + [ + htmlspecialchars($nombrePacWA, ENT_QUOTES), + htmlspecialchars($principalForm['nombre'], ENT_QUOTES), + $codigo, + ], + [$enlaceFirma] + ); + } catch (\Throwable $eTmpl) { + try { + $mensajeTexto = "Hola {$nombrePacWA}, le informamos que para su turno *{$codigo}* " + . "debe firmar el siguiente consentimiento informado:\n\n" + . "*{$principalForm['nombre']}*\n\n" + . "Puede firmarlo en el siguiente enlace:\n{$enlaceFirma}\n\n" + . "Si ya firmó este documento presencial, ignore este mensaje."; + $wa->sendTextMessage($cel, $mensajeTexto); + } catch (\Throwable $eTxt) { + // Silenciar error de WhatsApp, el turno ya se creó + } + } + } + } + } catch (\Throwable $e) { + // Silenciar error — el turno ya se creó exitosamente + } + // Contar posición en la cola $stmt = $pdo->prepare( ' diff --git a/modules/turnero/api/send_consentimiento.php b/modules/turnero/api/send_consentimiento.php index d3bd0b0..cdc2b49 100644 --- a/modules/turnero/api/send_consentimiento.php +++ b/modules/turnero/api/send_consentimiento.php @@ -117,6 +117,28 @@ if ($lugarIdSol > 0) { } } +// 3c. Consentimiento principal de recepción (is_principal) +$principalRow = null; +try { + $stmtPc = $pdo->prepare( + "SELECT id AS formulario_id, nombre AS formulario_nombre + FROM lab_formularios + WHERE is_principal = 1 AND tipo = 'consentimiento' AND is_active = 1 + LIMIT 1" + ); + $stmtPc->execute(); + $principalRow = $stmtPc->fetch(PDO::FETCH_ASSOC); +} catch (\Throwable $e) {} +if ($principalRow) { + $yaExiste = false; + foreach ($formulariosRequeridos as $fr) { + if ((int)$fr['formulario_id'] === (int)$principalRow['formulario_id']) { $yaExiste = true; break; } + } + if (!$yaExiste) { + $formulariosRequeridos[] = $principalRow; + } +} + if (empty($formulariosRequeridos)) { jsonOk(['consentimientos' => [], 'mensaje' => 'Ningún examen ni servicio requiere consentimiento.']); } diff --git a/modules/turnero/api/set_principal_consent.php b/modules/turnero/api/set_principal_consent.php new file mode 100644 index 0000000..944b2be --- /dev/null +++ b/modules/turnero/api/set_principal_consent.php @@ -0,0 +1,45 @@ +prepare( + "SELECT id FROM lab_formularios WHERE id = ? AND tipo = 'consentimiento' AND is_active = 1" + ); + $stmt->execute([$formularioId]); + if (!$stmt->fetch()) { + jsonError('Formulario no encontrado o no es un consentimiento activo.', 404); + } +} + +$pdo->beginTransaction(); +try { + // Quitar principal de todos + $pdo->exec("UPDATE lab_formularios SET is_principal = 0 WHERE is_principal = 1"); + // Marcar el nuevo principal (si aplica) + if ($formularioId) { + $stmt = $pdo->prepare("UPDATE lab_formularios SET is_principal = 1 WHERE id = ?"); + $stmt->execute([$formularioId]); + } + $pdo->commit(); + jsonOk([ + 'formulario_id' => $formularioId, + ], $formularioId ? 'Consentimiento principal actualizado' : 'Consentimiento principal desmarcado'); +} catch (\Throwable $e) { + $pdo->rollBack(); + jsonError('Error al actualizar: ' . $e->getMessage(), 500); +} diff --git a/modules/turnero/views/configuracion.php b/modules/turnero/views/configuracion.php index 0b3ae1f..9fb984a 100644 --- a/modules/turnero/views/configuracion.php +++ b/modules/turnero/views/configuracion.php @@ -58,7 +58,7 @@ try { try { $formulariosCons = $pdo->query( - "SELECT id, nombre FROM lab_formularios WHERE is_active=1 ORDER BY nombre ASC" + "SELECT id, nombre, COALESCE(is_principal,0) AS is_principal FROM lab_formularios WHERE is_active=1 ORDER BY nombre ASC" )->fetchAll(PDO::FETCH_ASSOC); } catch (\Throwable $e) { $_loadErrors[] = 'lab_formularios: ' . $e->getMessage(); } @@ -491,16 +491,37 @@ $tab = $_GET['tab'] ?? 'lugares';
Vista inversa: por consentimiento
+ +'.$e['codigo'].'', $exsVinculados))
@@ -508,6 +529,11 @@ $tab = $_GET['tab'] ?? 'lugares';