Suggestions
This commit is contained in:
@@ -381,6 +381,8 @@ function guardar()
|
|||||||
if ($descripcion_producto === '') $descripcion_producto = null;
|
if ($descripcion_producto === '') $descripcion_producto = null;
|
||||||
if ($tipo_mercancia === '') $tipo_mercancia = null;
|
if ($tipo_mercancia === '') $tipo_mercancia = null;
|
||||||
if ($proveedor === '') $proveedor = null;
|
if ($proveedor === '') $proveedor = null;
|
||||||
|
if ($certificado_origen === '') $certificado_origen = null;
|
||||||
|
if ($documento_en_original === '') $documento_en_original = null;
|
||||||
|
|
||||||
// INSERT
|
// INSERT
|
||||||
$sql = "INSERT INTO dbo.productos_frecuentes
|
$sql = "INSERT INTO dbo.productos_frecuentes
|
||||||
|
|||||||
@@ -80,13 +80,11 @@
|
|||||||
.form-group-animated:nth-child(10) { animation-delay: 1.0s; }
|
.form-group-animated:nth-child(10) { animation-delay: 1.0s; }
|
||||||
@keyframes slideUp { to { opacity: 1; transform: translateY(0); } }
|
@keyframes slideUp { to { opacity: 1; transform: translateY(0); } }
|
||||||
/* Estilos para las sugerencias de autocompletado */
|
/* 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;
|
#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 */ }
|
||||||
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 */ }
|
#global-autocomplete-suggestions .autocomplete-item { transition: background-color 0.15s ease; }
|
||||||
.autocomplete-item { padding: 8px 12px; cursor: pointer; transition: background-color 0.2s; }
|
#global-autocomplete-suggestions .autocomplete-item:hover,
|
||||||
.autocomplete-item:hover, .autocomplete-item.active { background-color: #f8f9fa; }
|
#global-autocomplete-suggestions .autocomplete-item.active { background-color: #f8f9fa !important; }
|
||||||
.autocomplete-item strong { display: block; margin-bottom: 2px; }
|
#global-autocomplete-suggestions .autocomplete-item:last-child { border-bottom: none; }
|
||||||
.autocomplete-item .text-muted { font-size: 0.85em; color: #6c757d; }
|
|
||||||
.cursor-pointer { cursor: pointer; }
|
|
||||||
/* Estilos para validación */
|
/* Estilos para validación */
|
||||||
.choices.required .choices__inner { border: 1px solid #ced4da; }
|
.choices.required .choices__inner { border: 1px solid #ced4da; }
|
||||||
.choices.is-invalid .choices__inner { border: 1px solid #dc3545; background-color: #fff5f5; }
|
.choices.is-invalid .choices__inner { border: 1px solid #dc3545; background-color: #fff5f5; }
|
||||||
@@ -666,27 +664,39 @@
|
|||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
let timeoutId;
|
let timeoutId;
|
||||||
let currentRequest = null;
|
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(`
|
||||||
|
<div id="global-autocomplete-suggestions"
|
||||||
|
class="bg-white border shadow-lg w-100"
|
||||||
|
style="display: none;
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: 10px;">
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const $globalSuggestions = $('#global-autocomplete-suggestions');
|
||||||
|
|
||||||
// Función para configurar autocompletado en un input
|
// Función para configurar autocompletado en un input
|
||||||
function setupAutocomplete(input) {
|
function setupAutocomplete(input) {
|
||||||
const $input = $(input);
|
const $input = $(input);
|
||||||
|
|
||||||
// Crear contenedor de sugerencias si no existe
|
|
||||||
if ($input.siblings('.autocomplete-suggestions').length === 0) {
|
|
||||||
$input.after('<div class="autocomplete-suggestions position-absolute w-100 bg-white border border-top-0 shadow-sm" style="max-height: 200px; overflow-y: auto; z-index: 1000; display: none;"></div>');
|
|
||||||
}
|
|
||||||
|
|
||||||
const $suggestions = $input.siblings('.autocomplete-suggestions');
|
|
||||||
const $row = $input.closest('tr');
|
const $row = $input.closest('tr');
|
||||||
|
|
||||||
$input.off('input').on('input', function() {
|
$input.off('input').on('input', function() {
|
||||||
const query = $(this).val().trim();
|
const query = $(this).val().trim();
|
||||||
|
currentInput = $input; // Guardar referencia del input activo
|
||||||
|
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
if (currentRequest) currentRequest.abort();
|
if (currentRequest) currentRequest.abort();
|
||||||
|
|
||||||
if (query.length < 2) {
|
if (query.length < 2) {
|
||||||
$suggestions.hide().empty();
|
$globalSuggestions.hide().empty();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -697,111 +707,200 @@
|
|||||||
data: { q: query },
|
data: { q: query },
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function(productos) {
|
success: function(productos) {
|
||||||
$suggestions.empty();
|
$globalSuggestions.empty();
|
||||||
|
|
||||||
if (productos && productos.length > 0) {
|
if (productos && productos.length > 0) {
|
||||||
productos.forEach(function(producto) {
|
productos.forEach(function(producto) {
|
||||||
const $item = $(`
|
const $item = $(`
|
||||||
<div class="autocomplete-item px-3 py-2 cursor-pointer border-bottom">
|
<div class="autocomplete-item px-3 py-2 cursor-pointer border-bottom hover-bg-light"
|
||||||
<strong>${producto.sinonimo}</strong>
|
style="cursor: pointer;">
|
||||||
<div class="row mb-3 small text-muted">
|
<strong style="color: #333;">${producto.sinonimo}</strong>
|
||||||
<span>${producto.fraccion || 'Sin fracción'}</span>
|
<div class="small text-muted mt-1">
|
||||||
<span>${producto.descripcion?.substring(0, 50) || ''}</span>
|
<div>Fracción: ${producto.fraccion || 'Sin fracción'}</div>
|
||||||
|
<div>Desc: ${producto.descripcion?.substring(0, 50) || ''}${producto.descripcion?.length > 50 ? '...' : ''}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).data('producto', producto);
|
`).data('producto', producto);
|
||||||
|
|
||||||
$item.on('click', function() { selectProduct($(this).data('producto'), $row); });
|
// Efectos hover
|
||||||
$suggestions.append($item);
|
$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', '');
|
||||||
});
|
});
|
||||||
$suggestions.show();
|
|
||||||
|
$item.on('click', function() {
|
||||||
|
selectProduct($(this).data('producto'), currentInput.closest('tr'));
|
||||||
|
});
|
||||||
|
|
||||||
|
$globalSuggestions.append($item);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Posicionar y mostrar
|
||||||
|
$globalSuggestions.show();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$globalSuggestions.hide();
|
||||||
}
|
}
|
||||||
else { $suggestions.hide(); }
|
|
||||||
},
|
},
|
||||||
error: function(xhr, status, error) {
|
error: function(xhr, status, error) {
|
||||||
if (status !== 'abort') { console.error("Error en búsqueda:", error); }
|
if (status !== 'abort') {
|
||||||
$suggestions.hide();
|
console.error("Error en búsqueda:", error);
|
||||||
|
}
|
||||||
|
$globalSuggestions.hide();
|
||||||
},
|
},
|
||||||
complete: function() { currentRequest = null; }
|
complete: function() {
|
||||||
|
currentRequest = null;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}, 300);
|
}, 300);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Manejar eventos de foco
|
||||||
|
$input.on('focus', function() {
|
||||||
|
currentInput = $input;
|
||||||
|
});
|
||||||
|
|
||||||
// Manejar teclado
|
// Manejar teclado
|
||||||
$input.on('keydown', function(e) {
|
$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');
|
const $active = $items.filter('.active');
|
||||||
|
|
||||||
switch(e.key) {
|
switch(e.key) {
|
||||||
case 'ArrowDown':
|
case 'ArrowDown':
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
$items.removeClass('active').css('background-color', '');
|
||||||
const $next = $active.length ? $active.next() : $items.first();
|
const $next = $active.length ? $active.next() : $items.first();
|
||||||
$items.removeClass('active');
|
$next.addClass('active').css('background-color', '#f8f9fa');
|
||||||
$next.addClass('active');
|
|
||||||
|
// 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;
|
break;
|
||||||
|
|
||||||
case 'ArrowUp':
|
case 'ArrowUp':
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
$items.removeClass('active').css('background-color', '');
|
||||||
const $prev = $active.length ? $active.prev() : $items.last();
|
const $prev = $active.length ? $active.prev() : $items.last();
|
||||||
$items.removeClass('active');
|
$prev.addClass('active').css('background-color', '#f8f9fa');
|
||||||
$prev.addClass('active');
|
|
||||||
|
// Scroll automático
|
||||||
|
if ($prev.position().top < 0) {
|
||||||
|
$globalSuggestions.scrollTop($globalSuggestions.scrollTop() - $prev.outerHeight());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Enter':
|
case 'Enter':
|
||||||
if ($active.length) {
|
if ($active.length) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
selectProduct($active.data('producto'), $row);
|
selectProduct($active.data('producto'), $input.closest('tr'));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Tab':
|
case 'Tab':
|
||||||
if ($suggestions.is(':visible')) {
|
if ($globalSuggestions.is(':visible')) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const $first = $items.first();
|
const $first = $items.first();
|
||||||
if ($first.length) selectProduct($first.data('producto'), $row);
|
if ($first.length) {
|
||||||
|
selectProduct($first.data('producto'), $input.closest('tr'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Escape':
|
case 'Escape':
|
||||||
$suggestions.hide();
|
$globalSuggestions.hide();
|
||||||
break;
|
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
|
// Función para seleccionar un producto
|
||||||
function selectProduct(producto, $row) {
|
function selectProduct(producto, $row) {
|
||||||
// Campos básicos
|
// Campos básicos
|
||||||
$row.find('input[name*="[descripcion]"]').val(producto.sinonimo);
|
$row.find('input[name*="[descripcion]"]').val(producto.sinonimo);
|
||||||
|
|
||||||
// Tasa preferencial
|
// Tasa preferencial
|
||||||
if (producto.preferencia) {
|
if (producto.preferencia) {
|
||||||
const $select = $row.find('select[name*="[tasa_preferencial]"]');
|
const $select = $row.find('select[name*="[tasa_preferencial]"]');
|
||||||
if ($select[0] && $select[0]._choices) { $select[0]._choices.setChoiceByValue(producto.preferencia); }
|
if ($select[0] && $select[0]._choices) {
|
||||||
else { $select.val(producto.preferencia).trigger('change'); }
|
$select[0]._choices.setChoiceByValue(producto.preferencia);
|
||||||
|
} else {
|
||||||
|
$select.val(producto.preferencia).trigger('change');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Unidad de medida
|
// Unidad de medida
|
||||||
if (producto.umc_id) {
|
if (producto.umc_id) {
|
||||||
const $select = $row.find('select[name*="[unidad_comercial_id]"]');
|
const $select = $row.find('select[name*="[unidad_comercial_id]"]');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if ($select[0] && $select[0]._choices) { $select[0]._choices.setChoiceByValue(producto.umc_id.toString()); }
|
if ($select[0] && $select[0]._choices) {
|
||||||
else { $select.val(producto.umc_id).trigger('change'); }
|
$select[0]._choices.setChoiceByValue(producto.umc_id.toString());
|
||||||
|
} else {
|
||||||
|
$select.val(producto.umc_id).trigger('change');
|
||||||
|
}
|
||||||
}, 50);
|
}, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Número de parte
|
// 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
|
// Incrementar frecuencia
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/IMPORTADORES/solicitud_importacion/incrementar_frecuencia',
|
url: '/IMPORTADORES/solicitud_importacion/incrementar_frecuencia',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { producto_id: producto.id }
|
data: { producto_id: producto.id }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ocultar sugerencias
|
// 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
|
// Inicializar autocompletado en inputs existentes
|
||||||
$('.descripcion-input').each(function() { setupAutocomplete(this); });
|
$('.descripcion-input').each(function() {
|
||||||
|
setupAutocomplete(this);
|
||||||
|
});
|
||||||
|
|
||||||
// Configurar autocompletado para nuevas filas
|
// 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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -81,13 +81,11 @@
|
|||||||
.form-group-animated:nth-child(10) { animation-delay: 1.0s; }
|
.form-group-animated:nth-child(10) { animation-delay: 1.0s; }
|
||||||
@keyframes slideUp { to { opacity: 1; transform: translateY(0); } }
|
@keyframes slideUp { to { opacity: 1; transform: translateY(0); } }
|
||||||
/* Estilos para las sugerencias de autocompletado */
|
/* 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;
|
#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 */ }
|
||||||
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 */ }
|
#global-autocomplete-suggestions .autocomplete-item { transition: background-color 0.15s ease; }
|
||||||
.autocomplete-item { padding: 8px 12px; cursor: pointer; transition: background-color 0.2s; }
|
#global-autocomplete-suggestions .autocomplete-item:hover,
|
||||||
.autocomplete-item:hover, .autocomplete-item.active { background-color: #f8f9fa; }
|
#global-autocomplete-suggestions .autocomplete-item.active { background-color: #f8f9fa !important; }
|
||||||
.autocomplete-item strong { display: block; margin-bottom: 2px; }
|
#global-autocomplete-suggestions .autocomplete-item:last-child { border-bottom: none; }
|
||||||
.autocomplete-item .text-muted { font-size: 0.85em; color: #6c757d; }
|
|
||||||
.cursor-pointer { cursor: pointer; }
|
|
||||||
/* Estilos para validación */
|
/* Estilos para validación */
|
||||||
.choices.required .choices__inner { border: 1px solid #ced4da; }
|
.choices.required .choices__inner { border: 1px solid #ced4da; }
|
||||||
.choices.is-invalid .choices__inner { border: 1px solid #dc3545; background-color: #fff5f5; }
|
.choices.is-invalid .choices__inner { border: 1px solid #dc3545; background-color: #fff5f5; }
|
||||||
@@ -361,6 +359,26 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0/dist/js/select2.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0/dist/js/select2.min.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
// Variables globales para el autocompletado
|
||||||
|
let timeoutId;
|
||||||
|
let currentRequest = null;
|
||||||
|
let currentInput = null;
|
||||||
|
|
||||||
|
// ✅ 1. INICIALIZACIÓN PRINCIPAL
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const proveedorActual = '<?= htmlspecialchars($proveedor_clave_actual ?? '') ?>';
|
||||||
|
console.log('📦 Proveedor actual:', proveedorActual);
|
||||||
|
|
||||||
|
// Inicializar Choices.js en todos los selects
|
||||||
|
cargarProveedores(proveedorActual).then(() => {
|
||||||
|
document.querySelectorAll('.searchable').forEach(safeInitializeChoices);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Inicializar el sistema de autocompletado
|
||||||
|
initAutocompleteSystem();
|
||||||
|
});
|
||||||
|
|
||||||
|
// ✅ 2. FUNCIÓN PARA INICIALIZAR CHOICES.JS
|
||||||
function safeInitializeChoices(element) {
|
function safeInitializeChoices(element) {
|
||||||
if (!element._choices) {
|
if (!element._choices) {
|
||||||
const isRequired = element.hasAttribute('required');
|
const isRequired = element.hasAttribute('required');
|
||||||
@@ -378,37 +396,39 @@
|
|||||||
|
|
||||||
if (isRequired) {
|
if (isRequired) {
|
||||||
const container = element.closest('.choices');
|
const container = element.closest('.choices');
|
||||||
if (container) { container.classList.add('required'); }
|
if (container) container.classList.add('required');
|
||||||
element.addEventListener('change', validateChoice.bind(null, element));
|
element.addEventListener('change', validateChoice.bind(null, element));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 1. INICIALIZACIÓN PRINCIPAL
|
// ✅ 3. FUNCIÓN PARA VALIDAR CAMPOS CHOICES
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
function validateChoice(selectElement) {
|
||||||
const proveedorActual = '<?= htmlspecialchars($proveedor_clave_actual ?? '') ?>';
|
const choicesInstance = selectElement._choices;
|
||||||
console.log('📦 Proveedor actual:', proveedorActual);
|
if (choicesInstance) {
|
||||||
|
const container = selectElement.closest('.choices');
|
||||||
|
const hasValue = choicesInstance.getValue(true).length > 0;
|
||||||
|
const isRequired = selectElement.hasAttribute('data-required');
|
||||||
|
|
||||||
cargarProveedores(proveedorActual).then(() => {
|
if (isRequired) {
|
||||||
document.querySelectorAll('.searchable').forEach(el => {
|
if (hasValue) {
|
||||||
// Destruir instancia previa si existe
|
container.classList.remove('is-invalid');
|
||||||
if (el.choicesInstance) el.choicesInstance.destroy();
|
container.classList.add('is-valid');
|
||||||
|
} else {
|
||||||
|
container.classList.remove('is-valid');
|
||||||
|
container.classList.add('is-invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const instance = new Choices(el, {
|
// ✅ 4. FUNCIÓN PARA CARGAR PROVEEDORES
|
||||||
searchEnabled: true,
|
|
||||||
itemSelectText: '',
|
|
||||||
shouldSort: false
|
|
||||||
});
|
|
||||||
|
|
||||||
// Guardar instancia
|
|
||||||
el.choicesInstance = instance;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// ✅ 2. FUNCIÓN PARA CARGAR PROVEEDORES (CON PROMESA)
|
|
||||||
function cargarProveedores(proveedorActual) {
|
function cargarProveedores(proveedorActual) {
|
||||||
const proveedorEl = document.getElementById('proveedor_id');
|
const proveedorEl = document.getElementById('proveedor_id');
|
||||||
|
|
||||||
|
// Asegurarse que el nombre sea consistente
|
||||||
|
proveedorEl.name = 'proveedor_id';
|
||||||
|
|
||||||
return fetch('/IMPORTADORES/solicitud_importacion/ajax_proveedores')
|
return fetch('/IMPORTADORES/solicitud_importacion/ajax_proveedores')
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||||
@@ -421,18 +441,16 @@
|
|||||||
const opt = document.createElement('option');
|
const opt = document.createElement('option');
|
||||||
opt.value = item.id;
|
opt.value = item.id;
|
||||||
opt.textContent = item.text;
|
opt.textContent = item.text;
|
||||||
|
|
||||||
if (String(item.id) === proveedorActual) {
|
if (String(item.id) === proveedorActual) {
|
||||||
opt.selected = true;
|
opt.selected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
proveedorEl.appendChild(opt);
|
proveedorEl.appendChild(opt);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Forzar manualmente el value
|
// Forzar manualmente el value
|
||||||
|
if (proveedorActual) {
|
||||||
proveedorEl.value = proveedorActual;
|
proveedorEl.value = proveedorActual;
|
||||||
console.log('✅ Proveedores cargados. Proveedor actual:', proveedorActual);
|
}
|
||||||
console.log('🧪 Select value actual (después de asignar):', proveedorEl.value);
|
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error('❌ Error cargando proveedores:', err);
|
console.error('❌ Error cargando proveedores:', err);
|
||||||
@@ -440,25 +458,22 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 3. AGREGAR PARTIDAS - VERSIÓN CORREGIDA
|
// ✅ 5. AGREGAR PARTIDAS
|
||||||
document.getElementById('add-partida').addEventListener('click', () => {
|
document.getElementById('add-partida').addEventListener('click', () => {
|
||||||
const tbody = document.querySelector('#tabla-partidas tbody');
|
const tbody = document.querySelector('#tabla-partidas tbody');
|
||||||
const rows = tbody.querySelectorAll('tr');
|
const idx = tbody.querySelectorAll('tr').length;
|
||||||
const idx = rows.length; // Esto asegura índices únicos y secuenciales
|
|
||||||
const row = document.createElement('tr');
|
const row = document.createElement('tr');
|
||||||
|
|
||||||
row.innerHTML = `
|
row.innerHTML = `
|
||||||
<td>
|
<td>
|
||||||
<div class="position-relative">
|
<div class="position-relative">
|
||||||
<input name="partidas[${idx}][descripcion]" class="form-control w-auto descripcion-input" required autocomplete="off">
|
<input name="partidas[${idx}][descripcion]" class="form-control w-auto descripcion-input" required autocomplete="off">
|
||||||
<div class="autocomplete-suggestions position-absolute w-100 bg-white border border-top-0 shadow-sm"
|
|
||||||
style="max-height: 200px; overflow-y: auto; z-index: 1000; display: none;"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td><input name="partidas[${idx}][cantidad_comercial]" type="number" step="0.0001" class="form-control w-auto" min="0" required></td>
|
<td><input name="partidas[${idx}][cantidad_comercial]" type="number" step="0.0001" class="form-control w-auto" min="0" required></td>
|
||||||
<td><input name="partidas[${idx}][cantidad_tarifa]" type="number" step="0.0001" class="form-control w-auto" min="0"></td>
|
<td><input name="partidas[${idx}][cantidad_tarifa]" type="number" step="0.0001" class="form-control w-auto" min="0"></td>
|
||||||
<td>
|
<td>
|
||||||
<select name="partidas[${idx}][unidad_comercial_id]" class="form-select searchable">
|
<select name="partidas[${idx}][unidad_comercial_id]" class="form-select searchable" data-required="true">
|
||||||
<option value="">-- Unidad --</option>
|
<option value="">-- Unidad --</option>
|
||||||
<?php foreach($unidades_medida as $um): ?>
|
<?php foreach($unidades_medida as $um): ?>
|
||||||
<option value="<?= htmlspecialchars($um['id']) ?>"><?= htmlspecialchars($um['descripcion']) ?></option>
|
<option value="<?= htmlspecialchars($um['id']) ?>"><?= htmlspecialchars($um['descripcion']) ?></option>
|
||||||
@@ -468,7 +483,7 @@
|
|||||||
<td><input name="partidas[${idx}][valor_factura]" type="number" step="0.01" class="form-control valor-partida w-auto" min="0"></td>
|
<td><input name="partidas[${idx}][valor_factura]" type="number" step="0.01" class="form-control valor-partida w-auto" min="0"></td>
|
||||||
<td><input name="partidas[${idx}][peso_bruto]" type="number" step="0.0001" class="form-control w-auto" min="0"></td>
|
<td><input name="partidas[${idx}][peso_bruto]" type="number" step="0.0001" class="form-control w-auto" min="0"></td>
|
||||||
<td>
|
<td>
|
||||||
<select name="partidas[${idx}][tasa_preferencial]" class="form-select searchable">
|
<select name="partidas[${idx}][tasa_preferencial]" class="form-select searchable" data-required="true">
|
||||||
<option value="">-- Selecciona --</option>
|
<option value="">-- Selecciona --</option>
|
||||||
<option>General</option>
|
<option>General</option>
|
||||||
<option>TLC</option>
|
<option>TLC</option>
|
||||||
@@ -484,39 +499,29 @@
|
|||||||
|
|
||||||
tbody.appendChild(row);
|
tbody.appendChild(row);
|
||||||
|
|
||||||
// Inicializamos Choices en los nuevos selects
|
// Inicializar Choices en los nuevos selects
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
row.querySelectorAll('.searchable').forEach(el => {
|
row.querySelectorAll('.searchable').forEach(el => {
|
||||||
if (!el._choices) {
|
safeInitializeChoices(el);
|
||||||
el._choices = new Choices(el, {
|
|
||||||
searchEnabled: true,
|
|
||||||
itemSelectText: '',
|
|
||||||
shouldSort: false,
|
|
||||||
silent: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Configurar autocompletado para el nuevo campo de descripción
|
// Configurar autocompletado para el nuevo campo
|
||||||
setupAutocomplete(row.querySelector('.descripcion-input'));
|
setupAutocomplete(row.querySelector('.descripcion-input'));
|
||||||
}, 50);
|
}, 50);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ✅ 4. REMOVER PARTIDAS
|
// ✅ 6. REMOVER PARTIDAS
|
||||||
document.querySelector('#tabla-partidas tbody').addEventListener('click', e => {
|
document.querySelector('#tabla-partidas tbody').addEventListener('click', e => {
|
||||||
if (e.target.matches('.remove-row')) {
|
if (e.target.matches('.remove-row')) {
|
||||||
// Destruir instancia de Choices.js antes de remover la fila
|
|
||||||
const row = e.target.closest('tr');
|
const row = e.target.closest('tr');
|
||||||
row.querySelectorAll('.searchable').forEach(el => {
|
row.querySelectorAll('.searchable').forEach(el => {
|
||||||
if (el.choicesInstance) {
|
if (el._choices) el._choices.destroy();
|
||||||
el.choicesInstance.destroy();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
row.remove();
|
row.remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ✅ 5. CONTROLAR OVERFLOW DE LA TABLA
|
// ✅ 7. CONTROLAR OVERFLOW DE LA TABLA
|
||||||
document.addEventListener('click', function(e) {
|
document.addEventListener('click', function(e) {
|
||||||
const tableContainer = document.querySelector('.table-responsive');
|
const tableContainer = document.querySelector('.table-responsive');
|
||||||
if (!tableContainer) return;
|
if (!tableContainer) return;
|
||||||
@@ -528,222 +533,89 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ✅ 6. VALIDACIÓN DEL FORMULARIO
|
// ✅ 8. VALIDACIÓN DEL FORMULARIO
|
||||||
$(document).ready(function() {
|
document.getElementById('solicitudForm').addEventListener('submit', function(e) {
|
||||||
$('#solicitudForm').submit(function(e){
|
// Validar selects de Choices.js
|
||||||
const total = parseFloat($('#valor_factura').val()) || 0;
|
let isValid = true;
|
||||||
|
|
||||||
|
document.querySelectorAll('.searchable[data-required]').forEach(el => {
|
||||||
|
validateChoice(el);
|
||||||
|
const choicesInstance = el._choices;
|
||||||
|
if (choicesInstance && choicesInstance.getValue(true).length === 0) {
|
||||||
|
isValid = false;
|
||||||
|
// Desplazarse al primer error
|
||||||
|
if (isValid === false) {
|
||||||
|
el.closest('.choices').scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Validar suma de partidas
|
||||||
|
const total = parseFloat(document.getElementById('valor_factura').value) || 0;
|
||||||
let sum = 0;
|
let sum = 0;
|
||||||
$('.valor-partida').each(function(){ sum += parseFloat($(this).val()) || 0; });
|
document.querySelectorAll('.valor-partida').forEach(input => {
|
||||||
|
sum += parseFloat(input.value) || 0;
|
||||||
|
});
|
||||||
|
|
||||||
if(Math.abs(sum - total) > 0.001){
|
if (Math.abs(sum - total) > 0.001) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Swal.fire({ icon: 'error', title: 'Error de validación', text: `La suma de partidas (${sum.toFixed(2)}) no coincide con Valor Factura (${total.toFixed(2)}).` });
|
Swal.fire({
|
||||||
}
|
icon: 'error',
|
||||||
|
title: 'Error de validación',
|
||||||
|
text: `La suma de partidas (${sum.toFixed(2)}) no coincide con Valor Factura (${total.toFixed(2)}).`
|
||||||
});
|
});
|
||||||
});
|
return;
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const transportistaSelect = document.getElementById('transportista_id');
|
|
||||||
const choferSelect = document.getElementById('chofer_id');
|
|
||||||
|
|
||||||
// Guardar todas las opciones de choferes originales (para fallback)
|
|
||||||
const allChoferes = Array.from(choferSelect.querySelectorAll('option')).slice(1); // Excluir la primera opción vacía
|
|
||||||
|
|
||||||
// Obtener valores actuales (para modo edición)
|
|
||||||
const transportistaActual = transportistaSelect.value;
|
|
||||||
const choferActual = choferSelect.value;
|
|
||||||
|
|
||||||
// Crear un mapa de choferes por transportista desde las opciones cargadas
|
|
||||||
const choferesPorTransportista = {};
|
|
||||||
allChoferes.forEach(option => {
|
|
||||||
const transportistaId = option.getAttribute('data-transportista');
|
|
||||||
if (transportistaId) {
|
|
||||||
if (!choferesPorTransportista[transportistaId]) {
|
|
||||||
choferesPorTransportista[transportistaId] = [];
|
|
||||||
}
|
}
|
||||||
choferesPorTransportista[transportistaId].push({
|
|
||||||
id_chofer: option.value,
|
if (!isValid) {
|
||||||
nombre: option.textContent.trim()
|
e.preventDefault();
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Campos requeridos',
|
||||||
|
text: 'Por favor complete todos los campos obligatorios marcados en rojo',
|
||||||
|
confirmButtonColor: '#3085d6'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Debug: mostrar el cache inicial
|
// ✅ 9. SISTEMA DE AUTOCOMPLETADO
|
||||||
console.log('Cache de choferes por transportista:', choferesPorTransportista);
|
function initAutocompleteSystem() {
|
||||||
|
// Crear contenedor de sugerencias si no existe
|
||||||
|
if (!document.getElementById('global-autocomplete-suggestions')) {
|
||||||
|
const suggestionsDiv = document.createElement('div');
|
||||||
|
suggestionsDiv.id = 'global-autocomplete-suggestions';
|
||||||
|
suggestionsDiv.className = 'bg-white border shadow-sm w-100';
|
||||||
|
suggestionsDiv.style.cssText = 'display: none; max-height: 200px; overflow-y: auto; border-radius: 4px; margin-top: 5px; margin-bottom: 10px;';
|
||||||
|
document.querySelector('#tabla-partidas').after(suggestionsDiv);
|
||||||
|
}
|
||||||
|
|
||||||
// Inicializar Choices.js si está disponible
|
// Configurar autocompletado para inputs existentes
|
||||||
let choferChoices = null;
|
document.querySelectorAll('.descripcion-input').forEach(input => {
|
||||||
if (typeof Choices !== 'undefined') {
|
setupAutocomplete(input);
|
||||||
choferChoices = new Choices(choferSelect, {
|
});
|
||||||
searchEnabled: true,
|
|
||||||
placeholderValue: '-- Selecciona Transportista primero --',
|
// Configurar autocompletado para nuevas filas
|
||||||
noResultsText: 'No se encontraron resultados',
|
document.getElementById('add-partida').addEventListener('click', function() {
|
||||||
itemSelectText: '',
|
setTimeout(() => {
|
||||||
searchPlaceholderValue: 'Buscar chofer...'
|
const newInput = document.querySelector('#tabla-partidas tbody tr:last-child .descripcion-input');
|
||||||
|
if (newInput) setupAutocomplete(newInput);
|
||||||
|
}, 100);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Función para filtrar choferes por transportista
|
|
||||||
function filtrarChoferes(transportistaId) {
|
|
||||||
// Limpiar opciones excepto la primera
|
|
||||||
if (choferChoices) { choferChoices.clearStore(); }
|
|
||||||
else { choferSelect.innerHTML = '<option value="">-- Selecciona Chofer --</option>'; }
|
|
||||||
|
|
||||||
if (transportistaId) {
|
|
||||||
// Intentar usar datos locales primero (más rápido)
|
|
||||||
if (choferesPorTransportista[transportistaId] && choferesPorTransportista[transportistaId].length > 0) {
|
|
||||||
console.log('Usando datos locales para transportista:', transportistaId);
|
|
||||||
cargarChoferes(choferesPorTransportista[transportistaId]);
|
|
||||||
} else {
|
|
||||||
// Si no hay datos locales, hacer petición AJAX
|
|
||||||
console.log('Haciendo petición AJAX para transportista:', transportistaId);
|
|
||||||
mostrarCargando();
|
|
||||||
|
|
||||||
fetch(`/IMPORTADORES/solicitud_importacion/obtenerChoferesPorTransportista?transportista_id=${transportistaId}`)
|
|
||||||
.then(response => {
|
|
||||||
console.log('Response status:', response.status);
|
|
||||||
if (!response.ok) { return response.json().then(data => { throw new Error(data.error || `Error ${response.status}`); }); }
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then(choferes => {
|
|
||||||
console.log('Choferes recibidos:', choferes);
|
|
||||||
// Guardar en cache local para futuras consultas
|
|
||||||
choferesPorTransportista[transportistaId] = choferes;
|
|
||||||
cargarChoferes(choferes);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error al cargar choferes:', error);
|
|
||||||
mostrarError();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Si no hay transportista seleccionado, mostrar mensaje
|
|
||||||
mostrarSeleccionarTransportista();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Función para mostrar estado de carga
|
|
||||||
function mostrarCargando() {
|
|
||||||
if (choferChoices) {
|
|
||||||
choferChoices.clearStore();
|
|
||||||
choferChoices.setChoices([{
|
|
||||||
value: '',
|
|
||||||
label: '-- Cargando choferes... --',
|
|
||||||
disabled: true
|
|
||||||
}], 'value', 'label', true);
|
|
||||||
}
|
|
||||||
else { choferSelect.innerHTML = '<option value="">-- Cargando choferes... --</option>'; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Función para mostrar mensaje de seleccionar transportista
|
|
||||||
function mostrarSeleccionarTransportista() {
|
|
||||||
if (choferChoices) {
|
|
||||||
choferChoices.clearStore();
|
|
||||||
choferChoices.setChoices([{
|
|
||||||
value: '',
|
|
||||||
label: '-- Selecciona Transportista primero --',
|
|
||||||
disabled: true
|
|
||||||
}], 'value', 'label', true);
|
|
||||||
}
|
|
||||||
else { choferSelect.innerHTML = '<option value="">-- Selecciona Transportista primero --</option>'; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Función para mostrar error
|
|
||||||
function mostrarError() {
|
|
||||||
const errorMessage = '-- Error al cargar choferes --';
|
|
||||||
if (choferChoices) {
|
|
||||||
choferChoices.clearStore();
|
|
||||||
choferChoices.setChoices([{
|
|
||||||
value: '',
|
|
||||||
label: errorMessage,
|
|
||||||
disabled: true
|
|
||||||
}], 'value', 'label', true);
|
|
||||||
}
|
|
||||||
else { choferSelect.innerHTML = `<option value="">${errorMessage}</option>`; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Función para cargar choferes en el select
|
|
||||||
function cargarChoferes(choferes) {
|
|
||||||
const opciones = [{
|
|
||||||
value: '',
|
|
||||||
label: '-- Selecciona Chofer --',
|
|
||||||
disabled: false
|
|
||||||
}];
|
|
||||||
|
|
||||||
choferes.forEach(chofer => {
|
|
||||||
opciones.push({
|
|
||||||
value: chofer.id_chofer,
|
|
||||||
label: chofer.nombre,
|
|
||||||
disabled: false
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (choferChoices) {
|
|
||||||
choferChoices.clearStore();
|
|
||||||
choferChoices.setChoices(opciones, 'value', 'label', true);
|
|
||||||
|
|
||||||
// Restaurar selección actual si es válida
|
|
||||||
if (choferActual && choferes.some(c => c.id_chofer == choferActual)) {
|
|
||||||
choferChoices.setChoiceByValue(choferActual);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
choferSelect.innerHTML = '';
|
|
||||||
opciones.forEach(opcion => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = opcion.value;
|
|
||||||
option.textContent = opcion.label;
|
|
||||||
if (opcion.disabled) option.disabled = true;
|
|
||||||
choferSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Restaurar selección actual si es válida
|
|
||||||
if (choferActual && choferes.some(c => c.id_chofer == choferActual)) { choferSelect.value = choferActual; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inicializar el filtro al cargar la página (para modo edición)
|
|
||||||
if (transportistaActual) { filtrarChoferes(transportistaActual); }
|
|
||||||
else { mostrarSeleccionarTransportista(); }
|
|
||||||
|
|
||||||
// Event listener para cambio de transportista
|
|
||||||
transportistaSelect.addEventListener('change', function() {
|
|
||||||
const transportistaId = this.value;
|
|
||||||
filtrarChoferes(transportistaId);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Event listener para validar que se seleccione un chofer válido
|
|
||||||
choferSelect.addEventListener('change', function() {
|
|
||||||
const choferSeleccionado = this.value;
|
|
||||||
if (choferSeleccionado && !transportistaSelect.value) {
|
|
||||||
alert('Por favor, selecciona un transportista primero.');
|
|
||||||
this.value = '';
|
|
||||||
if (choferChoices) { choferChoices.setChoiceByValue(''); }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
|
||||||
let timeoutId;
|
|
||||||
let currentRequest = null;
|
|
||||||
|
|
||||||
// Función para configurar autocompletado
|
|
||||||
function setupAutocomplete(input) {
|
function setupAutocomplete(input) {
|
||||||
const $input = $(input);
|
const $input = $(input);
|
||||||
|
const $globalSuggestions = $('#global-autocomplete-suggestions');
|
||||||
|
|
||||||
// Crear contenedor de sugerencias si no existe
|
$input.off('input').on('input', function() {
|
||||||
if ($input.siblings('.autocomplete-suggestions').length === 0) { $input.after('<div class="autocomplete-suggestions position-absolute w-100 bg-white border border-top-0 shadow-sm" style="max-height: 200px; overflow-y: auto; z-index: 1000; display: none;"></div>'); }
|
|
||||||
|
|
||||||
const $suggestions = $input.siblings('.autocomplete-suggestions');
|
|
||||||
const $row = $input.closest('tr');
|
|
||||||
|
|
||||||
$input.on('input', function() {
|
|
||||||
const query = $(this).val().trim();
|
const query = $(this).val().trim();
|
||||||
|
currentInput = $input;
|
||||||
|
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
if (currentRequest) currentRequest.abort();
|
if (currentRequest) currentRequest.abort();
|
||||||
|
|
||||||
if (query.length < 2) {
|
if (query.length < 2) {
|
||||||
$suggestions.hide().empty();
|
$globalSuggestions.hide().empty();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -754,109 +626,141 @@
|
|||||||
data: { q: query },
|
data: { q: query },
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function(productos) {
|
success: function(productos) {
|
||||||
$suggestions.empty();
|
$globalSuggestions.empty();
|
||||||
|
|
||||||
if (productos && productos.length > 0) {
|
if (productos && productos.length > 0) {
|
||||||
productos.forEach(function(producto) {
|
productos.forEach(function(producto) {
|
||||||
const $item = $(`
|
const $item = $(`
|
||||||
<div class="autocomplete-item px-3 py-2 cursor-pointer border-bottom">
|
<div class="autocomplete-item px-3 py-2 cursor-pointer border-bottom hover-bg-light">
|
||||||
<strong>${producto.sinonimo}</strong>
|
<strong style="color: #333;">${producto.sinonimo}</strong>
|
||||||
<div class="row mb-3 small text-muted">
|
<div class="small text-muted mt-1">
|
||||||
<span>${producto.fraccion || 'Sin fracción'}</span>
|
<div>Fracción: ${producto.fraccion || 'Sin fracción'}</div>
|
||||||
<span>${producto.descripcion?.substring(0, 50) || ''}</span>
|
<div>Desc: ${producto.descripcion?.substring(0, 50) || ''}${producto.descripcion?.length > 50 ? '...' : ''}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).data('producto', producto);
|
`).data('producto', producto);
|
||||||
$item.on('click', function() { selectProduct($(this).data('producto'), $row); });
|
|
||||||
$suggestions.append($item);
|
$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', '');
|
||||||
});
|
});
|
||||||
$suggestions.show();
|
|
||||||
|
$item.on('click', function() {
|
||||||
|
selectProduct($(this).data('producto'), $input.closest('tr'));
|
||||||
|
});
|
||||||
|
|
||||||
|
$globalSuggestions.append($item);
|
||||||
|
});
|
||||||
|
|
||||||
|
$globalSuggestions.show();
|
||||||
}
|
}
|
||||||
else { $suggestions.hide(); }
|
|
||||||
},
|
},
|
||||||
error: function(xhr, status, error) {
|
error: function(xhr, status, error) {
|
||||||
if (status !== 'abort') { console.error("Error en búsqueda:", error); }
|
if (status !== 'abort') console.error("Error en búsqueda:", error);
|
||||||
$suggestions.hide();
|
$globalSuggestions.hide();
|
||||||
},
|
},
|
||||||
complete: function() { currentRequest = null; }
|
complete: function() {
|
||||||
|
currentRequest = null;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}, 300);
|
}, 300);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Manejar teclado
|
// Manejar eventos de teclado
|
||||||
$input.on('keydown', function(e) {
|
$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');
|
const $active = $items.filter('.active');
|
||||||
|
|
||||||
switch(e.key) {
|
switch(e.key) {
|
||||||
case 'ArrowDown':
|
case 'ArrowDown':
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
$items.removeClass('active').css('background-color', '');
|
||||||
const $next = $active.length ? $active.next() : $items.first();
|
const $next = $active.length ? $active.next() : $items.first();
|
||||||
$items.removeClass('active');
|
$next.addClass('active').css('background-color', '#f8f9fa');
|
||||||
$next.addClass('active');
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'ArrowUp':
|
case 'ArrowUp':
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
$items.removeClass('active').css('background-color', '');
|
||||||
const $prev = $active.length ? $active.prev() : $items.last();
|
const $prev = $active.length ? $active.prev() : $items.last();
|
||||||
$items.removeClass('active');
|
$prev.addClass('active').css('background-color', '#f8f9fa');
|
||||||
$prev.addClass('active');
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Enter':
|
case 'Enter':
|
||||||
if ($active.length) {
|
if ($active.length) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
selectProduct($active.data('producto'), $row);
|
selectProduct($active.data('producto'), $input.closest('tr'));
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'Tab':
|
|
||||||
if ($suggestions.is(':visible')) {
|
|
||||||
e.preventDefault();
|
|
||||||
const $first = $items.first();
|
|
||||||
if ($first.length) selectProduct($first.data('producto'), $row);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Escape':
|
case 'Escape':
|
||||||
$suggestions.hide();
|
$globalSuggestions.hide();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ocultar al hacer clic fuera
|
// Ocultar sugerencias 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(); } });
|
$(document).on('click', function(e) {
|
||||||
|
if (!currentInput ||
|
||||||
|
(!currentInput.is(e.target) &&
|
||||||
|
!$globalSuggestions.is(e.target) &&
|
||||||
|
!$globalSuggestions.has(e.target).length)) {
|
||||||
|
$globalSuggestions.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Función para seleccionar un producto
|
|
||||||
function selectProduct(producto, $row) {
|
function selectProduct(producto, $row) {
|
||||||
// Campos básicos
|
// Campos básicos
|
||||||
$row.find('input[name*="[descripcion]"]').val(producto.sinonimo);
|
$row.find('input[name*="[descripcion]"]').val(producto.sinonimo);
|
||||||
|
|
||||||
// Tasa preferencial
|
// Tasa preferencial
|
||||||
if (producto.preferencia) {
|
if (producto.preferencia) {
|
||||||
const $select = $row.find('select[name*="[tasa_preferencial]"]');
|
const $select = $row.find('select[name*="[tasa_preferencial]"]');
|
||||||
if ($select[0] && $select[0]._choices) { $select[0]._choices.setChoiceByValue(producto.preferencia); }
|
if ($select[0] && $select[0]._choices) {
|
||||||
else { $select.val(producto.preferencia).trigger('change'); }
|
$select[0]._choices.setChoiceByValue(producto.preferencia);
|
||||||
|
} else {
|
||||||
|
$select.val(producto.preferencia).trigger('change');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Unidad de medida
|
// Unidad de medida
|
||||||
if (producto.umc_id) {
|
if (producto.umc_id) {
|
||||||
const $select = $row.find('select[name*="[unidad_comercial_id]"]');
|
const $select = $row.find('select[name*="[unidad_comercial_id]"]');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if ($select[0] && $select[0]._choices) { $select[0]._choices.setChoiceByValue(producto.umc_id.toString()); }
|
if ($select[0] && $select[0]._choices) {
|
||||||
else { $select.val(producto.umc_id).trigger('change'); }
|
$select[0]._choices.setChoiceByValue(producto.umc_id.toString());
|
||||||
|
} else {
|
||||||
|
$select.val(producto.umc_id).trigger('change');
|
||||||
|
}
|
||||||
}, 50);
|
}, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Número de parte
|
// 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
|
// Incrementar frecuencia
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/IMPORTADORES/solicitud_importacion/incrementar_frecuencia',
|
url: '/IMPORTADORES/solicitud_importacion/incrementar_frecuencia',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { producto_id: producto.id }
|
data: { producto_id: producto.id }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ocultar sugerencias
|
// Ocultar sugerencias
|
||||||
$row.find('.autocomplete-suggestions').hide();
|
$('#global-autocomplete-suggestions').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); });
|
|
||||||
// Configurar autocompletado para nuevas filas
|
|
||||||
$('#add-partida').on('click', function() { setTimeout(function() { $('.descripcion-input').last().each(function() { setupAutocomplete(this); }); }, 100); });
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -141,11 +141,11 @@
|
|||||||
|
|
||||||
function confirmChangeAgency(id) {
|
function confirmChangeAgency(id) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: '¿Quieres cambiar de agencia?',
|
title: '¿Quieres usar esta agencia?',
|
||||||
text: 'Dejaras de usar la agencia actual y cambiarás a la nueva.',
|
text: 'Cambiarás la agencia activa a esta.',
|
||||||
icon: 'warning',
|
icon: 'warning',
|
||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
confirmButtonText: 'Sí, cambiar agencia',
|
confirmButtonText: 'Sí, usar agencia',
|
||||||
cancelButtonText: 'Cancelar'
|
cancelButtonText: 'Cancelar'
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
if (result.isConfirmed) {
|
if (result.isConfirmed) {
|
||||||
|
|||||||
Reference in New Issue
Block a user