Files
MVE/views/solicitud_importacion/editar.php
2025-10-21 11:41:30 -06:00

767 lines
36 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>✏️ Editar Solicitud #<?= (int)$factura['id_solicitud'] ?></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css"/>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<!-- Sweet Alert 2 -->
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<!-- Animate.css para animaciones adicionales -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
<style>
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #495057; color: #fff; }
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
@media (min-width: 768px) { .content { margin-left: 250px; } }
@media (max-width: 767.98px) {
.content { margin-left: 0; }
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
}
.card { border-radius: 12px; }
/* Animaciones personalizadas */
.card-hover { transition: all 0.3s ease; cursor: pointer; }
.card-hover:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
.btn-pulse { animation: pulse 2s infinite; }
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
/* Responsive animations */
@media (max-width: 768px) { .card-hover:hover { transform: translateY(-4px) scale(1.01); } }
/* Efecto de glow para elementos activos */
.btn.active { box-shadow: 0 0 20px rgba(var(--bs-primary-rgb), 0.5); }
/* Animación para el título */
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
/* Efecto para botones */
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
.btn-animated:hover::before { left: 100%; }
.hide { display: none !important; }
/* ========== ANIMACIONES PARA INPUTS TEXTO ========== */
.form-control:not(.no-animation) { transition: all 0.3s ease; border: 2px solid #dee2e6; position: relative; }
.form-control:not(.no-animation).shake { animation: shake 0.5s ease-in-out; }
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
/* Animación para los campos del formulario */
.form-group-animated { opacity: 0; transform: translateY(20px); animation: slideUp 0.6s ease-out forwards; }
.form-group-animated:nth-child(1) { animation-delay: 0.1s; }
.form-group-animated:nth-child(2) { animation-delay: 0.2s; }
.form-group-animated:nth-child(3) { animation-delay: 0.3s; }
.form-group-animated:nth-child(4) { animation-delay: 0.4s; }
.form-group-animated:nth-child(5) { animation-delay: 0.5s; }
.form-group-animated:nth-child(6) { animation-delay: 0.6s; }
.form-group-animated:nth-child(7) { animation-delay: 0.7s; }
.form-group-animated:nth-child(8) { animation-delay: 0.8s; }
.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 */
#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; }
.choices.is-valid .choices__inner { border: 1px solid #28a745; background-color: #f5fff5; }
.choices__input { opacity: 1 !important; }
</style>
</head>
<body>
<div class="content">
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">✏️ Editar factura #<?= (int)$factura['id_solicitud'] ?></h4>
<div class="card p-4 bg-white shadow-sm card-hover position-relative h-auto fade-in-up">
<form id="solicitudForm" action="/IMPORTADORES/solicitud_importacion/actualizar" method="POST" enctype="multipart/form-data">
<input type="hidden" name="id_solicitud" value="<?= (int)$factura['id_solicitud'] ?>">
<!-- Datos principales -->
<div class="row">
<div class="col-md-3 mb-3 form-group-animated">
<label for="numero_factura" class="form-label">Número de Factura</label>
<input id="numero_factura" name="numero_factura" type="text" class="form-control" required
value="<?= htmlspecialchars($factura['numero_factura']) ?>">
</div>
<div class="col-md-3 mb-3 form-group-animated">
<label for="fecha_factura" class="form-label">Fecha de Factura</label>
<input id="fecha_factura" name="fecha_factura" type="date" class="form-control" required
value="<?= htmlspecialchars($factura['fecha_factura']) ?>">
</div>
<div class="col-md-3 mb-3">
<label for="proveedor_id" class="form-label">Proveedor</label>
<select id="proveedor_id" name="proveedor_clave" class="form-select searchable" data-required="true" required>
<option value="">Cargando proveedores...</option>
</select>
</div>
<div class="col-md-3 mb-3">
<label for="anexo22_apendice" class="form-label">Anexo 22 Apéndice</label>
<select id="anexo22_apendice" name="anexo22_apendice" class="form-select searchable" data-required="true" required>
<option value="">-- Selecciona Aduana --</option>
<?php foreach($aduanas as $a): ?>
<option value="<?= htmlspecialchars($a['aduana_seccion']) ?>" <?= $factura['anexo22_apendice']==$a['aduana_seccion']?'selected':'' ?>>
<?= htmlspecialchars($a['aduana_seccion'].' '.$a['nombre']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
<!-- Patente, Incoterm, País, Moneda -->
<div class="row">
<div class="col-md-3 mb-3">
<label for="patente" class="form-label">Patente Aduanal</label>
<select id="patente" name="patente" class="form-select searchable" data-required="true" required>
<option value="">-- Selecciona Patente --</option>
<?php foreach($patentes as $pt): ?>
<option value="<?= htmlspecialchars($pt['id_agente']) ?>"
<?= $factura['patente_id'] == $pt['id_agente'] ? 'selected' : '' ?>>
<?= htmlspecialchars($pt['patente'].' - '.$pt['agente_aduanal']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-3 mb-3">
<label for="incoterm" class="form-label">INCOTERM</label>
<select id="incoterm" name="incoterm" class="form-select searchable" data-required="true" required>
<option value="">-- Selecciona Incoterm --</option>
<?php foreach($incoterms as $inc): ?>
<option value="<?= htmlspecialchars($inc['INCOTERM']) ?>" <?= $factura['incoterm']==$inc['INCOTERM']?'selected':'' ?>>
<?= htmlspecialchars($inc['INCOTERM'].' - '.$inc['DESCESPANOL']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-3 mb-3">
<label for="pais_proveedor" class="form-label">País (Proveedor)</label>
<select id="pais_proveedor" name="pais_proveedor" class="form-select searchable" data-required="true" required>
<option value="">-- Selecciona País --</option>
<?php foreach($paises as $p): ?>
<option value="<?= htmlspecialchars($p['id_pais']) ?>" <?= $factura['pais_proveedor']==$p['id_pais']?'selected':'' ?>>
<?= htmlspecialchars($p['nombre']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-3 mb-3">
<label for="tipo_moneda" class="form-label">Moneda</label>
<select id="tipo_moneda" name="tipo_moneda" class="form-select searchable" data-required="true" required>
<?php foreach(['MXN'=>'Peso Mexicano','USD'=>'Dólar USD','EUR'=>'Euro','CNY'=>'Yuan','GBP'=>'Libra GBP','JPY'=>'Yen'] as $code=>$label): ?>
<option value="<?= $code ?>" <?= $factura['tipo_moneda']==$code?'selected':'' ?>>
<?= "$label ($code)" ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
<!-- Valor y Vinculación -->
<div class="row">
<div class="col-md-8 mb-3 form-group-animated">
<label for="valor_factura" class="form-label">Valor Factura</label>
<input id="valor_factura" name="valor_factura" type="number" step="0.01" min="0" class="form-control valor-total" required
value="<?= htmlspecialchars($factura['valor_factura']) ?>">
</div>
<div class="col-md-4 mb-3">
<label for="vinculacion" class="form-label">Vinculación</label>
<select id="vinculacion" name="vinculacion" class="form-select searchable" data-required="true" required>
<option value="0" <?= $factura['vinculacion']==0?'selected':'' ?>>No existe</option>
<option value="1" <?= $factura['vinculacion']==1?'selected':'' ?>>Existe, no afecta</option>
<option value="2" <?= $factura['vinculacion']==2?'selected':'' ?>>Existe y afecta</option>
</select>
</div>
</div>
<!-- Transportista, Ghofer y Foto -->
<div class="row">
<div class="col-md-4 mb-3">
<label for="transportista_id" class="form-label">Transportista</label>
<select id="transportista_id" name="transportista_id" class="form-select searchable" data-required="true" required>
<option value="">-- Selecciona --</option>
<?php foreach($transportistas as $t): ?>
<option value="<?= htmlspecialchars($t['id_transportista']) ?>" <?= $factura['transportista_id']==$t['id_transportista']?'selected':'' ?>>
<?= htmlspecialchars($t['clave_identificador'] . ' - ' . $t['nombre']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4 mb-3">
<label for="chofer_id" class="form-label">Chofer</label>
<select id="chofer_id" name="chofer_id" class="form-select searchable" data-required="true" required>
<option value="">-- Selecciona Chofer --</option>
<?php foreach($choferes as $c): ?>
<option value="<?= htmlspecialchars($c['id_chofer']) ?>"
data-transportista="<?= htmlspecialchars($c['transportista_id']) ?>"
<?= $factura['chofer_id'] == $c['id_chofer']?'selected':'' ?>>
<?= htmlspecialchars($c['nombre']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4 mb-3 form-group-animated">
<label for="foto_solicitud" class="form-label">Foto de la Carga (PIPA)</label>
<input id="foto_solicitud" name="foto_solicitud" type="file" class="form-control" accept="image/*">
</div>
</div>
<!-- Partidas Dinámicas -->
<hr>
<h5 class="form-group-animated">📦 Partidas</h5>
<div class="table-responsive">
<table class="table table-sm form-group-animated" id="tabla-partidas">
<thead>
<tr>
<th>Descripción</th>
<th>Cantidad Comercial</th>
<th>Cantidad de Tarifa</th>
<th>Unidad Comercial</th>
<th>Valor Factura</th>
<th>Peso Bruto</th>
<th>Tasa Preferencial</th>
<th class="hide">Precio Unitario</th>
<th class="hide">OMA (Factura)</th>
<th></th>
</tr>
</thead>
<tbody>
<?php if (!empty($partidas)): foreach($partidas as $i=>$p): ?>
<tr>
<td>
<div class="position-relative">
<input name="partidas[<?= $i ?>][descripcion]" class="form-control w-auto descripcion-input"
value="<?= htmlspecialchars($p['descripcion']) ?>" 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>
</td>
<td><input name="partidas[<?= $i ?>][cantidad_comercial]" type="number" step="0.0001" min="0" class="form-control w-auto" value="<?= htmlspecialchars($p['cantidad_comercial']) ?>"></td>
<td><input name="partidas[<?= $i ?>][cantidad_tarifa]" type="number" step="0.0001" min="0" class="form-control w-auto" value="<?= htmlspecialchars($p['cantidad_tarifa']) ?>"></td>
<td>
<select name="partidas[<?= $i ?>][unidad_comercial_id]" class="form-select searchable">
<option value="">-- Unidad --</option>
<?php foreach($unidades_medida as $um): ?>
<option value="<?= htmlspecialchars($um['id']) ?>" <?= $p['unidad_comercial_id']==$um['id']?'selected':'' ?>>
<?= htmlspecialchars($um['descripcion']) ?>
</option>
<?php endforeach; ?>
</select>
</td>
<td><input name="partidas[<?= $i ?>][valor_factura]" type="number" step="0.01" min="0" class="form-control valor-partida w-auto" value="<?= htmlspecialchars($p['valor_factura']) ?>"></td>
<td><input name="partidas[<?= $i ?>][peso_bruto]" type="number" step="0.0001" min="0" class="form-control w-auto" value="<?= htmlspecialchars($p['peso_bruto']) ?>"></td>
<td>
<select name="partidas[<?= $i ?>][tasa_preferencial]" class="form-select searchable">
<option value="">-- Selecciona --</option>
<?php foreach(['General','TLC','PROSEC','ALADI','COMERCIALIZADORA'] as $tp): ?>
<option <?= $p['tasa_preferencial']==$tp?'selected':'' ?>><?= $tp ?></option>
<?php endforeach; ?>
</select>
</td>
<td class="hide"><input name="partidas[<?= $i ?>][precio_unitario]" type="number" class="form-control w-auto" value="<?= htmlspecialchars($p['precio_unitario'] ?? '') ?>"></td>
<td class="hide"><input name="partidas[<?= $i ?>][oma_factura]" class="form-control w-auto" value="<?= htmlspecialchars($p['oma_factura'] ?? '') ?>"></td>
<td><button type="button" class="btn btn-danger btn-sm remove-row mt-auto w-auto btn-animated">✖️</button></td>
</tr>
<?php endforeach; else: ?>
<tr>
<td>
<div class="position-relative">
<input name="partidas[0][descripcion]" class="form-control w-auto descripcion-input"
value="<?= htmlspecialchars($p['descripcion']) ?>" 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>
</td>
<td><input name="partidas[0][cantidad_comercial]" type="number" step="0.0001" class="form-control w-auto" min="0"></td>
<td><input name="partidas[0][cantidad_tarifa]" type="number" step="0.0001" class="form-control w-auto" min="0"></td>
<td>
<select name="partidas[0][unidad_comercial_id]" class="form-select searchable" data-required="true" required>
<option value="">-- Unidad --</option>
<?php foreach($unidades_medida as $um): ?>
<option value="<?= htmlspecialchars($um['id']) ?>"><?= htmlspecialchars($um['descripcion']) ?></option>
<?php endforeach; ?>
</select>
</td>
<td><input name="partidas[0][valor_factura]" type="number" step="0.01" class="form-control valor-partida w-auto" min="0"></td>
<td><input name="partidas[0][peso_bruto]" type="number" step="0.0001" class="form-control w-auto" min="0"></td>
<td>
<select name="partidas[0][tasa_preferencial]" class="form-select searchable" data-required="true" required>
<option value="">-- Selecciona --</option>
<option>General</option>
<option>TLC</option>
<option>PROSEC</option>
<option>ALADI</option>
<option>COMERCIALIZADORA</option>
</select>
</td>
<td class="hide"><input name="partidas[0][precio_unitario]" type="number" class="form-control w-auto"></td>
<td class="hide"><input name="partidas[0][oma_factura]" class="form-control w-auto"></td>
<td><button type="button" class="btn btn-danger btn-sm remove-row mt-auto w-auto btn-animated">✖️</button></td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<div class="form-group-animated">
<button type="button" class="btn btn-secondary btn-sm mb-3 mt-auto w-auto btn-animated" id="add-partida"> Agregar partida</button>
</div>
<!-- Activo -->
<div class="form-check mb-4">
<input id="status" value=1 name="status" type="hidden" <?= $factura['status']==1?'checked':'' ?>>
<label for="status" class="hide form-check-label">Activo</label>
</div>
<!-- Botones finales -->
<div class="text-end mt-4 form-group-animated">
<button type="submit" class="btn btn-success mt-auto w-auto btn-animated">Actualizar</button>
<a href="/IMPORTADORES/solicitud_importacion/lista" class="btn btn-secondary ms-2 mt-auto w-auto btn-animated">Cancelar</a>
</div>
</form>
</div>
</div>
<!-- Choices.js JS -->
<script src="https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0/dist/js/select2.min.js"></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) {
if (!element._choices) {
const isRequired = element.hasAttribute('required');
if (isRequired) {
element.removeAttribute('required');
element.setAttribute('data-required', 'true');
}
element._choices = new Choices(element, {
searchEnabled: true,
itemSelectText: '',
shouldSort: false,
silent: true
});
if (isRequired) {
const container = element.closest('.choices');
if (container) container.classList.add('required');
element.addEventListener('change', validateChoice.bind(null, element));
}
}
}
// ✅ 3. FUNCIÓN PARA VALIDAR CAMPOS CHOICES
function validateChoice(selectElement) {
const choicesInstance = selectElement._choices;
if (choicesInstance) {
const container = selectElement.closest('.choices');
const hasValue = choicesInstance.getValue(true).length > 0;
const isRequired = selectElement.hasAttribute('data-required');
if (isRequired) {
if (hasValue) {
container.classList.remove('is-invalid');
container.classList.add('is-valid');
} else {
container.classList.remove('is-valid');
container.classList.add('is-invalid');
}
}
}
}
// ✅ 4. FUNCIÓN PARA CARGAR PROVEEDORES
function cargarProveedores(proveedorActual) {
const proveedorEl = document.getElementById('proveedor_id');
// Asegurarse que el nombre sea consistente
proveedorEl.name = 'proveedor_id';
return fetch('/IMPORTADORES/solicitud_importacion/ajax_proveedores')
.then(res => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
})
.then(json => {
proveedorEl.innerHTML = '<option value="">-- Selecciona Proveedor --</option>';
json.results.forEach(item => {
const opt = document.createElement('option');
opt.value = item.id;
opt.textContent = item.text;
if (String(item.id) === proveedorActual) {
opt.selected = true;
}
proveedorEl.appendChild(opt);
});
// Forzar manualmente el value
if (proveedorActual) {
proveedorEl.value = proveedorActual;
}
})
.catch(err => {
console.error('❌ Error cargando proveedores:', err);
proveedorEl.innerHTML = '<option value="">Error cargando proveedores</option>';
});
}
// ✅ 5. AGREGAR PARTIDAS
document.getElementById('add-partida').addEventListener('click', () => {
const tbody = document.querySelector('#tabla-partidas tbody');
const idx = tbody.querySelectorAll('tr').length;
const row = document.createElement('tr');
row.innerHTML = `
<td>
<div class="position-relative">
<input name="partidas[${idx}][descripcion]" class="form-control w-auto descripcion-input" required autocomplete="off">
</div>
</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>
<select name="partidas[${idx}][unidad_comercial_id]" class="form-select searchable" data-required="true">
<option value="">-- Unidad --</option>
<?php foreach($unidades_medida as $um): ?>
<option value="<?= htmlspecialchars($um['id']) ?>"><?= htmlspecialchars($um['descripcion']) ?></option>
<?php endforeach; ?>
</select>
</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>
<select name="partidas[${idx}][tasa_preferencial]" class="form-select searchable" data-required="true">
<option value="">-- Selecciona --</option>
<option>General</option>
<option>TLC</option>
<option>PROSEC</option>
<option>ALADI</option>
<option>COMERCIALIZADORA</option>
</select>
</td>
<td class="hide"><input name="partidas[${idx}][precio_unitario]" type="number" class="form-control w-auto"></td>
<td class="hide"><input name="partidas[${idx}][oma_factura]" class="form-control w-auto"></td>
<td><button type="button" class="btn btn-danger btn-sm remove-row mt-auto w-auto btn-animated">✖️</button></td>
`;
tbody.appendChild(row);
// Inicializar Choices en los nuevos selects
setTimeout(() => {
row.querySelectorAll('.searchable').forEach(el => {
safeInitializeChoices(el);
});
// Configurar autocompletado para el nuevo campo
setupAutocomplete(row.querySelector('.descripcion-input'));
}, 50);
});
// ✅ 6. REMOVER PARTIDAS
document.querySelector('#tabla-partidas tbody').addEventListener('click', e => {
if (e.target.matches('.remove-row')) {
const row = e.target.closest('tr');
row.querySelectorAll('.searchable').forEach(el => {
if (el._choices) el._choices.destroy();
});
row.remove();
}
});
// ✅ 7. CONTROLAR OVERFLOW DE LA TABLA
document.addEventListener('click', function(e) {
const tableContainer = document.querySelector('.table-responsive');
if (!tableContainer) return;
if (e.target.closest('.choices__inner')) {
tableContainer.style.overflow = 'visible';
} else {
tableContainer.style.overflow = 'auto';
}
});
// ✅ 8. VALIDACIÓN DEL FORMULARIO
document.getElementById('solicitudForm').addEventListener('submit', function(e) {
// Validar selects de Choices.js
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;
document.querySelectorAll('.valor-partida').forEach(input => {
sum += parseFloat(input.value) || 0;
});
if (Math.abs(sum - total) > 0.001) {
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)}).`
});
return;
}
if (!isValid) {
e.preventDefault();
Swal.fire({
icon: 'error',
title: 'Campos requeridos',
text: 'Por favor complete todos los campos obligatorios marcados en rojo',
confirmButtonColor: '#3085d6'
});
}
});
// ✅ 9. SISTEMA DE AUTOCOMPLETADO
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);
}
// Configurar autocompletado para inputs existentes
document.querySelectorAll('.descripcion-input').forEach(input => {
setupAutocomplete(input);
});
// Configurar autocompletado para nuevas filas
document.getElementById('add-partida').addEventListener('click', function() {
setTimeout(() => {
const newInput = document.querySelector('#tabla-partidas tbody tr:last-child .descripcion-input');
if (newInput) setupAutocomplete(newInput);
}, 100);
});
}
function setupAutocomplete(input) {
const $input = $(input);
const $globalSuggestions = $('#global-autocomplete-suggestions');
$input.off('input').on('input', function() {
const query = $(this).val().trim();
currentInput = $input;
clearTimeout(timeoutId);
if (currentRequest) currentRequest.abort();
if (query.length < 2) {
$globalSuggestions.hide().empty();
return;
}
timeoutId = setTimeout(() => {
currentRequest = $.ajax({
url: '/IMPORTADORES/solicitud_importacion/buscar_productos',
method: 'GET',
data: { q: query },
dataType: 'json',
success: function(productos) {
$globalSuggestions.empty();
if (productos && productos.length > 0) {
productos.forEach(function(producto) {
const $item = $(`
<div class="autocomplete-item px-3 py-2 cursor-pointer border-bottom hover-bg-light">
<strong style="color: #333;">${producto.sinonimo}</strong>
<div class="small text-muted mt-1">
<div>Fracción: ${producto.fraccion || 'Sin fracción'}</div>
<div>Desc: ${producto.descripcion?.substring(0, 50) || ''}${producto.descripcion?.length > 50 ? '...' : ''}</div>
</div>
</div>
`).data('producto', producto);
$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'), $input.closest('tr'));
});
$globalSuggestions.append($item);
});
$globalSuggestions.show();
}
},
error: function(xhr, status, error) {
if (status !== 'abort') console.error("Error en búsqueda:", error);
$globalSuggestions.hide();
},
complete: function() {
currentRequest = null;
}
});
}, 300);
});
// Manejar eventos de teclado
$input.on('keydown', function(e) {
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();
$next.addClass('active').css('background-color', '#f8f9fa');
break;
case 'ArrowUp':
e.preventDefault();
$items.removeClass('active').css('background-color', '');
const $prev = $active.length ? $active.prev() : $items.last();
$prev.addClass('active').css('background-color', '#f8f9fa');
break;
case 'Enter':
if ($active.length) {
e.preventDefault();
selectProduct($active.data('producto'), $input.closest('tr'));
}
break;
case 'Escape':
$globalSuggestions.hide();
break;
}
});
// Ocultar sugerencias al hacer clic fuera
$(document).on('click', function(e) {
if (!currentInput ||
(!currentInput.is(e.target) &&
!$globalSuggestions.is(e.target) &&
!$globalSuggestions.has(e.target).length)) {
$globalSuggestions.hide();
}
});
}
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');
}
}
// 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');
}
}, 50);
}
// Número de parte
if (producto.numero_parte) {
$row.find('input[name*="[oma_factura]"]').val(producto.numero_parte);
}
// Incrementar frecuencia
$.ajax({
url: '/IMPORTADORES/solicitud_importacion/incrementar_frecuencia',
method: 'POST',
data: { producto_id: producto.id }
});
// Ocultar sugerencias
$('#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);
}
}
</script>
</body>
</html>