diff --git a/agencias.txt b/agencias.txt index a655797..d695f0a 100644 --- a/agencias.txt +++ b/agencias.txt @@ -1,4 +1,33 @@ --- 1. Tabla de Agencias Aduanales +Sistemas -- Administrador General +INSERT INTO [Importaciones_HC].[dbo].[usuarios_sistema] +([nombre], [email], [password_hash], [tipo_usuario], [activo], [creado_en], [dos_factores], [notificaciones], [notificaciones_extra]) +VALUES ( + '4a77Tj9XtOCnvkUrxuxLCg==', -- nombre encriptado + 'QVaqkaWSvUtgAfKN51JNDkoyUiZGVQs3+ARpoGtdD/I=', -- email encriptado + '$2y$10$.8CI5zuJ6MICQRRGqksYjOyuIYnln4xgUKJiQkZdEpLWfvUpg1f9m', -- password hash bcrypt + 'super_admin', 1, GETDATE(), 0, 0, 0 +); + + +CREATE TABLE solicitudes_agencias( + [request_id] [int] IDENTITY(1,1) NOT NULL, + [agencia_name] [varchar](255) NULL, + [rfc] [varchar](100) NULL, + [email] [varchar](255) NULL, + [phone] [varchar](20) NULL, + [direccion] [varchar](100) NULL, + [opinion_file] [varchar](500) NULL, + [admin_name] [varchar](255) NULL, + [admin_email] [varchar](255) NULL, + [request_status] [varchar](20) NULL, + [request_date] [datetime] NULL, + [approval_date] [datetime] NULL, + [approved_by] [int] NULL, + [timestamp_request] [timestamp] NOT NULL +); + + +-- Tabla de Agencias Aduanales CREATE TABLE [dbo].[agencias_aduanales]( [id_agencia] [int] IDENTITY(1,1) NOT NULL, @@ -15,15 +44,33 @@ CREATE TABLE [dbo].[agencias_aduanales]( CONSTRAINT [PK_agencias_aduanales] PRIMARY KEY CLUSTERED ([id_agencia] ASC) ); - -- Agregar foreign key para administrador de agencia ALTER TABLE [dbo].[agencias_aduanales] ADD CONSTRAINT [FK_agencias_administrador] FOREIGN KEY ([id_administrador]) REFERENCES [dbo].[usuarios_sistema]([id_usuario]); +CREATE TABLE [dbo].[credenciales_db_agencias] ( + [id_credencial] INT IDENTITY(1,1) NOT NULL, + [id_agencia] INT NOT NULL, + [db_instance] NVARCHAR(255) NOT NULL, + [db_ip] NVARCHAR(50) NOT NULL, + [db_user] NVARCHAR(100) NOT NULL, + [db_password] NVARCHAR(255) NOT NULL, -- Considera encriptar o manejar de forma segura + [db_port] INT NOT NULL DEFAULT 1433, -- Default SQL Server port + [creado_en] DATETIME NOT NULL DEFAULT GETDATE(), + [actualizado_en] DATETIME NULL, --- 2. Actualizar tipos de usuario válidos + CONSTRAINT [PK_credenciales_db_agencias] PRIMARY KEY CLUSTERED ([id_credencial] ASC), + CONSTRAINT [FK_credenciales_db_agencia] + FOREIGN KEY ([id_agencia]) REFERENCES [dbo].[agencias_aduanales]([id_agencia]) +); + +CREATE UNIQUE INDEX [IX_credenciales_db_agencias_id_agencia] +ON [dbo].[credenciales_db_agencias]([id_agencia]); + + +-- Actualizar tipos de usuario válidos -- Los tipos de usuario ahora serán: @@ -34,11 +81,10 @@ FOREIGN KEY ([id_administrador]) REFERENCES [dbo].[usuarios_sistema]([id_usuario -- Campos de auditoría para usuarios_sistema ALTER TABLE [dbo].[usuarios_sistema] -ADD [actualizado_en] [datetime] NULL, - [creado_por] [int] NULL; +ADD [creado_por] [int] NULL; --- 3. Tabla de relación importador-agencia (many-to-many) +-- Tabla de relación importador-agencia (many-to-many) CREATE TABLE [dbo].[importador_agencia]( [id_relacion] [int] IDENTITY(1,1) NOT NULL, @@ -68,7 +114,7 @@ ON [dbo].[importador_agencia] ([id_importador], [id_agencia]) WHERE [activo] = 1; --- 4. Tabla de relación agente-agencia +-- Tabla de relación agente-agencia CREATE TABLE [dbo].[agente_agencia]( [id_relacion] INT IDENTITY(1,1) NOT NULL, @@ -81,11 +127,15 @@ CREATE TABLE [dbo].[agente_agencia]( CONSTRAINT [FK_agente_agencia_usuario] FOREIGN KEY ([id_agente]) REFERENCES [dbo].[usuarios_sistema]([id_usuario]), CONSTRAINT [FK_agente_agencia_agencia] - FOREIGN KEY ([id_agencia]) REFERENCES [dbo].[agencias_aduanales]([id_agencia]), - CONSTRAINT [UQ_agente_activo] - UNIQUE ([id_agente]) WHERE [activo] = 1 -- evita duplicados activos + FOREIGN KEY ([id_agencia]) REFERENCES [dbo].[agencias_aduanales]([id_agencia]) ); +-- Luego creas el índice filtrado: + +CREATE UNIQUE INDEX [UQ_agente_activo] +ON [dbo].[agente_agencia]([id_agente]) +WHERE [activo] = 1; + -- Índice único para evitar duplicados activos CREATE UNIQUE INDEX [IX_agente_agencia_unique] @@ -93,7 +143,7 @@ ON [dbo].[agente_agencia] ([id_agente], [id_agencia]) WHERE [activo] = 1; --- 5. Tabla para solicitudes de vinculación de importadores +-- Tabla para solicitudes de vinculación de importadores CREATE TABLE [dbo].[solicitudes_vinculacion]( [id_solicitud] [int] IDENTITY(1,1) NOT NULL, @@ -115,25 +165,32 @@ CREATE TABLE [dbo].[solicitudes_vinculacion]( ); --- 6. Tabla para auditoría de cambios de agencia +-- Tabla para auditoría de cambios de agencia -CREATE TABLE [dbo].[historial_agencia]( - [id_historial] [int] IDENTITY(1,1) NOT NULL, - [id_importador] [int] NULL, - [id_agente] [int] NULL, - [id_agencia_anterior] [int] NULL, - [id_agencia_nueva] [int] NULL, - [accion] [varchar](50) NOT NULL, -- 'VINCULACION', 'DESVINCULACION' - [motivo] [nvarchar](500) NULL, +CREATE TABLE [dbo].[bitacora_agencias]( + [id_bitacora] [int] IDENTITY(1,1) NOT NULL, + [id_agencia] [int] NULL, + [nombre] [varchar](100) NULL, + [accion] [varchar](50) NOT NULL, -- 'CREACION', 'DESACTIVACION' [realizado_por] [int] NOT NULL, - [fecha_cambio] [datetime] NOT NULL DEFAULT GETDATE(), - [datos_adicionales] [nvarchar](max) NULL, -- JSON con información adicional - CONSTRAINT [PK_historial_agencia_cambios] PRIMARY KEY CLUSTERED ([id_historial] ASC) + [fecha] [datetime] NOT NULL DEFAULT GETDATE(), + CONSTRAINT [PK_bitacora_agenciaS] PRIMARY KEY CLUSTERED ([id_bitacora] ASC) ); --- Índices adicionales para mejorar rendimiento +ALTER TABLE [dbo].[bitacora_agencias] +ADD CONSTRAINT [FK_bitacora_agencias_agencia] +FOREIGN KEY ([id_agencia]) REFERENCES [dbo].[agencias_aduanales]([id_agencia]); -CREATE INDEX [IX_usuarios_tipo_agencia] ON [dbo].[usuarios_sistema] ([tipo_usuario], [id_agencia]); +ALTER TABLE [dbo].[bitacora_agencias] +ADD CONSTRAINT [FK_bitacora_agencias_administrador] +FOREIGN KEY ([realizado_por]) REFERENCES [dbo].[usuarios_sistema]([id_usuario]); + +ALTER TABLE [dbo].[bitacora_agencias] +ADD CONSTRAINT [CK_bitacora_agencias_accion] +CHECK ([accion] IN ('CREACION', 'DESACTIVACION')); + + +-- Índices adicionales para mejorar rendimiento CREATE INDEX [IX_importador_agencia_importador] ON [dbo].[importador_agencia] ([id_importador]); diff --git a/app/controllers/administrador.php b/app/controllers/administrador.php new file mode 100644 index 0000000..629cf58 --- /dev/null +++ b/app/controllers/administrador.php @@ -0,0 +1,1057 @@ +isSMTP(); + $mail->Host = 'secure.emailsrvr.com'; + $mail->SMTPAuth = true; + $mail->Username = 'noreply@aduanasoft.com.mx'; + $mail->Password = $_ENV['SMTP_PASS']; + $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; + $mail->Port = 587; + + $mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | Sistema Integral para Importadores de Hidrocarburos'); + $mail->addAddress($email); + $mail->CharSet = 'UTF-8'; + $mail->isHTML(true); + $mail->Subject = 'Tu acceso a la plataforma SIIH ha sido autorizado'; + + $mail->Body = " +
+
+
+

¡Bienvenido a SIIH!

+
+
+

Tu registro como importador ha sido aprobado. Aquí tienes tus credenciales de acceso:

+

Correo: $email

+

Contraseña: $password_plain

+

📌 Te recomendamos cambiar tu contraseña una vez que ingreses al sistema.

+

Ir al sistema

+
+
+ © " . date('Y') . " SIIH · Sistema Integral para Importadores de Hidrocarburos +
+
+
"; + + $mail->send(); + } catch (Exception $e) { + error_log("Error al enviar correo: {$mail->ErrorInfo}"); + } + + header("Location: /IMPORTADORES/administrador/aprobarUsuarios?success=approved"); + exit; +} + +function altaUsuarios() +{ + if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'super_admin') { + header('Location: /IMPORTADORES/login'); + exit; + } + + $conn = getConnection(); + + $sql = "SELECT * FROM usuarios_sistema WHERE activo = 1 ORDER BY creado_en DESC"; + $stmt = sqlsrv_query($conn, $sql); + + $usuarios = []; + while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) { + $usuarios[] = $row; + } + + include __DIR__ . '/../../views/admin/alta_usuarios.php'; +} + +function guardar_usuario() +{ + if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'super_admin') { + header("Location: /IMPORTADORES/administrador/altaUsuarios?error=unauthorized"); + exit; + } + + $conn = getConnection(); + + // 1. Capturar y validar datos + $nombre = trim($_POST['nombre'] ?? ''); + $email = trim($_POST['email'] ?? ''); + $password = $_POST['password'] ?? ''; + $tipo = $_POST['tipo_usuario'] ?? ''; + + if (empty($nombre) || empty($email) || empty($password) || empty($tipo)) { + header("Location: /IMPORTADORES/administrador/altaUsuarios?error=invalid_data"); + exit; + } + + if (!in_array($tipo, ['importador', 'super_admin'])) { + header("Location: /IMPORTADORES/administrador/altaUsuarios?error=invalid_user_type"); + exit; + } + + // 2. Encriptar datos sensibles + $nombre_encrypted = encrypt($nombre); + $email_encrypted = encrypt($email); + $password_hash = password_hash($password, PASSWORD_DEFAULT); + + // 3. Validar duplicado por email encriptado + $sqlCheck = "SELECT COUNT(*) AS total FROM usuarios_sistema WHERE email = ?"; + $stmtCheck = sqlsrv_query($conn, $sqlCheck, [$email_encrypted]); + $rowCheck = sqlsrv_fetch_array($stmtCheck, SQLSRV_FETCH_ASSOC); + if ($rowCheck['total'] > 0) { + header("Location: /IMPORTADORES/administrador/altaUsuarios?error=email_exists"); + exit; + } + + // 4. Insertar usuario + $sqlInsert = "INSERT INTO usuarios_sistema (nombre, email, password_hash, tipo_usuario, activo, creado_en, dos_factores, creado_por) + VALUES (?, ?, ?, ?, 1, GETDATE(), 0, ?)"; + $params = [$nombre_encrypted, $email_encrypted, $password_hash, $tipo, $_SESSION['usuario_id']]; + + $stmtInsert = sqlsrv_query($conn, $sqlInsert, $params); + + if ($stmtInsert === false) { + header("Location: /IMPORTADORES/administrador/altaUsuarios?error=save_failed"); + exit; + } + + // 5. Obtener ID del nuevo usuario (para posibles tablas relacionadas) + $sqlGetId = "SELECT SCOPE_IDENTITY() AS id_usuario"; + $stmtGetId = sqlsrv_query($conn, $sqlGetId); + $idUsuarioRow = sqlsrv_fetch_array($stmtGetId, SQLSRV_FETCH_ASSOC); + $id_usuario = $idUsuarioRow['id_usuario'] ?? null; + + // 6. Enviar correo de bienvenida + $mail = new PHPMailer(true); + try { + $mail->isSMTP(); + $mail->Host = 'secure.emailsrvr.com'; + $mail->SMTPAuth = true; + $mail->Username = 'noreply@aduanasoft.com.mx'; + $mail->Password = $_ENV['SMTP_PASS']; + $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; + $mail->Port = 587; + + $mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | Sistema Integral para Importadores de Hidrocarburos'); + $mail->addAddress($email); // Email sin encriptar para el envío + $mail->CharSet = 'UTF-8'; + $mail->isHTML(true); + $mail->Subject = 'Tu acceso a la plataforma SIIH ha sido creado'; + + // Determinar el tipo de usuario para el mensaje + $tipoUsuarioTexto = match($tipo) { + 'importador' => 'importador', + 'super_admin' => 'administrador' + }; + + $mail->Body = " +
+
+
+

¡Bienvenido a SIIH!

+
+
+

Hola $nombre,

+

Se ha creado tu cuenta en la plataforma SIIH con el rol de $tipoUsuarioTexto.

+

Aquí tienes tus credenciales de acceso:

+
+

Correo: $email

+

Contraseña: $password

+
+

📌 Importante: Te recomendamos cambiar tu contraseña una vez que ingreses al sistema por primera vez.

+

+ + Acceder al Sistema + +

+
+
+ © " . date('Y') . " SIIH · Sistema Integral para Importadores de Hidrocarburos +
+
+
"; + + $mail->send(); + + // Log de éxito (opcional) + error_log("Correo de bienvenida enviado exitosamente a: $email"); + + } catch (Exception $e) { + // Log del error pero no detener el proceso + error_log("Error al enviar correo de bienvenida a $email: {$mail->ErrorInfo}"); + + // Opcional: Puedes mostrar una advertencia al usuario + $_SESSION['mensaje_advertencia'] = "Usuario creado exitosamente, pero no se pudo enviar el correo de bienvenida."; + } + + // 7. Redirigir con mensaje de éxito + $_SESSION['mensaje_exito'] = "Usuario creado exitosamente. Se ha enviado un correo con las credenciales de acceso."; + header("Location: /IMPORTADORES/administrador/altaUsuarios?success=created"); + exit; +} + +function usuariosInactivos() +{ + if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'super_admin') { + header('Location: /IMPORTADORES/login'); + exit; + } + + $conn = getConnection(); + $sql = "SELECT * FROM usuarios_sistema WHERE activo = 0 ORDER BY creado_en DESC"; + $stmt = sqlsrv_query($conn, $sql); + + $usuarios = []; + while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) { + $usuarios[] = $row; + } + + include __DIR__ . '/../../views/admin/usuarios_inactivos.php'; +} + +/** AGENCIAS **/ +function aprobarAgencias() +{ + if (!($_SESSION['usuario_id'] ?? false) || $_SESSION['tipo_usuario'] !== 'super_admin') { + header('Location: /IMPORTADORES/login'); + exit; + } + + $conn = getConnection(); + + $sql = "SELECT request_id, agencia_name, rfc, email, phone, request_date, opinion_file + FROM solicitudes_agencias + WHERE request_status = 'pending' + ORDER BY request_date DESC"; + + $stmt = sqlsrv_query($conn, $sql); + $solicitudes = []; + while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) { + $solicitudes[] = $row; + } + + include __DIR__ . '/../../views/admin/aprobar_agencias.php'; +} + +function aprobar_agencia() +{ + $conn = getConnection(); + $id = $_GET['id'] ?? null; + $usuario_id = $_SESSION['usuario_id']; + + if (!$id || !is_numeric($id)) { + die("❌ ID inválido."); + } + + // Obtener la solicitud + $sql = "SELECT * FROM solicitudes_agencias WHERE request_id = ?"; + $stmt = sqlsrv_query($conn, $sql, [$id]); + $solicitud = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC); + + if (!$solicitud) { + die("❌ Solicitud no encontrada."); + } + + // Validar que no haya sido aprobada ya + if ($solicitud['request_status'] === 'approved') { + die("⚠️ Esta solicitud ya fue aprobada."); + } + + // Preparar datos + $nombre = decrypt($solicitud['agencia_name']); + $email = $solicitud['email']; + $rfc = decrypt($solicitud['rfc']); + $phone = $solicitud['phone']; + $direccion = $solicitud['direccion']; + + // Preparar datos de administrador de agencia + $admin_name = decrypt($solicitud['admin_name']); + $admin_email = $solicitud['admin_email']; + $tipo = 'admin_agencia'; + + // Generar contraseña aleatoria + $password_plain = bin2hex(random_bytes(5)); + $password_hash = password_hash($password_plain, PASSWORD_DEFAULT); + + // Encriptar datos sensibles + $nombre_encrypt = encrypt($nombre); + $email_encrypt = encrypt($email); + $admin_name_encrypt = encrypt($admin_name); + $admin_email_encrypt = encrypt($admin_email); + + // Insertar en agencias_aduanales + $sqlInsert = "INSERT INTO agencias_aduanales (nombre_agencia, rfc_agencia, direccion, telefono, email, creado_en, creado_por) + VALUES (?, ?, ?, ?, ?, GETDATE()), ?"; + $stmtInsert = sqlsrv_query($conn, $sqlInsert, [ + $nombre_encrypt, $rfc, $direccion, $phone, $email_encrypt, $usuario_id + ]); + + if (!$stmtInsert) { + die("❌ Error al crear agencia: " . print_r(sqlsrv_errors(), true)); + } + + // Obtener ID de la agencia recién creada + $sqlGetAgenciaId = "SELECT SCOPE_IDENTITY() AS id_agencia"; + $stmtGetAgenciaId = sqlsrv_query($conn, $sqlGetAgenciaId); + $agenciaIdRow = sqlsrv_fetch_array($stmtGetAgenciaId, SQLSRV_FETCH_ASSOC); + $id_agencia = $idUsuarioRow['id_agencia'] ?? null; + + if (!$id_agencia) { + die("❌ No se pudo obtener el ID de la agencia recién creada."); + } + + // Insertar en usuarios_sistema + $sqlInsert2 = "INSERT INTO usuarios_sistema (nombre, email, password_hash, tipo_usuario, activo, creado_en, dos factores, creado_por) + VALUES (?, ?, ?, ?, 1, GETDATE()), 0, ?"; + $stmtInsert2 = sqlsrv_query($conn, $sqlInsert, [ + $admin_name_encrypt, $admin_email_encrypt, $password_hash, $tipo, $usuario_id + ]); + + if (!$stmtInsert2) { + die("❌ Error al crear usuario administrador: " . print_r(sqlsrv_errors(), true)); + } + + // Obtener ID del nuevo usuario + $sqlGetId = "SELECT SCOPE_IDENTITY() AS id_usuario"; + $stmtGetId = sqlsrv_query($conn, $sqlGetId); + $idUsuarioRow = sqlsrv_fetch_array($stmtGetId, SQLSRV_FETCH_ASSOC); + $id_usuario = $idUsuarioRow['id_usuario'] ?? null; + + if (!$id_usuario) { + die("❌ No se pudo obtener el ID del usuario recién creado."); + } + + // Actualizar agencia con el ID del administrador + $sqlUpdateAgencia = "UPDATE agencias_aduanales SET id_administrador = ? WHERE id_agencia = ?"; + $stmtUpdateAgencia = sqlsrv_query($conn, $sqlUpdateAgencia, [$id_usuario, $id_agencia]); + + if (!$stmtUpdateAgencia) { + die("❌ Error al actualizar agencia con administrador: " . print_r(sqlsrv_errors(), true)); + } + + // Insertar en informacion_general + $sqlInfo = "INSERT INTO informacion_general + (id_usuario, nombre, correo) + VALUES (?, ?, ?)"; + $stmtInfo = sqlsrv_query($conn, $sqlInfo, [ + $id_usuario, $admin_name, $admin_email + ]); + + if (!$stmtInfo) { + die("❌ Error al insertar información general: " . print_r(sqlsrv_errors(), true)); + } + + // Actualizar solicitud + $sqlUpdate = "UPDATE solicitudes_agencias + SET request_status = 'approved', approval_date = GETDATE(), approved_by = ? + WHERE request_id = ?"; + $stmtUpdate = sqlsrv_query($conn, $sqlUpdate, [$usuario_id, $id]); + + if (!$stmtUpdate) { + die("❌ Error al actualizar solicitud: " . print_r(sqlsrv_errors(), true)); + } + + // Enviar correo a la agencia + $mail = new PHPMailer(true); + try { + $mail->isSMTP(); + $mail->Host = 'secure.emailsrvr.com'; + $mail->SMTPAuth = true; + $mail->Username = 'noreply@aduanasoft.com.mx'; + $mail->Password = $_ENV['SMTP_PASS']; + $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; + $mail->Port = 587; + + $mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | Sistema Integral para Importadores de Hidrocarburos'); + $mail->addAddress($admin_email); + $mail->CharSet = 'UTF-8'; + $mail->isHTML(true); + $mail->Subject = 'Te asignaron como administrador de agencia en SIIH'; + + $mail->Body = " +
+
+
+

¡Bienvenido a SIIH!

+
+
+

Te asignaron como administrador de la agencia $nombre

+

Correo: $admin_email

+

Contraseña: $password_plain

+

📌 Te recomendamos cambiar tu contraseña una vez que ingreses al sistema.

+

Ir al sistema

+
+
+ © " . date('Y') . " SIIH · Sistema Integral para Importadores de Hidrocarburos +
+
+
"; + + $mail->send(); + + } catch (Exception $e) { + error_log("Error al enviar correo: {$mail->ErrorInfo}"); + } + + header("Location: /IMPORTADORES/administrador/agenciasActivas"); + exit; +} + +function altaAgencias() +{ + if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'super_admin') { + header('Location: /IMPORTADORES/login'); + exit; + } + + $conn = getConnection(); + $sql = "SELECT * FROM agencias_aduanales ORDER BY creado_en DESC"; + $stmt = sqlsrv_query($conn, $sql); + + $agencias = []; + while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) { + $agencias[] = $row; + } + + include __DIR__ . '/../../views/admin/alta_agencias.php'; +} + +function guardar_agencia() +{ + if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'super_admin') { + header("Location: /IMPORTADORES/administrador/altaAgencias?error=unauthorized"); + exit; + } + + $conn = getConnection(); + + try { + if (!sqlsrv_begin_transaction($conn)) { + error_log("ERROR: No se pudo iniciar la transacción"); + throw new Exception("Error al iniciar transacción"); + } + + error_log("Transacción iniciada correctamente"); + + // 1️⃣ Captura y validación de datos + $nombre_agencia = trim($_POST['nombre_agencia'] ?? ''); + $rfc_agencia = trim($_POST['rfc_agencia'] ?? ''); + $correo_agencia = trim($_POST['correo'] ?? ''); + $telefono = trim($_POST['telefono'] ?? ''); + $direccion = trim($_POST['direccion'] ?? ''); + $nombre_admin = trim($_POST['nombre_admin'] ?? ''); + $correo_admin = trim($_POST['correo_admin'] ?? ''); + + error_log("Datos recibidos:"); + error_log("- Nombre agencia: " . $nombre_agencia); + error_log("- RFC: " . $rfc_agencia); + error_log("- Correo agencia: " . $correo_agencia); + + if (empty($nombre_agencia) || empty($rfc_agencia) || empty($correo_agencia) || + empty($telefono) || empty($direccion) || empty($nombre_admin) || empty($correo_admin)) { + error_log("ERROR: Datos incompletos"); + throw new Exception("Datos incompletos"); + } + + // 2️⃣ Verificar función de encriptación + if (!function_exists('encrypt')) { + error_log("ERROR: Función encrypt no existe"); + throw new Exception("Función de encriptación no disponible"); + } + + // Encriptar datos + $nombre_agencia_enc = encrypt($nombre_agencia); + $correo_agencia_enc = encrypt($correo_agencia); + $nombre_admin_enc = encrypt($nombre_admin); + $correo_admin_enc = encrypt($correo_admin); + + error_log("Datos encriptados correctamente"); + + // 3️⃣ CREAR USUARIO ADMINISTRADOR Y OBTENER SU ID EN UNA SOLA CONSULTA + $password_plain = bin2hex(random_bytes(5)); + $password_hash = password_hash($password_plain, PASSWORD_DEFAULT); + + error_log("Password generado: " . $password_plain); + + // ✅ SOLUCIÓN: INSERT con OUTPUT para obtener el ID inmediatamente + $sqlInsertAdmin = " + INSERT INTO usuarios_sistema (nombre, email, password_hash, tipo_usuario, activo, creado_en, dos_factores, creado_por) + OUTPUT INSERTED.id_usuario + VALUES (?, ?, ?, 'admin_agencia', 1, GETDATE(), 0, ?); + "; + + $paramsAdmin = [$nombre_admin_enc, $correo_admin_enc, $password_hash, $_SESSION['usuario_id']]; + $stmtAdmin = sqlsrv_query($conn, $sqlInsertAdmin, $paramsAdmin); + + if (!$stmtAdmin) { + $errors = sqlsrv_errors(); + error_log("ERROR SQL Admin: " . print_r($errors, true)); + throw new Exception("Error al crear administrador: " . print_r($errors, true)); + } + + // Obtener el ID directamente del resultado del OUTPUT + $rowAdmin = sqlsrv_fetch_array($stmtAdmin, SQLSRV_FETCH_ASSOC); + $id_admin = $rowAdmin['id_usuario'] ?? null; + + if (!$id_admin) { + error_log("ERROR: ID de administrador es null"); + throw new Exception("No se pudo obtener ID de administrador"); + } + + error_log("Usuario administrador creado exitosamente con ID: " . $id_admin); + + // 4️⃣ CREAR AGENCIA Y OBTENER SU ID + $sqlInsertAgencia = " + INSERT INTO agencias_aduanales (nombre_agencia, rfc_agencia, direccion, telefono, email, id_administrador, activo, creado_en, creado_por) + OUTPUT INSERTED.id_agencia + VALUES (?, ?, ?, ?, ?, ?, 1, GETDATE(), ?) + "; + $paramsAgencia = [$nombre_agencia_enc, $rfc_agencia, $direccion, $telefono, $correo_agencia_enc, $id_admin, $_SESSION['usuario_id']]; + + error_log("Ejecutando SQL Agencia con ID admin: " . $id_admin); + $stmtAgencia = sqlsrv_query($conn, $sqlInsertAgencia, $paramsAgencia); + + if (!$stmtAgencia) { + $errors = sqlsrv_errors(); + error_log("ERROR SQL Agencia: " . print_r($errors, true)); + throw new Exception("Error al crear agencia: " . print_r($errors, true)); + } + + // Obtener el ID de la agencia directamente + $rowAgencia = sqlsrv_fetch_array($stmtAgencia, SQLSRV_FETCH_ASSOC); + $id_agencia = $rowAgencia['id_agencia'] ?? null; + + if (!$id_agencia) { + error_log("ERROR: ID de agencia es null"); + throw new Exception("No se pudo obtener ID de agencia"); + } + + error_log("Agencia creada exitosamente con ID: " . $id_agencia); + + // 5️⃣ CREAR RELACIÓN AGENTE-AGENCIA + $sqlInsertRelacion = "INSERT INTO agente_agencia (id_agente, id_agencia, fecha_asignacion, activo, asignado_por) + VALUES (?, ?, GETDATE(), 1, ?)"; + $paramsRelacion = [$id_admin, $id_agencia, $_SESSION['usuario_id']]; + + error_log("Creando relación agente-agencia"); + $stmtRelacion = sqlsrv_query($conn, $sqlInsertRelacion, $paramsRelacion); + + if (!$stmtRelacion) { + $errors = sqlsrv_errors(); + error_log("ERROR SQL Relación: " . print_r($errors, true)); + throw new Exception("Error al crear relación agente-agencia: " . print_r($errors, true)); + } + + error_log("Relación agente-agencia creada exitosamente"); + + // 6️⃣ CONFIRMAR TRANSACCIÓN ✅ + if (!sqlsrv_commit($conn)) { + error_log("ERROR: No se pudo confirmar la transacción"); + throw new Exception("Error al confirmar transacción"); + } + + error_log("Transacción confirmada exitosamente"); + + // 7️⃣ Enviar correo (DESPUÉS de confirmar transacción) + if (class_exists('PHPMailer\PHPMailer\PHPMailer')) { + $mail = new PHPMailer(true); + try { + $mail->isSMTP(); + $mail->Host = 'secure.emailsrvr.com'; + $mail->SMTPAuth = true; + $mail->Username = 'noreply@aduanasoft.com.mx'; + $mail->Password = $_ENV['SMTP_PASS'] ?? ''; + $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; + $mail->Port = 587; + + $mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | Sistema Integral para Importadores de Hidrocarburos'); + $mail->addAddress($correo_admin); + $mail->CharSet = 'UTF-8'; + $mail->isHTML(true); + $mail->Subject = 'Te asignaron como administrador de una nueva agencia en SIIH'; + + $mail->Body = " +
+
+
+

¡Bienvenido a SIIH!

+
+
+

Has sido registrado como Administrador de la agencia {$nombre_agencia}.

+

Correo de acceso: {$correo_admin}

+

Contraseña temporal: {$password_plain}

+

📌 Te recomendamos cambiar tu contraseña al primer inicio de sesión.

+

Ir al sistema

+
+
+ © " . date('Y') . " SIIH · Sistema Integral para Importadores de Hidrocarburos +
+
+
"; + + $mail->send(); + error_log("Correo enviado exitosamente a: " . $correo_admin); + + } catch (Exception $e) { + error_log("Error al enviar correo: " . $e->getMessage()); + // No es crítico, continúa + } + } + + // 8️⃣ Redirigir con éxito + error_log("=== FIN EXITOSO guardar_agencia ==="); + header("Location: /IMPORTADORES/administrador/altaAgencias?success=created"); + exit; + + } catch (Exception $e) { + error_log("ERROR CAPTURADO: " . $e->getMessage()); + if (!sqlsrv_rollback($conn)) { + error_log("ERROR: No se pudo revertir la transacción"); + } else { + error_log("Transacción revertida exitosamente"); + } + + error_log("=== FIN CON ERROR guardar_agencia ==="); + header("Location: /IMPORTADORES/administrador/altaAgencias?error=save_failed"); + exit; + } finally { + if ($conn) { + sqlsrv_close($conn); + error_log("Conexión cerrada"); + } + } +} + +function agenciasActivas() +{ + if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'super_admin') { + header('Location: /IMPORTADORES/login'); + exit; + } + + $conn = getConnection(); + $sql = "SELECT * FROM agencias_aduanales ORDER BY creado_en DESC"; + $stmt = sqlsrv_query($conn, $sql); + + $agencias = []; + while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) { + $agencias[] = $row; + } + + include __DIR__ . '/../../views/admin/agencias_activas.php'; +} + + +function suspender() +{ + if (!($_SESSION['usuario_id'] ?? false)) { + header("Location: /IMPORTADORES/administrador/altaUsuarios?error=unauthorized"); + exit; + } + + $conn = getConnection(); + + $id = $_GET['id'] ?? null; + + if (!$id || !is_numeric($id)) { + header("Location: /IMPORTADORES/administrador/altaUsuarios?error=invalid_id"); + exit; + } + + // Obtener el estado actual + $sqlEstado = "SELECT activo FROM usuarios_sistema WHERE id_usuario = ?"; + $stmtEstado = sqlsrv_query($conn, $sqlEstado, [$id]); + + if (!$stmtEstado || !($row = sqlsrv_fetch_array($stmtEstado, SQLSRV_FETCH_ASSOC))) { + header("Location: /IMPORTADORES/administrador/altaUsuarios?error=user_not_found"); + exit; + } + + $nuevoEstado = $row['activo'] ? 0 : 1; + + // Actualizar estado + $sqlUpdate = "UPDATE usuarios_sistema SET activo = ? WHERE id_usuario = ?"; + $stmtUpdate = sqlsrv_query($conn, $sqlUpdate, [$nuevoEstado, $id]); + + if (!$stmtUpdate) { + header("Location: /IMPORTADORES/administrador/altaUsuarios?error=update_failed"); + exit; + } + + // Determinar el tipo de cambio para el mensaje + $accion = $nuevoEstado ? 'activated' : 'deactivated'; + + header("Location: /IMPORTADORES/administrador/altaUsuarios?success=status_$accion"); + exit; +} + +function activar() +{ + if (!($_SESSION['tipo_usuario'] ?? false)) { + header("Location: /IMPORTADORES/administrador/usuariosInactivos?error=unauthorized"); + exit; + } + + $conn = getConnection(); + + $id = $_GET['id'] ?? null; + + if (!$id || !is_numeric($id)) { + header("Location: /IMPORTADORES/administrador/usuariosInactivos?error=invalid_id"); + exit; + } + + // Obtener el estado actual + $sqlEstado = "SELECT activo FROM usuarios_sistema WHERE id_usuario = ?"; + $stmtEstado = sqlsrv_query($conn, $sqlEstado, [$id]); + + if (!$stmtEstado || !($row = sqlsrv_fetch_array($stmtEstado, SQLSRV_FETCH_ASSOC))) { + header("Location: /IMPORTADORES/administrador/usuariosInactivos?error=user_not_found"); + exit; + } + + $nuevoEstado = $row['activo'] ? 0 : 1; + + // Actualizar estado + $sqlUpdate = "UPDATE usuarios_sistema SET activo = ? WHERE id_usuario = ?"; + $stmtUpdate = sqlsrv_query($conn, $sqlUpdate, [$nuevoEstado, $id]); + + if (!$stmtUpdate) { + header("Location: /IMPORTADORES/administrador/usuariosInactivos?error=update_failed"); + exit; + } + + // Determinar el tipo de cambio para el mensaje + $accion = $nuevoEstado ? 'activated' : 'deactivated'; + + header("Location: /IMPORTADORES/administrador/usuariosInactivos?success=status_$accion"); + exit; +} + +function reset_password() +{ + if (!($_SESSION['tipo_usuario'] ?? false)) { + header("Location: /IMPORTADORES/administrador/usuariosInactivos?error=unauthorized"); + exit; + } + + $conn = getConnection(); + + $id = $_GET['id'] ?? null; + + if (!$id || !is_numeric($id)) { + header("Location: /IMPORTADORES/administrador/usuariosInactivos?error=invalid_id"); + exit; + } + + // 🔐 Generar contraseña aleatoria de 10 caracteres + $randomPassword = bin2hex(random_bytes(5)); // genera algo como 'a8c4f1b92d' + $passwordHash = password_hash($randomPassword, PASSWORD_DEFAULT); + + $sqlEmail = "SELECT email FROM usuarios_sistema WHERE id_usuario = ?"; + $stmtEmail = sqlsrv_query($conn, $sqlEmail, [$id]); + $row = sqlsrv_fetch_array($stmtEmail, SQLSRV_FETCH_ASSOC); + + $sql = "UPDATE usuarios_sistema SET password_hash = ? WHERE id_usuario = ?"; + $stmt = sqlsrv_query($conn, $sql, [$passwordHash, $id]); + + if (!$stmt) { + header("Location: /IMPORTADORES/administrador/usuariosInactivos?error=update_failed"); + exit; + } + + enviar_password_directamente($row['email'], $randomPassword); + + // 🔁 Redirigir pasando la contraseña como parámetro temporal (solo visible para el admin) + header("Location: /IMPORTADORES/administrador/usuariosInactivos?success=update_success"); + exit; +} + +function enviar_password_directamente($email_encrypted, $pass) { + $email = decrypt($email_encrypted); + + $mail = new PHPMailer(true); + + try { + $mail->isSMTP(); + $mail->Host = 'secure.emailsrvr.com'; + $mail->SMTPAuth = true; + $mail->Username = 'noreply@aduanasoft.com.mx'; + $mail->Password = $_ENV['SMTP_PASS']; + $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; + $mail->Port = 587; + + $mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | AduanaSoft'); + $mail->addAddress($email); + + $mail->isHTML(true); + $mail->Subject = 'Nueva Contraseña de Acceso Temporal'; + $mail->CharSet = 'UTF-8'; + $logoURL = 'http://siih.aduanasoft.com/IMPORTADORES/public/assets/img/logo_siih.png'; + $mail->Body = " +
+
+
+ Logo SIIH +

Contraseña restablecida

+
+
+

Tu contraseña de acceso al SIIH ha sido restablecida por el administrador.

+

+ Nueva contraseña:
+ $pass +

+

Por favor, cambia esta contraseña una vez que inicies sesión.

+
+
+ © " . date('Y') . " SIIH · Sistema Integral para Importadores de Hidrocarburos +
+
+
"; + + $mail->send(); + } catch (Exception $e) { + error_log("Error al enviar correo: {$mail->ErrorInfo}"); + } +} + +function suspenderAgencia() +{ + if (!($_SESSION['usuario_id'] ?? false)) { + header("Location: /IMPORTADORES/administrador/agenciasActivas?error=unauthorized"); + exit; + } + + $conn = getConnection(); + + $id = $_GET['id'] ?? null; + + if (!$id || !is_numeric($id)) { + header("Location: /IMPORTADORES/administrador/agenciasActivas?error=invalid_id"); + exit; + } + + // Obtener el estado actual + $sqlEstado = "SELECT activo FROM agencias_aduanles WHERE id_usuario = ?"; + $stmtEstado = sqlsrv_query($conn, $sqlEstado, [$id]); + + if (!$stmtEstado || !($row = sqlsrv_fetch_array($stmtEstado, SQLSRV_FETCH_ASSOC))) { + header("Location: /IMPORTADORES/administrador/agenciasActivas?error=agencia_not_found"); + exit; + } + + $nuevoEstado = $row['activo'] ? 0 : 1; + + // Actualizar estado + $sqlUpdate = "UPDATE agencias_aduanles SET activo = ? WHERE id_usuario = ?"; + $stmtUpdate = sqlsrv_query($conn, $sqlUpdate, [$nuevoEstado, $id]); + + if (!$stmtUpdate) { + header("Location: /IMPORTADORES/administrador/agenciasActivas?error=update_failed"); + exit; + } + + // Determinar el tipo de cambio para el mensaje + $accion = $nuevoEstado ? 'activated' : 'deactivated'; + + header("Location: /IMPORTADORES/administrador/agenciasActivas?success=status_$accion"); + exit; +} + + +function bitacoraLogin() +{ + if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'super_admin') { + header('Location: /IMPORTADORES/login'); + exit; + } + + $conn = getConnection(); + $sql = "SELECT * FROM bitacora_login ORDER BY fecha DESC"; + $stmt = sqlsrv_query($conn, $sql); + + $registros = []; + while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) { + $registros[] = $row; + } + + include __DIR__ . '/../../views/admin/bitacora_login.php'; +} + +function bitacoraUsuarios() +{ + if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'super_admin') { + header('Location: /IMPORTADORES/login'); + exit; + } + + $conn = getConnection(); + $sql = "SELECT * FROM bitacora_usuarios ORDER BY fecha DESC"; + $stmt = sqlsrv_query($conn, $sql); + + $registros = []; + while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) { + $registros[] = $row; + } + + include __DIR__ . '/../../views/admin/bitacora_usuarios.php'; +} + +function configuracion() +{ + $conn = getConnection(); + + $id_usuario = $_SESSION['usuario_id'] ?? null; + + if (!$id_usuario) { + die("ID de usuario no disponible en la sesión."); + } + + $sql = "SELECT * FROM informacion_general WHERE id_usuario = ?"; + $params = [$id_usuario]; + + $stmt = sqlsrv_query($conn, $sql, $params); + + if ($stmt === false) { + die(print_r(sqlsrv_errors(), true)); + } + + $datos = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC); + + include __DIR__ . '/../../views/configuracion/dashboard_configuracion.php'; +} \ No newline at end of file diff --git a/app/controllers/agencias.php b/app/controllers/agencias.php index 7072f0b..42a4c98 100644 --- a/app/controllers/agencias.php +++ b/app/controllers/agencias.php @@ -14,32 +14,206 @@ loadEnv(); function dashboard() { - if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'agente_aduanal') { + if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'admin_agencia') { header("Location: /IMPORTADORES/login"); exit; } - $nombreAgencia = $_SESSION['nombre_agencia']; - include __DIR__ . '/../../views/agencias/dashboard_agencias.php'; } +function solicitudesVinculacion() +{ + include __DIR__ . '/../../views/agencias/solicitudes_vinculacion.php'; +} + function alta() { - include __DIR__ . '/../../views/agencias/.php'; + if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'admin_agencia') { + header('Location: /IMPORTADORES/login'); + exit; + } + + include __DIR__ . '/../../views/agencias/alta_agentes.php'; } function guardarAgente() { + if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'admin_agencia') { + header("Location: /IMPORTADORES/agencias/alta?error=unauthorized"); + exit; + } -} + $conn = getConnection(); -function activos() -{ - include __DIR__ . '/../../views/agencias/usuarios_activos.php'; -} + try { + if (!sqlsrv_begin_transaction($conn)) { + error_log("ERROR: No se pudo iniciar la transacción"); + throw new Exception("Error al iniciar transacción"); + } + + error_log("Transacción iniciada correctamente"); -function bitacora() -{ - include __DIR__ . '/../../views/agencias/dashboard_agencias.php'; + // 1. Capturar y validar datos + $nombre = trim($_POST['nombre'] ?? ''); + $email = trim($_POST['email'] ?? ''); + $password = $_POST['password'] ?? ''; + $tipo = $_POST['tipo_usuario'] ?? ''; + + if (empty($nombre) || empty($email) || empty($password) || empty($tipo)) { + header("Location: /IMPORTADORES/agencias/alta?error=invalid_data"); + exit; + } + + if ($tipo !== 'agente_aduanal') { + error_log("DEBUG 5: Tipo de usuario inválido: $tipo"); + header("Location: /IMPORTADORES/agencias/alta?error=invalid_user_type"); + exit; + } + + // 2. Encriptar datos sensibles + $nombre_encrypted = encrypt($nombre); + $email_encrypted = encrypt($email); + $password_hash = password_hash($password, PASSWORD_DEFAULT); + + // 3. Validar duplicado por email encriptado + $sqlCheck = "SELECT COUNT(*) AS total FROM usuarios_sistema WHERE email = ?"; + $stmtCheck = sqlsrv_query($conn, $sqlCheck, [$email_encrypted]); + $rowCheck = sqlsrv_fetch_array($stmtCheck, SQLSRV_FETCH_ASSOC); + if ($rowCheck['total'] > 0) { + header("Location: /IMPORTADORES/agencias/alta?error=email_exists"); + exit; + } + + // 4. Insertar usuario + $sqlInsert = " + INSERT INTO usuarios_sistema (nombre, email, password_hash, tipo_usuario, activo, creado_en, dos_factores, creado_por) + OUTPUT INSERTED.id_usuario + VALUES (?, ?, ?, ?, 1, GETDATE(), 0, ?)"; + $params = [$nombre_encrypted, $email_encrypted, $password_hash, $tipo, $_SESSION['usuario_id']]; + $stmtInsert = sqlsrv_query($conn, $sqlInsert, $params); + + if ($stmtInsert === false) { + error_log("ERROR: Fallo al insertar usuario"); + error_log(print_r(sqlsrv_errors(), true)); + throw new Exception("Error al insertar usuario"); + } + + $idUsuarioRow = sqlsrv_fetch_array($stmtInsert, SQLSRV_FETCH_ASSOC); + $id_usuario = $idUsuarioRow['id_usuario'] ?? null; + + if (!$id_usuario) { + error_log("ERROR: No se obtuvo id_usuario tras el insert"); + throw new Exception("Error al obtener ID del nuevo usuario"); + } + + // 6. Obtener la agencia del administrador actual + $sqlAgencia = "SELECT id_agencia FROM agencias_aduanales WHERE id_administrador = ?"; + $stmtAgencia = sqlsrv_query($conn, $sqlAgencia, [$_SESSION['usuario_id']]); + $agenciaRow = sqlsrv_fetch_array($stmtAgencia, SQLSRV_FETCH_ASSOC); + $id_agencia = $agenciaRow['id_agencia'] ?? null; + + if (!$id_agencia) { + error_log("ERROR: No se encontró agencia para el admin_agencia con id_usuario=" . $_SESSION['usuario_id']); + throw new Exception("No se encontró agencia asociada al administrador"); + } + + // 7. Crear la relación agente-agencia + $sqlRelacion = "INSERT INTO agente_agencia (id_agente, id_agencia, fecha_asignacion, activo, asignado_por) + VALUES (?, ?, GETDATE(), 1, ?)"; + $paramsRelacion = [$id_usuario, $id_agencia, $_SESSION['usuario_id']]; + $stmtRelacion = sqlsrv_query($conn, $sqlRelacion, $paramsRelacion); + + if ($stmtRelacion === false) { + error_log("ERROR: No se pudo insertar la relación agente-agencia"); + error_log(print_r(sqlsrv_errors(), true)); + throw new Exception("Error al guardar relación agente-agencia"); + } + + // 8. CONFIRMAR TRANSACCIÓN ✅ + if (!sqlsrv_commit($conn)) { + error_log("ERROR: No se pudo confirmar la transacción"); + throw new Exception("Error al confirmar transacción"); + } + + // 9. Enviar correo de bienvenida + if (class_exists('PHPMailer\PHPMailer\PHPMailer')) { + $mail = new PHPMailer(true); + try { + $mail->isSMTP(); + $mail->Host = 'secure.emailsrvr.com'; + $mail->SMTPAuth = true; + $mail->Username = 'noreply@aduanasoft.com.mx'; + $mail->Password = $_ENV['SMTP_PASS'] ?? ''; + $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; + $mail->Port = 587; + + $mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | Sistema Integral para Importadores de Hidrocarburos'); + $mail->addAddress($email); // Email sin encriptar para el envío + $mail->CharSet = 'UTF-8'; + $mail->isHTML(true); + $mail->Subject = 'Tu acceso a la plataforma SIIH ha sido creado'; + + // Determinar el tipo de usuario para el mensaje + $tipoUsuarioTexto = match($tipo) { + 'agente_aduanal' => 'agente aduanal', + default => 'usuario' + }; + + $mail->Body = " +
+
+
+

¡Bienvenido a SIIH!

+
+
+

Hola $nombre,

+

Se ha creado tu cuenta en la plataforma SIIH con el rol de $tipoUsuarioTexto.

+

Aquí tienes tus credenciales de acceso:

+
+

Correo: $email

+

Contraseña: $password

+
+

📌 Importante: Te recomendamos cambiar tu contraseña una vez que ingreses al sistema por primera vez.

+

+ + Acceder al Sistema + +

+
+
+ © " . date('Y') . " SIIH · Sistema Integral para Importadores de Hidrocarburos +
+
+
"; + + $mail->send(); + + } catch (Exception $e) { + error_log("Error al enviar correo: " . $e->getMessage()); + } + } + + // 10. Redirigir con mensaje de éxito + header("Location: /IMPORTADORES/agencias/alta?success=created"); + exit; + + } catch (Exception $e) { + error_log("ERROR CAPTURADO: " . $e->getMessage()); + if (!sqlsrv_rollback($conn)) { + error_log("ERROR: No se pudo revertir la transacción"); + } else { + error_log("Transacción revertida exitosamente"); + } + + error_log("=== FIN CON ERROR guardar_agencia ==="); + header("Location: /IMPORTADORES/agencias/alta?error=save_failed"); + exit; + } finally { + if ($conn) { + sqlsrv_close($conn); + error_log("Conexión cerrada"); + } + } } \ No newline at end of file diff --git a/app/controllers/agentes.php b/app/controllers/agentes.php index 53c77cb..17da4fa 100644 --- a/app/controllers/agentes.php +++ b/app/controllers/agentes.php @@ -60,7 +60,6 @@ function solicitudes_pendientes() { WHERE request_status = 'pending' ORDER BY request_date DESC"; - $stmt = sqlsrv_query($conn, $sql); $solicitudes = []; while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) { diff --git a/app/controllers/bitacoras.php b/app/controllers/bitacoras.php index ada0963..d46b819 100644 --- a/app/controllers/bitacoras.php +++ b/app/controllers/bitacoras.php @@ -1,47 +1,45 @@ isSMTP(); - $mail->Host = 'secure.emailsrvr.com'; - $mail->SMTPAuth = true; - $mail->Username = 'noreply@aduanasoft.com.mx'; - $mail->Password = $_ENV['SMTP_PASS']; + $mail->Host = 'secure.emailsrvr.com'; + $mail->SMTPAuth = true; + $mail->Username = 'noreply@aduanasoft.com.mx'; + $mail->Password = $_ENV['SMTP_PASS']; $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; - $mail->Port = 587; + $mail->Port = 587; $mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | AduanaSoft'); $mail->addAddress($email); @@ -219,9 +198,9 @@ function confirmarAcceso() } $codigoIngresado = trim($_POST['codigo'] ?? ''); - $usuarioId = $_SESSION['usuario_id'] ?? null; - $email = $_SESSION['usuario_email'] ?? ''; - $ip = $_SERVER['REMOTE_ADDR'] ?? 'N/A'; + $usuarioId = $_SESSION['usuario_id'] ?? null; + $email = $_SESSION['usuario_email'] ?? ''; + $ip = $_SERVER['REMOTE_ADDR'] ?? 'N/A'; // Verificar que el usuario tenga una sesión válida y pendiente de confirmación if (!$usuarioId || !$codigoIngresado || !($_SESSION['pendiente_confirmacion'] ?? false)) { @@ -234,9 +213,9 @@ function confirmarAcceso() exit; } - $sql = "SELECT codigo, expiracion FROM verificaciones - WHERE id_usuario = ? AND codigo = ? - ORDER BY id DESC"; + $sql = "SELECT codigo, expiracion FROM verificaciones + WHERE id_usuario = ? AND codigo = ? + ORDER BY id DESC"; $stmt = sqlsrv_query($conn, $sql, [$usuarioId, $codigoIngresado]); if (!$stmt || !($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC))) { @@ -262,7 +241,7 @@ function confirmarAcceso() registrarBitacora($conn, $usuarioId, $email, $ip, 1, 'Confirmación 2FA exitosa - Login completado'); $_SESSION['pendiente_confirmacion'] = false; - $_SESSION['codigo_exito'] = '✅ Verificación de dos factores completada correctamente.'; + $_SESSION['codigo_exito'] = '✅ Verificación de dos factores completada correctamente.'; // Obtener el tipo de usuario para redirección $tipoUsuario = $_SESSION['tipo_usuario'] ?? 'importador'; @@ -306,7 +285,7 @@ function enviarCodigo() $emailEncrypted = encrypt($email); - $sql = "SELECT id_usuario, activo FROM usuarios_sistema WHERE email = ?"; + $sql = "SELECT id_usuario, activo FROM usuarios_sistema WHERE email = ?"; $stmt = sqlsrv_query($conn, $sql, [$emailEncrypted]); if (!$stmt || !($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC))) { @@ -314,7 +293,7 @@ function enviarCodigo() exit; } $id_usuario = $row['id_usuario']; - $activo = $row['activo']; + $activo = $row['activo']; // Validación del estado de la cuenta if ($activo != 1) { @@ -322,19 +301,19 @@ function enviarCodigo() exit; } - $now = (new DateTime())->format('Y-m-d H:i:s'); + $now = (new DateTime())->format('Y-m-d H:i:s'); $sqlCheck = "SELECT COUNT(*) AS total FROM recuperacion_password WHERE email = ? AND estatus = 0 AND expiracion > ?"; $checkStmt = sqlsrv_query($conn, $sqlCheck, [$emailEncrypted, $now]); - $checkRow = sqlsrv_fetch_array($checkStmt, SQLSRV_FETCH_ASSOC); + $checkRow = sqlsrv_fetch_array($checkStmt, SQLSRV_FETCH_ASSOC); if ($checkRow['total'] > 0) { echo json_encode(['success' => false, 'message' => '⚠️ Ya tienes un código activo. Revisa tu correo o espera 10 minutos.']); exit; } - $codigo = strval(random_int(100000, 999999)); - $expira = (new DateTime('+10 minutes'))->format('Y-m-d H:i:s'); + $codigo = strval(random_int(100000, 999999)); + $expira = (new DateTime('+10 minutes'))->format('Y-m-d H:i:s'); $estatus = 0; $insert = "INSERT INTO recuperacion_password (email, codigo, expiracion, estatus) VALUES (?, ?, ?, ?)"; @@ -353,10 +332,10 @@ function enviarCodigo() try { $mail = new PHPMailer(true); $mail->isSMTP(); - $mail->Host = 'secure.emailsrvr.com'; - $mail->SMTPAuth = true; - $mail->Username = 'noreply@aduanasoft.com.mx'; - $mail->Password = $_ENV['SMTP_PASS']; + $mail->Host = 'secure.emailsrvr.com'; + $mail->SMTPAuth = true; + $mail->Username = 'noreply@aduanasoft.com.mx'; + $mail->Password = $_ENV['SMTP_PASS']; $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; $mail->Port = 587; @@ -398,7 +377,7 @@ function enviarCodigo() function enviarCorreoRespaldo($conn, $id_usuario, $codigo) { // Obtener correo de respaldo - $sql = "SELECT TOP 1 correo FROM correo_respaldo WHERE id_usuario = ?"; + $sql = "SELECT TOP 1 correo FROM correo_respaldo WHERE id_usuario = ?"; $stmt = sqlsrv_query($conn, $sql, [$id_usuario]); if (!$stmt || !($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC))) { @@ -412,10 +391,10 @@ function enviarCorreoRespaldo($conn, $id_usuario, $codigo) $mail = new PHPMailer(true); // ConfigEuración de servidor SMTP $mail->isSMTP(); - $mail->Host = 'secure.emailsrvr.com'; - $mail->SMTPAuth = true; - $mail->Username = 'noreply@aduanasoft.com.mx'; - $mail->Password = $_ENV['SMTP_PASS']; + $mail->Host = 'secure.emailsrvr.com'; + $mail->SMTPAuth = true; + $mail->Username = 'noreply@aduanasoft.com.mx'; + $mail->Password = $_ENV['SMTP_PASS']; $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; $mail->Port = 587; @@ -472,7 +451,7 @@ function reenviarCodigo() exit; } - $id_usuario = $_SESSION['id_usuario']; + $id_usuario = $_SESSION['id_usuario']; $emailEncrypted = encrypt($_SESSION['email_recuperacion']); // Obtener el último código activo de recuperación @@ -512,7 +491,7 @@ function verificarCodigo() // Validar existencia de conexión, código y email en sesión $codigo = isset($_POST['codigo']) ? trim($_POST['codigo']) : ''; - $email = $_SESSION['email_recuperacion'] ?? ''; + $email = $_SESSION['email_recuperacion'] ?? ''; if (!$conn) { echo json_encode(['success' => false, 'message' => '❌ Error de conexión a la base de datos.']); @@ -539,8 +518,8 @@ function verificarCodigo() $emailEncrypted = encrypt($email); // Consulta el último código válido para ese email - $sql = "SELECT TOP 1 id, codigo, expiracion FROM recuperacion_password - WHERE email = ? AND estatus = 0 ORDER BY id DESC"; + $sql = "SELECT TOP 1 id, codigo, expiracion FROM recuperacion_password + WHERE email = ? AND estatus = 0 ORDER BY id DESC"; $stmt = sqlsrv_query($conn, $sql, [$emailEncrypted]); if ($stmt === false) { @@ -555,9 +534,9 @@ function verificarCodigo() if (!$row) { echo json_encode([ - 'success' => false, - 'message' => '🚫 Has excedido el número de intentos permitidos. Tu cuenta ha sido bloqueada por seguridad. Contacta a tu agente aduanal.', - 'blocked' => true, + 'success' => false, + 'message' => '🚫 Has excedido el número de intentos permitidos. Tu cuenta ha sido bloqueada por seguridad. Contacta a tu agente aduanal.', + 'blocked' => true, 'redirect' => '/IMPORTADORES/login' ]); exit; @@ -607,7 +586,7 @@ function verificarCodigo() } // Validar expiración - puede venir como DateTime o string - $expiracion = $row['expiracion']; + $expiracion = $row['expiracion']; $expiracionTimestamp = null; if ($expiracion instanceof DateTime) { @@ -630,7 +609,7 @@ function verificarCodigo() } // Código correcto: actualizar estado y permitir cambio de contraseña - $sqlSuccess = "UPDATE recuperacion_password SET estatus = 1 WHERE id = ?"; + $sqlSuccess = "UPDATE recuperacion_password SET estatus = 1 WHERE id = ?"; $stmtSuccess = sqlsrv_query($conn, $sqlSuccess, [$row['id']]); if ($stmtSuccess) { @@ -682,7 +661,7 @@ function enviarNotificacionIntentosExcedidos($email) } // 🔐 Desencriptar datos sensibles - $usuario['email'] = decrypt($usuario['email']); + $usuario['email'] = decrypt($usuario['email']); $usuario['nombre'] = decrypt($usuario['nombre']); // Verificar si el usuario tiene habilitadas las notificaciones de seguridad @@ -693,10 +672,10 @@ function enviarNotificacionIntentosExcedidos($email) // Preparar datos para la notificación $datosNotificacion = [ - 'email' => $usuario['email'], - 'nombre' => $usuario['nombre'], + 'email' => $usuario['email'], + 'nombre' => $usuario['nombre'], 'fecha_hora' => date('Y-m-d H:i:s'), - 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'Desconocida', + 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'Desconocida', 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'Desconocido' ]; @@ -743,12 +722,12 @@ function enviarNotificacionSeguridadIntentos($emailDestino, $nombreUsuario, $dat // Configuración de servidor SMTP $mail->isSMTP(); - $mail->Host = 'secure.emailsrvr.com'; - $mail->SMTPAuth = true; - $mail->Username = 'noreply@aduanasoft.com.mx'; - $mail->Password = $_ENV['SMTP_PASS']; + $mail->Host = 'secure.emailsrvr.com'; + $mail->SMTPAuth = true; + $mail->Username = 'noreply@aduanasoft.com.mx'; + $mail->Password = $_ENV['SMTP_PASS']; $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; - $mail->Port = 587; + $mail->Port = 587; // Configuración del correo $mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | AduanaSoft'); @@ -868,10 +847,10 @@ function enviarNotificacionCuentaBloqueada($email) // Preparar datos para la notificación $datosNotificacion = [ - 'email' => $usuario['email'], - 'nombre' => $usuario['nombre'], + 'email' => $usuario['email'], + 'nombre' => $usuario['nombre'], 'fecha_hora' => date('Y-m-d H:i:s'), - 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'Desconocida', + 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'Desconocida', 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'Desconocido' ]; @@ -915,12 +894,12 @@ function enviarEmailCuentaBloqueada($emailDestino, $nombreUsuario, $datos, $esCo // Configuración de servidor SMTP $mail->isSMTP(); - $mail->Host = 'secure.emailsrvr.com'; - $mail->SMTPAuth = true; - $mail->Username = 'noreply@aduanasoft.com.mx'; - $mail->Password = $_ENV['SMTP_PASS']; + $mail->Host = 'secure.emailsrvr.com'; + $mail->SMTPAuth = true; + $mail->Username = 'noreply@aduanasoft.com.mx'; + $mail->Password = $_ENV['SMTP_PASS']; $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; - $mail->Port = 587; + $mail->Port = 587; // Configuración del correo $mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | AduanaSoft'); @@ -1028,8 +1007,8 @@ function cambiarPassword() return; } - $conn = getConnection(); - $password = $_POST['password'] ?? ''; + $conn = getConnection(); + $password = $_POST['password'] ?? ''; $confirmar = $_POST['confirmar'] ?? ''; // Validaciones @@ -1047,10 +1026,10 @@ function cambiarPassword() // 🔐 Usa encrypt() en vez de hash() para coincidir con lo que hay en la base de datos $emailEncrypted = encrypt($email); - $hash = password_hash($password, PASSWORD_BCRYPT); + $hash = password_hash($password, PASSWORD_BCRYPT); // Actualizar contraseña - $sql = "UPDATE usuarios_sistema SET password_hash = ? WHERE email = ?"; + $sql = "UPDATE usuarios_sistema SET password_hash = ? WHERE email = ?"; $stmt = sqlsrv_prepare($conn, $sql, [$hash, $emailEncrypted]); if (!$stmt || !sqlsrv_execute($stmt)) { @@ -1059,10 +1038,10 @@ function cambiarPassword() } // Obtener ID de usuario para registrar en bitácora - $sqlUsuario = "SELECT id_usuario FROM usuarios_sistema WHERE email = ?"; + $sqlUsuario = "SELECT id_usuario FROM usuarios_sistema WHERE email = ?"; $stmtUsuario = sqlsrv_query($conn, $sqlUsuario, [$emailEncrypted]); - $usuario = sqlsrv_fetch_array($stmtUsuario, SQLSRV_FETCH_ASSOC); - $idUsuario = $usuario['id_usuario'] ?? null; + $usuario = sqlsrv_fetch_array($stmtUsuario, SQLSRV_FETCH_ASSOC); + $idUsuario = $usuario['id_usuario'] ?? null; // Registrar en bitácora $ip = $_SERVER['REMOTE_ADDR'] ?? 'N/A'; @@ -1074,12 +1053,12 @@ function cambiarPassword() // Configuración de servidor SMTP $mail->isSMTP(); - $mail->Host = 'secure.emailsrvr.com'; - $mail->SMTPAuth = true; - $mail->Username = 'noreply@aduanasoft.com.mx'; - $mail->Password = $_ENV['SMTP_PASS']; + $mail->Host = 'secure.emailsrvr.com'; + $mail->SMTPAuth = true; + $mail->Username = 'noreply@aduanasoft.com.mx'; + $mail->Password = $_ENV['SMTP_PASS']; $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; - $mail->Port = 587; + $mail->Port = 587; // Configuración del correo $mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | AduanaSoft'); diff --git a/app/controllers/registro.php b/app/controllers/registro.php index 48dfc44..e07e2f6 100644 --- a/app/controllers/registro.php +++ b/app/controllers/registro.php @@ -1,32 +1,32 @@ $secretKey, + 'secret' => $secretKey, 'response' => $captchaResponse ]; @@ -39,58 +39,98 @@ function enviar() { ]; $context = stream_context_create($options); - $result = file_get_contents($verifyUrl, false, $context); + $result = file_get_contents($verifyUrl, false, $context); $response = json_decode($result); if (!$response->success) { die("❌ Error de verificación reCAPTCHA."); } + return true; +} - $conn = getConnection(); - - $empresa = encrypt(trim($_POST['company_name'] ?? '')); - $rfc = strtoupper(encrypt(trim($_POST['rfc'] ?? ''))); - $email = trim($_POST['email'] ?? ''); - $telefono = trim($_POST['phone'] ?? ''); - $archivo = $_FILES['opinion_file']; - - // Validar RFC (12 o 13 caracteres con formato correcto) - if (!preg_match('/^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/', strtoupper($_POST['rfc'] ?? ''))) { +// Función auxiliar para validaciones +function validarDatos($rfc, $email, $telefono) +{ + // Validar RFC + if (!preg_match('/^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/', strtoupper($rfc))) { die("❌ El RFC no tiene un formato válido."); } - // Validar email (solo minúsculas, sin caracteres especiales fuera del estándar) - if (!preg_match('/^[a-z0-9._+-]+@[a-z0-9.-]+\.[a-z]{2,}$/', $email)) { + // Validar email + if (!preg_match('/^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/', strtolower($email))) { die("❌ No es una dirección de correo válida."); } - // Validar teléfono (formato nacional: 3 dígitos + espacio + 7 dígitos) + // Validar teléfono if (!preg_match('/^\d{3}\s\d{7}$/', $telefono)) { die("❌ El número de teléfono no tiene un formato válido. Ejemplo: 123 4567890"); } +} - // Validar archivo - if ($archivo['error'] !== 0 || pathinfo($archivo['name'], PATHINFO_EXTENSION) !== 'pdf') { - die("❌ Archivo inválido. Solo se permiten PDFs."); +// Función auxiliar para manejar archivos +function procesarArchivo($archivo) +{ + if ($archivo['error'] !== UPLOAD_ERR_OK) { + die("❌ Error al subir el archivo."); + } + + // Validar tipo MIME real del archivo + $finfo = finfo_open(FILEINFO_MIME_TYPE); + $mimeType = finfo_file($finfo, $archivo['tmp_name']); + finfo_close($finfo); + + if ($mimeType !== 'application/pdf') { + die("❌ El archivo debe ser un PDF válido."); + } + + // Validar extensión + if (pathinfo($archivo['name'], PATHINFO_EXTENSION) !== 'pdf') { + die("❌ Solo se permiten archivos PDF."); + } + + // Validar tamaño (5MB máximo) + if ($archivo['size'] > 5 * 1024 * 1024) { + die("❌ El archivo no debe exceder 5MB."); } - // Guardar archivo + // Guardar archivo con nombre único $nombreArchivo = uniqid() . '_' . basename($archivo['name']); - $rutaDestino = __DIR__ . '/../../storage/opiniones/' . $nombreArchivo; - move_uploaded_file($archivo['tmp_name'], $rutaDestino); + $rutaDestino = __DIR__ . '/../../storage/opiniones/' . $nombreArchivo; + + if (!move_uploaded_file($archivo['tmp_name'], $rutaDestino)) { + die("❌ Error al guardar el archivo."); + } + + return $nombreArchivo; +} - // Insertar en la base de datos - $sql = "INSERT INTO solicitudes_importadores - (company_name, rfc, email, phone, opinion_file, request_status, request_date) - VALUES (?, ?, ?, ?, ?, 'pending', GETDATE())"; - - $params = [$empresa, $rfc, $email, $telefono, $nombreArchivo]; - $stmt = sqlsrv_query($conn, $sql, $params); - - if ($stmt === false) { - die(print_r(sqlsrv_errors(), true)); - } +// Función auxiliar para obtener configuración +function obtenerConfiguracion($conn) +{ + $sqlConf = "SELECT TOP 1 * FROM configuracion_sistema"; + $stmtConf = sqlsrv_query($conn, $sqlConf); + + if ($stmtConf === false) { + error_log("Error al obtener configuración: " . print_r(sqlsrv_errors(), true)); + return [ + 'nombre_plataforma' => 'Sistema Integral para Importadores de Hidrocarburos', + 'siglas' => 'SIIH', + 'logo_url' => 'assets/img/logo_siih.png', + 'colores_primarios' => '#003366,#0055A5' + ]; + } + + return sqlsrv_fetch_array($stmtConf, SQLSRV_FETCH_ASSOC) ?: [ + 'nombre_plataforma' => 'Sistema Integral para Importadores de Hidrocarburos', + 'siglas' => 'SIIH', + 'logo_url' => 'assets/img/logo_siih.png', + 'colores_primarios' => '#003366,#0055A5' + ]; +} +// Función auxiliar para enviar email +function enviarEmailConfirmacion($destinatario, $datosEmpresa, $esAgencia = false) +{ $mail = new PHPMailer(true); try { @@ -99,31 +139,31 @@ function enviar() { $mail->Host = 'secure.emailsrvr.com'; $mail->SMTPAuth = true; $mail->Username = 'noreply@aduanasoft.com.mx'; - $mail->Password = $_ENV['SMTP_PASS']; // desde el .env + $mail->Password = $_ENV['SMTP_PASS']; $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; $mail->Port = 587; - // Correo remitente y destinatario + // Configurar remitente y destinatario $mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | AduanaSoft'); - $mail->addAddress($email); // destinatario principal + $mail->addAddress($destinatario); - // Formato y contenido + // Obtener configuración + $conn = getConnection(); + $conf = obtenerConfiguracion($conn); + + $nombrePlataforma = $conf['nombre_plataforma']; + $siglas = $conf['siglas']; + $logoUrl = $conf['logo_url']; + $color2 = explode(',', $conf['colores_primarios'])[1]; + + // Contenido del email $mail->CharSet = 'UTF-8'; $mail->isHTML(true); $mail->Subject = 'Confirmación de solicitud de registro | SIIH'; $fechaRegistro = date('d/m/Y H:i'); - - // Consulta configuración institucional - $sqlConf = "SELECT TOP 1 * FROM configuracion_sistema"; - $stmtConf = sqlsrv_query($conn, $sqlConf); - $conf = sqlsrv_fetch_array($stmtConf, SQLSRV_FETCH_ASSOC); - - $nombrePlataforma = $conf['nombre_plataforma'] ?? 'Sistema Integral para Importadores de Hidrocarburos'; - $siglas = $conf['siglas'] ?? 'SIIH'; - $logoUrl = $conf['logo_url'] ?? 'assets/img/logo_siih.png'; - $color1 = explode(',', $conf['colores_primarios'] ?? '#003366,#0055A5')[0]; - $color2 = explode(',', $conf['colores_primarios'] ?? '#003366,#0055A5')[1]; + $tipoRegistro = $esAgencia ? 'Agencia' : 'Empresa'; + $nombreEntidad = $esAgencia ? $datosEmpresa['agencia'] : $datosEmpresa['empresa']; $mail->Body = "
@@ -138,28 +178,141 @@ function enviar() {
-

📋 Empresa: " . htmlspecialchars($_POST['company_name']) . "

-

📞 Teléfono: " . htmlspecialchars($telefono) . "

+

📋 $tipoRegistro: " . htmlspecialchars($nombreEntidad) . "

+

📞 Teléfono: " . htmlspecialchars($datosEmpresa['telefono']) . "

🕓 Fecha de registro: $fechaRegistro


-

Un agente aduanal revisará tu información y te notificará por este medio cuando tu solicitud sea aprobada.

+

Un administrador revisará tu información y te notificará por este medio cuando tu solicitud sea aprobada.

Por favor, mantente atento a tu correo (y revisa también tu carpeta de spam o promociones).

Este es un mensaje automático enviado por el sistema de registro de importadores.

- © " . date('Y') . " $siglas · Desarrollado por AduanaSoft + © " . date('Y') . " $siglas · $nombrePlataforma
- - "; + "; $mail->send(); + return true; } catch (Exception $e) { error_log("Error al enviar correo: {$mail->ErrorInfo}"); + return false; } +} - include __DIR__ . '/../../views/registro/gracias.php'; -} \ No newline at end of file +function enviarSoliImportador() +{ + try { + // Validar reCAPTCHA + validarRecaptcha($_POST['g-recaptcha-response'] ?? ''); + + $conn = getConnection(); + + // Sanitizar y obtener datos + $empresa = trim($_POST['company_name'] ?? ''); + $rfc = trim($_POST['rfc'] ?? ''); + $email = trim($_POST['email'] ?? ''); + $telefono = trim($_POST['phone'] ?? ''); + + // Validar datos + validarDatos($rfc, $email, $telefono); + + // Procesar archivo + $nombreArchivo = procesarArchivo($_FILES['opinion_file']); + + // Encriptar datos sensibles + $empresaEncriptada = encrypt($empresa); + $rfcEncriptado = encrypt(strtoupper($rfc)); + + // Insertar en base de datos + $sql = "INSERT INTO solicitudes_importadores + (company_name, rfc, email, phone, opinion_file, request_status, request_date) + VALUES (?, ?, ?, ?, ?, 'pending', GETDATE())"; + + $params = [$empresaEncriptada, $rfcEncriptado, $email, $telefono, $nombreArchivo]; + $stmt = sqlsrv_query($conn, $sql, $params); + + if ($stmt === false) { + error_log("Error en BD: " . print_r(sqlsrv_errors(), true)); + die("❌ Error al procesar la solicitud."); + } + + // Enviar email de confirmación + $datosEmpresa = [ + 'empresa' => $empresa, + 'telefono' => $telefono + ]; + enviarEmailConfirmacion($email, $datosEmpresa, false); + + include __DIR__ . '/../../views/registro/gracias.php'; + + } catch (Exception $e) { + error_log("Error en enviarSoliImportador: " . $e->getMessage()); + die("❌ Error interno del servidor."); + } +} + +function enviarSoliAgencia() +{ + try { + // Validar reCAPTCHA + validarRecaptcha($_POST['g-recaptcha-response'] ?? ''); + + $conn = getConnection(); + + // Sanitizar y obtener datos + $agencia = trim($_POST['nombre_agencia'] ?? ''); + $rfc = trim($_POST['rfc'] ?? ''); + $correo = trim($_POST['correo'] ?? ''); + $telefono = trim($_POST['telefono'] ?? ''); + $direccion = trim($_POST['direccion'] ?? ''); + $admin = trim($_POST['nombre_admin'] ?? ''); + $admin_correo = trim($_POST['correo_admin'] ?? ''); + + // Validar datos principales + validarDatos($rfc, $correo, $telefono); + + // Validar correo del administrador + if (!preg_match('/^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/', strtolower($admin_correo))) { + die("❌ El correo del administrador no es válido."); + } + + // Procesar archivo + $nombreArchivo = procesarArchivo($_FILES['opinion_file']); + + // Encriptar datos sensibles + $agenciaEncriptada = encrypt($agencia); + $rfcEncriptado = encrypt(strtoupper($rfc)); + $adminEncriptado = encrypt($admin); + + // Insertar en base de datos + $sql = "INSERT INTO solicitudes_agencias + (agencia_name, rfc, email, phone, direccion, opinion_file, admin_name, admin_email, request_status, request_date) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'pending', GETDATE())"; + + $params = [$agenciaEncriptada, $rfcEncriptado, $correo, $telefono, $direccion, $nombreArchivo, $adminEncriptado, $admin_correo]; + $stmt = sqlsrv_query($conn, $sql, $params); + + if ($stmt === false) { + error_log("Error en BD: " . print_r(sqlsrv_errors(), true)); + die("❌ Error al procesar la solicitud."); + } + + // Enviar email de confirmación + $datosAgencia = [ + 'agencia' => $agencia, + 'telefono' => $telefono + ]; + enviarEmailConfirmacion($correo, $datosAgencia, true); + + include __DIR__ . '/../../views/registro/gracias.php'; + + } catch (Exception $e) { + error_log("Error en enviarSoliAgencia: " . $e->getMessage()); + die("❌ Error interno del servidor."); + } +} +?> \ No newline at end of file diff --git a/app/controllers/sistemas.php b/app/controllers/sistemas.php index 5252620..74b4552 100644 --- a/app/controllers/sistemas.php +++ b/app/controllers/sistemas.php @@ -2,286 +2,15 @@ require_once __DIR__ . '/../../config/database.php'; require_once __DIR__ . '/../helpers/env.php'; require_once __DIR__ . '/../helpers/crypto.php'; - -use PHPMailer\PHPMailer\PHPMailer; -use PHPMailer\PHPMailer\Exception; +require_once __DIR__ . '/../helpers/session.php'; loadEnv(); -session_start(); - -function index() { - header("Location: /IMPORTADORES/sistemas/login"); - exit; -} - -function login() { - // Si es GET, mostrar el formulario - if ($_SERVER['REQUEST_METHOD'] === 'GET') { - include __DIR__ . '/../../views/admin/login_sistemas.php'; - return; - } - - // Si es POST, procesar login - $email = $_POST['email'] ?? ''; - $clave = $_POST['clave'] ?? ''; - - if ($email === 'sistemas@aduanasoft.com.mx' && $clave === 'rootSecure2025!') { - $_SESSION['usuario_sistemas'] = true; - header("Location: /IMPORTADORES/sistemas/alta_usuarios"); - } else { - echo "❌ Acceso denegado."; - } -} - -function alta_usuarios() { - if (!($_SESSION['usuario_sistemas'] ?? false)) { - die("⚠️ No autorizado."); - } - - $conn = getConnection(); - $sql = "SELECT * FROM usuarios_sistema ORDER BY creado_en DESC"; - $stmt = sqlsrv_query($conn, $sql); - - $usuarios = []; - while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) { - $usuarios[] = $row; - } - - include __DIR__ . '/../../views/admin/alta_usuarios.php'; -} - -function guardar_usuario() +function logout() { - if (!($_SESSION['usuario_sistemas'] ?? false)) { - die("⚠️ No autorizado."); - } - - - $conn = getConnection(); - - // 1. Capturar y validar datos - $nombre = trim($_POST['nombre'] ?? ''); - $email = trim($_POST['email'] ?? ''); - $password = $_POST['password'] ?? ''; - $tipo = $_POST['tipo_usuario'] ?? ''; - - if (empty($nombre) || empty($email) || empty($password) || empty($tipo)) { - die("❌ Todos los campos son obligatorios."); - } - - if (!in_array($tipo, ['importador', 'agente_aduanal'])) { - die("❌ Tipo de usuario inválido."); - } - - // 2. Encriptar datos sensibles - $nombre_encrypted = encrypt($nombre); - $email_encrypted = encrypt($email); - $password_hash = password_hash($password, PASSWORD_DEFAULT); - - // 3. Validar duplicado por email encriptado - $sqlCheck = "SELECT COUNT(*) AS total FROM usuarios_sistema WHERE email = ?"; - $stmtCheck = sqlsrv_query($conn, $sqlCheck, [$email_encrypted]); - $rowCheck = sqlsrv_fetch_array($stmtCheck, SQLSRV_FETCH_ASSOC); - if ($rowCheck['total'] > 0) { - die("❌ Este correo ya está registrado."); - } - - // 4. Insertar usuario - $sqlInsert = "INSERT INTO usuarios_sistema (nombre, email, password_hash, tipo_usuario, activo, creado_en) - VALUES (?, ?, ?, ?, 1, GETDATE())"; - $params = [$nombre_encrypted, $email_encrypted, $password_hash, $tipo]; - - $stmtInsert = sqlsrv_query($conn, $sqlInsert, $params); - - if ($stmtInsert === false) { - die("❌ Error al guardar: " . print_r(sqlsrv_errors(), true)); - } - - // 5. Redirigir - header("Location: /IMPORTADORES/sistemas/alta_usuarios"); - exit; -} - -function agencias_activas() -{ - -} - -function logout() { - session_start(); session_unset(); // Limpia variables de sesión session_destroy(); // Destruye la sesión header("Location: /IMPORTADORES/login"); exit; -} - -function toggle_estado() { - session_start(); - - if (!($_SESSION['usuario_sistemas'] ?? false)) { - die("⚠️ No autorizado."); - } - - $conn = getConnection(); - - $id = $_GET['id'] ?? null; - - if (!$id || !is_numeric($id)) { - die("❌ ID inválido."); - } - - // Obtener el estado actual - $sqlEstado = "SELECT activo FROM usuarios_sistema WHERE id_usuario = ?"; - $stmtEstado = sqlsrv_query($conn, $sqlEstado, [$id]); - - if (!$stmtEstado || !($row = sqlsrv_fetch_array($stmtEstado, SQLSRV_FETCH_ASSOC))) { - die("❌ Usuario no encontrado."); - } - - $nuevoEstado = $row['activo'] ? 0 : 1; - - // Actualizar estado - $sqlUpdate = "UPDATE usuarios_sistema SET activo = ? WHERE id_usuario = ?"; - $stmtUpdate = sqlsrv_query($conn, $sqlUpdate, [$nuevoEstado, $id]); - - if (!$stmtUpdate) { - die("❌ Error al actualizar: " . print_r(sqlsrv_errors(), true)); - } - - header("Location: /IMPORTADORES/sistemas/alta_usuarios"); - exit; -} - -function reset_password() { - session_start(); - - if (!($_SESSION['usuario_sistemas'] ?? false)) { - die("⚠️ No autorizado."); - } - - $conn = getConnection(); - - $id = $_GET['id'] ?? null; - - if (!$id || !is_numeric($id)) { - die("❌ ID inválido."); - } - - // 🔐 Generar contraseña aleatoria de 10 caracteres - $randomPassword = bin2hex(random_bytes(5)); // genera algo como 'a8c4f1b92d' - $passwordHash = password_hash($randomPassword, PASSWORD_DEFAULT); - - $sqlEmail = "SELECT email FROM usuarios_sistema WHERE id_usuario = ?"; - $stmtEmail = sqlsrv_query($conn, $sqlEmail, [$id]); - $row = sqlsrv_fetch_array($stmtEmail, SQLSRV_FETCH_ASSOC); - - $sql = "UPDATE usuarios_sistema SET password_hash = ? WHERE id_usuario = ?"; - $stmt = sqlsrv_query($conn, $sql, [$passwordHash, $id]); - - if (!$stmt) { - die("❌ Error al actualizar: " . print_r(sqlsrv_errors(), true)); - } - - // 🔁 Redirigir pasando la contraseña como parámetro temporal (solo visible para el admin) - header("Location: /IMPORTADORES/sistemas/alta_usuarios?reset=ok&pass=" . urlencode($randomPassword) . "&email=" . urlencode(decrypt($row['email']))); - - exit; -} - -function enviar_password() { - $email = $_GET['email'] ?? ''; - $pass = $_GET['pass'] ?? ''; - - if (!$email || !$pass) { - http_response_code(400); - echo "Datos incompletos."; - return; - } - - require_once __DIR__ . '/../../vendor/autoload.php'; - - $mail = new PHPMailer(true); - - try { - $mail->isSMTP(); - $mail->Host = 'secure.emailsrvr.com'; - $mail->SMTPAuth = true; - $mail->Username = 'noreply@aduanasoft.com.mx'; - $mail->Password = $_ENV['SMTP_PASS']; - $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; - $mail->Port = 587; - - $mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | AduanaSoft'); - $mail->addAddress($email); - - $mail->isHTML(true); - $mail->Subject = 'Nueva contraseña de acceso'; - $mail->CharSet = 'UTF-8'; - $logoURL='http://siih.aduanasoft.com/IMPORTADORES/public/assets/img/logo_siih.png'; - $mail->Body = " -
-
-
- Logo $siglas -

Contraseña restablecida

-
-
-

Tu contraseña de acceso al $nombreSistema ha sido restablecida por el administrador.

-

- Nueva contraseña:
- $pass -

-

Por favor, cambia esta contraseña una vez que inicies sesión.

-
-

Este es un mensaje automático generado por el sistema. Si no solicitaste esta acción, contacta al administrador.

-
-
- © " . date('Y') . " $siglas · Sistema Integral para Importadores de Hidrocarburos -
-
-
- "; - - $mail->send(); - echo "Correo enviado"; - } catch (Exception $e) { - http_response_code(500); - echo "Error al enviar correo: {$mail->ErrorInfo}"; - } -} - -function bitacora_login() { - if (!($_SESSION['usuario_sistemas'] ?? false)) { - die("⚠️ No autorizado."); - } - - $conn = getConnection(); - $sql = "SELECT * FROM bitacora_login ORDER BY fecha DESC"; - $stmt = sqlsrv_query($conn, $sql); - - $registros = []; - while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) { - $registros[] = $row; - } - - include __DIR__ . '/../../views/admin/bitacora_login.php'; -} - -function bitacora_usuarios() { - if (!($_SESSION['usuario_sistemas'] ?? false)) { - die("⚠️ No autorizado."); - } - - $conn = getConnection(); - $sql = "SELECT * FROM bitacora_usuarios ORDER BY fecha DESC"; - $stmt = sqlsrv_query($conn, $sql); - - $registros = []; - while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) { - $registros[] = $row; - } - - include __DIR__ . '/../../views/admin/bitacora_usuarios.php'; } \ No newline at end of file diff --git a/app/controllers/vinculaciones.php b/app/controllers/vinculaciones.php new file mode 100644 index 0000000..7a2b28e --- /dev/null +++ b/app/controllers/vinculaciones.php @@ -0,0 +1,648 @@ + 0) { + header('Location: /IMPORTADORES/vinculaciones/nuevaVinculacion?error=pending_request'); + exit; + } + } + + // Insertar la nueva solicitud + $sql = "INSERT INTO solicitudes_vinculacion (id_importador, id_agencia, mensaje, estado, fecha_solicitud) VALUES (?, ?, 'Solicitud de vinculación', 'PENDIENTE', GETDATE())"; + $params = [$id_importador, $id_agencia]; + $stmt = sqlsrv_prepare($conn, $sql, $params); + + if ($stmt && sqlsrv_execute($stmt)) { + header('Location: /IMPORTADORES/vinculaciones/nuevaVinculacion?success=sended_request'); + exit; + } else { + header('Location: /IMPORTADORES/vinculaciones/nuevaVinculacion?error=failed_request'); + exit; + } +} + +function desvincular() +{ + // ✅ CORREGIR: Debe ser admin_agencia, no importador + if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'admin_agencia') { + header('Location: /IMPORTADORES/login'); + exit; + } + + // Verificar que se recibió el ID de la relación + $id_relacion = $_GET['id'] ?? null; + if (!$id_relacion || !is_numeric($id_relacion)) { + header('Location: /IMPORTADORES/vinculaciones/usuariosVinculados?error=invalid_id'); + exit; + } + + $conn = getConnection(); + + try { + // Iniciar transacción + sqlsrv_begin_transaction($conn); + + // 1. Verificar que la relación existe y pertenece a la agencia del admin + $sqlVerificar = " + SELECT + ia.*, + aa.id_administrador, + u.nombre as importador_nombre + FROM importador_agencia ia + INNER JOIN agencias_aduanales aa ON ia.id_agencia = aa.id_agencia + INNER JOIN usuarios_sistema u ON ia.id_importador = u.id_usuario + WHERE ia.id_relacion = ? AND aa.id_administrador = ? + "; + $stmtVerificar = sqlsrv_query($conn, $sqlVerificar, [$id_relacion, $_SESSION['usuario_id']]); + $relacion = sqlsrv_fetch_array($stmtVerificar, SQLSRV_FETCH_ASSOC); + + if (!$relacion) { + throw new Exception('Relación no encontrada o no tienes permisos para desvincularla'); + } + + if ($relacion['activo'] == 0) { + throw new Exception('Esta relación ya está inactiva'); + } + + // 2. Desactivar la relación (no eliminar, mantener historial) + $sqlDesactivar = " + UPDATE importador_agencia + SET activo = 0, + fecha_desvinculacion = GETDATE(), + estado = 'DESVINCULADO' + WHERE id_relacion = ? + "; + $stmtDesactivar = sqlsrv_query($conn, $sqlDesactivar, [$id_relacion]); + + if (!$stmtDesactivar) { + throw new Exception('Error al desactivar la relación'); + } + + // Confirmar transacción + sqlsrv_commit($conn); + + header('Location: /IMPORTADORES/vinculaciones/usuariosVinculados?success=unlinked'); + exit; + + } catch (Exception $e) { + // Revertir transacción en caso de error + sqlsrv_rollback($conn); + header('Location: /IMPORTADORES/vinculaciones/usuariosVinculados?error=unlinked_failed'); + exit; + } +} + +function cancelar() +{ + if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador') { + header('Location: /IMPORTADORES/login'); + exit; + } + + $conn = getConnection(); + + $id_importador = $_SESSION['usuario_id']; + $id_agencia = (int) $_GET['id']; + + $sql = "UPDATE solicitudes_vinculacion + SET estado = 'CANCELADA', fecha_respuesta = GETDATE() + WHERE id_importador = ? AND id_agencia = ? AND estado = 'PENDIENTE'"; + + $params = [$id_importador, $id_agencia]; + $stmt = sqlsrv_prepare($conn, $sql, $params); + + if ($stmt && sqlsrv_execute($stmt)) { + header('Location: /IMPORTADORES/vinculaciones/nuevaVinculacion?success=cancelled'); + exit; + } +} + +function solicitudesVinculacion() +{ + if (!($_SESSION['usuario_id'] ?? false) || $_SESSION['tipo_usuario'] !== 'admin_agencia') { + header('Location: /IMPORTADORES/login'); + exit; + } + + $conn = getConnection(); + + // Obtener agencia... + $sqlAgencia = "SELECT id_agencia FROM agencias_aduanales WHERE id_administrador = ?"; + $params = [$_SESSION['usuario_id']]; + $stmtAgencia = sqlsrv_query($conn, $sqlAgencia, $params); + $rowAgencia = sqlsrv_fetch_array($stmtAgencia, SQLSRV_FETCH_ASSOC); + + if (!$rowAgencia) { + $solicitudes = []; + include __DIR__ . '/../../views/agencias/solicitudes_vinculacion.php'; + return; + } + + $id_agencia = $rowAgencia['id_agencia']; + + // Verificar solicitudes básicas + $debugSql = "SELECT COUNT(*) as total FROM solicitudes_vinculacion WHERE id_agencia = ?"; + $debugStmt = sqlsrv_query($conn, $debugSql, [$id_agencia]); + $debugRow = sqlsrv_fetch_array($debugStmt, SQLSRV_FETCH_ASSOC); + + // Verificar pendientes + $debugSql2 = "SELECT COUNT(*) as total FROM solicitudes_vinculacion WHERE estado = 'PENDIENTE' AND id_agencia = ?"; + $debugStmt2 = sqlsrv_query($conn, $debugSql2, [$id_agencia]); + $debugRow2 = sqlsrv_fetch_array($debugStmt2, SQLSRV_FETCH_ASSOC); + + // Consulta principal + $sql = " + SELECT + sv.id_solicitud, + u.nombre AS importador_nombre, + ig.rfc, + sv.mensaje, + sv.fecha_solicitud, + si.opinion_file + FROM solicitudes_vinculacion sv + INNER JOIN usuarios_sistema u ON sv.id_importador = u.id_usuario + LEFT JOIN informacion_general ig ON u.id_usuario = ig.id_usuario + LEFT JOIN solicitudes_importadores si ON LTRIM(RTRIM(LOWER(si.company_name))) = LTRIM(RTRIM(LOWER(u.nombre))) + WHERE sv.estado = 'PENDIENTE' AND sv.id_agencia = ? + ORDER BY sv.fecha_solicitud DESC + "; + $params = [$id_agencia]; + $stmt = sqlsrv_query($conn, $sql, $params); + + // ✅ INICIALIZAR EL ARRAY DE SOLICITUDES + $solicitudes = []; + + // ✅ VERIFICAR SI LA CONSULTA FUNCIONÓ + if ($stmt === false) { + // Usar consulta simplificada como backup + $sqlSimple = " + SELECT + sv.id_solicitud, + u.nombre AS importador_nombre, + sv.mensaje, + sv.fecha_solicitud + FROM solicitudes_vinculacion sv + INNER JOIN usuarios_sistema u ON sv.id_importador = u.id_usuario + WHERE sv.estado = 'PENDIENTE' AND sv.id_agencia = ? + ORDER BY sv.fecha_solicitud DESC + "; + $stmt = sqlsrv_query($conn, $sqlSimple, $params); + } + + // ✅ PROCESAR LOS RESULTADOS Y LLENAR EL ARRAY + if ($stmt !== false) { + while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) { + $solicitudes[] = $row; + } + } + + include __DIR__ . '/../../views/agencias/solicitudes_vinculacion.php'; +} + +function aprobarVinculacion() +{ + // Verificar autenticación y permisos + if (!($_SESSION['usuario_id'] ?? false) || $_SESSION['tipo_usuario'] !== 'admin_agencia') { + header('Location: /IMPORTADORES/login'); + exit; + } + + // Verificar que se recibió el ID de la solicitud + $id_solicitud = $_GET['id'] ?? null; + if (!$id_solicitud || !is_numeric($id_solicitud)) { + header('Location: /IMPORTADORES/vinculaciones/solicitudesVinculacion?error=request_id_invalid'); + exit; + } + + $conn = getConnection(); + + try { + // Iniciar transacción + sqlsrv_begin_transaction($conn); + + // 1. Obtener datos de la solicitud y verificar que pertenece a la agencia del admin + $sqlSolicitud = " + SELECT + sv.id_solicitud, + sv.id_importador, + sv.id_agencia, + sv.estado, + aa.id_administrador + FROM solicitudes_vinculacion sv + INNER JOIN agencias_aduanales aa ON sv.id_agencia = aa.id_agencia + WHERE sv.id_solicitud = ? AND aa.id_administrador = ? + "; + $stmtSolicitud = sqlsrv_query($conn, $sqlSolicitud, [$id_solicitud, $_SESSION['usuario_id']]); + $solicitud = sqlsrv_fetch_array($stmtSolicitud, SQLSRV_FETCH_ASSOC); + + if (!$solicitud) { + throw new Exception('Solicitud no encontrada o no tienes permisos para aprobarla'); + } + + if ($solicitud['estado'] !== 'PENDIENTE') { + throw new Exception('Esta solicitud ya ha sido procesada'); + } + + // 2. Verificar si ya existe una relación activa entre importador y agencia + $sqlVerificar = " + SELECT id_relacion + FROM importador_agencia + WHERE id_importador = ? AND id_agencia = ? AND activo = 1 + "; + $stmtVerificar = sqlsrv_query($conn, $sqlVerificar, [$solicitud['id_importador'], $solicitud['id_agencia']]); + $relacionExistente = sqlsrv_fetch_array($stmtVerificar, SQLSRV_FETCH_ASSOC); + + if ($relacionExistente) { + throw new Exception('Ya existe una vinculación activa entre este importador y la agencia'); + } + + // 3. Actualizar el estado de la solicitud a APROBADO + $sqlActualizar = " + UPDATE solicitudes_vinculacion + SET estado = 'APROBADO', + fecha_respuesta = GETDATE(), + respondido_por = ?, + comentarios_respuesta = 'Vinculación aprobada' + WHERE id_solicitud = ? + "; + $stmtActualizar = sqlsrv_query($conn, $sqlActualizar, [$_SESSION['usuario_id'], $id_solicitud]); + + if (!$stmtActualizar) { + throw new Exception('Error al actualizar la solicitud'); + } + + // 4. Crear la relación en importador_agencia + $sqlRelacion = " + INSERT INTO importador_agencia ( + id_importador, + id_agencia, + activo, + fecha_vinculacion, + creado_por, + aprobado_por, + estado + ) VALUES (?, ?, 1, GETDATE(), ?, ?, 'APROBADO') + "; + $stmtRelacion = sqlsrv_query($conn, $sqlRelacion, [ + $solicitud['id_importador'], + $solicitud['id_agencia'], + $solicitud['id_importador'], // creado_por (el importador que solicitó) + $_SESSION['usuario_id'] // aprobado_por (el admin de agencia) + ]); + + if (!$stmtRelacion) { + throw new Exception('Error al crear la relación importador-agencia'); + } + + // Confirmar transacción + sqlsrv_commit($conn); + + header('Location: /IMPORTADORES/vinculaciones/solicitudesVinculacion?success=approved'); + exit; + + } catch (Exception $e) { + // Revertir transacción en caso de error + sqlsrv_rollback($conn); + header('Location: /IMPORTADORES/vinculaciones/solicitudesVinculacion?error=denied'); + exit; + } +} + +function denegarVinculacion() +{ + // Verificar autenticación y permisos + if (!($_SESSION['usuario_id'] ?? false) || $_SESSION['tipo_usuario'] !== 'admin_agencia') { + header('Location: /IMPORTADORES/login'); + exit; + } + + // Verificar que se recibió el ID de la solicitud + $id_solicitud = $_GET['id'] ?? null; + if (!$id_solicitud || !is_numeric($id_solicitud)) { + header('Location: /IMPORTADORES/vinculaciones/solicitudesVinculacion?error=request_id_invalid'); + exit; + } + + $conn = getConnection(); + + try { + // Iniciar transacción + sqlsrv_begin_transaction($conn); + + // 1. Obtener datos de la solicitud y verificar que pertenece a la agencia del admin + $sqlSolicitud = " + SELECT + sv.id_solicitud, + sv.id_importador, + sv.id_agencia, + sv.estado, + aa.id_administrador + FROM solicitudes_vinculacion sv + INNER JOIN agencias_aduanales aa ON sv.id_agencia = aa.id_agencia + WHERE sv.id_solicitud = ? AND aa.id_administrador = ? + "; + $stmtSolicitud = sqlsrv_query($conn, $sqlSolicitud, [$id_solicitud, $_SESSION['usuario_id']]); + $solicitud = sqlsrv_fetch_array($stmtSolicitud, SQLSRV_FETCH_ASSOC); + + if (!$solicitud) { + throw new Exception('Solicitud no encontrada o no tienes permisos para denegarla'); + } + + if ($solicitud['estado'] !== 'PENDIENTE') { + throw new Exception('Esta solicitud ya ha sido procesada'); + } + + // 2. Actualizar el estado de la solicitud a DENEGADO + $sqlActualizar = " + UPDATE solicitudes_vinculacion + SET estado = 'DENEGADO', + fecha_respuesta = GETDATE(), + respondido_por = ? + WHERE id_solicitud = ? + "; + $stmtActualizar = sqlsrv_query($conn, $sqlActualizar, [$_SESSION['usuario_id'], $id_solicitud]); + + if (!$stmtActualizar) { + throw new Exception('Error al actualizar la solicitud'); + } + + if (!$stmtRelacion) { + throw new Exception('Error al registrar el rechazo en el historial'); + } + + // Confirmar transacción + sqlsrv_commit($conn); + + header('Location: /IMPORTADORES/vinculaciones/solicitudesVinculacion?success=denied'); + exit; + + } catch (Exception $e) { + // Revertir transacción en caso de error + sqlsrv_rollback($conn); + header('Location: /IMPORTADORES/vinculaciones/solicitudesVinculacion?error=denied_error'); + exit; + } +} + +function desvincularUsuario() +{ + // ✅ CORREGIR: Debe ser admin_agencia, no importador + if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador') { + header('Location: /IMPORTADORES/login'); + exit; + } + + // Verificar que se recibió el ID de la relación + $id_relacion = $_GET['id'] ?? null; + if (!$id_relacion || !is_numeric($id_relacion)) { + header('Location: /IMPORTADORES/vinculaciones/vinculacionesUsuario?error=invalid_id'); + exit; + } + + $conn = getConnection(); + + try { + // Iniciar transacción + sqlsrv_begin_transaction($conn); + + // 1. Verificar que la relación existe y pertenece a la agencia del admin + $sqlVerificar = " + SELECT + ia.*, + u.id_usuario, u.nombre as importador_nombre, aa.nombre_agencia + FROM importador_agencia ia + INNER JOIN usuarios_sistema u ON ia.id_importador = u.id_usuario + INNER JOIN agencias_aduanales aa ON ia.id_agencia = aa.id_agencia + WHERE ia.id_relacion = ? AND ia.id_importador = ? + "; + $stmtVerificar = sqlsrv_query($conn, $sqlVerificar, [$id_relacion, $_SESSION['usuario_id']]); + + // Verificar si la consulta fue exitosa + if ($stmtVerificar === false) { + $errors = sqlsrv_errors(); + error_log("Error en consulta verificar: " . print_r($errors, true)); + throw new Exception('Error en la consulta de verificación'); + } + + $relacion = sqlsrv_fetch_array($stmtVerificar, SQLSRV_FETCH_ASSOC); + + if (!$relacion) { + throw new Exception('Relación no encontrada o no tienes permisos para desvincularla'); + } + + if ($relacion['activo'] == 0) { + throw new Exception('Esta relación ya está inactiva'); + } + + // 2. Desactivar la relación (no eliminar, mantener historial) + $sqlDesactivar = " + UPDATE importador_agencia + SET activo = 0, + fecha_desvinculacion = GETDATE(), + estado = 'DESVINCULADO' + WHERE id_relacion = ? AND id_importador = ? + "; + + $stmtDesactivar = sqlsrv_query($conn, $sqlDesactivar, [$id_relacion, $_SESSION['usuario_id']]); + + if ($stmtDesactivar === false) { + $errors = sqlsrv_errors(); + error_log("Error en actualización: " . print_r($errors, true)); + throw new Exception('Error al desactivar la relación'); + } + + // Verificar que se actualizó al menos una fila + $filasAfectadas = sqlsrv_rows_affected($stmtDesactivar); + if ($filasAfectadas === 0) { + throw new Exception('No se pudo actualizar la relación'); + } + + // Limpiar statements + sqlsrv_free_stmt($stmtVerificar); + sqlsrv_free_stmt($stmtDesactivar); + + // Confirmar transacción + sqlsrv_commit($conn); + sqlsrv_close($conn); + + header('Location: /IMPORTADORES/vinculaciones/vinculacionesUsuario?success=unlinked'); + exit; + + } catch (Exception $e) { + // Revertir transacción en caso de error + sqlsrv_rollback($conn); + sqlsrv_close($conn); + + header('Location: /IMPORTADORES/vinculaciones/vinculacionesUsuario?error=unlinked_failed&msg=' . urlencode($e->getMessage())); + exit; + } +} \ No newline at end of file diff --git a/app/helpers/bitacoras.php b/app/helpers/bitacoras.php index 8fc5f78..38f70a2 100644 --- a/app/helpers/bitacoras.php +++ b/app/helpers/bitacoras.php @@ -17,4 +17,12 @@ function registrar_bitacora_usuario($usuarioId, $accion, $descripcion) { sqlsrv_query($conn, $sql, $params); } +function registrar_bitacora_agencia($agenciaId, $nombreAgencia, $accion, $idAdmin) { + $conn = getConnection(); + $sql = "INSERT INTO bitacora_agencias (id_agencia, nombre, accion, realizado_por) + VALUES (?, ?, ?, ?)"; + $params = [$usuarioId, $nombre, $accion, $idAdmin]; + + sqlsrv_query($conn, $sql, $params); +} diff --git a/uploads/expedientes/53/685414da683db_Reporte-de-movimiento-de-cuentas-por-cobrar-768x447.png b/uploads/expedientes/53/685414da683db_Reporte-de-movimiento-de-cuentas-por-cobrar-768x447.png new file mode 100644 index 0000000..ab00d4c Binary files /dev/null and b/uploads/expedientes/53/685414da683db_Reporte-de-movimiento-de-cuentas-por-cobrar-768x447.png differ diff --git a/uploads/expedientes/53/685414da6b0a7_IMG-20250520-WA0001.jpg b/uploads/expedientes/53/685414da6b0a7_IMG-20250520-WA0001.jpg new file mode 100644 index 0000000..0e4436b Binary files /dev/null and b/uploads/expedientes/53/685414da6b0a7_IMG-20250520-WA0001.jpg differ diff --git a/uploads/expedientes/53/685414da6c671_Screenshot_20250520_130018_com.mercadopago.wallet.jpg b/uploads/expedientes/53/685414da6c671_Screenshot_20250520_130018_com.mercadopago.wallet.jpg new file mode 100644 index 0000000..7cfbe1a Binary files /dev/null and b/uploads/expedientes/53/685414da6c671_Screenshot_20250520_130018_com.mercadopago.wallet.jpg differ diff --git a/uploads/expedientes/53/685414da6ce58_IMG_20250520_125855.jpg b/uploads/expedientes/53/685414da6ce58_IMG_20250520_125855.jpg new file mode 100644 index 0000000..143d4a9 Binary files /dev/null and b/uploads/expedientes/53/685414da6ce58_IMG_20250520_125855.jpg differ diff --git a/uploads/expedientes/53/685414da6e0db_Ismael_Camarillo_Molina.pdf b/uploads/expedientes/53/685414da6e0db_Ismael_Camarillo_Molina.pdf new file mode 100644 index 0000000..4612dc4 Binary files /dev/null and b/uploads/expedientes/53/685414da6e0db_Ismael_Camarillo_Molina.pdf differ diff --git a/views/admin/agencias_activas.php b/views/admin/agencias_activas.php index fc85ff9..096816f 100644 --- a/views/admin/agencias_activas.php +++ b/views/admin/agencias_activas.php @@ -1,11 +1,17 @@ - + - + + - Dashboard | Agencia Aduanal + + Dashboard | Administrador + + + + -
-

✅ Agencias Activas

+

✅ Agencias Activas

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#NombreRFCCorreoTeléfonoDirecciónFechaAdministradorAcciones
format('Y-m-d H:i') : '' ?> + + Suspender + +
No hay agencias registradas aún.
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/views/admin/alta_agencias.php b/views/admin/alta_agencias.php new file mode 100644 index 0000000..c1a119e --- /dev/null +++ b/views/admin/alta_agencias.php @@ -0,0 +1,202 @@ + + + + + + + Dashboard | Administrador + + + + + + + + + +
+

🏪 Alta de Agencias Aduanales

+ + +
+
+
➕ Nueva Agencia Aduanal
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ + +

+

Administrador de la Agencia

+
+ + +
+
+ + +
+ +
+ +
+ +
+ +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/views/admin/alta_usuarios.php b/views/admin/alta_usuarios.php index 9502b1a..35b6047 100644 --- a/views/admin/alta_usuarios.php +++ b/views/admin/alta_usuarios.php @@ -1,128 +1,153 @@ - + - Panel de Administración - Usuarios + Dashboard | Administrador + + + + + + + -
-

Administración de Usuarios

- +
+

👥 Alta de Usuarios

-
-
➕ Nuevo Usuario
-
-
- +
+ +
➕ Nuevo Usuario
+
+
+ +
+
+ +
+
+ +
+
+ +
-
- +
+
-
- -
-
- -
-
-
- -
- + +
- -
-
👁️ Usuarios Registrados
+

✅ Usuarios Activos

+
- - +
+ - + + - - + - - - + + + + + - - - - - - - +
# Nombre Correo TipoEstadoCreado porFecha Registro Acciones
format('Y-m-d H:i') : '' ?> - - - - - Toggle - Reset + + Suspender +
No hay usuarios registrados aún.
+
- - - - - function copiarPassword() { - navigator.clipboard.writeText(nuevaPassword).then(() => { - Swal.fire('Copiado', 'La contraseña fue copiada al portapapeles.', 'success'); - }); - } + + + - function enviarPorCorreo() { - fetch(`/IMPORTADORES/sistemas/enviar_password?email=${encodeURIComponent(emailUsuario)}&pass=${encodeURIComponent(nuevaPassword)}`) - .then(response => response.text()) - .then(data => { - Swal.fire('Enviado', 'La contraseña fue enviada al correo del usuario.', 'success'); - }) - .catch(() => { - Swal.fire('Error', 'No se pudo enviar el correo.', 'error'); - }); - } + + diff --git a/views/admin/aprobar_agencias.php b/views/admin/aprobar_agencias.php new file mode 100644 index 0000000..f5bc794 --- /dev/null +++ b/views/admin/aprobar_agencias.php @@ -0,0 +1,91 @@ + + + + + + + Dashboard | Administrador + + + + + + + + +
+

✅ Aprobación de Agencias Aduanales

+ + +
+
🏪 Agencias Solicitantes
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#NombreRFCCorreoTeléfonoDirecciónFechaArchivo SATAcciones
format('Y-m-d H:i') : '' ?> + + Ver PDF + + No adjunto + + + Aprobar +
No hay agencias registradas aún.
+
+
+
+ + + \ No newline at end of file diff --git a/views/admin/aprobar_usuarios.php b/views/admin/aprobar_usuarios.php new file mode 100644 index 0000000..cae7129 --- /dev/null +++ b/views/admin/aprobar_usuarios.php @@ -0,0 +1,119 @@ + + + + + + + Dashboard | Administrador + + + + + + + + +
+

✅ Aprobación de Usuarios

+ + +
+
👥 Usuarios Solicitantes
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#EmpresaRFCCorreoTeléfonoFechaArchivo SATAcciones
format('Y-m-d H:i') : '' ?> + + Ver PDF + + No adjunto + + + Aprobar +
No hay solicitudes registradas aún.
+
+
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/views/admin/bitacora_login.php b/views/admin/bitacora_login.php deleted file mode 100644 index 80eadf4..0000000 --- a/views/admin/bitacora_login.php +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - Panel de Administración - Usuarios - - - - - - -
-

🕵️ Bitácora de Accesos

- - - - - - - - - - - - - - - - - - - - - - - - - - -
#ID UsuarioCorreoIPFechaÉxitoDetalle
format('Y-m-d H:i') : htmlspecialchars($r['fecha']) ?> - - - -
-
- - - \ No newline at end of file diff --git a/views/admin/bitacora_usuarios.php b/views/admin/bitacora_usuarios.php deleted file mode 100644 index c873c31..0000000 --- a/views/admin/bitacora_usuarios.php +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - Panel de Administración - Usuarios - - - - - - -
-

🛠️ Bitácora de Cambios de Usuario

- - - - - - - - - - - - - - - - - - - - - - -
#ID UsuarioAcciónDescripciónFecha
format('Y-m-d H:i') : htmlspecialchars($r['fecha']) ?>
-
- - - \ No newline at end of file diff --git a/views/admin/dashboard_admin.php b/views/admin/dashboard_admin.php new file mode 100644 index 0000000..99dd884 --- /dev/null +++ b/views/admin/dashboard_admin.php @@ -0,0 +1,113 @@ + + + + + + + Dashboard | Administrador + + + + + + + + +
+

📦 Panel de Administrador

+ +
+
+
+
Aprobar agencias
+

Aprueba las agencias que solicitaron un registro.

+ + Ver bitácora +
+
+ +
+
+
Alta de agencias
+

Da de alta manualmente una agencia.

+ + Nuevo agente + +
+
+ +
+
+
Aprobar de usuarios
+

Aprueba los usuarios que solicitaron un registro.

+ + Ver usuarios + +
+
+ +
+
+
Alta de usuarios
+

Da de alta manualmente a un usuario.

+ + Nuevo agente + +
+
+ +
+
+
Configuración
+

Administra los datos de tu agencia.

+ + Configurar + +
+
+ +
+
+
Cerrar sesión
+

Salir del sistema de forma segura.

+ Logout +
+
+
+ +
+ + + \ No newline at end of file diff --git a/views/admin/login_sistemas.php b/views/admin/login_sistemas.php deleted file mode 100644 index 7b2927d..0000000 --- a/views/admin/login_sistemas.php +++ /dev/null @@ -1,28 +0,0 @@ - - - - - Login Sistemas - - - - -
-

Acceso Administrativo

-
-
- - -
-
- - -
-
- -
-
-
- - - \ No newline at end of file diff --git a/views/admin/usuarios_inactivos.php b/views/admin/usuarios_inactivos.php new file mode 100644 index 0000000..245e9df --- /dev/null +++ b/views/admin/usuarios_inactivos.php @@ -0,0 +1,117 @@ + + + + + + + Dashboard | Administrador + + + + + + + + + + + + + + +
+

🚷 Usuarios Inactivos

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
#NombreCorreoTipoCreado porFecha RegistroAcciones
format('Y-m-d H:i') : '' ?> + + Activar + Nueva Contraseña + +
+
+
+ +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/views/agencias/alta_agentes.php b/views/agencias/alta_agentes.php index 2efbe6e..e08ba3a 100644 --- a/views/agencias/alta_agentes.php +++ b/views/agencias/alta_agentes.php @@ -9,6 +9,7 @@ + + + + +
+

🔗 Solicitudes de Vinculación

+ +
+
👥 Usuarios Solicitantes
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
#NombreRFCMensajeFechaArchivo SATAcciones
format('Y-m-d H:i') : '' ?> + + Ver PDF + + No adjunto + + + Aprobar + Denegar +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/views/agencias/usuarios_vinculados.php b/views/agencias/usuarios_vinculados.php new file mode 100644 index 0000000..c2714b1 --- /dev/null +++ b/views/agencias/usuarios_vinculados.php @@ -0,0 +1,105 @@ + + + + + Dashboard | Agencia Aduanal + + + + + + + + + + + +
+

👥 Usuarios Vinculados

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
#NombreRFCTeléfonoFecha de VinculaciónAcciones
format('Y-m-d H:i') : '' ?> + Desvincular +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/views/agentes/bitacora.php b/views/agentes/bitacora.php deleted file mode 100644 index 55b3269..0000000 --- a/views/agentes/bitacora.php +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - Dashboard | Agente Aduanal - - - - - - - - - - -
-

👥 Acceso de Usuarios

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
IDNombreCorreoIPFechaEstatusDetalles
format('Y-m-d H:i:s') ?> - -
-
-
-
- - - - - \ No newline at end of file diff --git a/views/agentes/dashboard_agentes.php b/views/agentes/dashboard_agentes.php index ffd1f85..07bf44e 100644 --- a/views/agentes/dashboard_agentes.php +++ b/views/agentes/dashboard_agentes.php @@ -45,7 +45,10 @@ include __DIR__ . '/../partials/sidebar_agente.php';
Importadores activos

Consulta los que ya fueron autorizados.

- Ver importadores + + Ver importadores +
@@ -53,15 +56,21 @@ include __DIR__ . '/../partials/sidebar_agente.php';
Solicitudes pendientes

Valida nuevas solicitudes de registro.

- Ver solicitudes + + Ver solicitudes +
-
Bitácora del sistema
+
Bitácora del sistema

Revisa los accesos y acciones recientes.

- Ver bitácora + + Ver bitácora +
@@ -69,7 +78,21 @@ include __DIR__ . '/../partials/sidebar_agente.php';
Locaciones

Gestiona la locaciones validas para nuevos registros.

- Gestionar locaciones + + Gestionar locaciones + +
+
+ +
+
+
Configuración
+

Administra tus datos y preferencias.

+ + Configurar +
diff --git a/views/agentes/solicitudes_pendientes.php b/views/agentes/solicitudes_vinculacion.php similarity index 100% rename from views/agentes/solicitudes_pendientes.php rename to views/agentes/solicitudes_vinculacion.php diff --git a/views/bitacoras/bitacora_agencias.php b/views/bitacoras/bitacora_agencias.php new file mode 100644 index 0000000..e69de29 diff --git a/views/bitacoras/bitacora_login.php b/views/bitacoras/bitacora_sistema.php similarity index 100% rename from views/bitacoras/bitacora_login.php rename to views/bitacoras/bitacora_sistema.php diff --git a/views/agencias/bitacora.php b/views/bitacoras/bitacora_usuario.php similarity index 74% rename from views/agencias/bitacora.php rename to views/bitacoras/bitacora_usuario.php index 3eec37b..d9a705c 100644 --- a/views/agencias/bitacora.php +++ b/views/bitacoras/bitacora_usuario.php @@ -1,4 +1,30 @@ - + diff --git a/views/agencias/usuarios_activos.php b/views/bitacoras/bitacora_usuarios.php similarity index 60% rename from views/agencias/usuarios_activos.php rename to views/bitacoras/bitacora_usuarios.php index f6f8fdb..c450a35 100644 --- a/views/agencias/usuarios_activos.php +++ b/views/bitacoras/bitacora_usuarios.php @@ -1,12 +1,13 @@ - + - + + - Dashboard | Agencia Aduanal + + Dashboard | Administrador - + + + + + + + + + \ No newline at end of file diff --git a/views/partials/sidebar_agencia.php b/views/partials/sidebar_agencia.php index d94a387..80fda4c 100644 --- a/views/partials/sidebar_agencia.php +++ b/views/partials/sidebar_agencia.php @@ -38,27 +38,34 @@ + class="nav-link px-3 py-2 "> 🏠 Inicio + + + 🔗 Vinculación + + - - ✅ Usuarios Activos + + 👥 Usuarios Vinculados + class="nav-link px-3 py-2 "> 📥 Alta de Agentes - - - 🕓 Bitácora + + + ⚙️ Configuración +
@@ -71,27 +78,33 @@
- + 🏠 Inicio + + + 🔗 Vinculación + + - - ✅ Usuarios Activos + + ✅ Usuarios Vinculados + class="nav-link px-3 py-2 "> 📥 Alta de Agentes - - - 🕓 Bitácora + + + ⚙️ Configuración
diff --git a/views/partials/sidebar_agente.php b/views/partials/sidebar_agente.php index 40bdcd0..b99f045 100644 --- a/views/partials/sidebar_agente.php +++ b/views/partials/sidebar_agente.php @@ -1,10 +1,3 @@ - - @@ -25,9 +153,22 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador - SIIH | Configuración + + SIIH | Configuración + +
- + '/IMPORTADORES/administrador/dashboard', + 'admin_agencia' => '/IMPORTADORES/agencias/dashboard', + 'agente_aduanal' => '/IMPORTADORES/agentes/dashboard', + 'importador' => '/IMPORTADORES/importadores/dashboard', + default => '/IMPORTADORES/importadores/dashboard' + }; + ?> + ← Panel Principal
@@ -41,118 +182,148 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador - - ℹ️ Información General - + + + ℹ️ Información General + + - - - - ⚙️ Automatizaciones - - - - ⚙️ Automatizaciones - + + + + + ⚙️ Automatizaciones + + + + ⚙️ Automatizaciones + + - - - - role="button" aria-expanded="true" aria-controls="submenuPreferencias" - onclick=""> - ✅ Preferencias - 1 - - - - - ✅ Preferencias - 1 - + + + + + role="button" aria-expanded="true" aria-controls="submenuPreferencias" + onclick=""> + ✅ Preferencias + 1 + + + + + ✅ Preferencias + 1 + + - - - - role="button" aria-expanded="true" aria-controls="submenuSeguridad" - onclick=""> - 👮 Seguridad - 1 - - - - - 👮 Seguridad - 1 - + + + + + role="button" aria-expanded="true" aria-controls="submenuSeguridad" + onclick=""> + 👮 Seguridad + 1 + + + + + 👮 Seguridad + 1 + + - - + + - role="button" aria-expanded="true" aria-controls="submenuBitacoras" onclick=""> 📖 Bitácoras - 1 + -