diff --git a/app/controllers/productos_frecuentes.php b/app/controllers/productos_frecuentes.php index 9c7114e..b94ba70 100644 --- a/app/controllers/productos_frecuentes.php +++ b/app/controllers/productos_frecuentes.php @@ -106,7 +106,7 @@ function lista() } // === Consulta de productos frecuentes === - $sql = "SELECT + $sql = "SELECT pf.id_producto_frecuente, pf.sinonimo, pf.fraccion, @@ -120,16 +120,16 @@ function lista() pf.estado_mercancia, pf.preferencia, pf.frecuencia_uso - FROM dbo.productos_frecuentes pf - LEFT JOIN dbo.unidades_medida_apendice7 u + FROM dbo.productos_frecuentes pf + LEFT JOIN dbo.unidades_medida_apendice7 u ON pf.umc_id = u.id - LEFT JOIN dbo.paises por + LEFT JOIN dbo.paises por ON pf.pais_origen_destino = por.nombre - LEFT JOIN dbo.paises pc + LEFT JOIN dbo.paises pc ON pf.pais_comprador_vendedor = pc.nombre - WHERE pf.id_importador = ? + WHERE pf.id_importador = ? AND pf.status = 1 - ORDER BY pf.frecuencia_uso DESC + ORDER BY pf.frecuencia_uso DESC "; $stmt = sqlsrv_query($conn, $sql, [$_SESSION['usuario_id']]); @@ -142,9 +142,9 @@ function lista() $productos = []; while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) { - $clave = $row['proveedor_guardado']; + $clave = $row['proveedor_guardado']; $row['nombre_proveedor'] = $proveedoresApi[$clave] ?? 'N/D'; - $productos[] = $row; + $productos[] = $row; } // === Cargar la vista con productos y nombres de proveedor === @@ -369,18 +369,20 @@ function guardar() } // Convertir valores vacíos a NULL para campos opcionales - if ($numero_parte === '') $numero_parte = null; - if ($descripcion === '') $descripcion = null; - if ($uso_mercancia === '') $uso_mercancia = null; - if ($estado_mercancia === '') $estado_mercancia = null; - if ($vinculacion === '') $vinculacion = null; - if ($observaciones === '') $observaciones = null; - if ($preferencia === '') $preferencia = null; - if ($criterio_preferencia === '') $criterio_preferencia = null; - if ($uso_producto === '') $uso_producto = null; - if ($descripcion_producto === '') $descripcion_producto = null; - if ($tipo_mercancia === '') $tipo_mercancia = null; - if ($proveedor === '') $proveedor = null; + if ($numero_parte === '') $numero_parte = null; + if ($descripcion === '') $descripcion = null; + if ($uso_mercancia === '') $uso_mercancia = null; + if ($estado_mercancia === '') $estado_mercancia = null; + if ($vinculacion === '') $vinculacion = null; + if ($observaciones === '') $observaciones = null; + if ($preferencia === '') $preferencia = null; + if ($criterio_preferencia === '') $criterio_preferencia = null; + if ($uso_producto === '') $uso_producto = null; + if ($descripcion_producto === '') $descripcion_producto = null; + if ($tipo_mercancia === '') $tipo_mercancia = null; + if ($proveedor === '') $proveedor = null; + if ($certificado_origen === '') $certificado_origen = null; + if ($documento_en_original === '') $documento_en_original = null; // INSERT $sql = "INSERT INTO dbo.productos_frecuentes diff --git a/productos_frecuentes.txt b/productos_frecuentes.txt index d700944..5ed9682 100644 --- a/productos_frecuentes.txt +++ b/productos_frecuentes.txt @@ -8,4 +8,4 @@ ALTER COLUMN [certificado_origen] NVARCHAR(100); -- 3. (Opcional) Si quieres volver a agregar un valor por defecto ALTER TABLE [Importaciones_HC].[dbo].[productos_frecuentes] -ADD CONSTRAINT DF_productos_certificado_origen DEFAULT '' FOR [certificado_origen]; +ADD CONSTRAINT DF_productos_certificado_origen DEFAULT '' FOR [certificado_origen]; \ No newline at end of file diff --git a/views/solicitud_importacion/crear.php b/views/solicitud_importacion/crear.php index 01053de..dd9b130 100644 --- a/views/solicitud_importacion/crear.php +++ b/views/solicitud_importacion/crear.php @@ -80,13 +80,11 @@ .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; } + #global-autocomplete-suggestions { box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important; border: 1px solid #dee2e6 !important; width: calc(100% - 30px) !important; /* Ajusta según el padding de tu contenedor */ } + #global-autocomplete-suggestions .autocomplete-item { transition: background-color 0.15s ease; } + #global-autocomplete-suggestions .autocomplete-item:hover, + #global-autocomplete-suggestions .autocomplete-item.active { background-color: #f8f9fa !important; } + #global-autocomplete-suggestions .autocomplete-item:last-child { border-bottom: none; } /* Estilos para validación */ .choices.required .choices__inner { border: 1px solid #ced4da; } .choices.is-invalid .choices__inner { border: 1px solid #dc3545; background-color: #fff5f5; } @@ -666,142 +664,243 @@ $(document).ready(function() { let timeoutId; let currentRequest = null; + let currentInput = null; // Para rastrear qué input está activo + + // Crear un único contenedor de sugerencias al final del body + if ($('#global-autocomplete-suggestions').length === 0) { + $('#tabla-partidas').after(` + + `); + } + + const $globalSuggestions = $('#global-autocomplete-suggestions'); // Función para configurar autocompletado en un input function setupAutocomplete(input) { const $input = $(input); - - // Crear contenedor de sugerencias si no existe - if ($input.siblings('.autocomplete-suggestions').length === 0) { - $input.after(''); - } - - const $suggestions = $input.siblings('.autocomplete-suggestions'); - const $row = $input.closest('tr'); + const $row = $input.closest('tr'); $input.off('input').on('input', function() { const query = $(this).val().trim(); + currentInput = $input; // Guardar referencia del input activo clearTimeout(timeoutId); if (currentRequest) currentRequest.abort(); if (query.length < 2) { - $suggestions.hide().empty(); + $globalSuggestions.hide().empty(); return; } timeoutId = setTimeout(() => { currentRequest = $.ajax({ - url: '/IMPORTADORES/solicitud_importacion/buscar_productos', - method: 'GET', - data: { q: query }, + url: '/IMPORTADORES/solicitud_importacion/buscar_productos', + method: 'GET', + data: { q: query }, dataType: 'json', - success: function(productos) { - $suggestions.empty(); + success: function(productos) { + $globalSuggestions.empty(); if (productos && productos.length > 0) { productos.forEach(function(producto) { const $item = $(` -
- ${producto.sinonimo} -
- ${producto.fraccion || 'Sin fracción'} - ${producto.descripcion?.substring(0, 50) || ''} +
+ ${producto.sinonimo} +
+
Fracción: ${producto.fraccion || 'Sin fracción'}
+
Desc: ${producto.descripcion?.substring(0, 50) || ''}${producto.descripcion?.length > 50 ? '...' : ''}
`).data('producto', producto); - $item.on('click', function() { selectProduct($(this).data('producto'), $row); }); - $suggestions.append($item); + // Efectos hover + $item.on('mouseenter', function() { + $globalSuggestions.find('.autocomplete-item').removeClass('active'); + $(this).addClass('active').css('background-color', '#f8f9fa'); + }).on('mouseleave', function() { + $(this).removeClass('active').css('background-color', ''); + }); + + $item.on('click', function() { + selectProduct($(this).data('producto'), currentInput.closest('tr')); + }); + + $globalSuggestions.append($item); }); - $suggestions.show(); + + // Posicionar y mostrar + $globalSuggestions.show(); + } + else { + $globalSuggestions.hide(); } - else { $suggestions.hide(); } }, error: function(xhr, status, error) { - if (status !== 'abort') { console.error("Error en búsqueda:", error); } - $suggestions.hide(); + if (status !== 'abort') { + console.error("Error en búsqueda:", error); + } + $globalSuggestions.hide(); }, - complete: function() { currentRequest = null; } + complete: function() { + currentRequest = null; + } }); }, 300); }); + // Manejar eventos de foco + $input.on('focus', function() { + currentInput = $input; + }); + // Manejar teclado $input.on('keydown', function(e) { - const $items = $suggestions.find('.autocomplete-item'); + if (!$globalSuggestions.is(':visible')) return; + + const $items = $globalSuggestions.find('.autocomplete-item'); const $active = $items.filter('.active'); + switch(e.key) { case 'ArrowDown': e.preventDefault(); + $items.removeClass('active').css('background-color', ''); const $next = $active.length ? $active.next() : $items.first(); - $items.removeClass('active'); - $next.addClass('active'); + $next.addClass('active').css('background-color', '#f8f9fa'); + + // Scroll automático + const containerHeight = $globalSuggestions.height(); + const itemTop = $next.position().top; + const itemHeight = $next.outerHeight(); + if (itemTop + itemHeight > containerHeight) { + $globalSuggestions.scrollTop($globalSuggestions.scrollTop() + itemHeight); + } break; + case 'ArrowUp': e.preventDefault(); + $items.removeClass('active').css('background-color', ''); const $prev = $active.length ? $active.prev() : $items.last(); - $items.removeClass('active'); - $prev.addClass('active'); + $prev.addClass('active').css('background-color', '#f8f9fa'); + + // Scroll automático + if ($prev.position().top < 0) { + $globalSuggestions.scrollTop($globalSuggestions.scrollTop() - $prev.outerHeight()); + } break; + case 'Enter': if ($active.length) { e.preventDefault(); - selectProduct($active.data('producto'), $row); + selectProduct($active.data('producto'), $input.closest('tr')); } break; + case 'Tab': - if ($suggestions.is(':visible')) { + if ($globalSuggestions.is(':visible')) { e.preventDefault(); const $first = $items.first(); - if ($first.length) selectProduct($first.data('producto'), $row); + if ($first.length) { + selectProduct($first.data('producto'), $input.closest('tr')); + } } break; + case 'Escape': - $suggestions.hide(); + $globalSuggestions.hide(); break; } }); - - // Ocultar al hacer clic fuera - $(document).on('click', function(e) { if (!$input.is(e.target) && !$suggestions.is(e.target) && !$suggestions.has(e.target).length) { $suggestions.hide(); } }); } + // Ocultar sugerencias al hacer clic fuera o al hacer scroll + $(document).on('click', function(e) { + if (!currentInput || + (!currentInput.is(e.target) && + !$globalSuggestions.is(e.target) && + !$globalSuggestions.has(e.target).length)) { + $globalSuggestions.hide(); + } + }); + + // Ocultar al hacer scroll (opcional) + $(window).on('scroll resize', function() { + if ($globalSuggestions.is(':visible') && currentInput) { + // Reposicionar en lugar de ocultar + positionGlobalSuggestions(currentInput); + } + }); + // Función para seleccionar un producto function selectProduct(producto, $row) { // Campos básicos $row.find('input[name*="[descripcion]"]').val(producto.sinonimo); + // Tasa preferencial if (producto.preferencia) { const $select = $row.find('select[name*="[tasa_preferencial]"]'); - if ($select[0] && $select[0]._choices) { $select[0]._choices.setChoiceByValue(producto.preferencia); } - else { $select.val(producto.preferencia).trigger('change'); } + if ($select[0] && $select[0]._choices) { + $select[0]._choices.setChoiceByValue(producto.preferencia); + } else { + $select.val(producto.preferencia).trigger('change'); + } } + // Unidad de medida if (producto.umc_id) { const $select = $row.find('select[name*="[unidad_comercial_id]"]'); setTimeout(() => { - if ($select[0] && $select[0]._choices) { $select[0]._choices.setChoiceByValue(producto.umc_id.toString()); } - else { $select.val(producto.umc_id).trigger('change'); } + if ($select[0] && $select[0]._choices) { + $select[0]._choices.setChoiceByValue(producto.umc_id.toString()); + } else { + $select.val(producto.umc_id).trigger('change'); + } }, 50); } + // Número de parte - if (producto.numero_parte) { $row.find('input[name*="[oma_factura]"]').val(producto.numero_parte); } + if (producto.numero_parte) { + $row.find('input[name*="[oma_factura]"]').val(producto.numero_parte); + } + // Incrementar frecuencia $.ajax({ - url: '/IMPORTADORES/solicitud_importacion/incrementar_frecuencia', + url: '/IMPORTADORES/solicitud_importacion/incrementar_frecuencia', method: 'POST', - data: { producto_id: producto.id } + data: { producto_id: producto.id } }); + // Ocultar sugerencias - $row.find('.autocomplete-suggestions').hide(); + $globalSuggestions.hide(); + + // Enfocar el siguiente campo si existe + const $nextInput = $row.next('tr').find('.descripcion-input'); + if ($nextInput.length) { + setTimeout(() => $nextInput.focus(), 100); + } } // Inicializar autocompletado en inputs existentes - $('.descripcion-input').each(function() { setupAutocomplete(this); }); + $('.descripcion-input').each(function() { + setupAutocomplete(this); + }); // Configurar autocompletado para nuevas filas - $('#add-partida').on('click', function() { setTimeout(function() { $('.descripcion-input').last().each(function() { setupAutocomplete(this); }); }, 100); }); + $('#add-partida').on('click', function() { + setTimeout(function() { + $('.descripcion-input').last().each(function() { + setupAutocomplete(this); + }); + }, 100); + }); }); diff --git a/views/solicitud_importacion/editar.php b/views/solicitud_importacion/editar.php index a4611a9..95962a0 100644 --- a/views/solicitud_importacion/editar.php +++ b/views/solicitud_importacion/editar.php @@ -81,13 +81,11 @@ .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; } + #global-autocomplete-suggestions { box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important; border: 1px solid #dee2e6 !important; width: calc(100% - 30px) !important; /* Ajusta según el padding de tu contenedor */ } + #global-autocomplete-suggestions .autocomplete-item { transition: background-color 0.15s ease; } + #global-autocomplete-suggestions .autocomplete-item:hover, + #global-autocomplete-suggestions .autocomplete-item.active { background-color: #f8f9fa !important; } + #global-autocomplete-suggestions .autocomplete-item:last-child { border-bottom: none; } /* Estilos para validación */ .choices.required .choices__inner { border: 1px solid #ced4da; } .choices.is-invalid .choices__inner { border: 1px solid #dc3545; background-color: #fff5f5; } @@ -361,6 +359,26 @@ diff --git a/views/vinculaciones/vinculaciones_importador.php b/views/vinculaciones/vinculaciones_importador.php index da7c715..e9eb38d 100644 --- a/views/vinculaciones/vinculaciones_importador.php +++ b/views/vinculaciones/vinculaciones_importador.php @@ -141,11 +141,11 @@ function confirmChangeAgency(id) { Swal.fire({ - title: '¿Quieres cambiar de agencia?', - text: 'Dejaras de usar la agencia actual y cambiarás a la nueva.', + title: '¿Quieres usar esta agencia?', + text: 'Cambiarás la agencia activa a esta.', icon: 'warning', showCancelButton: true, - confirmButtonText: 'Sí, cambiar agencia', + confirmButtonText: 'Sí, usar agencia', cancelButtonText: 'Cancelar' }).then((result) => { if (result.isConfirmed) {