Validaciones - Alta de: Proveedores, Transportistas, Transportes y Choferes

This commit is contained in:
2025-06-10 12:45:59 -06:00
parent f1d4b7c98f
commit b0b9672f11
17 changed files with 757 additions and 129 deletions

View File

@@ -1,5 +1,4 @@
<?php
session_start();
require_once __DIR__ . '/../../vendor/autoload.php';
require_once __DIR__ . '/../helpers/session.php';

View File

@@ -1,6 +1,7 @@
<?php
session_start();
require_once __DIR__ . '/../helpers/session.php';
require_once __DIR__ . '/../../config/database.php';
require_once __DIR__ . '/../helpers/env.php';
function lista() {
if (!($_SESSION['usuario_id'] ?? false)) {

View File

@@ -1,6 +1,7 @@
<?php
session_start();
require_once __DIR__ . '/../helpers/session.php';
require_once __DIR__ . '/../../config/database.php';
require_once __DIR__ . '/../helpers/env.php';
// Mostrar tabla de expedientes
function index() {
@@ -214,8 +215,3 @@ function descargar_zip($id_solicitud)
unlink($zip_file);
exit;
}

View File

@@ -1,6 +1,7 @@
<?php
// app/controllers/proveedores.php
session_start();
require_once __DIR__ . '/../helpers/session.php';
require_once __DIR__ . '/../../config/database.php';
// 1) Composer autoload (phpdotenv y demás libs)
require_once __DIR__ . '/../../vendor/autoload.php';

View File

@@ -5,16 +5,15 @@ use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require_once __DIR__ . '/../../config/database.php';
require_once __DIR__ . '/../helpers/session.php';
require_once __DIR__ . '/../helpers/env.php';
require_once __DIR__ . '/../helpers/crypto.php';
loadEnv();
function index() {
include __DIR__ . '/../../views/registro/form.php';
}
require_once __DIR__ . '/../helpers/crypto.php';
require_once __DIR__ . '/../helpers/env.php';
loadEnv();
function enviar() {
// Validar reCAPTCHA
$captchaResponse = $_POST['g-recaptcha-response'] ?? '';

View File

@@ -1,5 +1,5 @@
<?php
session_start();
require_once __DIR__ . '/../helpers/session.php';
require_once __DIR__ . '/../../config/database.php';
// 1) Composer autoload (phpdotenv y demás libs)
require_once __DIR__ . '/../../vendor/autoload.php';
@@ -1309,7 +1309,7 @@ function generarHtmlNotificacion($datos) {
<strong>&copy; {$datos['anioActual']} SIIH</strong> · Desarrollado por
<span style='color: #007bff; font-weight: 600;'>AduanaSoft</span>
<br>
<small style='color: #adb5bd;'>Sistema Integral de Importación y Herramientas</small>
<small style='color: #adb5bd;'>Sistema Integral para Importadores de Hidrocarburos</small>
</div>
</div>
</div>";

View File

@@ -1,6 +1,7 @@
<?php
session_start();
require_once __DIR__ . '/../helpers/session.php';
require_once __DIR__ . '/../../config/database.php';
require_once __DIR__ . '/../helpers/env.php';
/** Listado de transportes (sólo activos) para el importador logueado **/
function lista() {

View File

@@ -1,6 +1,9 @@
<?php
session_start();
function guardar() {
require_once __DIR__ . '/../helpers/session.php';
require_once __DIR__ . '/../../config/database.php';
require_once __DIR__ . '/../helpers/env.php';
function guardar() {
if (!($_SESSION['usuario_id'] ?? false)) {
die("⚠️ No autorizado.");
}
@@ -41,8 +44,6 @@
exit;
}
require_once __DIR__ . '/../../config/database.php';
function alta() {
$conn = getConnection();
// 1) Cargar países
@@ -393,8 +394,8 @@ function editar() {
}
/** Procesa el POST de actualización **/
function actualizar() {
session_start();
function actualizar()
{
if (!($_SESSION['usuario_id'] ?? false)) {
die("⚠️ No autorizado.");
}

View File

@@ -37,7 +37,7 @@
<h4> Nuevo Chofer</h4>
<div class="card p-4 shadow-sm bg-white">
<!-- Se agrega enctype para subir archivos -->
<form action="/IMPORTADORES/choferes/guardar" method="POST" enctype="multipart/form-data">
<form action="/IMPORTADORES/choferes/guardar" method="POST" enctype="multipart/form-data" id="formAltaChoferes">
<div class="mb-3">
<label for="transportista_id" class="form-label">Transportista</label>
<select name="transportista_id" id="transportista_id" class="form-select" required>
@@ -62,12 +62,12 @@
<div class="mb-3">
<label for="numero_licencia" class="form-label">Número de Licencia</label>
<input name="numero_licencia" id="numero_licencia" type="text" class="form-control" required>
<input name="numero_licencia" id="numero_licencia" type="text" maxlength="11" class="form-control" required>
</div>
<div class="mb-3">
<label for="telefono" class="form-label">Teléfono</label>
<input name="telefono" id="telefono" type="tel" class="form-control">
<input name="telefono" id="telefono" type="tel" maxlength="11" class="form-control">
</div>
<div class="mb-3">
@@ -92,5 +92,94 @@
</div>
</div>
<script>
document.getElementById('formAltaChoferes').addEventListener('submit', function(e) {
e.preventDefault(); // Prevenir envío por defecto
// Obtener todos los campos correctamente
const transportista_id = document.getElementById('transportista_id').value;
const nombre = document.getElementById('nombre').value.trim();
const apellido = document.getElementById('apellido').value.trim();
const numero_licencia = document.getElementById('numero_licencia').value.trim();
const telefono = document.getElementById('telefono').value.trim();
const email = document.getElementById('email').value.trim();
const fecha_ingreso = document.getElementById('fecha_ingreso').value;
const foto = document.getElementById('foto').files[0];
// Expresiones regulares
const telefonoRegex = /^\d{3}\s\d{7}$/;
const emailRegex = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const soloNumerosRegex = /^[0-9]+$/;
// VALIDAR CAMPOS OBLIGATORIOS PRIMERO
if (!transportista_id) {
Swal.fire({ icon: 'error', title: 'Transportista requerido', text: 'Por favor selecciona un transportista.', confirmButtonColor: '#dc3545' });
document.getElementById('transportista_id').focus();
return;
}
if (!nombre) {
Swal.fire({ icon: 'error', title: 'Nombre requerido', text: 'Por favor ingresa el nombre.', confirmButtonColor: '#dc3545' });
document.getElementById('nombre').focus();
return;
}
if (!apellido) {
Swal.fire({ icon: 'error', title: 'Apellido requerido', text: 'Por favor ingresa el apellido.', confirmButtonColor: '#dc3545' });
document.getElementById('apellido').focus();
return;
}
if (!numero_licencia) {
Swal.fire({ icon: 'error', title: 'Número de Licencia requerido', text: 'Por favor ingresa el número de licencia.', confirmButtonColor: '#dc3545' });
document.getElementById('numero_licencia').focus();
return;
}
if (!soloNumerosRegex.test(numero_licencia)) {
Swal.fire({ icon: 'error', title: 'Número de Licencia inválido', text: 'El número de licencia solo puede contener números', confirmButtonColor: '#dc3545'
});
document.getElementById('Clave').focus();
return;
}
// VALIDAR CAMPOS OPCIONALES (solo si tienen contenido)
if (telefono && !telefonoRegex.test(telefono)) {
Swal.fire({ icon: 'error', title: 'Teléfono inválido', text: 'Debe tener 10 dígitos en el formato: 3 dígitos (LADA), espacio, y 7 dígitos. Ejemplo: 123 4567890', confirmButtonColor: '#dc3545' });
document.getElementById('telefono').focus();
return;
}
if (email && !emailRegex.test(email)) {
Swal.fire({ icon: 'error', title: 'Email inválido', text: 'Por favor ingresa una dirección de email válida.', confirmButtonColor: '#dc3545' });
document.getElementById('email').focus();
return;
}
// VALIDAR FECHA DE INGRESO (solo si se proporciona)
if (fecha_ingreso) {
const fechaIngreso = new Date(fecha_ingreso);
const fechaActual = new Date();
if (fechaIngreso > fechaActual) {
Swal.fire({ icon: 'error', title: 'Fecha inválida', text: 'La fecha de ingreso no puede ser futura.', confirmButtonColor: '#dc3545' });
document.getElementById('fecha_ingreso').focus();
return;
}
}
// VALIDAR FOTO (solo si se seleccionó)
if (foto) {
// Validar tamaño (máximo 2 MB)
if (foto.size > 2 * 1024 * 1024) {
Swal.fire({ icon: 'error', title: 'Foto demasiado grande', text: 'La imagen no debe exceder 2 MB.', confirmButtonColor: '#dc3545' });
document.getElementById('foto').focus();
return;
}
// Validar tipo de archivo
const tiposPermitidos = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
if (!tiposPermitidos.includes(foto.type)) {
Swal.fire({ icon: 'error', title: 'Tipo de archivo inválido', text: 'Solo se permiten archivos de imagen (JPG, PNG, GIF).', confirmButtonColor: '#dc3545' });
document.getElementById('foto').focus();
return;
}
}
// SI TODAS LAS VALIDACIONES PASAN, ENVIAR EL FORMULARIO
this.submit();
});
</script>
</body>
</html>

View File

@@ -35,7 +35,7 @@
<div class="content">
<h4>✏️ Editar Chofer #<?= (int)$chofer['id_chofer'] ?></h4>
<div class="card p-4 shadow-sm bg-white">
<form action="/IMPORTADORES/choferes/actualizar" method="POST" enctype="multipart/form-data">
<form action="/IMPORTADORES/choferes/actualizar" method="POST" enctype="multipart/form-data" id="formEditChoferes">
<input type="hidden" name="id_chofer" value="<?= (int)$chofer['id_chofer'] ?>">
<!-- Transportista -->
@@ -72,14 +72,14 @@
<!-- Licencia -->
<div class="col-md-6 mb-3">
<label for="numero_licencia" class="form-label">Número de Licencia</label>
<input name="numero_licencia" id="numero_licencia" type="text" class="form-control"
<input name="numero_licencia" id="numero_licencia" type="text" maxlength="11" class="form-control"
value="<?= htmlspecialchars($chofer['numero_licencia']) ?>" required>
</div>
<!-- Teléfono -->
<div class="col-md-6 mb-3">
<label for="telefono" class="form-label">Teléfono</label>
<input name="telefono" id="telefono" type="tel" class="form-control"
<input name="telefono" id="telefono" type="tel" maxlength="11" class="form-control"
value="<?= htmlspecialchars($chofer['telefono']) ?>">
</div>
</div>
@@ -129,5 +129,93 @@
</div>
</div>
<script>
document.getElementById('formEditChoferes').addEventListener('submit', function(e) {
e.preventDefault(); // Prevenir envío por defecto
// Obtener todos los campos correctamente
const transportista_id = document.getElementById('transportista_id').value;
const nombre = document.getElementById('nombre').value.trim();
const apellido = document.getElementById('apellido').value.trim();
const numero_licencia = document.getElementById('numero_licencia').value.trim();
const telefono = document.getElementById('telefono').value.trim();
const email = document.getElementById('email').value.trim();
const fecha_ingreso = document.getElementById('fecha_ingreso').value;
const foto = document.getElementById('foto').files[0];
// Expresiones regulares
const telefonoRegex = /^\d{3}\s\d{7}$/;
const emailRegex = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const soloNumerosRegex = /^[0-9]+$/;
// VALIDAR CAMPOS OBLIGATORIOS PRIMERO
if (!transportista_id) {
Swal.fire({ icon: 'error', title: 'Transportista requerido', text: 'Por favor selecciona un transportista.', confirmButtonColor: '#dc3545' });
document.getElementById('transportista_id').focus();
return;
}
if (!nombre) {
Swal.fire({ icon: 'error', title: 'Nombre requerido', text: 'Por favor ingresa el nombre.', confirmButtonColor: '#dc3545' });
document.getElementById('nombre').focus();
return;
}
if (!apellido) {
Swal.fire({ icon: 'error', title: 'Apellido requerido', text: 'Por favor ingresa el apellido.', confirmButtonColor: '#dc3545' });
document.getElementById('apellido').focus();
return;
}
if (!numero_licencia) {
Swal.fire({ icon: 'error', title: 'Número de Licencia requerido', text: 'Por favor ingresa el número de licencia.', confirmButtonColor: '#dc3545' });
document.getElementById('numero_licencia').focus();
return;
}
if (!soloNumerosRegex.test(numero_licencia)) {
Swal.fire({ icon: 'error', title: 'Número de Licencia inválido', text: 'El número de licencia solo puede contener números', confirmButtonColor: '#dc3545' });
document.getElementById('Clave').focus();
return;
}
// VALIDAR CAMPOS OPCIONALES (solo si tienen contenido)
if (telefono && !telefonoRegex.test(telefono)) {
Swal.fire({ icon: 'error', title: 'Teléfono inválido', text: 'Debe tener 10 dígitos en el formato: 3 dígitos (LADA), espacio, y 7 dígitos. Ejemplo: 123 4567890', confirmButtonColor: '#dc3545' });
document.getElementById('telefono').focus();
return;
}
if (email && !emailRegex.test(email)) {
Swal.fire({ icon: 'error', title: 'Email inválido', text: 'Por favor ingresa una dirección de email válida.', confirmButtonColor: '#dc3545' });
document.getElementById('email').focus();
return;
}
// VALIDAR FECHA DE INGRESO (solo si se proporciona)
if (fecha_ingreso) {
const fechaIngreso = new Date(fecha_ingreso);
const fechaActual = new Date();
if (fechaIngreso > fechaActual) {
Swal.fire({ icon: 'error', title: 'Fecha inválida', text: 'La fecha de ingreso no puede ser futura.', confirmButtonColor: '#dc3545' });
document.getElementById('fecha_ingreso').focus();
return;
}
}
// VALIDAR FOTO (solo si se seleccionó)
if (foto) {
// Validar tamaño (máximo 2 MB)
if (foto.size > 2 * 1024 * 1024) {
Swal.fire({ icon: 'error', title: 'Foto demasiado grande', text: 'La imagen no debe exceder 2 MB.', confirmButtonColor: '#dc3545' });
document.getElementById('foto').focus();
return;
}
// Validar tipo de archivo
const tiposPermitidos = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
if (!tiposPermitidos.includes(foto.type)) {
Swal.fire({ icon: 'error', title: 'Tipo de archivo inválido', text: 'Solo se permiten archivos de imagen (JPG, PNG, GIF).', confirmButtonColor: '#dc3545' });
document.getElementById('foto').focus();
return;
}
}
// SI TODAS LAS VALIDACIONES PASAN, ENVIAR EL FORMULARIO
this.submit();
});
</script>
</body>
</html>

View File

@@ -44,11 +44,11 @@
<div class="content">
<h4 class="mb-4"> Registro de Proveedor</h4>
<div class="card p-4 shadow-sm">
<form action="/IMPORTADORES/proveedores/guardar" method="POST">
<form action="/IMPORTADORES/proveedores/guardar" method="POST" enctype="multipart/form-data" id="formAltaProveedores">
<div class="row g-3">
<div class="col-md-4">
<label for="Clave" class="form-label">Clave *</label>
<input type="text" name="Clave" id="Clave" class="form-control" required>
<input type="text" name="Clave" id="Clave" class="form-control" maxlength="20" required>
</div>
<div class="col-md-8">
<label for="Nombre" class="form-label">Nombre *</label>
@@ -56,7 +56,7 @@
</div>
<div class="col-md-4">
<label for="RFC" class="form-label">RFC *</label>
<input type="text" name="RFC" id="RFC" class="form-control" required>
<input type="text" name="RFC" id="RFC" class="form-control" maxlength="13" required>
</div>
<div class="col-md-4">
<label for="Ciudad" class="form-label">Ciudad *</label>
@@ -64,7 +64,7 @@
</div>
<div class="col-md-4">
<label for="Telefono" class="form-label">Teléfono</label>
<input type="tel" name="Telefono" id="Telefono" class="form-control">
<input type="tel" name="Telefono" id="Telefono" maxlength="11" class="form-control">
</div>
<div class="col-md-6">
<label for="Pais" class="form-label">País</label>
@@ -84,7 +84,7 @@
</div>
<div class="col-md-4">
<label for="CodigoPostal" class="form-label">Código Postal</label>
<input type="text" name="CodigoPostal" id="CodigoPostal" class="form-control">
<input type="text" name="CodigoPostal" id="CodigoPostal" maxlength="5" class="form-control">
</div>
</div>
<div class="mt-4 text-end">
@@ -98,5 +98,148 @@
<!-- Bootstrap JS Bundle -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.getElementById('formAltaProveedores').addEventListener('submit', function(e) {
e.preventDefault(); // Prevenir envío por defecto
// Obtener todos los campos
const clave = document.getElementById('Clave').value.trim();
const nombre = document.getElementById('Nombre').value.trim();
const rfc = document.getElementById('RFC').value.trim().toUpperCase();
const ciudad = document.getElementById('Ciudad').value.trim();
const telefono = document.getElementById('Telefono').value.trim();
const pais = document.getElementById('Pais').value.trim();
const entidadFederativa = document.getElementById('EntidadFederativa').value.trim();
const municipio = document.getElementById('Municipio').value.trim();
const colonia = document.getElementById('Colonia').value.trim();
const codigoPostal = document.getElementById('CodigoPostal').value.trim();
// Expresiones regulares
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
const telefonoRegex = /^\d{3}\s\d{7}$/;
const codigoPostalRegex = /^\d{5}$/;
const soloLetrasRegex = /^[a-zA-ZáéíóúÁÉÍÓÚñÑ\s]+$/;
const alfanumericoRegex = /^[a-zA-Z0-9áéíóúÁÉÍÓÚñÑ\s]+$/;
// VALIDAR CAMPOS OBLIGATORIOS
if (!clave) {
Swal.fire({ icon: 'error', title: 'Clave requerida', text: 'Por favor ingresa la clave del proveedor.', confirmButtonColor: '#dc3545' });
document.getElementById('Clave').focus();
return;
}
if (clave.length < 2 || clave.length > 20) {
Swal.fire({ icon: 'error', title: 'Clave inválida', text: 'La clave debe tener entre 2 y 20 caracteres.', confirmButtonColor: '#dc3545' });
document.getElementById('Clave').focus();
return;
}
if (!alfanumericoRegex.test(clave)) {
Swal.fire({ icon: 'error', title: 'Clave inválida', text: 'La clave solo puede contener letras, números y espacios.', confirmButtonColor: '#dc3545'
});
document.getElementById('Clave').focus();
return;
}
if (!nombre) {
Swal.fire({ icon: 'error', title: 'Nombre requerido', text: 'Por favor ingresa el nombre del proveedor.', confirmButtonColor: '#dc3545' });
document.getElementById('Nombre').focus();
return;
}
if (nombre.length < 2 || nombre.length > 100) {
Swal.fire({ icon: 'error', title: 'Nombre inválido', text: 'El nombre debe tener entre 2 y 100 caracteres.', confirmButtonColor: '#dc3545' });
document.getElementById('Nombre').focus();
return;
}
if (!rfc) {
Swal.fire({ icon: 'error', title: 'RFC requerido', text: 'Por favor ingresa el RFC del proveedor.', confirmButtonColor: '#dc3545' });
document.getElementById('RFC').focus();
return;
}
if (!rfcRegex.test(rfc)) {
Swal.fire({ icon: 'error', title: 'RFC inválido', text: 'El RFC debe tener 12 o 13 caracteres con el formato correcto (ej. ABC123456XYZ).', confirmButtonColor: '#dc3545' });
document.getElementById('RFC').focus();
return;
}
if (!ciudad) {
Swal.fire({ icon: 'error', title: 'Ciudad requerida', text: 'Por favor ingresa la ciudad del proveedor.', confirmButtonColor: '#dc3545' });
document.getElementById('Ciudad').focus();
return;
}
if (!soloLetrasRegex.test(ciudad)) {
Swal.fire({ icon: 'error', title: 'Ciudad inválida', text: 'La ciudad solo puede contener letras y espacios.', confirmButtonColor: '#dc3545' });
document.getElementById('Ciudad').focus();
return;
}
// VALIDAR CAMPOS OPCIONALES (solo si tienen contenido)
if (telefono) {
if (!telefonoRegex.test(telefono)) {
Swal.fire({ icon: 'error', title: 'Teléfono inválido', text: 'Debe tener 10 dígitos en el formato: 3 dígitos (LADA), espacio, y 7 dígitos. Ejemplo: 123 4567890', confirmButtonColor: '#dc3545' });
document.getElementById('Telefono').focus();
return;
}
}
if (pais) {
if (!soloLetrasRegex.test(pais)) {
Swal.fire({ icon: 'error', title: 'País inválido', text: 'El país solo puede contener letras y espacios.', confirmButtonColor: '#dc3545' });
document.getElementById('Pais').focus();
return;
}
if (pais.length < 2 || pais.length > 50) {
Swal.fire({ icon: 'error', title: 'País inválido', text: 'El país debe tener entre 2 y 50 caracteres.', confirmButtonColor: '#dc3545'
});
document.getElementById('Pais').focus();
return;
}
}
if (entidadFederativa) {
if (!soloLetrasRegex.test(entidadFederativa)) {
Swal.fire({ icon: 'error', title: 'Entidad Federativa inválida', text: 'La entidad federativa solo puede contener letras y espacios.', confirmButtonColor: '#dc3545' });
document.getElementById('EntidadFederativa').focus();
return;
}
if (entidadFederativa.length < 2 || entidadFederativa.length > 50) {
Swal.fire({ icon: 'error', title: 'Entidad Federativa inválida', text: 'La entidad federativa debe tener entre 2 y 50 caracteres.', confirmButtonColor: '#dc3545' });
document.getElementById('EntidadFederativa').focus();
return;
}
}
if (municipio) {
if (!soloLetrasRegex.test(municipio)) {
Swal.fire({ icon: 'error', title: 'Municipio inválido', text: 'El municipio solo puede contener letras y espacios.', confirmButtonColor: '#dc3545' });
document.getElementById('Municipio').focus();
return;
}
if (municipio.length < 2 || municipio.length > 50) {
Swal.fire({ icon: 'error', title: 'Municipio inválido', text: 'El municipio debe tener entre 2 y 50 caracteres.', confirmButtonColor: '#dc3545' });
document.getElementById('Municipio').focus();
return;
}
}
if (colonia) {
if (!alfanumericoRegex.test(colonia)) {
Swal.fire({ icon: 'error', title: 'Colonia inválida', text: 'La colonia solo puede contener letras, números y espacios.', confirmButtonColor: '#dc3545'
});
document.getElementById('Colonia').focus();
return;
}
if (colonia.length < 2 || colonia.length > 50) {
Swal.fire({ icon: 'error', title: 'Colonia inválida', text: 'La colonia debe tener entre 2 y 50 caracteres.', confirmButtonColor: '#dc3545' });
document.getElementById('Colonia').focus();
return;
}
}
if (codigoPostal) {
if (!codigoPostalRegex.test(codigoPostal)) {
Swal.fire({ icon: 'error', title: 'Código Postal inválido', text: 'El código postal debe tener exactamente 5 dígitos.', confirmButtonColor: '#dc3545' });
document.getElementById('CodigoPostal').focus();
return;
}
}
// Actualizar el valor del RFC en mayúsculas antes de enviar
document.getElementById('RFC').value = rfc;
// SI TODAS LAS VALIDACIONES PASAN, ENVIAR EL FORMULARIO
this.submit();
});
</script>
</body>
</html>

View File

@@ -98,7 +98,7 @@
<div class="row">
<div class="col-md-8 mb-3">
<label for="valor_factura" class="form-label">Valor Factura</label>
<input id="valor_factura" name="valor_factura" type="number" step="0.01" class="form-control" required>
<input id="valor_factura" name="valor_factura" type="number" step="0.01" min="0" class="form-control" required>
</div>
<div class="col-md-4 mb-3">
<label for="vinculacion" class="form-label">Vinculación</label>
@@ -244,7 +244,7 @@
shouldSort: false
});
// seleccionar valor actual
const current = '<?= htmlspecialchars($factura['proveedor_id'], ENT_QUOTES) ?>';
const current = "<?= htmlspecialchars($factura['proveedor_id'], ENT_QUOTES) ?>";
if (current) {
proveedorChoices.setChoiceByValue(current);
}

View File

@@ -107,7 +107,7 @@
<div class="row">
<div class="col-md-8 mb-3">
<label for="valor_factura" class="form-label">Valor Factura</label>
<input id="valor_factura" name="valor_factura" type="number" step="0.01" class="form-control valor-total" required
<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">
@@ -173,8 +173,8 @@
<?php if (!empty($partidas)): foreach($partidas as $i=>$p): ?>
<tr>
<td><input name="partidas[<?= $i ?>][descripcion]" class="form-control" value="<?= htmlspecialchars($p['descripcion']) ?>"></td>
<td><input name="partidas[<?= $i ?>][cantidad_comercial]" type="number" step="0.0001" class="form-control" value="<?= htmlspecialchars($p['cantidad_comercial']) ?>"></td>
<td><input name="partidas[<?= $i ?>][cantidad_tarifa]" type="number" step="0.0001" class="form-control" value="<?= htmlspecialchars($p['cantidad_tarifa']) ?>"></td>
<td><input name="partidas[<?= $i ?>][cantidad_comercial]" type="number" step="0.0001" min="0" class="form-control" value="<?= htmlspecialchars($p['cantidad_comercial']) ?>"></td>
<td><input name="partidas[<?= $i ?>][cantidad_tarifa]" type="number" step="0.0001" min="0" class="form-control" value="<?= htmlspecialchars($p['cantidad_tarifa']) ?>"></td>
<td>
<select name="partidas[<?= $i ?>][unidad_comercial_id]" class="form-select searchable">
<option value="">-- Unidad --</option>
@@ -185,8 +185,8 @@
<?php endforeach; ?>
</select>
</td>
<td><input name="partidas[<?= $i ?>][valor_factura]" type="number" step="0.01" class="form-control valor-partida" value="<?= htmlspecialchars($p['valor_factura']) ?>"></td>
<td><input name="partidas[<?= $i ?>][peso_bruto]" type="number" step="0.0001" class="form-control" value="<?= htmlspecialchars($p['peso_bruto']) ?>"></td>
<td><input name="partidas[<?= $i ?>][valor_factura]" type="number" step="0.01" min="0" class="form-control valor-partida" value="<?= htmlspecialchars($p['valor_factura']) ?>"></td>
<td><input name="partidas[<?= $i ?>][peso_bruto]" type="number" step="0.0001" min="0" class="form-control" value="<?= htmlspecialchars($p['peso_bruto']) ?>"></td>
<td>
<select name="partidas[<?= $i ?>][tasa_preferencial]" class="form-select searchable">
<option value="">-- Selecciona --</option>

View File

@@ -76,17 +76,24 @@
const trans = document.getElementById('transportista').value;
const fotoF = document.getElementById('foto').files[0];
// Validaciones de campos obligatorios
if (!veh) {
e.preventDefault();
return Swal.fire({ icon:'error', title:'Vehículo requerido', text:'Por favor ingresa el nombre del vehículo.' });
Swal.fire({ icon:'error', title:'Vehículo requerido', text:'Por favor ingresa el nombre del vehículo.', confirmButtonColor: '#dc3545'});
document.querySelector('input[name="veh"]').focus();
return;
}
if (!fisc) {
e.preventDefault();
return Swal.fire({ icon:'error', title:'Identificador fiscal requerido', text:'Por favor ingresa el identificador fiscal.' });
Swal.fire({ icon:'error', title:'Identificador fiscal requerido', text:'Por favor ingresa el identificador fiscal.', confirmButtonColor: '#dc3545' });
document.querySelector('input[name="fisc"]').focus();
return;
}
if (!trans) {
e.preventDefault();
return Swal.fire({ icon:'error', title:'Transportista no seleccionado', text:'Debes elegir un transportista.' });
Swal.fire({ icon:'error', title:'Transportista no seleccionado', text:'Debes elegir un transportista.', confirmButtonColor: '#dc3545' });
document.querySelector('input[name="trans"]').focus();
return;
}
if (fotoF && fotoF.size > 2 * 1024 * 1024) { // 2 MB
e.preventDefault();

View File

@@ -40,7 +40,7 @@
<input type="hidden" name="id_transporte" value="<?= $t['id_transporte'] ?>">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">Vehículo *</label>
<label class="form-label">Contenedor *</label>
<input name="vehiculo" id="vehEdit" class="form-control"
value="<?= htmlspecialchars($t['vehiculo']) ?>" required>
</div>
@@ -82,28 +82,36 @@
<script>
document.getElementById('formTransEdit').addEventListener('submit', function(e) {
// Campos que validamos:
const veh = document.getElementById('vehEdit').value.trim();
const fisc = document.getElementById('fiscEdit').value.trim();
const trans = document.getElementById('trEdit').value;
const fotoF = document.getElementById('fotoEdit').files[0];
// Validaciones de campos obligatorios
if (!veh) {
e.preventDefault();
return Swal.fire({ icon:'error', title:'Vehículo requerido', text:'Por favor ingresa el nombre del vehículo.' });
Swal.fire({ icon:'error', title:'Vehículo requerido', text:'Por favor ingresa el nombre del vehículo.', confirmButtonColor: '#dc3545'});
document.querySelector('input[name="veh"]').focus();
return;
}
if (!fisc) {
e.preventDefault();
return Swal.fire({ icon:'error', title:'Identificador fiscal requerido', text:'Por favor ingresa el identificador fiscal.' });
Swal.fire({ icon:'error', title:'Identificador fiscal requerido', text:'Por favor ingresa el identificador fiscal.', confirmButtonColor: '#dc3545' });
document.querySelector('input[name="fisc"]').focus();
return;
}
if (!trans) {
e.preventDefault();
return Swal.fire({ icon:'error', title:'Transportista no seleccionado', text:'Debes elegir un transportista.' });
Swal.fire({ icon:'error', title:'Transportista no seleccionado', text:'Debes elegir un transportista.', confirmButtonColor: '#dc3545' });
document.querySelector('input[name="trans"]').focus();
return;
}
if (fotoF && fotoF.size > 2 * 1024 * 1024) {
if (fotoF && fotoF.size > 2 * 1024 * 1024) { // 2 MB
e.preventDefault();
return Swal.fire({ icon:'error', title:'Foto demasiado grande', text:'La imagen no debe exceder 2 MB.' });
}
// Si todo OK, se envía
// Si todas las validaciones pasan, el formulario se envía.
});
</script>

View File

@@ -33,32 +33,32 @@
<div class="row g-3">
<div class="col-md-4">
<label for="clave" class="form-label">Clave *</label>
<input name="clave" class="form-control" required>
<input name="clave" id="clave" class="form-control" maxlength="8" required>
</div>
<div class="col-md-4">
<label for="nombre" class="form-label">Nombre *</label>
<input name="nombre" class="form-control" required>
<input name="nombre" id="nombre" class="form-control" required>
</div>
<div class="col-md-4">
<label for="rfc" class="form-label">RFC *</label>
<input name="rfc" class="form-control" maxlength="13" required>
<input name="rfc" id="rfc" class="form-control" maxlength="13" required>
</div>
<div class="col-md-4">
<label for="curp" class="form-label">CURP *</label>
<input name="curp" class="form-control" maxlength="18" required>
<input name="curp" id="curp" class="form-control" maxlength="18" required>
</div>
<div class="col-md-4">
<label for="telefono" class="form-label">Teléfono *</label>
<input name="telefono" class="form-control" maxlength="11" required>
<input name="telefono" id="telefono" class="form-control" maxlength="11" required>
</div>
<div class="col-md-4">
<label for="caat" class="form-label">Código CAAT *</label>
<input name="caat" class="form-control" required>
<input name="caat" id="caat" class="form-control" maxlength="20" required>
</div>
<div class="col-md-4">
@@ -75,7 +75,7 @@
<div class="col-md-4">
<label for="entidad" class="form-label">Entidad *</label>
<select id="estado" name="entidad" class="form-select" required disabled>
<select id="entidad" name="entidad" class="form-select" required disabled>
<option value="">Primero país…</option>
</select>
</div>
@@ -89,7 +89,7 @@
<div class="col-md-8">
<label for="domicilio" class="form-label">Domicilio *</label>
<input name="domicilio" class="form-control" required>
<input name="domicilio" id="domicilio" class="form-control" required>
</div>
</div>
@@ -102,79 +102,201 @@
<script>
document.getElementById('formAltaTransportista').addEventListener('submit', function(e) {
const rfcInput = document.querySelector('input[name="rfc"]');
const curpInput = document.querySelector('input[name="curp"]');
const telefonoInput = document.querySelector('input[name="telefono"]');
e.preventDefault(); // Prevenir envío por defecto
const rfcInput = document.getElementById('rfc');
const curpInput = document.getElementById('curp');
const telefonoInput = document.getElementById('telefono');
const rfc = rfcInput.value.trim().toUpperCase();
const curp = curpInput.value.trim();
const telefono = telefonoInput.value.trim();
const clave = document.getElementById('clave').value.trim();
const nombre = document.getElementById('nombre').value.trim();
const caat = document.getElementById('caat').value.trim();
const pais = document.getElementById('pais').value;
const entidad = document.getElementById('entidad').value;
const ciudad = document.getElementById('ciudad').value;
const domicilio = document.getElementById('domicilio').value.trim();
// Expresiones regulares
const curpRegex = /^[A-Z]{4}[0-9]{6}[HM](AS|BC|BS|CC|CL|CM|CS|CH|DF|DG|GT|GR|HG|JC|MC|MN|MS|NT|NL|OC|PL|QT|QR|SP|SL|SR|TC|TS|TL|VZ|YN|ZS)[A-Z]{3}[A-Z0-9]{2}$/;
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
const telefonoRegex = /^\d{3}\s\d{7}$/;
const soloNumerosRegex = /^[0-9]+$/;
// Validación de correo
if (!curpRegex.test(curp)) {
e.preventDefault();
Swal.fire("CURP inválido", "No tiene el formato correcto.", "error");
emailInput.focus();
// Validaciones de campos obligatorios
if (!clave) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'La clave es obligatoria.', confirmButtonColor: '#dc3545' });
document.getElementById('clave').focus();
return;
}
if (clave.length > 8) {
Swal.fire({ icon: 'error', title: 'Clave inválida', text: 'La clave no puede tener más de 8 caracteres.', confirmButtonColor: '#dc3545' });
document.getElementById('clave').focus();
return;
}
if (!nombre) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El nombre es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('nombre').focus();
return;
}
if (!rfc) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El RFC es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('rfc').focus();
return;
}
// Validación de RFC
if (!rfcRegex.test(rfc)) {
e.preventDefault();
Swal.fire("RFC inválido", "El RFC debe tener 12 o 13 caracteres con el formato correcto (ej. ABC123456XYZ).", "error");
rfcInput.focus();
Swal.fire({ icon: 'error', title: 'RFC inválido', text: 'El RFC debe tener 12 o 13 caracteres con el formato correcto (ej. ABC123456XYZ).', confirmButtonColor: '#dc3545' });
document.getElementById('rfc').focus();
return;
}
if (!curp) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El CURP es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('curp').focus();
return;
}
// Validación de CURP (solo si se ingresó)
if (curp && !curpRegex.test(curp)) {
Swal.fire({ icon: 'error', title: 'CURP inválido', text: 'El CURP no tiene el formato correcto.', confirmButtonColor: '#dc3545'
});
document.getElementById('curp').focus();
return;
}
if (!telefono) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El teléfono es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('telefono').focus();
return;
}
// Validación de teléfono
if (!telefonoRegex.test(telefono)) {
e.preventDefault();
Swal.fire("Teléfono inválido", "Debe tener 10 dígitos en el formato: 3 dígitos (LADA), espacio, y 7 dígitos. Ejemplo: 123 4567890", "error");
phoneInput.focus();
Swal.fire({ icon: 'error', title: 'Teléfono inválido', text: 'Debe tener 10 dígitos en el formato: 3 dígitos (LADA), espacio, y 7 dígitos. Ejemplo: 123 4567890', confirmButtonColor: '#dc3545' });
document.getElementById('telefono').focus();
return;
}
if (telefono.length > 11) {
Swal.fire({ icon: 'error', title: 'Teléfono inválido', text: 'El teléfono no puede tener más de 11 caracteres.', confirmButtonColor: '#dc3545' });
document.getElementById('telefono').focus();
return;
}
if (!caat) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El código CAAT es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('caat').focus();
return;
}
if (!soloNumerosRegex.test(caat)) {
Swal.fire({ icon: 'error', title: 'Código CAAT inválido', text: 'El código caat solo puede contener números', confirmButtonColor: '#dc3545' });
document.getElementById('caat').focus();
return;
}
if (!pais) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'Debe seleccionar un país.', confirmButtonColor: '#dc3545' });
document.getElementById('pais').focus();
return;
}
if (!entidad) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'Debe seleccionar un estado.', confirmButtonColor: '#dc3545' });
document.getElementById('entidad').focus();
return;
}
if (!ciudad) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'Debe seleccionar una ciudad.', confirmButtonColor: '#dc3545' });
document.getElementById('ciudad').focus();
return;
}
if (!domicilio) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El domicilio es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('domicilio').focus();
return;
}
// Si llegamos aquí, todas las validaciones pasaron
// Actualizar los valores en los campos antes de enviar
document.getElementById('rfc').value = rfc;
document.getElementById('curp').value = curp;
// Si no hay errores, enviar el formulario
this.submit(); // Esto sí lo envía manualmente
// Enviar el formulario
this.submit();
});
// Active inputs entidad y ciudad
// Funcionalidad de selects en cascada
// 1) Cuando elige país → cargo estados
document.getElementById('pais').addEventListener('change', e => {
const pid = e.target.value;
const sel = document.getElementById('estado');
sel.innerHTML = '<option>Cargando…</option>';
sel.disabled = true;
document.getElementById('ciudad').innerHTML = '<option>Primero estado…</option>';
document.getElementById('ciudad').disabled = true;
const estadoSelect = document.getElementById('entidad');
const ciudadSelect = document.getElementById('ciudad');
// Resetear selects dependientes
estadoSelect.innerHTML = '<option value="">Cargando…</option>';
estadoSelect.disabled = true;
ciudadSelect.innerHTML = '<option value="">Primero estado…</option>';
ciudadSelect.disabled = true;
if (!pid) {
estadoSelect.innerHTML = '<option value="">Primero país…</option>';
return;
}
// Simulación de carga de estados (reemplaza con tu endpoint real)
fetch(`/IMPORTADORES/transportistas/estados?pais=${pid}`)
.then(r=>r.json())
.then(list=>{
sel.innerHTML = '<option value="">Selecciona estado</option>';
list.forEach(st => sel.add(new Option(st.nombre, st.id_estado)));
sel.disabled = false;
.then(r => {
if (!r.ok) throw new Error('Error al cargar estados');
return r.json();
})
.then(list => {
estadoSelect.innerHTML = '<option value="">Selecciona estado</option>';
list.forEach(st => {
const option = new Option(st.nombre, st.id_estado);
estadoSelect.add(option);
});
estadoSelect.disabled = false;
})
.catch(error => {
console.error('Error:', error);
estadoSelect.innerHTML = '<option value="">Error al cargar</option>';
Swal.fire({
icon: 'error',
title: 'Error',
text: 'No se pudieron cargar los estados',
confirmButtonColor: '#dc3545'
});
});
});
// 2) Cuando elige estado → cargo ciudades
document.getElementById('estado').addEventListener('change', e => {
document.getElementById('entidad').addEventListener('change', e => {
const eid = e.target.value;
const sel = document.getElementById('ciudad');
sel.innerHTML = '<option>Cargando…</option>';
sel.disabled = true;
const ciudadSelect = document.getElementById('ciudad');
ciudadSelect.innerHTML = '<option value="">Cargando…</option>';
ciudadSelect.disabled = true;
if (!eid) {
ciudadSelect.innerHTML = '<option value="">Primero estado…</option>';
return;
}
// Simulación de carga de ciudades (reemplaza con tu endpoint real)
fetch(`/IMPORTADORES/transportistas/ciudades?estado=${eid}`)
.then(r=>r.json())
.then(list=>{
sel.innerHTML = '<option value="">Selecciona ciudad</option>';
list.forEach(ct => sel.add(new Option(ct.nombre, ct.id_ciudad)));
sel.disabled = false;
.then(r => {
if (!r.ok) throw new Error('Error al cargar ciudades');
return r.json();
})
.then(list => {
ciudadSelect.innerHTML = '<option value="">Selecciona ciudad</option>';
list.forEach(ct => {
const option = new Option(ct.nombre, ct.id_ciudad);
ciudadSelect.add(option);
});
ciudadSelect.disabled = false;
})
.catch(error => {
console.error('Error:', error);
ciudadSelect.innerHTML = '<option value="">Error al cargar</option>';
Swal.fire({
icon: 'error',
title: 'Error',
text: 'No se pudieron cargar las ciudades',
confirmButtonColor: '#dc3545'
});
});
});
</script>

View File

@@ -8,6 +8,8 @@
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<!-- SweetAlert2 -->
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<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; }
@@ -32,38 +34,38 @@
<div class="content">
<h4 class="mb-4">✏️ Editar Transportista #<?= $t['id_transportista'] ?></h4>
<form action="/IMPORTADORES/transportistas/actualizar" method="POST" class="card p-4 shadow-sm">
<form action="/IMPORTADORES/transportistas/actualizar" method="POST" class="card p-4 shadow-sm" id="formEditTransportista">
<input type="hidden" name="id_transportista" value="<?= $t['id_transportista'] ?>">
<div class="row g-3">
<div class="col-md-4">
<label class="form-label">Clave</label>
<input name="clave" value="<?= htmlspecialchars($t['clave_identificador']) ?>"
class="form-control" required>
id="clave" class="form-control" maxlength="8" required>
</div>
<div class="col-md-4">
<label class="form-label">Nombre</label>
<input name="nombre" value="<?= htmlspecialchars($t['nombre']) ?>"
class="form-control" required>
id="nombre" class="form-control" required>
</div>
<div class="col-md-4">
<label class="form-label">RFC</label>
<input name="rfc" value="<?= htmlspecialchars($t['rfc']) ?>"
class="form-control" required>
id="rfc" class="form-control" maxlength="13" required>
</div>
<div class="col-md-4">
<label class="form-label">CURP</label>
<input name="curp" value="<?= htmlspecialchars($t['curp']) ?>"
class="form-control">
id="curp" class="form-control" maxlength="18">
</div>
<div class="col-md-4">
<label class="form-label">Teléfono</label>
<input name="telefono" value="<?= htmlspecialchars($t['telefono']) ?>"
class="form-control" required>
id="telefono" class="form-control" maxlength="11" required>
</div>
<div class="col-md-4">
<label class="form-label">Código CAAT</label>
<input name="caat" value="<?= htmlspecialchars($t['caat']) ?>"
class="form-control" required>
id="caat" class="form-control" maxlength="20" required>
</div>
<div class="col-md-4">
@@ -80,7 +82,7 @@
</div>
<div class="col-md-4">
<label class="form-label">Estado</label>
<select id="estado" name="entidad" class="form-select" required>
<select id="entidad" name="entidad" class="form-select" required>
<option value="">Selecciona estado</option>
<?php foreach($estados as $e): ?>
<option value="<?= $e['id_estado'] ?>"
@@ -105,7 +107,7 @@
<div class="col-md-12">
<label class="form-label">Domicilio Completo</label>
<textarea name="domicilio" class="form-control" rows="3" required><?= htmlspecialchars($t['domicilio']) ?></textarea>
<textarea name="domicilio" id="domicilio" class="form-control" rows="3" required><?= htmlspecialchars($t['domicilio']) ?></textarea>
</div>
</div>
<div class="text-end mt-4">
@@ -116,31 +118,202 @@
</div>
<script>
// Al cambiar país → recargar estados
document.getElementById('pais').addEventListener('change', function(){
const pid = this.value, selE = document.getElementById('estado');
selE.innerHTML = '<option>Cargando…</option>'; selE.disabled = true;
document.getElementById('formEditTransportista').addEventListener('submit', function(e) {
e.preventDefault(); // Prevenir envío por defecto
const rfcInput = document.getElementById('rfc');
const curpInput = document.getElementById('curp');
const telefonoInput = document.getElementById('telefono');
const rfc = rfcInput.value.trim().toUpperCase();
const curp = curpInput.value.trim();
const telefono = telefonoInput.value.trim();
const clave = document.getElementById('clave').value.trim();
const nombre = document.getElementById('nombre').value.trim();
const caat = document.getElementById('caat').value.trim();
const pais = document.getElementById('pais').value;
const entidad = document.getElementById('entidad').value;
const ciudad = document.getElementById('ciudad').value;
const domicilio = document.getElementById('domicilio').value.trim();
// Expresiones regulares
const curpRegex = /^[A-Z]{4}[0-9]{6}[HM](AS|BC|BS|CC|CL|CM|CS|CH|DF|DG|GT|GR|HG|JC|MC|MN|MS|NT|NL|OC|PL|QT|QR|SP|SL|SR|TC|TS|TL|VZ|YN|ZS)[A-Z]{3}[A-Z0-9]{2}$/;
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
const telefonoRegex = /^\d{3}\s\d{7}$/;
const soloNumerosRegex = /^[0-9]+$/;
// Validaciones de campos obligatorios
if (!clave) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'La clave es obligatoria.', confirmButtonColor: '#dc3545' });
document.getElementById('clave').focus();
return;
}
if (clave.length > 8) {
Swal.fire({ icon: 'error', title: 'Clave inválida', text: 'La clave no puede tener más de 8 caracteres.', confirmButtonColor: '#dc3545' });
document.getElementById('clave').focus();
return;
}
if (!nombre) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El nombre es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('nombre').focus();
return;
}
if (!rfc) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El RFC es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('rfc').focus();
return;
}
if (!rfcRegex.test(rfc)) {
Swal.fire({ icon: 'error', title: 'RFC inválido', text: 'El RFC debe tener 12 o 13 caracteres con el formato correcto (ej. ABC123456XYZ).', confirmButtonColor: '#dc3545' });
document.getElementById('rfc').focus();
return;
}
if (!curp) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El CURP es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('curp').focus();
return;
}
// Validación de CURP (solo si se ingresó)
if (curp && !curpRegex.test(curp)) {
Swal.fire({ icon: 'error', title: 'CURP inválido', text: 'El CURP no tiene el formato correcto.', confirmButtonColor: '#dc3545'
});
document.getElementById('curp').focus();
return;
}
if (!telefono) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El teléfono es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('telefono').focus();
return;
}
if (!telefonoRegex.test(telefono)) {
Swal.fire({ icon: 'error', title: 'Teléfono inválido', text: 'Debe tener 10 dígitos en el formato: 3 dígitos (LADA), espacio, y 7 dígitos. Ejemplo: 123 4567890', confirmButtonColor: '#dc3545' });
document.getElementById('telefono').focus();
return;
}
if (telefono.length > 11) {
Swal.fire({ icon: 'error', title: 'Teléfono inválido', text: 'El teléfono no puede tener más de 11 caracteres.', confirmButtonColor: '#dc3545' });
document.getElementById('telefono').focus();
return;
}
if (!caat) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El código CAAT es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('caat').focus();
return;
}
if (!soloNumerosRegex.test(caat)) {
Swal.fire({ icon: 'error', title: 'Código CAAT inválido', text: 'El código caat solo puede contener números', confirmButtonColor: '#dc3545' });
document.getElementById('caat').focus();
return;
}
if (!pais) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'Debe seleccionar un país.', confirmButtonColor: '#dc3545' });
document.getElementById('pais').focus();
return;
}
if (!entidad) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'Debe seleccionar un estado.', confirmButtonColor: '#dc3545' });
document.getElementById('entidad').focus();
return;
}
if (!ciudad) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'Debe seleccionar una ciudad.', confirmButtonColor: '#dc3545' });
document.getElementById('ciudad').focus();
return;
}
if (!domicilio) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El domicilio es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('domicilio').focus();
return;
}
// Si llegamos aquí, todas las validaciones pasaron
// Actualizar los valores en los campos antes de enviar
document.getElementById('rfc').value = rfc;
document.getElementById('curp').value = curp;
// Enviar el formulario
this.submit();
});
// Funcionalidad de selects en cascada
// 1) Cuando elige país → cargo estados
document.getElementById('pais').addEventListener('change', e => {
const pid = e.target.value;
const estadoSelect = document.getElementById('entidad');
const ciudadSelect = document.getElementById('ciudad');
// Resetear selects dependientes
estadoSelect.innerHTML = '<option value="">Cargando…</option>';
estadoSelect.disabled = true;
ciudadSelect.innerHTML = '<option value="">Primero estado…</option>';
ciudadSelect.disabled = true;
if (!pid) {
estadoSelect.innerHTML = '<option value="">Primero país…</option>';
return;
}
// Simulación de carga de estados (reemplaza con tu endpoint real)
fetch(`/IMPORTADORES/transportistas/estados?pais=${pid}`)
.then(r=>r.json())
.then(list=>{
selE.innerHTML = '<option value="">Selecciona estado</option>';
list.forEach(e=> selE.add(new Option(e.nombre,e.id_estado)) );
selE.disabled = false;
// Dispara cambio para recargar ciudades
selE.dispatchEvent(new Event('change'));
.then(r => {
if (!r.ok) throw new Error('Error al cargar estados');
return r.json();
})
.then(list => {
estadoSelect.innerHTML = '<option value="">Selecciona estado</option>';
list.forEach(st => {
const option = new Option(st.nombre, st.id_estado);
estadoSelect.add(option);
});
estadoSelect.disabled = false;
})
.catch(error => {
console.error('Error:', error);
estadoSelect.innerHTML = '<option value="">Error al cargar</option>';
Swal.fire({
icon: 'error',
title: 'Error',
text: 'No se pudieron cargar los estados',
confirmButtonColor: '#dc3545'
});
});
});
// Al cambiar estado → recargar ciudades
document.getElementById('estado').addEventListener('change', function(){
const eid = this.value, selC = document.getElementById('ciudad');
selC.innerHTML = '<option>Cargando…</option>'; selC.disabled = true;
// 2) Cuando elige estado → cargo ciudades
document.getElementById('entidad').addEventListener('change', e => {
const eid = e.target.value;
const ciudadSelect = document.getElementById('ciudad');
ciudadSelect.innerHTML = '<option value="">Cargando…</option>';
ciudadSelect.disabled = true;
if (!eid) {
ciudadSelect.innerHTML = '<option value="">Primero estado…</option>';
return;
}
// Simulación de carga de ciudades (reemplaza con tu endpoint real)
fetch(`/IMPORTADORES/transportistas/ciudades?estado=${eid}`)
.then(r=>r.json())
.then(list=>{
selC.innerHTML = '<option value="">Selecciona ciudad</option>';
list.forEach(c=> selC.add(new Option(c.nombre,c.id_ciudad)) );
selC.disabled = false;
.then(r => {
if (!r.ok) throw new Error('Error al cargar ciudades');
return r.json();
})
.then(list => {
ciudadSelect.innerHTML = '<option value="">Selecciona ciudad</option>';
list.forEach(ct => {
const option = new Option(ct.nombre, ct.id_ciudad);
ciudadSelect.add(option);
});
ciudadSelect.disabled = false;
})
.catch(error => {
console.error('Error:', error);
ciudadSelect.innerHTML = '<option value="">Error al cargar</option>';
Swal.fire({
icon: 'error',
title: 'Error',
text: 'No se pudieron cargar las ciudades',
confirmButtonColor: '#dc3545'
});
});
});
</script>