⚙️ Automatizaciones
+⚙️ Automatizaciones
+ + +Conexión con WINSAAI
++ + Configura la conexión con el sistema WINSAAI para importar automáticamente pedimentos y COVES. +
+ + + +diff --git a/app/controllers/catalogo_pedimentos.php b/app/controllers/catalogo_pedimentos.php
new file mode 100644
index 0000000..0bba1d5
--- /dev/null
+++ b/app/controllers/catalogo_pedimentos.php
@@ -0,0 +1,558 @@
+ intval($_GET['draw'] ?? 0),
+ "recordsTotal" => 0,
+ "recordsFiltered" => 0,
+ "data" => [],
+ "error" => "Error al consultar información del importador"
+ ]);
+ exit;
+ }
+
+ $importador = sqlsrv_fetch_array($stmtImportador, SQLSRV_FETCH_ASSOC);
+
+ if (!$importador) {
+ echo json_encode([
+ "draw" => intval($_GET['draw'] ?? 0),
+ "recordsTotal" => 0,
+ "recordsFiltered" => 0,
+ "data" => []
+ ]);
+ exit;
+ }
+
+ // Parámetros de DataTables
+ $draw = intval($_GET['draw'] ?? 0);
+ $start = intval($_GET['start'] ?? 0);
+ $length = intval($_GET['length'] ?? 10);
+ $search = $_GET['search']['value'] ?? '';
+
+ // Total registros sin filtro
+ $sqlTotal = "SELECT COUNT(*) AS total FROM PREVIOS_COMPARTIDOS_WS WHERE ClienteRFC = ?";
+ $stmt = sqlsrv_query($conn, $sqlTotal, [$importador['rfc']]);
+
+ if ($stmt === false) {
+ echo json_encode([
+ "draw" => $draw,
+ "recordsTotal" => 0,
+ "recordsFiltered" => 0,
+ "data" => [],
+ "error" => "Error al contar registros totales"
+ ]);
+ exit;
+ }
+
+ $row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
+ $recordsTotal = (int)($row['total'] ?? 0);
+
+ // Construir condiciones de filtro
+ $where = "ClienteRFC = ?";
+ $params = [$importador['rfc']];
+
+ if ($search !== '') {
+ $where .= " AND (Pedimento LIKE ? OR ClienteNombre LIKE ? OR ClavePed LIKE ?)";
+ $like = "%{$search}%";
+ $params = array_merge($params, [$like, $like, $like]);
+ }
+
+ // Total registros filtrados
+ $sqlFiltered = "SELECT COUNT(*) AS total FROM PREVIOS_COMPARTIDOS_WS WHERE $where";
+ $stmtF = sqlsrv_query($conn, $sqlFiltered, $params);
+
+ if ($stmtF === false) {
+ echo json_encode([
+ "draw" => $draw,
+ "recordsTotal" => $recordsTotal,
+ "recordsFiltered" => 0,
+ "data" => [],
+ "error" => "Error al contar registros filtrados"
+ ]);
+ exit;
+ }
+
+ $rowF = sqlsrv_fetch_array($stmtF, SQLSRV_FETCH_ASSOC);
+ $recordsFiltered = (int)($rowF['total'] ?? 0);
+
+ // Datos de la página
+ $sqlData = "SELECT IdPrevio, Pedimento, ClienteRFC, ClienteNombre, Timestamp, Status
+ FROM PREVIOS_COMPARTIDOS_WS
+ WHERE $where
+ ORDER BY Timestamp DESC
+ OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
+ $params[] = $start;
+ $params[] = $length;
+
+ $stmtD = sqlsrv_query($conn, $sqlData, $params);
+
+ $data = [];
+ if ($stmtD !== false) {
+ while ($r = sqlsrv_fetch_array($stmtD, SQLSRV_FETCH_ASSOC)) {
+ $timestamp = $r['Timestamp'] instanceof DateTime ? $r['Timestamp']->format('Y-m-d H:i:s') : '';
+ $status_text = $r['Status'] == 1 ? 'Activo' : 'Inactivo';
+
+ $data[] = [
+ $r['IdPrevio'],
+ $r['Pedimento'],
+ $r['ClienteRFC'],
+ $r['ClienteNombre'],
+ $timestamp,
+ $status_text
+ ];
+ }
+ }
+
+ $response = [
+ "draw" => $draw,
+ "recordsTotal" => $recordsTotal,
+ "recordsFiltered" => $recordsFiltered,
+ "data" => $data
+ ];
+
+ header('Content-Type: application/json; charset=UTF-8');
+ echo json_encode($response, JSON_UNESCAPED_UNICODE);
+ exit;
+}
+
+function buscar_pedimentos()
+{
+ if (!($_SESSION['usuario_id'] ?? false)) {
+ http_response_code(403);
+ echo json_encode([]);
+ exit;
+ }
+
+ $id_usuario = $_SESSION['usuario_id'];
+ $query = trim($_GET['q'] ?? '');
+ $limit = intval($_GET['limit'] ?? 10); // ✅ NUEVO: Parámetro limit con valor por defecto
+
+ // ✅ MODIFICADO: Para el panel de referencia, si no hay query, traer los más recientes
+ if (empty($query)) {
+ // Si no hay query, obtener los pedimentos más recientes para el panel de referencia
+ $whereCondition = "ClienteRFC = ? AND Status = 1";
+ $searchParams = [];
+ } else {
+ // Si hay query, mantener la lógica original de búsqueda
+ if (strlen($query) < 3) {
+ echo json_encode([]);
+ exit;
+ }
+ $whereCondition = "ClienteRFC = ? AND (Pedimento LIKE ? OR ClienteNombre LIKE ? OR ClavePed LIKE ?) AND Status = 1";
+ $like = "%{$query}%";
+ $searchParams = [$like, $like, $like];
+ }
+
+ $conn = getConnection();
+
+ // Obtener RFC del usuario para filtrar solo sus pedimentos
+ $sqlImportador = "SELECT rfc FROM informacion_general WHERE id_usuario = ?";
+ $stmtImportador = sqlsrv_query($conn, $sqlImportador, [$id_usuario]);
+
+ if ($stmtImportador === false) {
+ echo json_encode([]);
+ exit;
+ }
+
+ $importador = sqlsrv_fetch_array($stmtImportador, SQLSRV_FETCH_ASSOC);
+
+ if (!$importador) {
+ echo json_encode([]);
+ exit;
+ }
+
+ // ✅ MODIFICADO: Query dinámico con límite configurable
+ $sql = "SELECT TOP {$limit} IdPrevio, Pedimento, ClienteRFC, ClienteNombre, ClavePed, Timestamp
+ FROM PREVIOS_COMPARTIDOS_WS
+ WHERE {$whereCondition}
+ ORDER BY Timestamp DESC";
+
+ $params = array_merge([$importador['rfc']], $searchParams);
+ $stmt = sqlsrv_query($conn, $sql, $params);
+
+ $pedimentos = [];
+ if ($stmt !== false) {
+ while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
+ $fecha_formateada = '';
+ if ($row['Timestamp'] instanceof DateTime) {
+ $fecha_formateada = $row['Timestamp']->format('d/m/Y');
+ }
+
+ $pedimentos[] = [
+ 'IdPrevio' => $row['IdPrevio'],
+ 'Pedimento' => $row['Pedimento'],
+ 'ClienteRFC' => $row['ClienteRFC'],
+ 'ClienteNombre' => $row['ClienteNombre'],
+ 'ClavePed' => $row['ClavePed'],
+ 'fecha_formateada' => $fecha_formateada
+ ];
+ }
+ }
+
+ header('Content-Type: application/json; charset=UTF-8');
+ echo json_encode($pedimentos, JSON_UNESCAPED_UNICODE);
+ exit;
+}
\ No newline at end of file
diff --git a/app/controllers/claves_pedimentos.php b/app/controllers/claves_pedimentos.php
new file mode 100644
index 0000000..c8c522b
--- /dev/null
+++ b/app/controllers/claves_pedimentos.php
@@ -0,0 +1,553 @@
+ 10) {
+ die("❌ El código no puede tener más de 10 caracteres.");
+ }
+
+ if (!preg_match('/^[A-Z0-9]+$/', $codigo)) {
+ die("❌ El código solo puede contener letras y números.");
+ }
+
+ // Verificar que no exista el código para este usuario
+ $sqlCheck = "SELECT COUNT(*) as count FROM claves_pedimentos_usuario WHERE id_usuario = ? AND codigo = ?";
+ $stmtCheck = sqlsrv_query($conn, $sqlCheck, [$id_usuario, $codigo]);
+
+ if ($stmtCheck === false) {
+ die("❌ Error al verificar código existente: " . print_r(sqlsrv_errors(), true));
+ }
+
+ $result = sqlsrv_fetch_array($stmtCheck, SQLSRV_FETCH_ASSOC);
+
+ if ($result['count'] > 0) {
+ die("❌ Ya existe una clave con el código '{$codigo}' para este usuario.");
+ }
+
+ $sql = "INSERT INTO claves_pedimentos_usuario
+ (id_usuario, codigo, descripcion, tipo_operacion, activo, fecha_creacion, fecha_modificacion)
+ VALUES (?, ?, ?, ?, ?, GETDATE(), GETDATE())";
+
+ $params = [$id_usuario, $codigo, $descripcion, $tipo_operacion, $activo];
+
+ $stmt = sqlsrv_query($conn, $sql, $params);
+
+ if ($stmt === false) {
+ die("❌ Error al guardar: " . print_r(sqlsrv_errors(), true));
+ }
+
+ header('Location: /IMPORTADORES/claves_pedimentos/lista?created=ok');
+ exit;
+}
+
+function editar()
+{
+ if (!($_SESSION['usuario_id'] ?? false)) {
+ header('Location: /IMPORTADORES/login');
+ exit;
+ }
+
+ $id = $_GET['id'] ?? null;
+ $id_usuario = $_SESSION['usuario_id'];
+
+ if (!$id || !is_numeric($id)) {
+ die("❌ ID inválido.");
+ }
+
+ $conn = getConnection();
+
+ $sql = "SELECT * FROM claves_pedimentos_usuario WHERE id_clave_pedimento = ? AND id_usuario = ?";
+ $stmt = sqlsrv_query($conn, $sql, [$id, $id_usuario]);
+
+ if ($stmt === false) {
+ die("❌ Error en consulta: " . print_r(sqlsrv_errors(), true));
+ }
+
+ $clave = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
+
+ if (!$clave) {
+ die("❌ Clave de pedimento no encontrada.");
+ }
+
+ include __DIR__ . '/../../views/claves_pedimentos/editar.php';
+}
+
+function actualizar()
+{
+ if (!($_SESSION['usuario_id'] ?? false)) {
+ die("⚠️ No autorizado.");
+ }
+
+ $conn = getConnection();
+ $id_usuario = $_SESSION['usuario_id'];
+
+ $id_clave_pedimento = $_POST['id_clave_pedimento'] ?? null;
+ $codigo = strtoupper(trim($_POST['codigo'] ?? ''));
+ $descripcion = trim($_POST['descripcion'] ?? '');
+ $tipo_operacion = $_POST['tipo_operacion'] ?? 'importacion';
+ $activo = isset($_POST['activo']) ? 1 : 0;
+
+ if (!$id_clave_pedimento || !is_numeric($id_clave_pedimento)) {
+ die("❌ ID inválido.");
+ }
+
+ // Validaciones
+ if (empty($codigo) || empty($descripcion)) {
+ die("❌ El código y descripción son obligatorios.");
+ }
+
+ if (strlen($codigo) > 10) {
+ die("❌ El código no puede tener más de 10 caracteres.");
+ }
+
+ if (!preg_match('/^[A-Z0-9]+$/', $codigo)) {
+ die("❌ El código solo puede contener letras y números.");
+ }
+
+ // Verificar que no exista otro código igual (excepto el actual)
+ $sqlCheck = "SELECT COUNT(*) as count FROM claves_pedimentos_usuario
+ WHERE id_usuario = ? AND codigo = ? AND id_clave_pedimento != ?";
+ $stmtCheck = sqlsrv_query($conn, $sqlCheck, [$id_usuario, $codigo, $id_clave_pedimento]);
+
+ if ($stmtCheck === false) {
+ die("❌ Error al verificar código existente: " . print_r(sqlsrv_errors(), true));
+ }
+
+ $result = sqlsrv_fetch_array($stmtCheck, SQLSRV_FETCH_ASSOC);
+
+ if ($result['count'] > 0) {
+ die("❌ Ya existe otra clave con el código '{$codigo}' para este usuario.");
+ }
+
+ $sql = "UPDATE claves_pedimentos_usuario SET
+ codigo = ?, descripcion = ?, tipo_operacion = ?, activo = ?, fecha_modificacion = GETDATE()
+ WHERE id_clave_pedimento = ? AND id_usuario = ?";
+
+ $params = [$codigo, $descripcion, $tipo_operacion, $activo, $id_clave_pedimento, $id_usuario];
+
+ $stmt = sqlsrv_query($conn, $sql, $params);
+
+ if ($stmt === false) {
+ die("❌ Error al actualizar: " . print_r(sqlsrv_errors(), true));
+ }
+
+ header('Location: /IMPORTADORES/claves_pedimentos/lista?updated=ok');
+ exit;
+}
+
+function eliminar()
+{
+ if (!($_SESSION['usuario_id'] ?? false)) {
+ header('Location: /IMPORTADORES/login');
+ exit;
+ }
+
+ $id = $_GET['id'] ?? null;
+ $id_usuario = $_SESSION['usuario_id'];
+
+ if (!$id || !is_numeric($id)) {
+ die("❌ ID inválido.");
+ }
+
+ $conn = getConnection();
+
+ $sql = "DELETE FROM claves_pedimentos_usuario WHERE id_clave_pedimento = ? AND id_usuario = ?";
+ $stmt = sqlsrv_query($conn, $sql, [$id, $id_usuario]);
+
+ if ($stmt === false) {
+ die("❌ Error al eliminar: " . print_r(sqlsrv_errors(), true));
+ }
+
+ header('Location: /IMPORTADORES/claves_pedimentos/lista?deleted=ok');
+ exit;
+}
+
+function ajax_lista()
+{
+ if (!($_SESSION['usuario_id'] ?? false)) {
+ http_response_code(403);
+ echo json_encode([]);
+ exit;
+ }
+
+ $id_usuario = $_SESSION['usuario_id'];
+ $conn = getConnection();
+
+ // Parámetros de DataTables
+ $draw = intval($_GET['draw'] ?? 0);
+ $start = intval($_GET['start'] ?? 0);
+ $length = intval($_GET['length'] ?? 10);
+ $search = $_GET['search']['value'] ?? '';
+
+ // Total registros sin filtro
+ $sqlTotal = "SELECT COUNT(*) AS total FROM claves_pedimentos_usuario WHERE id_usuario = ?";
+ $stmt = sqlsrv_query($conn, $sqlTotal, [$id_usuario]);
+
+ if ($stmt === false) {
+ echo json_encode([
+ "draw" => $draw,
+ "recordsTotal" => 0,
+ "recordsFiltered" => 0,
+ "data" => [],
+ "error" => "Error al contar registros totales"
+ ]);
+ exit;
+ }
+
+ $row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
+ $recordsTotal = (int)($row['total'] ?? 0);
+
+ // Construir condiciones de filtro
+ $where = "id_usuario = ?";
+ $params = [$id_usuario];
+
+ if ($search !== '') {
+ $where .= " AND (codigo LIKE ? OR descripcion LIKE ? OR tipo_operacion LIKE ?)";
+ $like = "%{$search}%";
+ $params = array_merge($params, [$like, $like, $like]);
+ }
+
+ // Total registros filtrados
+ $sqlFiltered = "SELECT COUNT(*) AS total FROM claves_pedimentos_usuario WHERE $where";
+ $stmtF = sqlsrv_query($conn, $sqlFiltered, $params);
+
+ if ($stmtF === false) {
+ echo json_encode([
+ "draw" => $draw,
+ "recordsTotal" => $recordsTotal,
+ "recordsFiltered" => 0,
+ "data" => [],
+ "error" => "Error al contar registros filtrados"
+ ]);
+ exit;
+ }
+
+ $rowF = sqlsrv_fetch_array($stmtF, SQLSRV_FETCH_ASSOC);
+ $recordsFiltered = (int)($rowF['total'] ?? 0);
+
+ // Datos de la página
+ $sqlData = "SELECT id_clave_pedimento, codigo, descripcion, tipo_operacion,
+ CASE WHEN activo = 1 THEN 'Activo' ELSE 'Inactivo' END as estado,
+ FORMAT(fecha_creacion, 'dd/MM/yyyy HH:mm') as fecha_creacion
+ FROM claves_pedimentos_usuario
+ WHERE $where
+ ORDER BY codigo ASC
+ OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
+ $params[] = $start;
+ $params[] = $length;
+
+ $stmtD = sqlsrv_query($conn, $sqlData, $params);
+
+ $data = [];
+ if ($stmtD !== false) {
+ while ($r = sqlsrv_fetch_array($stmtD, SQLSRV_FETCH_ASSOC)) {
+ $data[] = [
+ $r['id_clave_pedimento'],
+ $r['codigo'],
+ $r['descripcion'],
+ ucfirst($r['tipo_operacion']),
+ $r['estado'],
+ $r['fecha_creacion']
+ ];
+ }
+ }
+
+ $response = [
+ "draw" => $draw,
+ "recordsTotal" => $recordsTotal,
+ "recordsFiltered" => $recordsFiltered,
+ "data" => $data
+ ];
+
+ header('Content-Type: application/json; charset=UTF-8');
+ echo json_encode($response, JSON_UNESCAPED_UNICODE);
+ exit;
+}
+
+function inicializar_claves_usuario()
+{
+ if (!($_SESSION['usuario_id'] ?? false)) {
+ header('Location: /IMPORTADORES/login');
+ exit;
+ }
+
+ $conn = getConnection();
+ $id_usuario = $_SESSION['usuario_id'];
+
+ // Verificar si ya tiene claves configuradas
+ $sqlCheck = "SELECT COUNT(*) as count FROM claves_pedimentos_usuario WHERE id_usuario = ?";
+ $stmtCheck = sqlsrv_query($conn, $sqlCheck, [$id_usuario]);
+
+ if ($stmtCheck === false) {
+ die("❌ Error al verificar claves existentes: " . print_r(sqlsrv_errors(), true));
+ }
+
+ $result = sqlsrv_fetch_array($stmtCheck, SQLSRV_FETCH_ASSOC);
+
+ if ($result['count'] > 0) {
+ header('Location: /IMPORTADORES/claves_pedimentos/lista?info=already_initialized');
+ exit;
+ }
+
+ // Claves de pedimentos por defecto para importación
+ $claves_default = [
+ ['A1', 'Importación definitiva de mercancías', 'importacion'],
+ ['A3', 'Importación definitiva de vehículos usados', 'importacion'],
+ ['A4', 'Importación definitiva de vehículos nuevos', 'importacion'],
+ ['B1', 'Importación temporal para elaborar, transformar o reparar', 'importacion'],
+ ['C1', 'Importación definitiva de mercancías donadas', 'importacion'],
+ ['G1', 'Importación de mercancías con Programa IMMEX', 'importacion'],
+ ['I1', 'Importación definitiva exenta', 'importacion'],
+ ['J1', 'Importación temporal para reexportación en el mismo estado', 'importacion'],
+ ['L1', 'Importación definitiva con franquicia arancelaria con TLC', 'importacion'],
+ ['M1', 'Importación de menajes de casa', 'importacion'],
+ ['N1', 'Importación de equipaje', 'importacion'],
+ ['P1', 'Importación temporal de remolques y semirremolques', 'importacion'],
+ ['R1', 'Importación temporal de contenedores', 'importacion'],
+ ['S1', 'Importación temporal de vehículos', 'importacion'],
+ ['T1', 'Importación temporal de enseres de tripulantes', 'importacion'],
+ ['V1', 'Importación temporal de mercancías para exposición', 'importacion']
+ ];
+
+ $sql = "INSERT INTO claves_pedimentos_usuario
+ (id_usuario, codigo, descripcion, tipo_operacion, activo, fecha_creacion, fecha_modificacion)
+ VALUES (?, ?, ?, ?, 1, GETDATE(), GETDATE())";
+
+ $insertadas = 0;
+ foreach ($claves_default as $clave) {
+ $params = [$id_usuario, $clave[0], $clave[1], $clave[2]];
+ $stmt = sqlsrv_query($conn, $sql, $params);
+
+ if ($stmt !== false) {
+ $insertadas++;
+ }
+ }
+
+ if ($insertadas > 0) {
+ header('Location: /IMPORTADORES/claves_pedimentos/lista?created=initialized');
+ } else {
+ die("❌ Error al inicializar las claves de pedimentos.");
+ }
+ exit;
+}
+
+function importar_csv()
+{
+ if (!($_SESSION['usuario_id'] ?? false)) {
+ header('Location: /IMPORTADORES/login');
+ exit;
+ }
+
+ include __DIR__ . '/../../views/claves_pedimentos/importar_csv.php';
+}
+
+function procesar_csv()
+{
+ if (!($_SESSION['usuario_id'] ?? false)) {
+ die("⚠️ No autorizado.");
+ }
+
+ $conn = getConnection();
+ $id_usuario = $_SESSION['usuario_id'];
+
+ // Verificar que se subió un archivo
+ if (!isset($_FILES['csv_file']) || $_FILES['csv_file']['error'] !== UPLOAD_ERR_OK) {
+ die("❌ Error: No se pudo cargar el archivo CSV.");
+ }
+
+ $archivo_csv = $_FILES['csv_file']['tmp_name'];
+ $nombre_archivo = $_FILES['csv_file']['name'];
+
+ // Validar extensión
+ if (!str_ends_with(strtolower($nombre_archivo), '.csv')) {
+ die("❌ Error: El archivo debe tener extensión .csv");
+ }
+
+ // Opciones de importación
+ $omitir_duplicados = isset($_POST['omitir_duplicados']);
+ $activar_todas = isset($_POST['activar_todas']);
+
+ try {
+ // Leer archivo CSV
+ $archivo = fopen($archivo_csv, 'r');
+ if (!$archivo) {
+ die("❌ Error: No se pudo abrir el archivo CSV.");
+ }
+
+ // Leer encabezados
+ $encabezados = fgetcsv($archivo, 1000, ',');
+ if (!$encabezados) {
+ fclose($archivo);
+ die("❌ Error: El archivo CSV está vacío o no tiene el formato correcto.");
+ }
+
+ // Validar encabezados requeridos
+ $encabezados_requeridos = ['codigo', 'descripcion', 'tipo_operacion', 'activo'];
+ $encabezados_faltantes = array_diff($encabezados_requeridos, $encabezados);
+
+ if (!empty($encabezados_faltantes)) {
+ fclose($archivo);
+ die("❌ Error: Faltan las siguientes columnas: " . implode(', ', $encabezados_faltantes));
+ }
+
+ $insertadas = 0;
+ $omitidas = 0;
+ $errores = [];
+ $fila_numero = 1;
+
+ // SQL para verificar códigos existentes
+ $sqlCheck = "SELECT COUNT(*) as count FROM claves_pedimentos_usuario WHERE id_usuario = ? AND codigo = ?";
+
+ // SQL para insertar
+ $sqlInsert = "INSERT INTO claves_pedimentos_usuario
+ (id_usuario, codigo, descripcion, tipo_operacion, activo, fecha_creacion, fecha_modificacion)
+ VALUES (?, ?, ?, ?, ?, GETDATE(), GETDATE())";
+
+ // Procesar cada fila
+ while (($fila = fgetcsv($archivo, 1000, ',')) !== FALSE) {
+ $fila_numero++;
+
+ if (count($fila) < count($encabezados_requeridos)) {
+ $errores[] = "Fila $fila_numero: Datos insuficientes";
+ continue;
+ }
+
+ // Crear array asociativo
+ $datos = array_combine($encabezados, $fila);
+
+ // Validar datos
+ $codigo = strtoupper(trim($datos['codigo']));
+ $descripcion = trim($datos['descripcion']);
+ $tipo_operacion = trim($datos['tipo_operacion']);
+ $activo = $activar_todas ? 1 : (int)($datos['activo'] ?? 1);
+
+ // Validaciones
+ if (empty($codigo)) {
+ $errores[] = "Fila $fila_numero: Código vacío";
+ continue;
+ }
+
+ if (empty($descripcion)) {
+ $errores[] = "Fila $fila_numero: Descripción vacía";
+ continue;
+ }
+
+ if (!in_array($tipo_operacion, ['importacion', 'exportacion'])) {
+ $errores[] = "Fila $fila_numero: Tipo de operación inválido ($tipo_operacion)";
+ continue;
+ }
+
+ if (strlen($codigo) > 10) {
+ $errores[] = "Fila $fila_numero: Código muy largo (máximo 10 caracteres)";
+ continue;
+ }
+
+ if (!preg_match('/^[A-Z0-9]+$/', $codigo)) {
+ $errores[] = "Fila $fila_numero: Código inválido (solo letras y números)";
+ continue;
+ }
+
+ // Verificar si ya existe
+ if ($omitir_duplicados) {
+ $stmtCheck = sqlsrv_query($conn, $sqlCheck, [$id_usuario, $codigo]);
+
+ if ($stmtCheck === false) {
+ $errores[] = "Fila $fila_numero: Error al verificar código existente";
+ continue;
+ }
+
+ $result = sqlsrv_fetch_array($stmtCheck, SQLSRV_FETCH_ASSOC);
+
+ if ($result['count'] > 0) {
+ $omitidas++;
+ continue;
+ }
+ }
+
+ // Insertar registro
+ $params = [$id_usuario, $codigo, $descripcion, $tipo_operacion, $activo];
+ $stmt = sqlsrv_query($conn, $sqlInsert, $params);
+
+ if ($stmt === false) {
+ $sql_errors = sqlsrv_errors();
+ $errores[] = "Fila $fila_numero: Error al insertar - " . $sql_errors[0]['message'];
+ } else {
+ $insertadas++;
+ }
+ }
+
+ fclose($archivo);
+
+ // Preparar mensaje de resultado
+ $mensaje = "✅ Proceso completado:";
+ $mensaje .= "
• Registros insertados: $insertadas";
+ if ($omitidas > 0) {
+ $mensaje .= "
• Registros omitidos (duplicados): $omitidas";
+ }
+ if (!empty($errores)) {
+ $mensaje .= "
• Errores encontrados: " . count($errores);
+ $mensaje .= "
Detalle de errores:
" . implode("
", array_slice($errores, 0, 10));
+ if (count($errores) > 10) {
+ $mensaje .= "
... y " . (count($errores) - 10) . " errores más.";
+ }
+ }
+
+ // Redirigir con resultado
+ $encoded_message = urlencode($mensaje);
+ header("Location: /IMPORTADORES/claves_pedimentos/lista?imported=ok&message=" . $encoded_message);
+ exit;
+
+ } catch (Exception $e) {
+ die("❌ Error inesperado: " . $e->getMessage());
+ }
+}
\ No newline at end of file
diff --git a/app/controllers/login.php b/app/controllers/login.php
index f1423a1..8ef9dc5 100644
--- a/app/controllers/login.php
+++ b/app/controllers/login.php
@@ -1112,4 +1112,38 @@ function cambiarPassword()
";
-}
\ No newline at end of file
+}
+
+// MÉTODOS ADICIONALES CON NOMBRES DE RUTA COMPATIBLES
+
+function enviar_codigo()
+{
+ // Redirigir al método camelCase existente
+ return enviarCodigo();
+}
+
+function verificar_codigo_recuperacion()
+{
+ // Redirigir al método camelCase existente
+ return verificarCodigo();
+}
+
+function reenviar_codigo()
+{
+ // Redirigir al método camelCase existente
+ return reenviarCodigo();
+}
+
+function actualizar_password()
+{
+ // Redirigir al método camelCase existente
+ return cambiarPassword();
+}
+
+function verificar_codigo()
+{
+ // Mostrar vista de verificar código
+ return verificarCodigoVista();
+}
+
+?>
\ No newline at end of file
diff --git a/app/controllers/mve.php b/app/controllers/mve.php
new file mode 100644
index 0000000..21f9f6c
--- /dev/null
+++ b/app/controllers/mve.php
@@ -0,0 +1,190 @@
+ false, 'message' => 'Usuario no autenticado']);
+ return;
+ }
+
+ $id_factura = $_POST['id_factura'] ?? null;
+ $id_pedimento = $_POST['id_pedimento'] ?? null;
+ $datos_art65 = json_decode($_POST['datos_art65'] ?? '{}', true);
+ $datos_art66 = json_decode($_POST['datos_art66'] ?? '{}', true);
+
+ if (!$id_factura || !$id_pedimento) {
+ echo json_encode(['success' => false, 'message' => 'Faltan datos requeridos']);
+ return;
+ }
+
+ $db = getDB();
+
+ // Verificar si ya existen datos para esta factura
+ $stmt = $db->prepare("SELECT id FROM mve_facturas_datos WHERE id_pedimento = ? AND id_factura = ?");
+ $stmt->execute([$id_pedimento, $id_factura]);
+ $existe = $stmt->fetch();
+
+ if ($existe) {
+ // Actualizar registro existente
+ $sql = "UPDATE mve_facturas_datos SET
+ art65_fecha_transporte = ?, art65_importe_transporte = ?,
+ art65_fecha_descuentos = ?, art65_importe_descuentos = ?,
+ art65_fecha_posteriores = ?, art65_importe_posteriores = ?,
+ art65_fecha_contribuciones = ?, art65_importe_contribuciones = ?,
+ art65_fecha_pagos_vendedor = ?, art65_importe_pagos_vendedor = ?,
+
+ art66_fecha_comisiones = ?, art66_importe_comisiones = ?, art66_cargo_comisiones = ?,
+ art66_fecha_envases = ?, art66_importe_envases = ?, art66_cargo_envases = ?,
+ art66_fecha_embalaje = ?, art66_importe_embalaje = ?, art66_cargo_embalaje = ?,
+ art66_fecha_transporte_dec = ?, art66_importe_transporte_dec = ?, art66_cargo_transporte_dec = ?,
+ art66_fecha_ingenieria = ?, art66_importe_ingenieria = ?, art66_cargo_ingenieria = ?,
+ art66_fecha_regalias = ?, art66_importe_regalias = ?, art66_cargo_regalias = ?,
+ art66_fecha_producto = ?, art66_importe_producto = ?, art66_cargo_producto = ?,
+
+ fecha_actualizacion = NOW()
+ WHERE id_pedimento = ? AND id_factura = ?";
+
+ $params = [
+ $datos_art65['fecha_transporte'] ?: null, $datos_art65['importe_transporte'] ?: null,
+ $datos_art65['fecha_descuentos'] ?: null, $datos_art65['importe_descuentos'] ?: null,
+ $datos_art65['fecha_posteriores'] ?: null, $datos_art65['importe_posteriores'] ?: null,
+ $datos_art65['fecha_contribuciones'] ?: null, $datos_art65['importe_contribuciones'] ?: null,
+ $datos_art65['fecha_pagos_vendedor'] ?: null, $datos_art65['importe_pagos_vendedor'] ?: null,
+
+ $datos_art66['fecha_comisiones'] ?: null, $datos_art66['importe_comisiones'] ?: null, $datos_art66['cargo_comisiones'] ?: null,
+ $datos_art66['fecha_envases'] ?: null, $datos_art66['importe_envases'] ?: null, $datos_art66['cargo_envases'] ?: null,
+ $datos_art66['fecha_embalaje'] ?: null, $datos_art66['importe_embalaje'] ?: null, $datos_art66['cargo_embalaje'] ?: null,
+ $datos_art66['fecha_transporte_dec'] ?: null, $datos_art66['importe_transporte_dec'] ?: null, $datos_art66['cargo_transporte_dec'] ?: null,
+ $datos_art66['fecha_ingenieria'] ?: null, $datos_art66['importe_ingenieria'] ?: null, $datos_art66['cargo_ingenieria'] ?: null,
+ $datos_art66['fecha_regalias'] ?: null, $datos_art66['importe_regalias'] ?: null, $datos_art66['cargo_regalias'] ?: null,
+ $datos_art66['fecha_producto'] ?: null, $datos_art66['importe_producto'] ?: null, $datos_art66['cargo_producto'] ?: null,
+
+ $id_pedimento, $id_factura
+ ];
+ } else {
+ // Crear nuevo registro
+ $sql = "INSERT INTO mve_facturas_datos (
+ id_pedimento, id_factura, numero_factura,
+ art65_fecha_transporte, art65_importe_transporte,
+ art65_fecha_descuentos, art65_importe_descuentos,
+ art65_fecha_posteriores, art65_importe_posteriores,
+ art65_fecha_contribuciones, art65_importe_contribuciones,
+ art65_fecha_pagos_vendedor, art65_importe_pagos_vendedor,
+
+ art66_fecha_comisiones, art66_importe_comisiones, art66_cargo_comisiones,
+ art66_fecha_envases, art66_importe_envases, art66_cargo_envases,
+ art66_fecha_embalaje, art66_importe_embalaje, art66_cargo_embalaje,
+ art66_fecha_transporte_dec, art66_importe_transporte_dec, art66_cargo_transporte_dec,
+ art66_fecha_ingenieria, art66_importe_ingenieria, art66_cargo_ingenieria,
+ art66_fecha_regalias, art66_importe_regalias, art66_cargo_regalias,
+ art66_fecha_producto, art66_importe_producto, art66_cargo_producto,
+
+ usuario_creacion
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+
+ $params = [
+ $id_pedimento, $id_factura, "FACTURA-$id_factura",
+ $datos_art65['fecha_transporte'] ?: null, $datos_art65['importe_transporte'] ?: null,
+ $datos_art65['fecha_descuentos'] ?: null, $datos_art65['importe_descuentos'] ?: null,
+ $datos_art65['fecha_posteriores'] ?: null, $datos_art65['importe_posteriores'] ?: null,
+ $datos_art65['fecha_contribuciones'] ?: null, $datos_art65['importe_contribuciones'] ?: null,
+ $datos_art65['fecha_pagos_vendedor'] ?: null, $datos_art65['importe_pagos_vendedor'] ?: null,
+
+ $datos_art66['fecha_comisiones'] ?: null, $datos_art66['importe_comisiones'] ?: null, $datos_art66['cargo_comisiones'] ?: null,
+ $datos_art66['fecha_envases'] ?: null, $datos_art66['importe_envases'] ?: null, $datos_art66['cargo_envases'] ?: null,
+ $datos_art66['fecha_embalaje'] ?: null, $datos_art66['importe_embalaje'] ?: null, $datos_art66['cargo_embalaje'] ?: null,
+ $datos_art66['fecha_transporte_dec'] ?: null, $datos_art66['importe_transporte_dec'] ?: null, $datos_art66['cargo_transporte_dec'] ?: null,
+ $datos_art66['fecha_ingenieria'] ?: null, $datos_art66['importe_ingenieria'] ?: null, $datos_art66['cargo_ingenieria'] ?: null,
+ $datos_art66['fecha_regalias'] ?: null, $datos_art66['importe_regalias'] ?: null, $datos_art66['cargo_regalias'] ?: null,
+ $datos_art66['fecha_producto'] ?: null, $datos_art66['importe_producto'] ?: null, $datos_art66['cargo_producto'] ?: null,
+
+ $_SESSION['user_id']
+ ];
+ }
+
+ $stmt = $db->prepare($sql);
+ $resultado = $stmt->execute($params);
+
+ if ($resultado) {
+ echo json_encode(['success' => true, 'message' => 'Datos guardados correctamente']);
+ } else {
+ echo json_encode(['success' => false, 'message' => 'Error al guardar los datos']);
+ }
+
+ } catch (Exception $e) {
+ echo json_encode(['success' => false, 'message' => 'Error interno: ' . $e->getMessage()]);
+ }
+}
+
+function ajax_obtener_datos_factura() {
+ try {
+ $id_factura = $_GET['id_factura'] ?? null;
+
+ if (!$id_factura) {
+ echo json_encode(['success' => false, 'message' => 'ID de factura requerido']);
+ return;
+ }
+
+ $db = getDB();
+ $stmt = $db->prepare("SELECT * FROM mve_facturas_datos WHERE id_factura = ?");
+ $stmt->execute([$id_factura]);
+ $datos = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ if ($datos) {
+ // Estructurar datos para el frontend
+ $datosEstructurados = [
+ 'art65' => [
+ 'fecha_transporte' => $datos['art65_fecha_transporte'],
+ 'importe_transporte' => $datos['art65_importe_transporte'],
+ 'fecha_descuentos' => $datos['art65_fecha_descuentos'],
+ 'importe_descuentos' => $datos['art65_importe_descuentos'],
+ 'fecha_posteriores' => $datos['art65_fecha_posteriores'],
+ 'importe_posteriores' => $datos['art65_importe_posteriores'],
+ 'fecha_contribuciones' => $datos['art65_fecha_contribuciones'],
+ 'importe_contribuciones' => $datos['art65_importe_contribuciones'],
+ 'fecha_pagos_vendedor' => $datos['art65_fecha_pagos_vendedor'],
+ 'importe_pagos_vendedor' => $datos['art65_importe_pagos_vendedor']
+ ],
+ 'art66' => [
+ 'fecha_comisiones' => $datos['art66_fecha_comisiones'],
+ 'importe_comisiones' => $datos['art66_importe_comisiones'],
+ 'cargo_comisiones' => $datos['art66_cargo_comisiones'],
+ 'fecha_envases' => $datos['art66_fecha_envases'],
+ 'importe_envases' => $datos['art66_importe_envases'],
+ 'cargo_envases' => $datos['art66_cargo_envases'],
+ 'fecha_embalaje' => $datos['art66_fecha_embalaje'],
+ 'importe_embalaje' => $datos['art66_importe_embalaje'],
+ 'cargo_embalaje' => $datos['art66_cargo_embalaje'],
+ 'fecha_transporte_dec' => $datos['art66_fecha_transporte_dec'],
+ 'importe_transporte_dec' => $datos['art66_importe_transporte_dec'],
+ 'cargo_transporte_dec' => $datos['art66_cargo_transporte_dec'],
+ 'fecha_ingenieria' => $datos['art66_fecha_ingenieria'],
+ 'importe_ingenieria' => $datos['art66_importe_ingenieria'],
+ 'cargo_ingenieria' => $datos['art66_cargo_ingenieria'],
+ 'fecha_regalias' => $datos['art66_fecha_regalias'],
+ 'importe_regalias' => $datos['art66_importe_regalias'],
+ 'cargo_regalias' => $datos['art66_cargo_regalias'],
+ 'fecha_producto' => $datos['art66_fecha_producto'],
+ 'importe_producto' => $datos['art66_importe_producto'],
+ 'cargo_producto' => $datos['art66_cargo_producto']
+ ]
+ ];
+
+ echo json_encode(['success' => true, 'datos' => $datosEstructurados]);
+ } else {
+ echo json_encode(['success' => true, 'datos' => null]);
+ }
+
+ } catch (Exception $e) {
+ echo json_encode(['success' => false, 'message' => 'Error interno: ' . $e->getMessage()]);
+ }
+}
+
+
diff --git a/app/controllers/seguridad.php b/app/controllers/seguridad.php
index 1ab764f..fc08766 100644
--- a/app/controllers/seguridad.php
+++ b/app/controllers/seguridad.php
@@ -342,4 +342,281 @@ function obtenerCorreos($conn, $id_usuario)
}
return $correos;
+}
+
+function ventanillaUnica()
+{
+ $conn = getConnection();
+
+ $id_usuario = $_SESSION['usuario_id'] ?? null;
+
+ // Asegúrate de que el usuario está autenticado
+ if (!$id_usuario || ($_SESSION['pendiente_confirmacion'] ?? false)) {
+ die("Usuario no autenticado.");
+ }
+
+ // Obtener configuración actual de ventanilla única
+ $configuracion_vu = obtenerConfiguracionVU($conn, $id_usuario);
+
+ include __DIR__ . '/../../views/seguridad/ventanilla_unica.php';
+}
+
+function guardarConfiguracionVU()
+{
+ // Solo ejecutar si es una petición POST
+ if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
+ header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
+ exit;
+ }
+
+ $conn = getConnection();
+
+ $id_usuario = $_SESSION['usuario_id'] ?? null;
+ if (!$id_usuario || ($_SESSION['pendiente_confirmacion'] ?? false)) {
+ die("No autorizado - Sesión inválida");
+ }
+
+ // Obtener datos del formulario (sin ruta_ejecutable)
+ $clave_fiel = trim($_POST['clave_fiel'] ?? '');
+ $rfc_usuario_vu = trim($_POST['rfc_usuario_vu'] ?? '');
+ $clave_webservice = trim($_POST['clave_webservice'] ?? '');
+
+ // Validar campos obligatorios (sin ruta_ejecutable)
+ if (empty($clave_fiel) || empty($rfc_usuario_vu)) {
+ $_SESSION['config_error'] = 'Los campos Clave FIEL y RFC Usuario VU son obligatorios.';
+ header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
+ exit;
+ }
+
+ // Obtener configuración actual para conservar archivos existentes
+ $configuracion_actual = obtenerConfiguracionVU($conn, $id_usuario);
+ $ruta_archivo_key = $configuracion_actual['ruta_archivo_key'];
+ $ruta_archivo_cer = $configuracion_actual['ruta_archivo_cer'];
+
+ // Directorio para archivos de certificados
+ $upload_dir = __DIR__ . '/../../storage/certificados/';
+ if (!is_dir($upload_dir)) {
+ mkdir($upload_dir, 0755, true);
+ }
+
+ // Procesar archivo KEY
+ if (!empty($_FILES['archivo_key']['tmp_name']) && $_FILES['archivo_key']['error'] === UPLOAD_ERR_OK) {
+ $key_extension = pathinfo($_FILES['archivo_key']['name'], PATHINFO_EXTENSION);
+
+ if (strtolower($key_extension) !== 'key') {
+ $_SESSION['config_error'] = 'El archivo KEY debe tener extensión .key';
+ header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
+ exit;
+ }
+
+ $key_filename = 'key_' . $id_usuario . '_' . time() . '.key';
+ $key_destination = $upload_dir . $key_filename;
+
+ if (move_uploaded_file($_FILES['archivo_key']['tmp_name'], $key_destination)) {
+ // Eliminar archivo anterior si existe
+ if ($ruta_archivo_key && file_exists($ruta_archivo_key)) {
+ unlink($ruta_archivo_key);
+ }
+ $ruta_archivo_key = $key_destination;
+ } else {
+ $_SESSION['config_error'] = 'Error al subir el archivo KEY.';
+ header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
+ exit;
+ }
+ }
+
+ // Procesar archivo CER
+ if (!empty($_FILES['archivo_cer']['tmp_name']) && $_FILES['archivo_cer']['error'] === UPLOAD_ERR_OK) {
+ $cer_extension = pathinfo($_FILES['archivo_cer']['name'], PATHINFO_EXTENSION);
+
+ if (strtolower($cer_extension) !== 'cer') {
+ $_SESSION['config_error'] = 'El archivo CER debe tener extensión .cer';
+ header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
+ exit;
+ }
+
+ $cer_filename = 'cer_' . $id_usuario . '_' . time() . '.cer';
+ $cer_destination = $upload_dir . $cer_filename;
+
+ if (move_uploaded_file($_FILES['archivo_cer']['tmp_name'], $cer_destination)) {
+ // Eliminar archivo anterior si existe
+ if ($ruta_archivo_cer && file_exists($ruta_archivo_cer)) {
+ unlink($ruta_archivo_cer);
+ }
+ $ruta_archivo_cer = $cer_destination;
+ } else {
+ $_SESSION['config_error'] = 'Error al subir el archivo CER.';
+ header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
+ exit;
+ }
+ }
+
+ // Validar que se hayan subido los certificados (obligatorios para nueva configuración)
+ if (empty($ruta_archivo_key) || empty($ruta_archivo_cer)) {
+ $_SESSION['config_error'] = 'Los archivos CER y KEY son obligatorios.';
+ header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
+ exit;
+ }
+
+ // Encriptar las contraseñas sensibles
+ $clave_fiel_encrypted = encrypt($clave_fiel);
+ $clave_webservice_encrypted = !empty($clave_webservice) ? encrypt($clave_webservice) : '';
+
+ // Verificar si ya existe configuración - CORREGIR ERROR SQL
+ $sql_check = "SELECT COUNT(*) as total FROM configuracion_ventanilla_unica WHERE id_usuario = ?";
+ $stmt_check = sqlsrv_query($conn, $sql_check, [$id_usuario]);
+
+ if ($stmt_check === false) {
+ $_SESSION['config_error'] = 'Error en la consulta de configuración: ' . print_r(sqlsrv_errors(), true);
+ header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
+ exit;
+ }
+
+ $row = sqlsrv_fetch_array($stmt_check, SQLSRV_FETCH_ASSOC);
+
+ if ($row['total'] > 0) {
+ // Actualizar configuración existente (sin ruta_ejecutable)
+ $sql = "UPDATE configuracion_ventanilla_unica SET
+ ruta_archivo_key = ?,
+ ruta_archivo_cer = ?,
+ clave_fiel = ?,
+ rfc_usuario_vu = ?,
+ clave_webservice = ?,
+ fecha_actualizacion = GETDATE()
+ WHERE id_usuario = ?";
+ $params = [
+ $ruta_archivo_key,
+ $ruta_archivo_cer,
+ $clave_fiel_encrypted,
+ $rfc_usuario_vu,
+ $clave_webservice_encrypted,
+ $id_usuario
+ ];
+ } else {
+ // Insertar nueva configuración (sin ruta_ejecutable)
+ $sql = "INSERT INTO configuracion_ventanilla_unica
+ (id_usuario, ruta_archivo_key, ruta_archivo_cer,
+ clave_fiel, rfc_usuario_vu, clave_webservice, fecha_creacion)
+ VALUES (?, ?, ?, ?, ?, ?, GETDATE())";
+ $params = [
+ $id_usuario,
+ $ruta_archivo_key,
+ $ruta_archivo_cer,
+ $clave_fiel_encrypted,
+ $rfc_usuario_vu,
+ $clave_webservice_encrypted
+ ];
+ }
+
+ $stmt = sqlsrv_prepare($conn, $sql, $params);
+
+ if (!$stmt) {
+ $_SESSION['config_error'] = "Error en la preparación: " . print_r(sqlsrv_errors(), true);
+ header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
+ exit;
+ }
+
+ $result = sqlsrv_execute($stmt);
+
+ if ($result === false) {
+ $_SESSION['config_error'] = "Error al guardar configuración: " . print_r(sqlsrv_errors(), true);
+ header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
+ exit;
+ }
+
+ sqlsrv_free_stmt($stmt);
+ sqlsrv_close($conn);
+
+ $_SESSION['config_success'] = 'Configuración de Ventanilla Única guardada correctamente.';
+ header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
+ exit;
+}
+
+function probarConexionVU()
+{
+ header('Content-Type: application/json');
+
+ $id_usuario = $_SESSION['usuario_id'] ?? null;
+ if (!$id_usuario || ($_SESSION['pendiente_confirmacion'] ?? false)) {
+ echo json_encode(['success' => false, 'message' => 'Usuario no autenticado']);
+ exit;
+ }
+
+ $conn = getConnection();
+ $configuracion = obtenerConfiguracionVU($conn, $id_usuario);
+
+ // Verificar que todos los campos requeridos estén configurados (sin ruta_ejecutable)
+ if (empty($configuracion['rfc_usuario_vu']) || empty($configuracion['clave_fiel'])) {
+ echo json_encode([
+ 'success' => false,
+ 'message' => 'Configuración incompleta. Verifica que RFC Usuario VU y Clave FIEL estén configurados.'
+ ]);
+ exit;
+ }
+
+ // Verificar archivos de certificados
+ if (!empty($configuracion['ruta_archivo_cer']) && !file_exists($configuracion['ruta_archivo_cer'])) {
+ echo json_encode([
+ 'success' => false,
+ 'message' => 'El archivo CER no existe en la ruta especificada.'
+ ]);
+ exit;
+ }
+
+ if (!empty($configuracion['ruta_archivo_key']) && !file_exists($configuracion['ruta_archivo_key'])) {
+ echo json_encode([
+ 'success' => false,
+ 'message' => 'El archivo KEY no existe en la ruta especificada.'
+ ]);
+ exit;
+ }
+
+ // Verificar que existan ambos archivos de certificados
+ if (empty($configuracion['ruta_archivo_cer']) || empty($configuracion['ruta_archivo_key'])) {
+ echo json_encode([
+ 'success' => false,
+ 'message' => 'Los archivos CER y KEY son obligatorios para la configuración.'
+ ]);
+ exit;
+ }
+
+ // Si llegamos aquí, la configuración es válida
+ echo json_encode([
+ 'success' => true,
+ 'message' => 'Configuración válida. Los archivos de certificados existen y todos los campos obligatorios están completos.'
+ ]);
+ exit;
+}
+
+function obtenerConfiguracionVU($conn, $id_usuario)
+{
+ $configuracion = [
+ 'ruta_archivo_key' => '',
+ 'ruta_archivo_cer' => '',
+ 'clave_fiel' => '',
+ 'rfc_usuario_vu' => '',
+ 'clave_webservice' => '',
+ 'fecha_creacion' => null,
+ 'fecha_actualizacion' => null
+ ];
+
+ $sql = "SELECT * FROM configuracion_ventanilla_unica WHERE id_usuario = ?";
+ $stmt = sqlsrv_prepare($conn, $sql, [$id_usuario]);
+
+ if ($stmt && sqlsrv_execute($stmt)) {
+ if ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
+ $configuracion['ruta_archivo_key'] = $row['ruta_archivo_key'] ?? '';
+ $configuracion['ruta_archivo_cer'] = $row['ruta_archivo_cer'] ?? '';
+ $configuracion['rfc_usuario_vu'] = $row['rfc_usuario_vu'] ?? '';
+
+ // Desencriptar contraseñas
+ $configuracion['clave_fiel'] = !empty($row['clave_fiel']) ? decrypt($row['clave_fiel']) : '';
+ $configuracion['clave_webservice'] = !empty($row['clave_webservice']) ? decrypt($row['clave_webservice']) : '';
+
+ $configuracion['fecha_creacion'] = $row['fecha_creacion'];
+ $configuracion['fecha_actualizacion'] = $row['fecha_actualizacion'];
+ }
+ }
+
+ return $configuracion;
}
\ No newline at end of file
diff --git a/app/controllers/solicitud_importacion.php b/app/controllers/solicitud_importacion.php
index 44b88d1..ed6e10d 100644
--- a/app/controllers/solicitud_importacion.php
+++ b/app/controllers/solicitud_importacion.php
@@ -410,6 +410,7 @@ function guardar()
$aduana_seccion = $_POST['anexo22_apendice'] ?? null;
$num_factura = trim($_POST['numero_factura'] ?? '');
$fecha = $_POST['fecha_factura'] ?? null;
+ $pedimento = trim($_POST['pedimento'] ?? ''); // ✅ NUEVO CAMPO
$incoterm = $_POST['incoterm'] ?? null;
$pais_proveedor = $_POST['pais_proveedor'] ?? null;
$tipo_moneda = $_POST['tipo_moneda'] ?? null;
@@ -462,15 +463,16 @@ function guardar()
$fotoUrl,
$status,
$proveedor_clave,
- $patente_id ? (int)$patente_id : null
+ $patente_id ? (int)$patente_id : null,
+ $pedimento ?: null // ✅ CORREGIDO: Campo pedimento_vinculado al final
];
$sql = "INSERT INTO dbo.solicitud_importacion_factura
(id_importador, id_agencia, aduana, anexo22_apendice, numero_factura,
- fecha_factura, numero_pedimento, incoterm, pais_proveedor, tipo_moneda,
+ fecha_factura, incoterm, pais_proveedor, tipo_moneda,
valor_factura, vinculacion, transportista_id, chofer_id,
- foto_solicitud_url, status, proveedor_clave, patente_id)
+ foto_solicitud_url, status, proveedor_clave, patente_id, pedimento_vinculado)
OUTPUT INSERTED.id_solicitud
- VALUES(?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
";
$stmt = sqlsrv_query($conn, $sql, $params, ['Scrollable' => SQLSRV_CURSOR_KEYSET]);
@@ -947,8 +949,8 @@ function actualizar()
} else {
// INSERTAR nueva partida (asegurarse que no tenga id_partida o sea 0)
$sql = "INSERT INTO dbo.solicitud_importacion_partidas
- (id_solicitud, descripcion, cantidad_comercial, cantidad_tarifa,
- valor_factura, peso_bruto, unidad_comercial_id, tasa_preferencial)
+ (id_solicitud, descripcion, cantidad_comercial, cantidad_tarifa, valor_factura,
+ peso_bruto, unidad_comercial_id, tasa_preferencial)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
";
$params = [ $id_solicitud, $desc, $cantCom, $cantTar, $valPart, $peso, $umId, $tasaPref ];
@@ -1968,7 +1970,7 @@ function generarHTMLPDF($solicitud, $partidas, $configuracion, $proveedor_info =
$unidades = ['', 'uno', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve'];
$decenas = ['', '', 'veinte', 'treinta', 'cuarenta', 'cincuenta', 'sesenta', 'setenta', 'ochenta', 'noventa'];
$especiales = ['diez', 'once', 'doce', 'trece', 'catorce', 'quince', 'dieciséis', 'diecisiete', 'dieciocho', 'diecinueve'];
- $centenas = ['', 'ciento', 'doscientos', 'trescientos', 'cuatrocientos', 'quinientos', 'seiscientos', 'setecientos', 'ochocientos', 'novecientos'];
+ $centenas = ['', 'ciento', 'doscientos', 'trescientos', 'cuatrocientos', 'quinientos', 'seiscientos', 'setecientos', 'ochocientos'];
if ($numero == 0) return 'cero';
if ($numero == 100) return 'cien';
@@ -2212,4 +2214,89 @@ function generarHTMLPDF($solicitud, $partidas, $configuracion, $proveedor_info =
';
return $html;
+}
+
+// NUEVO ENDPOINT: Pedimentos de catálogo activos del importador para panel de referencia
+function ajax_pedimentos_catalogo()
+{
+ header('Content-Type: application/json; charset=utf-8');
+ if (!($_SESSION['usuario_id'] ?? false)) {
+ http_response_code(401);
+ echo json_encode(['data' => []]);
+ exit;
+ }
+ $id_usuario = $_SESSION['usuario_id'];
+ $limit = isset($_GET['limit']) ? intval($_GET['limit']) : 10;
+ $conn = getConnection();
+ // Obtener RFC del importador
+ $sqlImportador = "SELECT rfc FROM informacion_general WHERE id_usuario = ?";
+ $stmtImportador = sqlsrv_query($conn, $sqlImportador, [$id_usuario]);
+ if ($stmtImportador === false) {
+ echo json_encode(['data' => []]);
+ exit;
+ }
+ $importador = sqlsrv_fetch_array($stmtImportador, SQLSRV_FETCH_ASSOC);
+ if (!$importador) {
+ echo json_encode(['data' => []]);
+ exit;
+ }
+ // Obtener pedimentos activos del catálogo
+ $sql = "SELECT TOP {$limit} IdPrevio, Pedimento, ClienteNombre, ClavePed, Timestamp
+ FROM PREVIOS_COMPARTIDOS_WS
+ WHERE ClienteRFC = ? AND Status = 1
+ ORDER BY Timestamp DESC";
+ $stmt = sqlsrv_query($conn, $sql, [$importador['rfc']]);
+ $pedimentos = [];
+ if ($stmt !== false) {
+ while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
+ $fecha = '';
+ if ($row['Timestamp'] instanceof DateTime) {
+ $fecha = $row['Timestamp']->format('d/m/Y');
+ }
+ $pedimentos[] = [
+ 'numero' => $row['Pedimento'],
+ 'cliente' => $row['ClienteNombre'],
+ 'clave' => $row['ClavePed'],
+ 'fecha' => $fecha
+ ];
+ }
+ }
+ echo json_encode(['data' => $pedimentos]);
+ exit;
+}
+
+function ajax_facturas_por_pedimento() {
+ header('Content-Type: application/json');
+ $id_previo = isset($_GET['id_pedimento']) ? trim($_GET['id_pedimento']) : '';
+ if (!$id_previo) {
+ echo json_encode(['success' => false, 'error' => 'ID de pedimento no válido']);
+ return;
+ }
+ $conn = getConnection();
+ // 1. Buscar el número de pedimento real en PREVIOS_COMPARTIDOS_WS
+ $stmt = sqlsrv_query($conn, "SELECT Pedimento FROM PREVIOS_COMPARTIDOS_WS WHERE IdPrevio = ?", [$id_previo]);
+ if ($stmt === false || !($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC))) {
+ echo json_encode(['success' => false, 'error' => 'No se encontró el pedimento en PREVIOS_COMPARTIDOS_WS']);
+ return;
+ }
+ $numero_pedimento = $row['Pedimento'];
+
+ // 2. Buscar las facturas en solicitud_importacion_factura usando el número de pedimento
+ // ✅ CORREGIDO: Incluir id_solicitud como id_factura
+ $stmt2 = sqlsrv_query($conn, "SELECT id_solicitud, numero_factura, fecha_factura, valor_factura FROM solicitud_importacion_factura WHERE numero_pedimento = ?", [$numero_pedimento]);
+ if ($stmt2 === false) {
+ echo json_encode(['success' => false, 'error' => 'Error en la consulta de facturas']);
+ return;
+ }
+
+ $facturas = [];
+ while ($row2 = sqlsrv_fetch_array($stmt2, SQLSRV_FETCH_ASSOC)) {
+ $facturas[] = [
+ 'id_factura' => $row2['id_solicitud'], // ✅ AGREGADO: Usar id_solicitud como id_factura
+ 'numero_factura' => $row2['numero_factura'],
+ 'fecha' => ($row2['fecha_factura'] instanceof DateTime) ? $row2['fecha_factura']->format('Y-m-d') : $row2['fecha_factura'],
+ 'monto' => $row2['valor_factura']
+ ];
+ }
+ echo json_encode(['success' => true, 'facturas' => $facturas]);
}
\ No newline at end of file
diff --git a/app/controllers/templates_rapidos.php b/app/controllers/templates_rapidos.php
new file mode 100644
index 0000000..326b2bf
--- /dev/null
+++ b/app/controllers/templates_rapidos.php
@@ -0,0 +1,913 @@
+ $_POST['tipo_moneda'] ?? '',
+ 'incoterm' => $_POST['incoterm'] ?? '',
+ 'vinculacion' => $_POST['vinculacion'] ?? '',
+ 'pais_proveedor' => $_POST['pais_proveedor'] ?? '',
+ 'pais_proveedor_texto' => $_POST['pais_proveedor_texto'] ?? '',
+ 'anexo22_apendice' => $_POST['anexo22_apendice'] ?? '',
+ 'patente' => $_POST['patente'] ?? '',
+ 'transportista_id' => $_POST['transportista_id'] ?? '',
+ 'chofer_id' => $_POST['chofer_id'] ?? '',
+ 'tasa_preferencial' => $_POST['tasa_preferencial'] ?? '',
+ 'unidad_comercial_id' => $_POST['unidad_comercial_id'] ?? ''
+ ];
+
+ $config_json = json_encode($config, JSON_UNESCAPED_UNICODE);
+
+ // Validaciones
+ if (empty($nombre)) {
+ die("❌ El nombre del template es obligatorio.");
+ }
+
+ if (strlen($nombre) > 100) {
+ die("❌ El nombre del template es muy largo (máximo 100 caracteres).");
+ }
+
+ $sql = "INSERT INTO dbo.templates_rapidos
+ (nombre, descripcion, icono, config_json, tipo_moneda, incoterm,
+ vinculacion, pais_proveedor, tasa_preferencial, id_agencia, id_usuario_creador)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+
+ $params = [
+ $nombre,
+ $descripcion,
+ $icono,
+ $config_json,
+ $config['tipo_moneda'],
+ $config['incoterm'],
+ $config['vinculacion'] ?: null,
+ $config['pais_proveedor'],
+ $config['tasa_preferencial'],
+ $id_agencia,
+ $id_usuario
+ ];
+
+ $stmt = sqlsrv_query($conn, $sql, $params);
+
+ if ($stmt === false) {
+ die("❌ Error al guardar template: " . print_r(sqlsrv_errors(), true));
+ }
+
+ header('Location: /IMPORTADORES/templates_rapidos/lista?created=ok');
+ exit;
+}
+
+function editar()
+{
+ if (!($_SESSION['usuario_id'] ?? false)) {
+ header('Location: /IMPORTADORES/login');
+ exit;
+ }
+
+ $id = $_GET['id'] ?? null;
+
+ if (!$id || !is_numeric($id)) {
+ die("❌ ID inválido.");
+ }
+
+ $conn = getConnection();
+ $id_usuario = $_SESSION['usuario_id'];
+ $id_agencia = $_SESSION['id_agencia_en_uso'] ?? null;
+
+ // Obtener el template
+ $sql = "SELECT * FROM dbo.templates_rapidos
+ WHERE id = ? AND (id_usuario_creador = ? OR id_agencia = ? OR id_agencia IS NULL)";
+ $stmt = sqlsrv_query($conn, $sql, [$id, $id_usuario, $id_agencia]);
+
+ if ($stmt === false) {
+ die("❌ Error en consulta: " . print_r(sqlsrv_errors(), true));
+ }
+
+ $template = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
+
+ if (!$template) {
+ die("❌ Template no encontrado o sin permisos.");
+ }
+
+ // Decodificar configuración JSON
+ $template['config'] = json_decode($template['config_json'], true) ?: [];
+
+ // Obtener datos para los selects
+ $conn = getConnection();
+ $aduanas = obtenerAduanas($conn);
+ $patentes = obtenerPatentes($conn);
+ $incoterms = obtenerIncoterms($conn);
+ $paises = obtenerPaises($conn);
+ $transportistas = obtenerTransportistas($conn);
+ $choferes = obtenerChoferes($conn);
+ $unidades_medida = obtenerUnidadesMedida($conn);
+
+ include __DIR__ . '/../../views/templates_rapidos/editar.php';
+}
+
+function actualizar()
+{
+ if (!($_SESSION['usuario_id'] ?? false)) {
+ die("⚠️ No autorizado.");
+ }
+
+ $conn = getConnection();
+ $id_usuario = $_SESSION['usuario_id'];
+ $id_agencia = $_SESSION['id_agencia_en_uso'] ?? null;
+
+ $id = $_POST['id'] ?? null;
+ $nombre = trim($_POST['nombre'] ?? '');
+ $descripcion = trim($_POST['descripcion'] ?? '');
+ $icono = trim($_POST['icono'] ?? '🏢');
+
+ if (!$id || !is_numeric($id)) {
+ die("❌ ID inválido.");
+ }
+
+ // Configuración del template
+ $config = [
+ 'tipo_moneda' => $_POST['tipo_moneda'] ?? '',
+ 'incoterm' => $_POST['incoterm'] ?? '',
+ 'vinculacion' => $_POST['vinculacion'] ?? '',
+ 'pais_proveedor' => $_POST['pais_proveedor'] ?? '',
+ 'pais_proveedor_texto' => $_POST['pais_proveedor_texto'] ?? '',
+ 'anexo22_apendice' => $_POST['anexo22_apendice'] ?? '',
+ 'patente' => $_POST['patente'] ?? '',
+ 'transportista_id' => $_POST['transportista_id'] ?? '',
+ 'chofer_id' => $_POST['chofer_id'] ?? '',
+ 'tasa_preferencial' => $_POST['tasa_preferencial'] ?? '',
+ 'unidad_comercial_id' => $_POST['unidad_comercial_id'] ?? ''
+ ];
+
+ $config_json = json_encode($config, JSON_UNESCAPED_UNICODE);
+
+ // Validaciones
+ if (empty($nombre)) {
+ die("❌ El nombre del template es obligatorio.");
+ }
+
+ // Verificar permisos
+ $sqlCheck = "SELECT id FROM dbo.templates_rapidos
+ WHERE id = ? AND (id_usuario_creador = ? OR id_agencia = ?)";
+ $stmtCheck = sqlsrv_query($conn, $sqlCheck, [$id, $id_usuario, $id_agencia]);
+ $exists = sqlsrv_fetch_array($stmtCheck, SQLSRV_FETCH_ASSOC);
+
+ if (!$exists) {
+ die("❌ Template no encontrado o sin permisos para editarlo.");
+ }
+
+ $sql = "UPDATE dbo.templates_rapidos SET
+ nombre = ?, descripcion = ?, icono = ?, config_json = ?,
+ tipo_moneda = ?, incoterm = ?, vinculacion = ?,
+ pais_proveedor = ?, tasa_preferencial = ?, fecha_modificacion = GETDATE()
+ WHERE id = ?";
+
+ $params = [
+ $nombre,
+ $descripcion,
+ $icono,
+ $config_json,
+ $config['tipo_moneda'],
+ $config['incoterm'],
+ $config['vinculacion'] ?: null,
+ $config['pais_proveedor'],
+ $config['tasa_preferencial'],
+ $id
+ ];
+
+ $stmt = sqlsrv_query($conn, $sql, $params);
+
+ if ($stmt === false) {
+ die("❌ Error al actualizar template: " . print_r(sqlsrv_errors(), true));
+ }
+
+ header('Location: /IMPORTADORES/templates_rapidos/lista?updated=ok');
+ exit;
+}
+
+function eliminar()
+{
+ if (!($_SESSION['usuario_id'] ?? false)) {
+ header('Location: /IMPORTADORES/login');
+ exit;
+ }
+
+ $id = $_GET['id'] ?? null;
+
+ if (!$id || !is_numeric($id)) {
+ die("❌ ID inválido.");
+ }
+
+ $conn = getConnection();
+ $id_usuario = $_SESSION['usuario_id'];
+ $id_agencia = $_SESSION['id_agencia_en_uso'] ?? null;
+
+ // Verificar permisos
+ $sqlCheck = "SELECT id FROM dbo.templates_rapidos
+ WHERE id = ? AND (id_usuario_creador = ? OR id_agencia = ?)";
+ $stmtCheck = sqlsrv_query($conn, $sqlCheck, [$id, $id_usuario, $id_agencia]);
+ $exists = sqlsrv_fetch_array($stmtCheck, SQLSRV_FETCH_ASSOC);
+
+ if (!$exists) {
+ die("❌ Template no encontrado o sin permisos para eliminarlo.");
+ }
+
+ $sql = "UPDATE dbo.templates_rapidos SET activo = 0 WHERE id = ?";
+ $stmt = sqlsrv_query($conn, $sql, [$id]);
+
+ if ($stmt === false) {
+ die("❌ Error al eliminar template: " . print_r(sqlsrv_errors(), true));
+ }
+
+ header('Location: /IMPORTADORES/templates_rapidos/lista?deleted=ok');
+ exit;
+}
+
+function ajax_lista()
+{
+ if (!($_SESSION['usuario_id'] ?? false)) {
+ http_response_code(403);
+ echo json_encode([]);
+ exit;
+ }
+
+ $id_usuario = $_SESSION['usuario_id'];
+ $id_agencia = $_SESSION['id_agencia_en_uso'] ?? null;
+ $conn = getConnection();
+
+ // Parámetros de DataTables
+ $draw = intval($_GET['draw'] ?? 0);
+ $start = intval($_GET['start'] ?? 0);
+ $length = intval($_GET['length'] ?? 10);
+ $search = $_GET['search']['value'] ?? '';
+
+ // Construir condiciones de filtro
+ $where = "activo = 1 AND (id_agencia IS NULL OR id_agencia = ? OR id_usuario_creador = ?)";
+ $params = [$id_agencia, $id_usuario];
+
+ if ($search !== '') {
+ $where .= " AND (nombre LIKE ? OR descripcion LIKE ?)";
+ $like = "%{$search}%";
+ $params = array_merge($params, [$like, $like]);
+ }
+
+ // Total registros filtrados
+ $sqlFiltered = "SELECT COUNT(*) AS total FROM dbo.templates_rapidos WHERE $where";
+ $stmtF = sqlsrv_query($conn, $sqlFiltered, $params);
+ $rowF = sqlsrv_fetch_array($stmtF, SQLSRV_FETCH_ASSOC);
+ $recordsFiltered = (int)($rowF['total'] ?? 0);
+
+ // Total registros sin filtro
+ $sqlTotal = "SELECT COUNT(*) AS total FROM dbo.templates_rapidos WHERE activo = 1 AND (id_agencia IS NULL OR id_agencia = ? OR id_usuario_creador = ?)";
+ $stmtT = sqlsrv_query($conn, $sqlTotal, [$id_agencia, $id_usuario]);
+ $rowT = sqlsrv_fetch_array($stmtT, SQLSRV_FETCH_ASSOC);
+ $recordsTotal = (int)($rowT['total'] ?? 0);
+
+ // Consulta principal con paginación
+ $sql = "SELECT t.id, t.nombre, t.descripcion, t.icono, t.tipo_moneda, t.incoterm,
+ t.vinculacion, t.pais_proveedor, t.tasa_preferencial, t.fecha_creacion,
+ u.nombre_usuario AS usuario_creador,
+ CASE WHEN t.id_agencia IS NULL THEN 'Sistema' ELSE a.nombre END AS ambito
+ FROM dbo.templates_rapidos t
+ LEFT JOIN dbo.usuarios u ON t.id_usuario_creador = u.id
+ LEFT JOIN dbo.agencias a ON t.id_agencia = a.id
+ WHERE $where
+ ORDER BY t.fecha_creacion DESC
+ OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
+
+ $paramsData = array_merge($params, [$start, $length]);
+ $stmt = sqlsrv_query($conn, $sql, $paramsData);
+
+ $data = [];
+ while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
+ $fecha = $row['fecha_creacion'] instanceof DateTime
+ ? $row['fecha_creacion']->format('d/m/Y H:i')
+ : 'N/A';
+
+ $data[] = [
+ 'id' => $row['id'],
+ 'icono' => htmlspecialchars($row['icono'] ?? '🏢'),
+ 'nombre' => htmlspecialchars($row['nombre']),
+ 'descripcion' => htmlspecialchars($row['descripcion'] ?? ''),
+ 'tipo_moneda' => htmlspecialchars($row['tipo_moneda'] ?? ''),
+ 'incoterm' => htmlspecialchars($row['incoterm'] ?? ''),
+ 'pais_proveedor' => htmlspecialchars($row['pais_proveedor'] ?? ''),
+ 'ambito' => htmlspecialchars($row['ambito'] ?? ''),
+ 'usuario_creador' => htmlspecialchars($row['usuario_creador'] ?? ''),
+ 'fecha_creacion' => $fecha
+ ];
+ }
+
+ echo json_encode([
+ 'draw' => $draw,
+ 'recordsTotal' => $recordsTotal,
+ 'recordsFiltered' => $recordsFiltered,
+ 'data' => $data
+ ]);
+}
+
+function ajax_obtener_templates_debug()
+{
+ header('Content-Type: application/json; charset=UTF-8');
+
+ if (!($_SESSION['usuario_id'] ?? false)) {
+ echo json_encode(['error' => 'Usuario no autenticado']);
+ exit;
+ }
+
+ $id_usuario = $_SESSION['usuario_id'];
+ $id_agencia = $_SESSION['id_agencia_en_uso'] ?? null;
+
+ try {
+ $conn = getConnection();
+
+ // ✅ CONSULTA SIN FILTROS para debug
+ $sql = "SELECT id, nombre, descripcion, icono, config_json,
+ ISNULL(veces_usado, 0) as veces_usado,
+ id_usuario_creador, id_agencia, activo,
+ fecha_creacion
+ FROM dbo.templates_rapidos
+ ORDER BY fecha_creacion DESC";
+
+ $stmt = sqlsrv_query($conn, $sql);
+
+ if ($stmt === false) {
+ $errors = sqlsrv_errors();
+ echo json_encode([
+ 'error' => 'Error en consulta SQL',
+ 'message' => $errors[0]['message'] ?? 'Error desconocido',
+ 'sql_errors' => $errors
+ ]);
+ exit;
+ }
+
+ $templates = [];
+ $debug_info = [
+ 'usuario_actual' => $id_usuario,
+ 'agencia_actual' => $id_agencia,
+ 'todos_los_templates' => []
+ ];
+
+ while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
+ // Info completa para debug
+ $template_debug = [
+ 'id' => $row['id'],
+ 'nombre' => $row['nombre'],
+ 'id_usuario_creador' => $row['id_usuario_creador'],
+ 'id_agencia' => $row['id_agencia'],
+ 'activo' => $row['activo'],
+ 'fecha_creacion' => $row['fecha_creacion'] instanceof DateTime
+ ? $row['fecha_creacion']->format('Y-m-d H:i:s')
+ : $row['fecha_creacion'],
+ 'es_del_usuario_actual' => ($row['id_usuario_creador'] == $id_usuario),
+ 'es_de_la_agencia' => ($row['id_agencia'] == $id_agencia),
+ 'deberia_mostrarse' => (
+ $row['activo'] == 1 && (
+ $row['id_agencia'] === null ||
+ $row['id_agencia'] == $id_agencia ||
+ $row['id_usuario_creador'] == $id_usuario
+ )
+ )
+ ];
+
+ $debug_info['todos_los_templates'][] = $template_debug;
+
+ // Solo agregar a templates para mostrar si cumple condiciones
+ if ($template_debug['deberia_mostrarse']) {
+ $config = [];
+ if (!empty($row['config_json'])) {
+ $decoded = json_decode($row['config_json'], true);
+ $config = is_array($decoded) ? $decoded : [];
+ }
+
+ $templates[] = [
+ 'id' => (int)$row['id'],
+ 'nombre' => $row['nombre'],
+ 'descripcion' => $row['descripcion'] ?? '',
+ 'icono' => $row['icono'] ?? '📋',
+ 'config' => $config,
+ 'veces_usado' => (int)$row['veces_usado'],
+ 'id_usuario_creador' => $row['id_usuario_creador'],
+ 'es_mio' => ($row['id_usuario_creador'] == $id_usuario)
+ ];
+ }
+ }
+
+ echo json_encode([
+ 'success' => true,
+ 'count' => count($templates),
+ 'templates' => $templates,
+ 'debug_info' => $debug_info
+ ], JSON_UNESCAPED_UNICODE);
+
+ } catch (Exception $e) {
+ echo json_encode([
+ 'error' => 'Error interno del servidor',
+ 'message' => $e->getMessage()
+ ]);
+ }
+
+ exit;
+}
+
+function ajax_obtener_templates()
+{
+ // ✅ VERSIÓN CORREGIDA: Función simplificada sin filtros complejos
+
+ header('Content-Type: application/json; charset=UTF-8');
+
+ if (!($_SESSION['usuario_id'] ?? false)) {
+ http_response_code(403);
+ echo json_encode(['error' => 'Usuario no autenticado', 'templates' => []]);
+ exit;
+ }
+
+ $id_usuario = $_SESSION['usuario_id'];
+ $id_agencia = $_SESSION['id_agencia_en_uso'] ?? null;
+
+ try {
+ $conn = getConnection();
+
+ // ✅ CONSULTA SIMPLIFICADA: Mostrar todos los templates activos del usuario o agencia
+ $sql = "SELECT id, nombre, descripcion, icono, config_json,
+ ISNULL(veces_usado, 0) as veces_usado,
+ id_usuario_creador, id_agencia
+ FROM dbo.templates_rapidos
+ WHERE activo = 1
+ AND (id_usuario_creador = ? OR id_agencia = ? OR id_agencia IS NULL)
+ ORDER BY
+ CASE WHEN id_usuario_creador = ? THEN 0 ELSE 1 END,
+ veces_usado DESC,
+ nombre ASC";
+
+ $params = [$id_usuario, $id_agencia, $id_usuario];
+ $stmt = sqlsrv_query($conn, $sql, $params);
+
+ if ($stmt === false) {
+ $errors = sqlsrv_errors();
+ echo json_encode([
+ 'error' => 'Error en consulta SQL',
+ 'message' => $errors[0]['message'] ?? 'Error desconocido',
+ 'templates' => []
+ ]);
+ exit;
+ }
+
+ $templates = [];
+ $templates_personales = [];
+ $templates_otros = [];
+
+ while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
+ // Decodificar JSON con manejo de errores
+ $config = [];
+ if (!empty($row['config_json'])) {
+ $decoded = json_decode($row['config_json'], true);
+ $config = is_array($decoded) ? $decoded : [];
+ }
+
+ $template = [
+ 'id' => (int)$row['id'],
+ 'nombre' => $row['nombre'],
+ 'descripcion' => $row['descripcion'] ?? '',
+ 'icono' => $row['icono'] ?? '📋',
+ 'config' => $config,
+ 'veces_usado' => (int)$row['veces_usado'],
+ 'id_usuario_creador' => $row['id_usuario_creador'],
+ 'id_agencia' => $row['id_agencia'],
+ 'es_mio' => ($row['id_usuario_creador'] == $id_usuario),
+ 'ambito' => ($row['id_usuario_creador'] == $id_usuario) ? 'personal' :
+ (($row['id_agencia'] == $id_agencia) ? 'agencia' : 'sistema')
+ ];
+
+ // Separar templates personales de otros
+ if ($template['es_mio']) {
+ $templates_personales[] = $template;
+ } else {
+ $templates_otros[] = $template;
+ }
+
+ $templates[] = $template;
+ }
+
+ // Respuesta con información detallada
+ echo json_encode([
+ 'success' => true,
+ 'count' => count($templates),
+ 'count_personales' => count($templates_personales),
+ 'count_otros' => count($templates_otros),
+ 'usuario_id' => $id_usuario,
+ 'agencia_id' => $id_agencia,
+ 'templates' => $templates,
+ 'templates_personales' => $templates_personales,
+ 'templates_otros' => $templates_otros
+ ], JSON_UNESCAPED_UNICODE);
+
+ } catch (Exception $e) {
+ echo json_encode([
+ 'error' => 'Error interno del servidor',
+ 'message' => $e->getMessage(),
+ 'templates' => []
+ ]);
+ }
+
+ exit;
+}
+
+function ajax_lista_por_seccion()
+{
+ if (!($_SESSION['usuario_id'] ?? false)) {
+ die(json_encode(['error' => 'No autorizado']));
+ }
+
+ $conn = getConnection();
+ $id_usuario = $_SESSION['usuario_id'];
+ $id_agencia = $_SESSION['id_agencia_en_uso'] ?? null;
+ $seccion = $_GET['seccion'] ?? 'personal';
+
+ $data = [];
+
+ try {
+ switch($seccion) {
+ case 'personal':
+ // Solo templates creados por el usuario actual
+ $sql = "SELECT id, nombre, descripcion, icono, config_json,
+ veces_usado, fecha_creacion, id_usuario_creador
+ FROM dbo.templates_rapidos
+ WHERE id_usuario_creador = ? AND estado = 1
+ ORDER BY veces_usado DESC, fecha_creacion DESC";
+ $params = [$id_usuario];
+ break;
+
+ case 'agencia':
+ // Templates de la agencia (excluyendo los personales ya mostrados)
+ $sql = "SELECT id, nombre, descripcion, icono, config_json,
+ veces_usado, fecha_creacion, id_usuario_creador
+ FROM dbo.templates_rapidos
+ WHERE id_agencia = ? AND id_usuario_creador != ? AND estado = 1
+ ORDER BY veces_usado DESC, fecha_creacion DESC";
+ $params = [$id_agencia, $id_usuario];
+ break;
+
+ case 'global':
+ // Templates globales del sistema (solo si se solicitan explícitamente)
+ $sql = "SELECT id, nombre, descripcion, icono, config_json,
+ veces_usado, fecha_creacion, id_usuario_creador
+ FROM dbo.templates_rapidos
+ WHERE id_agencia IS NULL AND id_usuario_creador IS NULL AND estado = 1
+ ORDER BY veces_usado DESC, fecha_creacion DESC";
+ $params = [];
+ break;
+
+ default:
+ throw new Exception('Sección inválida');
+ }
+
+ $stmt = sqlsrv_query($conn, $sql, $params);
+
+ if ($stmt === false) {
+ throw new Exception('Error en consulta: ' . print_r(sqlsrv_errors(), true));
+ }
+
+ while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
+ // Formatear fecha
+ $fecha = $row['fecha_creacion'] instanceof DateTime
+ ? $row['fecha_creacion']->format('d/m/Y')
+ : date('d/m/Y', strtotime($row['fecha_creacion']));
+
+ // Determinar acciones según el tipo de template
+ $acciones = '';
+ $esPropio = ($row['id_usuario_creador'] == $id_usuario);
+ $esAgencia = ($seccion === 'agencia');
+ $esGlobal = ($seccion === 'global');
+
+ if ($esPropio) {
+ $acciones = '
+
Usuario actual: " . $_SESSION['usuario_id'] . "
"; +echo "Agencia actual: " . ($_SESSION['id_agencia_en_uso'] ?? 'NULL') . "
"; + +try { + $conn = getConnection(); + + // 1. Ver todos los templates sin filtros + echo "| ID | +Nombre | +Descripción | +Activo | +Usuario Creador | +Agencia | +Fecha Creación | +Tiene Config | +
|---|---|---|---|---|---|---|---|
| " . $row['id'] . " | "; + echo "" . htmlspecialchars($row['nombre']) . " | "; + echo "" . htmlspecialchars($row['descripcion'] ?? '') . " | "; + echo "" . ($row['activo'] ? '✅' : '❌') . " | "; + echo "" . ($row['id_usuario_creador'] ?? 'NULL') . " | "; + echo "" . ($row['id_agencia'] ?? 'NULL') . " | "; + echo "" . $fecha . " | "; + echo "" . $tieneConfig . " | "; + echo "
Total de templates encontrados: $count
"; + } + + // 2. Probar la consulta exacta del endpoint + echo "| ID | +Nombre | +Es Mío | +Usuario Creador | +Agencia | +Debería Aparecer | +
|---|---|---|---|---|---|
| " . $row['id'] . " | "; + echo "" . htmlspecialchars($row['nombre']) . " | "; + echo "$esMio | "; + echo "" . ($row['id_usuario_creador'] ?? 'NULL') . " | "; + echo "" . ($row['id_agencia'] ?? 'NULL') . " | "; + echo "✅ SÍ | "; + echo "
Templates que deberían aparecer en el formulario: $count_endpoint
"; + } + + // 3. Probar el endpoint AJAX directamente + echo "🔗 Abrir endpoint AJAX en nueva pestaña
"; + + // 4. Verificar la sesión + echo "";
+ echo "SESSION:\n";
+ foreach ($_SESSION as $key => $value) {
+ if (is_string($value) || is_numeric($value)) {
+ echo " $key: $value\n";
+ }
+ }
+ echo "";
+
+} catch (Exception $e) {
+ echo "💡 Instrucciones:
+1. Revisa los templates en la tabla de arriba
+2. Verifica que tu usuario_id y agencia_id sean correctos
+3. Haz clic en "Probar AJAX ahora" para ver la respuesta en tiempo real
+4. Abre la consola del navegador (F12) para ver logs detallados
templates_rapidos existe| Columna | Tipo | Nullable |
|---|---|---|
| " . $col['COLUMN_NAME'] . " | "; + echo "" . $col['DATA_TYPE'] . " | "; + echo "" . $col['IS_NULLABLE'] . " | "; + echo "
$col: Existe$col: NO EXISTE" . str_replace('?', "'$id_agencia', '$id_usuario'", $sql_controller) . "| ID | Nombre | Activo | Usuario | Agencia |
|---|---|---|---|---|
| " . $sample['id'] . " | "; + echo "" . htmlspecialchars($sample['nombre']) . " | "; + echo "" . ($sample['activo'] ? '✅' : '❌') . " | "; + echo "" . ($sample['id_usuario_creador'] ?? 'NULL') . " | "; + echo "" . ($sample['id_agencia'] ?? 'NULL') . " | "; + echo "
templates_rapidos NO EXISTE+ + Configura la conexión con el sistema WINSAAI para importar automáticamente pedimentos y COVES. +
+ + + +