Files
MVE/views/solicitud_importacion/editar.php

863 lines
41 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 */
.autocomplete-suggestions { position: absolute; width: calc(100% - 2px); /* Ajustar al ancho del input */ background: white; border: 1px solid #ced4da; border-top: none;
border-radius: 0 0 4px 4px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); z-index: 1000; max-height: 200px; overflow-y: auto; display: none; /* Inicialmente oculto */ }
.autocomplete-item { padding: 8px 12px; cursor: pointer; transition: background-color 0.2s; }
.autocomplete-item:hover, .autocomplete-item.active { background-color: #f8f9fa; }
.autocomplete-item strong { display: block; margin-bottom: 2px; }
.autocomplete-item .text-muted { font-size: 0.85em; color: #6c757d; }
.cursor-pointer { cursor: pointer; }
/* Estilos para validación */
.choices.required .choices__inner { border: 1px solid #ced4da; }
.choices.is-invalid .choices__inner { border: 1px solid #dc3545; background-color: #fff5f5; }
.choices.is-valid .choices__inner { border: 1px solid #28a745; background-color: #f5fff5; }
.choices__input { opacity: 1 !important; }
</style>
</head>
<body>
<div class="content">
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">✏️ Editar Solicitud #<?= (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>
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));
}
}
}
// ✅ 1. INICIALIZACIÓN PRINCIPAL
document.addEventListener('DOMContentLoaded', function() {
const proveedorActual = '<?= htmlspecialchars($proveedor_clave_actual ?? '') ?>';
console.log('📦 Proveedor actual:', proveedorActual);
cargarProveedores(proveedorActual).then(() => {
document.querySelectorAll('.searchable').forEach(el => {
// Destruir instancia previa si existe
if (el.choicesInstance) el.choicesInstance.destroy();
const instance = new Choices(el, {
searchEnabled: true,
itemSelectText: '',
shouldSort: false
});
// Guardar instancia
el.choicesInstance = instance;
});
});
});
// ✅ 2. FUNCIÓN PARA CARGAR PROVEEDORES (CON PROMESA)
function cargarProveedores(proveedorActual) {
const proveedorEl = document.getElementById('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
proveedorEl.value = proveedorActual;
console.log('✅ Proveedores cargados. Proveedor actual:', proveedorActual);
console.log('🧪 Select value actual (después de asignar):', proveedorEl.value);
})
.catch(err => {
console.error('❌ Error cargando proveedores:', err);
proveedorEl.innerHTML = '<option value="">Error cargando proveedores</option>';
});
}
// ✅ 3. AGREGAR PARTIDAS - VERSIÓN CORREGIDA
document.getElementById('add-partida').addEventListener('click', () => {
const tbody = document.querySelector('#tabla-partidas tbody');
const rows = tbody.querySelectorAll('tr');
const idx = rows.length; // Esto asegura índices únicos y secuenciales
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 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[${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">
<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">
<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);
// Inicializamos Choices en los nuevos selects
setTimeout(() => {
row.querySelectorAll('.searchable').forEach(el => {
if (!el._choices) {
el._choices = new Choices(el, {
searchEnabled: true,
itemSelectText: '',
shouldSort: false,
silent: true
});
}
});
// Configurar autocompletado para el nuevo campo de descripción
setupAutocomplete(row.querySelector('.descripcion-input'));
}, 50);
});
// ✅ 4. REMOVER PARTIDAS
document.querySelector('#tabla-partidas tbody').addEventListener('click', e => {
if (e.target.matches('.remove-row')) {
// Destruir instancia de Choices.js antes de remover la fila
const row = e.target.closest('tr');
row.querySelectorAll('.searchable').forEach(el => {
if (el.choicesInstance) {
el.choicesInstance.destroy();
}
});
row.remove();
}
});
// ✅ 5. 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';
}
});
// ✅ 6. VALIDACIÓN DEL FORMULARIO
$(document).ready(function() {
$('#solicitudForm').submit(function(e){
const total = parseFloat($('#valor_factura').val()) || 0;
let sum = 0;
$('.valor-partida').each(function(){ sum += parseFloat($(this).val()) || 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)}).` });
}
});
});
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,
nombre: option.textContent.trim()
});
}
});
// Debug: mostrar el cache inicial
console.log('Cache de choferes por transportista:', choferesPorTransportista);
// Inicializar Choices.js si está disponible
let choferChoices = null;
if (typeof Choices !== 'undefined') {
choferChoices = new Choices(choferSelect, {
searchEnabled: true,
placeholderValue: '-- Selecciona Transportista primero --',
noResultsText: 'No se encontraron resultados',
itemSelectText: '',
searchPlaceholderValue: 'Buscar chofer...'
});
}
// 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) {
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');
$input.on('input', function() {
const query = $(this).val().trim();
clearTimeout(timeoutId);
if (currentRequest) currentRequest.abort();
if (query.length < 2) {
$suggestions.hide().empty();
return;
}
timeoutId = setTimeout(() => {
currentRequest = $.ajax({
url: '/IMPORTADORES/solicitud_importacion/buscar_productos',
method: 'GET',
data: { q: query },
dataType: 'json',
success: function(productos) {
$suggestions.empty();
if (productos && productos.length > 0) {
productos.forEach(function(producto) {
const $item = $(`
<div class="autocomplete-item px-3 py-2 cursor-pointer border-bottom">
<strong>${producto.sinonimo}</strong>
<div class="row mb-3 small text-muted">
<span>${producto.fraccion || 'Sin fracción'}</span>
<span>${producto.descripcion?.substring(0, 50) || ''}</span>
</div>
</div>
`).data('producto', producto);
$item.on('click', function() { selectProduct($(this).data('producto'), $row); });
$suggestions.append($item);
});
$suggestions.show();
}
else { $suggestions.hide(); }
},
error: function(xhr, status, error) {
if (status !== 'abort') { console.error("Error en búsqueda:", error); }
$suggestions.hide();
},
complete: function() { currentRequest = null; }
});
}, 300);
});
// Manejar teclado
$input.on('keydown', function(e) {
const $items = $suggestions.find('.autocomplete-item');
const $active = $items.filter('.active');
switch(e.key) {
case 'ArrowDown':
e.preventDefault();
const $next = $active.length ? $active.next() : $items.first();
$items.removeClass('active');
$next.addClass('active');
break;
case 'ArrowUp':
e.preventDefault();
const $prev = $active.length ? $active.prev() : $items.last();
$items.removeClass('active');
$prev.addClass('active');
break;
case 'Enter':
if ($active.length) {
e.preventDefault();
selectProduct($active.data('producto'), $row);
}
break;
case 'Tab':
if ($suggestions.is(':visible')) {
e.preventDefault();
const $first = $items.first();
if ($first.length) selectProduct($first.data('producto'), $row);
}
break;
case 'Escape':
$suggestions.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(); } });
}
// 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'); }
}
// 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
$row.find('.autocomplete-suggestions').hide();
}
// 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>
</body>
</html>