prepare( "SELECT t.id, t.codigo, t.paciente_nombre, t.paciente_cel, t.estado, s.id AS solicitud_id, s.paciente_id, s.lugar_id, p.telefono AS pac_telefono, p.telefono AS pac_celular, p.nombre_completo AS pac_nombre FROM turnero_turnos t LEFT JOIN turnero_solicitudes s ON s.turno_id = t.id LEFT JOIN lab_pacientes p ON p.id = s.paciente_id WHERE t.id = ?" ); $stmt->execute([$turnoId]); $turno = $stmt->fetch(PDO::FETCH_ASSOC); if (!$turno) jsonError('Turno no encontrado.', 404); if (!$turno['solicitud_id']) jsonError('El turno no tiene solicitud registrada. Guarde la solicitud primero.', 422); // Determinar celular de contacto (preferencia: paciente → kiosko) $celular = $turno['pac_celular'] ?? $turno['pac_telefono'] ?? $turno['paciente_cel'] ?? null; if (!$celular) { jsonError('El paciente no tiene número de celular registrado. Actualice el paciente antes de enviar.', 422); } // Normalizar celular (quitar espacios / guiones, agregar +57 si no tiene código) $celular = preg_replace('/[\s\-\.]/', '', $celular); if (!str_starts_with($celular, '+')) { // Asumir Colombia si no tiene prefijo internacional $celular = '+57' . ltrim($celular, '0'); } // ── 2. Obtener exámenes de la solicitud ─────────────────────── $stmt = $pdo->prepare( "SELECT exam_tipo_id FROM turnero_examen_items WHERE solicitud_id = ?" ); $stmt->execute([$turno['solicitud_id']]); $examIds = $stmt->fetchAll(PDO::FETCH_COLUMN); if (empty($examIds)) { jsonError('La solicitud no tiene exámenes registrados.', 422); } // ── 3. Obtener formularios configurados para este turno ─────── $stmt = $pdo->prepare( "SELECT tc.formulario_id, f.nombre AS formulario_nombre FROM turnero_consentimientos tc JOIN lab_formularios f ON f.id = tc.formulario_id WHERE tc.turno_id = ? ORDER BY f.nombre ASC" ); $stmt->execute([$turnoId]); $formulariosRequeridos = $stmt->fetchAll(PDO::FETCH_ASSOC); if (empty($formulariosRequeridos)) { jsonOk(['consentimientos' => [], 'mensaje' => 'Este turno no tiene consentimientos requeridos.']); } // ── 4. Generar / recuperar tokens UUID ──────────────────────── function generarUuid(): string { return 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) ); } $pdo->beginTransaction(); $consentimientosResultado = []; $erroresEnvio = []; try { $stmtBuscar = $pdo->prepare( "SELECT id, token, estado FROM turnero_consentimientos WHERE turno_id = ? AND formulario_id = ?" ); $stmtInsertar = $pdo->prepare( "INSERT INTO turnero_consentimientos (turno_id, formulario_id, token, estado) VALUES (?, ?, ?, 'pendiente')" ); $stmtActualizar = $pdo->prepare( "UPDATE turnero_consentimientos SET estado = 'enviado', enviado_at = NOW() WHERE id = ?" ); $wa = new WhatsAppService(); // URL base del sistema (para generar el enlace de firma) $baseUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http') . '://' . ($_SERVER['HTTP_HOST'] ?? 'localhost'); // Si BASE_URL está definida, úsala if (defined('BASE_URL')) { $baseUrl = rtrim(BASE_URL, '/'); } foreach ($formulariosRequeridos as $form) { $formularioId = (int) $form['formulario_id']; $formularioNom = $form['formulario_nombre']; // Buscar si ya existe $stmtBuscar->execute([$turnoId, $formularioId]); $existente = $stmtBuscar->fetch(PDO::FETCH_ASSOC); if ($existente) { $consentId = (int) $existente['id']; $token = $existente['token']; // Si ya está firmado/rechazado, no reenviar if (in_array($existente['estado'], ['firmado', 'rechazado'], true)) { $consentimientosResultado[] = [ 'id' => $consentId, 'formulario_id' => $formularioId, 'formulario_nombre'=> $formularioNom, 'estado' => $existente['estado'], 'enviado' => false, ]; continue; } } else { $token = generarUuid(); $stmtInsertar->execute([$turnoId, $formularioId, $token]); $consentId = (int) $pdo->lastInsertId(); } // ── Construir enlace de firma ────────────────────────── $enlaceFirma = $baseUrl . '/ver_formulario_enviado.php?token=' . urlencode($token); // ── Nombre del paciente para el mensaje ─────────────── $nombrePac = $turno['pac_nombre'] ?? $turno['paciente_nombre'] ?? 'Paciente'; // ── Enviar mensaje WhatsApp ──────────────────────────── // Intentamos enviar con template "consentimiento_turno". // Si el template no existe, enviamos texto plano como fallback. $enviado = false; try { $wa->sendTemplateMessage( $celular, 'consentimiento_turno', 'es', // Parámetros del body: {{1}} = nombre, {{2}} = nombre formulario, {{3}} = código turno [ htmlspecialchars($nombrePac, ENT_QUOTES), htmlspecialchars($formularioNom, ENT_QUOTES), $turno['codigo'], ], // Parámetro del header o botón URL (URL del enlace) [$enlaceFirma] ); $enviado = true; } catch (\Throwable $eTemplate) { // Fallback: enviar mensaje de texto plano try { $mensajeTexto = "Hola {$nombrePac}, le informamos que para su turno *{$turno['codigo']}* " . "debe firmar el siguiente consentimiento informado:\n\n" . "*{$formularioNom}*\n\n" . "Puede firmarlo en el siguiente enlace:\n{$enlaceFirma}\n\n" . "Si ya firmó este documento presencial, ignore este mensaje."; $wa->sendTextMessage($celular, $mensajeTexto); $enviado = true; } catch (\Throwable $eTexto) { $erroresEnvio[] = "Formulario '{$formularioNom}': " . $eTexto->getMessage(); } } if ($enviado) { $stmtActualizar->execute([$consentId]); $estado = 'enviado'; } else { $estado = 'pendiente'; } $consentimientosResultado[] = [ 'id' => $consentId, 'formulario_id' => $formularioId, 'formulario_nombre' => $formularioNom, 'token' => $token, 'estado' => $estado, 'enviado' => $enviado, 'enlace_firma' => $enlaceFirma, ]; } $pdo->commit(); $msgExtra = !empty($erroresEnvio) ? ' (Advertencias: ' . implode('; ', $erroresEnvio) . ')' : ''; jsonOk( ['consentimientos' => $consentimientosResultado], count(array_filter($consentimientosResultado, fn($c) => $c['enviado'])) . ' consentimiento(s) enviado(s) por WhatsApp' . $msgExtra ); } catch (\Throwable $e) { if ($pdo->inTransaction()) $pdo->rollBack(); jsonError('Error al procesar consentimientos: ' . $e->getMessage(), 500); }