diff --git a/app/controllers/productos_frecuentes.php b/app/controllers/productos_frecuentes.php index 7ab4da0..9c7114e 100644 --- a/app/controllers/productos_frecuentes.php +++ b/app/controllers/productos_frecuentes.php @@ -129,7 +129,7 @@ function lista() ON pf.pais_comprador_vendedor = pc.nombre WHERE pf.id_importador = ? AND pf.status = 1 - ORDER BY pf.fecha_alta DESC + ORDER BY pf.frecuencia_uso DESC "; $stmt = sqlsrv_query($conn, $sql, [$_SESSION['usuario_id']]); diff --git a/app/controllers/solicitud_importacion.php b/app/controllers/solicitud_importacion.php index 62db3de..b93afda 100644 --- a/app/controllers/solicitud_importacion.php +++ b/app/controllers/solicitud_importacion.php @@ -239,6 +239,150 @@ function obtenerChoferesPorTransportista() } } +function buscar_productos() +{ + header('Content-Type: application/json'); + + // 1. Validar usuario autenticado + if (!($_SESSION['usuario_id'] ?? false)) { + http_response_code(401); + echo json_encode(['error' => 'No autorizado']); + exit; + } + + $id_importador = $_SESSION['usuario_id']; + $query = trim($_GET['q'] ?? ''); + + // 2. Validar longitud mínima + if (strlen($query) < 2) { + echo json_encode([]); + exit; + } + + try { + $conn = getConnection(); // Obtener conexión + + // 3. Búsqueda mejorada con ponderación + $searchTerm = "%$query%"; + $sql = "SELECT TOP 10 + id_producto_frecuente, + sinonimo, + descripcion, + preferencia, + fraccion, + nico, + numero_parte, + CAST(umc_id AS VARCHAR) AS umc_id, + -- Campos para cálculo de relevancia + CASE + WHEN sinonimo LIKE ? THEN 100 + WHEN descripcion LIKE ? THEN 50 + ELSE 0 + END AS relevancia + FROM dbo.productos_frecuentes + WHERE id_importador = ? + AND status = 1 + AND (sinonimo LIKE ? OR descripcion LIKE ? OR numero_parte LIKE ?) + ORDER BY relevancia DESC, frecuencia_uso DESC, sinonimo"; + + $params = [ + "$query%", // Para búsqueda al inicio del sinonimo + "$query%", // Para búsqueda al inicio de descripción + $id_importador, + $searchTerm, + $searchTerm, + $searchTerm + ]; + + $stmt = sqlsrv_query($conn, $sql, $params); + + if ($stmt === false) { + error_log("Error en búsqueda de productos: " . print_r(sqlsrv_errors(), true)); + echo json_encode([]); + exit; + } + + $productos = []; + while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) { + $productos[] = [ + 'id' => $row['id_producto_frecuente'], + 'sinonimo' => $row['sinonimo'], + 'descripcion' => $row['descripcion'] ?? '', + 'preferencia' => $row['preferencia'] ?? '', + 'fraccion' => $row['fraccion'] ?? '', + 'nico' => $row['nico'] ?? '', + 'numero_parte' => $row['numero_parte'] ?? '', + 'umc_id' => $row['umc_id'] ?? null + ]; + } + + sqlsrv_free_stmt($stmt); + echo json_encode($productos); + + } catch (Exception $e) { + error_log("Excepción en buscar_productos: " . $e->getMessage()); + http_response_code(500); + echo json_encode(['error' => 'Error interno']); + } + exit; +} + +function incrementar_frecuencia() +{ + header('Content-Type: application/json'); + + // 1. Validar usuario + if (!($_SESSION['usuario_id'] ?? false)) { + http_response_code(401); + echo json_encode(['success' => false, 'error' => 'No autorizado']); + exit; + } + + $id_importador = $_SESSION['usuario_id']; + $producto_id = (int)($_POST['producto_id'] ?? 0); + + if ($producto_id <= 0) { + echo json_encode(['success' => false, 'error' => 'ID inválido']); + exit; + } + + try { + $conn = getConnection(); + + // 2. Verificar que el producto pertenece al usuario + $sqlValidate = "SELECT 1 FROM dbo.productos_frecuentes + WHERE id_producto_frecuente = ? AND id_importador = ?"; + $stmtValidate = sqlsrv_query($conn, $sqlValidate, [$producto_id, $id_importador]); + + if (!$stmtValidate || !sqlsrv_fetch_array($stmtValidate)) { + http_response_code(403); + echo json_encode(['success' => false, 'error' => 'Producto no válido']); + exit; + } + + // 3. Actualizar frecuencia + $sqlUpdate = "UPDATE dbo.productos_frecuentes + SET frecuencia_uso = frecuencia_uso + 1 + WHERE id_producto_frecuente = ?"; + + $stmtUpdate = sqlsrv_query($conn, $sqlUpdate, [$producto_id]); + + if ($stmtUpdate === false) { + error_log("Error actualizando frecuencia: " . print_r(sqlsrv_errors(), true)); + echo json_encode(['success' => false, 'error' => 'Error en actualización']); + exit; + } + + echo json_encode(['success' => true]); + + } catch (Exception $e) { + error_log("Excepción en incrementar_frecuencia: " . $e->getMessage()); + http_response_code(500); + echo json_encode(['success' => false, 'error' => 'Error interno']); + } + exit; +} + /** Procesa la creación de una nueva factura y sus partidas **/ function guardar() { @@ -759,20 +903,17 @@ function actualizar() die("❌ Error ejecutando UPDATE: " . print_r(sqlsrv_errors(), true)); } - // 7) Borrar partidas anteriores - $del = sqlsrv_query($conn, "DELETE FROM dbo.solicitud_importacion_partidas WHERE id_solicitud = ?", [ $id_solicitud ]); - - if ($del === false) { - die("❌ Error borrando partidas previas: " . print_r(sqlsrv_errors(), true)); - } - - // 8) Reinsertar partidas desde el formulario + // 7) Manejo de partidas - Versión mejorada if (!empty($_POST['partidas']) && is_array($_POST['partidas'])) { - $sqlP = "INSERT INTO dbo.solicitud_importacion_partidas - (id_solicitud, descripcion, cantidad_comercial, cantidad_tarifa, - valor_factura, peso_bruto, unidad_comercial_id, tasa_preferencial) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) - "; + // Obtener partidas existentes de la base de datos + $partidasExistentes = []; + $stmtPartidas = sqlsrv_query($conn, "SELECT id_partida FROM dbo.solicitud_importacion_partidas WHERE id_solicitud = ?", [$id_solicitud]); + + while ($row = sqlsrv_fetch_array($stmtPartidas, SQLSRV_FETCH_ASSOC)) { + $partidasExistentes[] = $row['id_partida']; + } + + // Procesar cada partida del formulario foreach ($_POST['partidas'] as $i => $p) { $desc = trim($p['descripcion'] ?? ''); $cantCom = floatval($p['cantidad_comercial'] ?? 0); @@ -781,29 +922,58 @@ function actualizar() $peso = floatval($p['peso_bruto'] ?? 0); $umId = intval($p['unidad_comercial_id'] ?? 0) ?: null; $tasaPref = trim($p['tasa_preferencial'] ?? ''); - - // Sólo inserta si descripción y cantidad comercial válidos + + // Solo procesar si tiene descripción y cantidad válida if ($desc !== '' && $cantCom > 0) { - $paramsP = [ - $id_solicitud, - $desc, - $cantCom, - $cantTar, - $valPart, - $peso, - $umId, - $tasaPref - ]; - $stmtP = sqlsrv_query($conn, $sqlP, $paramsP); - - if ($stmtP === false) { - die("❌ Error insertando partida #$i: " . print_r(sqlsrv_errors(), true)); + // Verificar si es una partida existente (tiene id_partida numérico > 0) + if (!empty($p['id_partida']) && intval($p['id_partida']) > 0) { + // ACTUALIZAR partida existente + $sql = "UPDATE dbo.solicitud_importacion_partidas SET + descripcion = ?, cantidad_comercial = ?, cantidad_tarifa = ?, + valor_factura = ?, peso_bruto = ?, unidad_comercial_id = ?, tasa_preferencial = ? + WHERE id_partida = ? + AND id_solicitud = ? + "; + $params = [ $desc, $cantCom, $cantTar, $valPart, $peso, $umId, $tasaPref, intval($p['id_partida']), $id_solicitud ]; + + // Eliminar de la lista de existentes + if (($key = array_search($p['id_partida'], $partidasExistentes)) !== false) { + unset($partidasExistentes[$key]); + } + } 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) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + "; + $params = [ $id_solicitud, $desc, $cantCom, $cantTar, $valPart, $peso, $umId, $tasaPref ]; + + error_log("Insertando nueva partida: " . print_r($params, true)); } + + $stmt = sqlsrv_query($conn, $sql, $params); + if ($stmt === false) { + error_log("Error en consulta SQL: " . print_r(sqlsrv_errors(), true)); + die("❌ Error procesando partida #$i: " . print_r(sqlsrv_errors(), true)); + } + } + } + // Eliminar partidas que ya no están en el formulario + if (!empty($partidasExistentes)) { + $ids = implode(',', $partidasExistentes); + $sql = "DELETE FROM dbo.solicitud_importacion_partidas + WHERE id_partida IN ($ids) + AND id_solicitud = ? + "; + $stmt = sqlsrv_query($conn, $sql, [$id_solicitud]); + if ($stmt === false) { + die("❌ Error eliminando partidas obsoletas: " . print_r(sqlsrv_errors(), true)); } } } - // 9) Redirigir + // 8) Redirigir header('Location: /IMPORTADORES/solicitud_importacion/lista?updated=ok'); exit; } diff --git a/productos_frecuentes.sql b/productos_frecuentes.sql index da81dc9..66f2325 100644 --- a/productos_frecuentes.sql +++ b/productos_frecuentes.sql @@ -23,9 +23,7 @@ CREATE TABLE dbo.productos_frecuentes ( id_importador INT NOT NULL, -- FK a usuarios_sistema(id_usuario) fecha_alta DATETIME2 NOT NULL DEFAULT GETDATE(), status INT NOT NULL DEFAULT 1, - - frecuencia_uso INT NOT NULL DEFAULT 1, -- cuántas veces se ha utilizado - + frecuencia_uso INT NOT NULL DEFAULT 1, -- cuántas veces se ha utilizado CONSTRAINT FK_prodFreq_UMC FOREIGN KEY (umc_id) @@ -34,4 +32,4 @@ CREATE TABLE dbo.productos_frecuentes ( CONSTRAINT FK_prodFreq_Importador FOREIGN KEY (id_importador) REFERENCES dbo.usuarios_sistema(id_usuario) -); +); \ No newline at end of file diff --git a/views/solicitud_importacion/crear.php b/views/solicitud_importacion/crear.php index d6f2afe..01053de 100644 --- a/views/solicitud_importacion/crear.php +++ b/views/solicitud_importacion/crear.php @@ -79,6 +79,19 @@ .form-group-animated:nth-child(9) { animation-delay: 0.9s; } .form-group-animated:nth-child(10) { animation-delay: 1.0s; } @keyframes slideUp { to { opacity: 1; transform: translateY(0); } } + /* Estilos para las sugerencias de autocompletado */ + .autocomplete-suggestions { position: absolute; width: calc(100% - 2px); /* Ajustar al ancho del input */ background: white; border: 1px solid #ced4da; border-top: none; + border-radius: 0 0 4px 4px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); z-index: 1000; max-height: 200px; overflow-y: auto; display: none; /* Inicialmente oculto */ } + .autocomplete-item { padding: 8px 12px; cursor: pointer; transition: background-color 0.2s; } + .autocomplete-item:hover, .autocomplete-item.active { background-color: #f8f9fa; } + .autocomplete-item strong { display: block; margin-bottom: 2px; } + .autocomplete-item .text-muted { font-size: 0.85em; color: #6c757d; } + .cursor-pointer { cursor: pointer; } + /* Estilos para validación */ + .choices.required .choices__inner { border: 1px solid #ced4da; } + .choices.is-invalid .choices__inner { border: 1px solid #dc3545; background-color: #fff5f5; } + .choices.is-valid .choices__inner { border: 1px solid #28a745; background-color: #f5fff5; } + .choices__input { opacity: 1 !important; } @@ -117,14 +130,14 @@
-
-