From 43659192113c4690c420782f4a915c3e7bf066b3 Mon Sep 17 00:00:00 2001 From: Alexeer Date: Mon, 16 Jun 2025 14:36:01 -0600 Subject: [PATCH] =?UTF-8?q?Revisi=C3=B3n=202.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/choferes.php | 171 +++++++++++++++--- app/controllers/transportes.php | 115 ++++++++++-- app/controllers/transportistas.php | 35 ++-- views/choferes/crear.php | 32 +++- views/choferes/editar.php | 34 +++- views/choferes/lista.php | 2 + views/locaciones/alta_locaciones.php | 12 +- views/patente/alta_patente.php | 6 +- views/patente/dashboard_patente.php | 10 +- views/patente/editar.php | 8 +- views/solicitud_importacion/crear.php | 237 ++++++++++++------------- views/solicitud_importacion/editar.php | 3 +- views/transportes/crear.php | 87 ++++++--- views/transportes/editar.php | 80 ++++++--- views/transportes/importar_masivo.php | 2 +- views/transportistas/alta.php | 19 +- views/transportistas/editar.php | 15 +- views/transportistas/lista.php | 23 +-- 18 files changed, 601 insertions(+), 290 deletions(-) diff --git a/app/controllers/choferes.php b/app/controllers/choferes.php index 9c64616..5bbfd4d 100644 --- a/app/controllers/choferes.php +++ b/app/controllers/choferes.php @@ -15,7 +15,7 @@ function lista() { SELECT c.*, (c.nombre + ' ' + c.apellido) AS nombre_completo, - tr.nombre AS transportista + (tr.clave_identificador + ' - ' + tr.nombre) AS transportista FROM dbo.choferes c JOIN dbo.transportistas tr ON c.transportista_id = tr.id_transportista @@ -45,8 +45,10 @@ function crear() { // OJO: aquí usamos "activo" según tu esquema original $sql = " - SELECT id_transportista, nombre - FROM dbo.transportistas + SELECT t.id_transportista, t.clave_identificador, t.nombre, t.ciudad, t.domicilio, + c.nombre AS ciudad_nombre + FROM dbo.transportistas t + LEFT JOIN dbo.ciudades c ON t.ciudad = c.id_ciudad WHERE id_usuario = ? AND activo = 1 ORDER BY nombre "; @@ -70,44 +72,86 @@ function guardar() { $nombre = trim($_POST['nombre'] ?? ''); $apellido = trim($_POST['apellido'] ?? ''); $licencia = trim($_POST['numero_licencia'] ?? ''); + $gafete = trim($_POST['numero_gafete'] ?? ''); $telefono = trim($_POST['telefono'] ?? ''); $email = trim($_POST['email'] ?? ''); $fecha_ingreso = $_POST['fecha_ingreso'] ?: null; - if (!$transportista_id || $nombre === '' || $apellido === '' || $licencia === '') { + if (!$transportista_id || !is_numeric($transportista_id) || $nombre === '' || + $apellido === '' || $licencia === '' || $gafete === '') { die("❌ Todos los campos obligatorios deben llenarse."); } - // Manejo de foto + $conn = getConnection(); + + // ✅ CRÍTICO: Verificar que el transportista pertenece al usuario + $sqlVerify = "SELECT id_transportista FROM dbo.transportistas WHERE id_transportista = ? AND id_usuario = ?"; + $stmtVerify = sqlsrv_query($conn, $sqlVerify, [(int)$transportista_id, $_SESSION['usuario_id']]); + if (!$stmtVerify || !sqlsrv_fetch($stmtVerify)) { + die("❌ Transportista no autorizado."); + } + + // ✅ CRÍTICO: Verificar que el número de gafete no existe + $sqlCheckGafete = "SELECT id_chofer FROM dbo.choferes WHERE numero_gafete = ? AND status = 1"; + $stmtCheck = sqlsrv_query($conn, $sqlCheckGafete, [$gafete]); + if ($stmtCheck && sqlsrv_fetch($stmtCheck)) { + die("❌ El número de gafete '{$gafete}' ya está en uso. Por favor, use otro número."); + } + + // ✅ MEJORADO: Manejo de foto con validación $fotoUrl = null; - if (!empty($_FILES['foto']['tmp_name'])) { - $ext = strtolower(pathinfo($_FILES['foto']['name'], PATHINFO_EXTENSION)); + if (!empty($_FILES['foto']['tmp_name']) && $_FILES['foto']['error'] === UPLOAD_ERR_OK) { + $allowedTypes = ['jpg', 'jpeg', 'png', 'gif']; + $ext = strtolower(pathinfo($_FILES['foto']['name'], PATHINFO_EXTENSION)); + + if (!in_array($ext, $allowedTypes)) { + die("❌ Tipo de archivo no permitido. Solo JPG, PNG, GIF."); + } + + // Validar tamaño (ej: máximo 5MB) + if ($_FILES['foto']['size'] > 5 * 1024 * 1024) { + die("❌ El archivo es demasiado grande. Máximo 5MB."); + } + $dest = __DIR__ . '/../../public/uploads/chofer_'.uniqid().".{$ext}"; - if (!is_dir(dirname($dest))) mkdir(dirname($dest), 0755, true); + if (!is_dir(dirname($dest))) { + mkdir(dirname($dest), 0755, true); + } if (move_uploaded_file($_FILES['foto']['tmp_name'], $dest)) { $fotoUrl = "/IMPORTADORES/public/uploads/" . basename($dest); + } else { + error_log("Error al mover foto en guardar(): {$dest}"); + die("❌ Error al subir la foto."); } } - $conn = getConnection(); $sql = " INSERT INTO dbo.choferes - (transportista_id, nombre, apellido, numero_licencia, telefono, email, fecha_ingreso, foto_url, status) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1) + (transportista_id, nombre, apellido, numero_licencia, numero_gafete, telefono, email, fecha_ingreso, foto_url, status) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1, GETDATE()) "; $params = [ (int)$transportista_id, $nombre, $apellido, $licencia, + $gafete, $telefono, $email, $fecha_ingreso, $fotoUrl ]; + $stmt = sqlsrv_query($conn, $sql, $params); if ($stmt === false) { - die("Error en guardar(): " . print_r(sqlsrv_errors(), true)); + $errors = sqlsrv_errors(); + // ✅ Manejo específico de error de duplicado + foreach ($errors as $error) { + if (strpos($error['message'], 'UQ_numero_gafete') !== false) { + die("❌ El número de gafete ya está en uso. Por favor, use otro número."); + } + } + die("❌ Error en guardar(): " . print_r($errors, true)); } header('Location: /IMPORTADORES/choferes/lista?created=ok'); @@ -126,13 +170,15 @@ function editar() { $conn = getConnection(); $sql = " - SELECT c.*, tr.nombre AS transportista - FROM dbo.choferes c - JOIN dbo.transportistas tr - ON c.transportista_id = tr.id_transportista - WHERE c.id_chofer = ? - AND tr.id_usuario = ? - AND c.status = 1 + SELECT + ch.*, tr.clave_identificador, tr.nombre AS transportista_nombre, tr.ciudad, tr.domicilio, + ciu.nombre AS ciudad_nombre + FROM dbo.choferes ch + LEFT JOIN dbo.transportistas tr ON ch.transportista_id = tr.id_transportista + LEFT JOIN dbo.ciudades ciu ON tr.ciudad = ciu.id_ciudad + WHERE ch.id_chofer = ? + AND tr.id_usuario = ? + AND ch.status = 1 "; $stmt = sqlsrv_query($conn, $sql, [(int)$id, $_SESSION['usuario_id']]); if ($stmt === false) { @@ -150,10 +196,12 @@ function editar() { // Lista de transportistas $sql2 = " - SELECT id_transportista, nombre - FROM dbo.transportistas - WHERE id_usuario = ? AND activo = 1 - ORDER BY nombre + SELECT tr.id_transportista, tr.clave_identificador, tr.nombre, tr.domicilio, + ciu.nombre AS ciudad_nombre + FROM dbo.transportistas tr + LEFT JOIN dbo.ciudades ciu ON tr.ciudad = ciu.id_ciudad + WHERE tr.id_usuario = ? AND tr.activo = 1 + ORDER BY tr.nombre "; $stmt2 = sqlsrv_query($conn, $sql2, [$_SESSION['usuario_id']]); if ($stmt2 === false) { @@ -167,6 +215,42 @@ function editar() { include __DIR__ . '/../../views/choferes/editar.php'; } +/** Valida que el número de gafete sea único **/ +function validarNumeroGafete() { + if (!($_SESSION['usuario_id'] ?? false)) { + http_response_code(403); + echo json_encode(['success' => false, 'message' => 'No autorizado']); + exit; + } + + $gafete = trim($_GET['numero_gafete'] ?? ''); + $id_chofer = $_GET['id_chofer'] ?? null; + + if ($gafete === '') { + echo json_encode(['success' => false, 'message' => 'Número de gafete vacío']); + exit; + } + + $conn = getConnection(); + + if ($id_chofer) { + // Edición: excluir el chofer actual + $sql = "SELECT id_chofer FROM dbo.choferes WHERE numero_gafete = ? AND id_chofer <> ? AND status = 1"; + $stmt = sqlsrv_query($conn, $sql, [$gafete, $id_chofer]); + } else { + // Alta nueva + $sql = "SELECT id_chofer FROM dbo.choferes WHERE numero_gafete = ? AND status = 1"; + $stmt = sqlsrv_query($conn, $sql, [$gafete]); + } + + if ($stmt && sqlsrv_fetch($stmt)) { + echo json_encode(['success' => true, 'existe' => true]); + } else { + echo json_encode(['success' => true, 'existe' => false]); + } + exit; +} + /** Procesa la actualización de un chofer **/ function actualizar() { if (!($_SESSION['usuario_id'] ?? false)) { @@ -178,6 +262,7 @@ function actualizar() { $nombre = trim($_POST['nombre'] ?? ''); $apellido = trim($_POST['apellido'] ?? ''); $licencia = trim($_POST['numero_licencia']?? ''); + $gafete = trim($_POST['numero_gafete']?? ''); $telefono = trim($_POST['telefono'] ?? ''); $email = trim($_POST['email'] ?? ''); $fecha_ingreso = $_POST['fecha_ingreso'] ?: null; @@ -187,15 +272,35 @@ function actualizar() { if ( !$id || !is_numeric($id) || !$transportista_id || !is_numeric($transportista_id) || - $nombre === '' || $apellido === '' || $licencia === '' + $nombre === '' || $apellido === '' || $licencia === '' || $gafete === '' ) { die("❌ Datos inválidos o incompletos."); } - // Manejo de foto nueva (opcional) + $conn = getConnection(); + + // ✅ CRÍTICO: Verificar que el número de gafete no existe (excluyendo el chofer actual) + $sqlCheckGafete = "SELECT id_chofer FROM dbo.choferes WHERE numero_gafete = ? AND id_chofer <> ? AND status = 1"; + $stmtCheck = sqlsrv_query($conn, $sqlCheckGafete, [$gafete, $id_chofer]); + if ($stmtCheck && sqlsrv_fetch($stmtCheck)) { + die("❌ El número de gafete '{$gafete}' ya está en uso. Por favor, use otro número."); + } + + // ✅ MEJORADO: Manejo de foto nueva con validación $fotoUrl = null; if (!empty($_FILES['foto']['tmp_name']) && $_FILES['foto']['error'] === UPLOAD_ERR_OK) { + $allowedTypes = ['jpg', 'jpeg', 'png', 'gif']; $ext = strtolower(pathinfo($_FILES['foto']['name'], PATHINFO_EXTENSION)); + + if (!in_array($ext, $allowedTypes)) { + die("❌ Tipo de archivo no permitido. Solo JPG, PNG, GIF."); + } + + // Validar tamaño (ej: máximo 5MB) + if ($_FILES['foto']['size'] > 5 * 1024 * 1024) { + die("❌ El archivo es demasiado grande. Máximo 5MB."); + } + $dest = __DIR__ . '/../../public/uploads/chofer_'.uniqid().".{$ext}"; if (!is_dir(dirname($dest))) { mkdir(dirname($dest), 0755, true); @@ -204,11 +309,10 @@ function actualizar() { $fotoUrl = "/IMPORTADORES/public/uploads/" . basename($dest); } else { error_log("Error al mover foto en actualizar(): {$dest}"); + die("❌ Error al subir la foto."); } } - $conn = getConnection(); - if ($fotoUrl) { $sql = " UPDATE dbo.choferes SET @@ -216,6 +320,7 @@ function actualizar() { nombre = ?, apellido = ?, numero_licencia = ?, + numero_gafete = ?, telefono = ?, email = ?, fecha_ingreso = ?, @@ -229,6 +334,7 @@ function actualizar() { $nombre, $apellido, $licencia, + $gafete, $telefono, $email, $fecha_ingreso, @@ -243,6 +349,7 @@ function actualizar() { nombre = ?, apellido = ?, numero_licencia = ?, + numero_gafete = ?, telefono = ?, email = ?, fecha_ingreso = ?, @@ -255,6 +362,7 @@ function actualizar() { $nombre, $apellido, $licencia, + $gafete, $telefono, $email, $fecha_ingreso, @@ -265,7 +373,14 @@ function actualizar() { $stmt = sqlsrv_query($conn, $sql, $params); if ($stmt === false) { - die("❌ Error en actualizar(): " . print_r(sqlsrv_errors(), true)); + $errors = sqlsrv_errors(); + // ✅ Manejo específico de error de duplicado + foreach ($errors as $error) { + if (strpos($error['message'], 'UQ_numero_gafete') !== false) { + die("❌ El número de gafete ya está en uso por otro chofer. Por favor, use otro número."); + } + } + die("❌ Error en actualizar(): " . print_r($errors, true)); } header('Location: /IMPORTADORES/choferes/lista?updated=ok'); diff --git a/app/controllers/transportes.php b/app/controllers/transportes.php index 9cb7167..ee5b867 100644 --- a/app/controllers/transportes.php +++ b/app/controllers/transportes.php @@ -14,7 +14,7 @@ function lista() { // Sólo mostrar transportes de los transportistas que le pertenecen al usuario $sql = " - SELECT t.*, tr.nombre AS transportista + SELECT t.*, (tr.clave_identificador + ' - ' + tr.nombre) AS transportista FROM dbo.transportes t JOIN dbo.transportistas tr ON t.id_transportista = tr.id_transportista @@ -41,8 +41,10 @@ function crear() { // Traer transportistas propios para el select $sql = " - SELECT id_transportista, clave_identificador, nombre - FROM dbo.transportistas + SELECT t.id_transportista, t.clave_identificador, t.nombre, t.ciudad, t.domicilio, + c.nombre AS ciudad_nombre + FROM dbo.transportistas t + LEFT JOIN dbo.ciudades c ON t.ciudad = c.id_ciudad WHERE id_usuario = ? AND activo = 1 ORDER BY nombre "; @@ -56,31 +58,62 @@ function crear() { } /** Procesa la creación de un nuevo transporte **/ -function guardar() { - +function guardar() +{ if (!($_SESSION['usuario_id'] ?? false)) { die("⚠️ No autorizado."); } - $vehiculo = trim($_POST['vehiculo'] ?? ''); - $identFiscal= trim($_POST['identificador_fiscal'] ?? ''); - $idTrans = $_POST['id_transportista'] ?? null; + + $vehiculo = trim($_POST['vehiculo'] ?? ''); + $identFiscal = trim($_POST['identificador_fiscal'] ?? ''); + $idTrans = $_POST['id_transportista'] ?? null; if ($vehiculo === '' || $identFiscal === '' || !$idTrans) { die("❌ Todos los campos son obligatorios."); } - // Manejo de foto + // Validar que el transportista pertenece al usuario actual + $conn = getConnection(); + $sqlCheck = " + SELECT 1 FROM dbo.transportistas + WHERE id_transportista = ? AND id_usuario = ? AND activo = 1 + "; + $stmtCheck = sqlsrv_query($conn, $sqlCheck, [$idTrans, $_SESSION['usuario_id']]); + if (!sqlsrv_fetch($stmtCheck)) { + die("❌ Transportista no válido o no autorizado."); + } + + // Manejo de foto mejorado $fotoUrl = null; - if (!empty($_FILES['foto']['tmp_name'])) { - $ext = pathinfo($_FILES['foto']['name'], PATHINFO_EXTENSION); - $dest = __DIR__ . '/../../public/uploads/transporte_'.uniqid().".{$ext}"; + if (isset($_FILES['foto']) && $_FILES['foto']['error'] === UPLOAD_ERR_OK) { + $allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp']; + $ext = strtolower(pathinfo($_FILES['foto']['name'], PATHINFO_EXTENSION)); + + if (!in_array($ext, $allowedExtensions)) { + die("❌ Formato de imagen no válido. Solo se permiten: " . implode(', ', $allowedExtensions)); + } + + // Validar tamaño (2MB max) + if ($_FILES['foto']['size'] > 2 * 1024 * 1024) { + die("❌ La imagen no debe exceder 2 MB."); + } + + $uploadDir = __DIR__ . '/../../public/uploads/'; + if (!is_dir($uploadDir)) { + mkdir($uploadDir, 0755, true); + } + + $dest = $uploadDir . 'transporte_' . uniqid() . ".{$ext}"; + if (move_uploaded_file($_FILES['foto']['tmp_name'], $dest)) { - // ruta relativa $fotoUrl = "/IMPORTADORES/public/uploads/" . basename($dest); + } else { + error_log("Error al mover archivo en guardar(): {$dest}"); + die("❌ Error al subir la imagen."); } } - $conn = getConnection(); + // Insertar el nuevo transporte $sql = " INSERT INTO dbo.transportes (vehiculo, identificador_fiscal, foto_url, status, id_transportista) @@ -88,8 +121,17 @@ function guardar() { "; $params = [$vehiculo, $identFiscal, $fotoUrl, $idTrans]; $stmt = sqlsrv_query($conn, $sql, $params); + if ($stmt === false) { - die("❌ Error al guardar: ".print_r(sqlsrv_errors(),true)); + $errors = sqlsrv_errors(); + error_log("Error SQL en guardar transporte: " . print_r($errors, true)); + die("❌ Error al guardar: " . $errors[0]['message']); + } + + // Verificar que se insertó correctamente + $rowsAffected = sqlsrv_rows_affected($stmt); + if ($rowsAffected === 0) { + die("❌ No se pudo crear el registro."); } header('Location: /IMPORTADORES/transportes/lista?created=ok'); @@ -124,8 +166,10 @@ function editar() { // Mismo select de transportistas que en crear() $sql2 = " - SELECT id_transportista, nombre - FROM dbo.transportistas + SELECT t.id_transportista, t.clave_identificador, t.nombre, t.ciudad, t.domicilio, + c.nombre AS ciudad_nombre + FROM dbo.transportistas t + LEFT JOIN dbo.ciudades c ON t.ciudad = c.id_ciudad WHERE id_usuario = ? AND activo = 1 ORDER BY nombre "; @@ -145,24 +189,49 @@ function actualizar() { $vehiculo = trim($_POST['vehiculo'] ?? ''); $identFiscal = trim($_POST['identificador_fiscal'] ?? ''); $idTrans = $_POST['id_transportista'] ?? null; + if (!$id || !is_numeric($id) || $vehiculo === '' || $identFiscal === '' || !$idTrans) { die("❌ Faltan datos."); } $conn = getConnection(); + // Antes del UPDATE, validar que el transporte pertenece al usuario + $sqlCheck = " + SELECT 1 FROM dbo.transportes t + JOIN dbo.transportistas tr ON t.id_transportista = tr.id_transportista + wHERE t.id_transporte = ? AND tr.id_usuario = ? + "; + $stmtCheck = sqlsrv_query($conn, $sqlCheck, [$id, $_SESSION['usuario_id']]); + if ($stmtCheck === false) { + $errors = sqlsrv_errors(); + error_log("Error SQL al validar transporte: " . print_r($errors, true)); + die("❌ Error en la validación SQL."); + } + if (!sqlsrv_fetch($stmtCheck)) { + die("❌ No autorizado para modificar este transporte."); + } + // —– Manejo de nueva foto —– $fotoUrl = null; if (isset($_FILES['foto']) && $_FILES['foto']['error'] === UPLOAD_ERR_OK) { + $allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp']; $ext = strtolower(pathinfo($_FILES['foto']['name'], PATHINFO_EXTENSION)); + + if (!in_array($ext, $allowedExtensions)) { + die("❌ Formato de imagen no válido."); + } + $dest = __DIR__ . '/../../public/uploads/transporte_'.uniqid().".{$ext}"; if (!is_dir(dirname($dest))) { mkdir(dirname($dest), 0755, true); } + if (move_uploaded_file($_FILES['foto']['tmp_name'], $dest)) { $fotoUrl = "/IMPORTADORES/public/uploads/" . basename($dest); } else { error_log("Error al mover archivo en actualizar(): {$dest}"); + die("❌ Error al subir la imagen."); } } @@ -174,7 +243,7 @@ function actualizar() { identificador_fiscal = ?, id_transportista = ?, foto_url = ? - WHERE id_transporte = ? + WHERE id_transporte = ? "; $params = [$vehiculo, $identFiscal, $idTrans, $fotoUrl, $id]; } else { @@ -190,7 +259,15 @@ function actualizar() { $stmt = sqlsrv_query($conn, $sql, $params); if ($stmt === false) { - die("❌ Error al actualizar: " . print_r(sqlsrv_errors(), true)); + $errors = sqlsrv_errors(); + error_log("Error SQL en actualizar transporte: " . print_r($errors, true)); + die("❌ Error al actualizar: " . $errors[0]['message']); + } + + // Verificar si se afectó alguna fila + $rowsAffected = sqlsrv_rows_affected($stmt); + if ($rowsAffected === 0) { + die("❌ No se pudo actualizar el registro."); } header('Location: /IMPORTADORES/transportes/lista?updated=ok'); diff --git a/app/controllers/transportistas.php b/app/controllers/transportistas.php index add0845..67ca8f6 100644 --- a/app/controllers/transportistas.php +++ b/app/controllers/transportistas.php @@ -2,6 +2,7 @@ require_once __DIR__ . '/../helpers/session.php'; require_once __DIR__ . '/../../config/database.php'; require_once __DIR__ . '/../helpers/env.php'; +ob_clean(); function guardar() { if (!($_SESSION['usuario_id'] ?? false)) { @@ -264,21 +265,13 @@ function ajax_lista() { $params = [$usr]; if ($search !== '') { - // Ahora buscamos en campos de transportista Y en el nombre de la ciudad $where .= " AND ( - t.clave_identificador LIKE ? OR t.nombre LIKE ? OR t.rfc LIKE ? OR - t.curp LIKE ? OR - t.telefono LIKE ? OR - t.caat LIKE ? OR - c.nombre LIKE ? OR - p.nombre LIKE ? OR - e.nombre LIKE ? + c.nombre LIKE ? )"; $like = "%{$search}%"; - // Agregamos el parámetro para cada campo de búsqueda - $params = array_merge($params, array_fill(0, 9, $like)); + $params = array_merge($params, array_fill(0, 3, $like)); } // 5) Total registros filtrados (CON JOIN) @@ -332,13 +325,23 @@ function ajax_lista() { } // 7) Devolver JSON + $response = [ + "draw" => $draw, + "recordsTotal" => $recordsTotal, + "recordsFiltered" => $recordsFiltered, + "data" => $data + ]; + + $json = json_encode($response, JSON_UNESCAPED_UNICODE); + if (json_last_error() !== JSON_ERROR_NONE) { + http_response_code(500); + echo json_encode(["error" => "JSON encoding error: " . json_last_error_msg()]); + exit; + } + header('Content-Type: application/json; charset=UTF-8'); - echo json_encode([ - "draw" => $draw, - "recordsTotal" => $recordsTotal, - "recordsFiltered" => $recordsFiltered, - "data" => $data - ]); + header('Cache-Control: no-cache, must-revalidate'); + echo $json; exit; } diff --git a/views/choferes/crear.php b/views/choferes/crear.php index 241ec0d..0c85477 100644 --- a/views/choferes/crear.php +++ b/views/choferes/crear.php @@ -44,7 +44,7 @@ @@ -65,6 +65,11 @@ +
+ + +
+
@@ -101,6 +106,7 @@ const nombre = document.getElementById('nombre').value.trim(); const apellido = document.getElementById('apellido').value.trim(); const numero_licencia = document.getElementById('numero_licencia').value.trim(); + const numero_gafete = document.getElementById('numero_gafete').value.trim(); const telefono = document.getElementById('telefono').value.trim(); const email = document.getElementById('email').value.trim(); const fecha_ingreso = document.getElementById('fecha_ingreso').value; @@ -132,6 +138,11 @@ document.getElementById('numero_licencia').focus(); return; } + if (!numero_gafete) { + Swal.fire({ icon: 'error', title: 'Número de Gafete requerido', text: 'Por favor ingresa el número de gafete.', confirmButtonColor: '#dc3545' }); + document.getElementById('numero_gafete').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' }); @@ -175,9 +186,22 @@ return; } } - - // SI TODAS LAS VALIDACIONES PASAN, ENVIAR EL FORMULARIO - this.submit(); + // VALIDACIÓN ASÍNCRONA DE DUPLICADO DE GAFETE + fetch(`/IMPORTADORES/choferes/validarNumeroGafete?numero_gafete=${encodeURIComponent(numero_gafete)}`) + .then(response => response.json()) + .then(data => { + if (data.success && data.existe) { + Swal.fire({ icon: 'error', title: 'Número de Gafete duplicado', text: 'El número de gafete ya está en uso, por favor ingresa uno diferente.', confirmButtonColor: '#dc3545' }); + document.getElementById('numero_gafete').focus(); + } else { + // Si no existe, enviamos el formulario + e.target.submit(); + } + }) + .catch(error => { + console.error('Error validando el número de gafete:', error); + Swal.fire({ icon: 'error', title: 'Error de validación', text: 'No fue posible validar el número de gafete. Intenta de nuevo.', confirmButtonColor: '#dc3545' }); + }); }); diff --git a/views/choferes/editar.php b/views/choferes/editar.php index cc4184c..e5f1ca0 100644 --- a/views/choferes/editar.php +++ b/views/choferes/editar.php @@ -46,7 +46,7 @@ @@ -76,6 +76,13 @@ value="" required>
+ +
+ + +
+
@@ -138,6 +145,7 @@ const nombre = document.getElementById('nombre').value.trim(); const apellido = document.getElementById('apellido').value.trim(); const numero_licencia = document.getElementById('numero_licencia').value.trim(); + const numero_gafete = document.getElementById('numero_gafete').value.trim(); const telefono = document.getElementById('telefono').value.trim(); const email = document.getElementById('email').value.trim(); const fecha_ingreso = document.getElementById('fecha_ingreso').value; @@ -169,6 +177,11 @@ document.getElementById('numero_licencia').focus(); return; } + if (!numero_gafete) { + Swal.fire({ icon: 'error', title: 'Número de Gafete requerido', text: 'Por favor ingresa el número de gafete.', confirmButtonColor: '#dc3545' }); + document.getElementById('numero_gafete').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(); @@ -211,9 +224,24 @@ return; } } + // VALIDACIÓN ASÍNCRONA DE DUPLICADO DE GAFETE + const id_chofer = document.querySelector('input[name="id_chofer"]').value; - // SI TODAS LAS VALIDACIONES PASAN, ENVIAR EL FORMULARIO - this.submit(); + fetch(`/IMPORTADORES/choferes/validarNumeroGafete?numero_gafete=${encodeURIComponent(numero_gafete)}&id_chofer={id_chofer}`) + .then(response => response.json()) + .then(data => { + if (data.success && data.existe) { + Swal.fire({ icon: 'error', title: 'Número de Gafete duplicado', text: 'El número de gafete ya está en uso, por favor ingresa uno diferente.', confirmButtonColor: '#dc3545' }); + document.getElementById('numero_gafete').focus(); + } else { + // Si no existe, enviamos el formulario + e.target.submit(); + } + }) + .catch(error => { + console.error('Error validando el número de gafete:', error); + Swal.fire({ icon: 'error', title: 'Error de validación', text: 'No fue posible validar el número de gafete. Intenta de nuevo.', confirmButtonColor: '#dc3545' }); + }); }); diff --git a/views/choferes/lista.php b/views/choferes/lista.php index 0b20e28..a36a85f 100644 --- a/views/choferes/lista.php +++ b/views/choferes/lista.php @@ -47,6 +47,7 @@ # Nombre Completo Licencia + Gafete Teléfono Email Ingreso @@ -60,6 +61,7 @@ + diff --git a/views/locaciones/alta_locaciones.php b/views/locaciones/alta_locaciones.php index 949b361..c7b6c4f 100644 --- a/views/locaciones/alta_locaciones.php +++ b/views/locaciones/alta_locaciones.php @@ -59,7 +59,7 @@ - Cancelar + Cancelar
@@ -69,7 +69,7 @@
-
+

➕ Nueva Ciudad

@@ -102,7 +102,7 @@ - Cancelar + Cancelar
@@ -117,7 +117,7 @@ selectElement.innerHTML = ''; selectElement.disabled = true; - fetch(`/IMPORTADORES/agentes/estados?pais=${paisId}`) + fetch(`/IMPORTADORES/locaciones/estados?pais=${paisId}`) .then(response => response.json()) .then(estados => { selectElement.innerHTML = ''; @@ -193,7 +193,7 @@ entidad: entidadValue }); - fetch('/IMPORTADORES/agentes/guardarEstado', { + fetch('/IMPORTADORES/locaciones/guardarEstado', { method: 'POST', body: formData }) @@ -241,7 +241,7 @@ submitBtn.innerHTML = ' Guardando...'; submitBtn.disabled = true; - fetch('/IMPORTADORES/agentes/guardarCiudad', { + fetch('/IMPORTADORES/locaciones/guardarCiudad', { method: 'POST', body: formData }) diff --git a/views/patente/alta_patente.php b/views/patente/alta_patente.php index ee9a44e..8687229 100644 --- a/views/patente/alta_patente.php +++ b/views/patente/alta_patente.php @@ -47,7 +47,7 @@ /* Asegurar que las pestañas sean completamente visibles */ .nav-tabs .nav-item:first-child .nav-link { margin-left: 0; } .nav-tabs .nav-item:last-child .nav-link { margin-right: 0; } - .tab-content { padding: 5px; } + .tab-content { padding: 10px; } @@ -119,7 +119,7 @@
-


+

Capturar para el llenado de la Manifestación de Valor

@@ -251,7 +251,7 @@
- + Cancelar
diff --git a/views/patente/dashboard_patente.php b/views/patente/dashboard_patente.php index 198a8a5..323d25a 100644 --- a/views/patente/dashboard_patente.php +++ b/views/patente/dashboard_patente.php @@ -37,7 +37,7 @@
-

📦 Panel del Importador

+

📦 Panel de Patentes

@@ -45,8 +45,8 @@
Ver patentes

Revisa y gestiona las patentes.

- Ver transportes + class="btn btn-primary btn-sm mt-2 "> + Ver patentes
@@ -56,8 +56,8 @@
Nueva patente

Agrega nuevas patentes:

- Ver transportistas + class="btn btn-success btn-sm mt-2 "> + Registrar nueva patente
diff --git a/views/patente/editar.php b/views/patente/editar.php index f23e654..4d3ac16 100644 --- a/views/patente/editar.php +++ b/views/patente/editar.php @@ -44,7 +44,7 @@ /* Asegurar que las pestañas sean completamente visibles */ .nav-tabs .nav-item:first-child .nav-link { margin-left: 0; } .nav-tabs .nav-item:last-child .nav-link { margin-right: 0; } - .tab-content { padding: 5px; } + .tab-content { padding: 10px; } @@ -124,11 +124,11 @@ -

+

Capturar para el llenado de la Manifestación de Valor

-

Datos del Agente Aduanal:

+

Datos del Agente Aduanal:

@@ -274,7 +274,7 @@
- + Cancelar
diff --git a/views/solicitud_importacion/crear.php b/views/solicitud_importacion/crear.php index b92d2fc..2196206 100644 --- a/views/solicitud_importacion/crear.php +++ b/views/solicitud_importacion/crear.php @@ -135,7 +135,7 @@
- +
@@ -217,135 +217,134 @@ - - diff --git a/views/solicitud_importacion/editar.php b/views/solicitud_importacion/editar.php index d9adbc2..2531965 100644 --- a/views/solicitud_importacion/editar.php +++ b/views/solicitud_importacion/editar.php @@ -145,7 +145,7 @@
- +
@@ -243,7 +243,6 @@ - diff --git a/views/transportes/crear.php b/views/transportes/crear.php index 59887fb..4b962c4 100644 --- a/views/transportes/crear.php +++ b/views/transportes/crear.php @@ -29,6 +29,13 @@ .sidebar .nav-link.active { background-color: #e9ecef; color: #212529; } } .card { border-radius: 12px; } + /* Forzar z-index más alto para modales anidados */ + .modal { z-index: 9999 !important; } + .modal-backdrop { z-index: 9998 !important; } + /* Asegurar que el contenido del modal esté por encima */ + .modal-dialog { z-index: 10000 !important; position: relative; } + /* Opcional: Mejorar la apariencia del overlay */ + .modal-backdrop.show { opacity: 0.5; } @@ -39,12 +46,20 @@
- +
+ + +
+
- - + +
+ + +
+
@@ -55,7 +70,7 @@ @@ -68,38 +83,70 @@
+ + + + + + + diff --git a/views/transportes/editar.php b/views/transportes/editar.php index dc38824..e49fd4b 100644 --- a/views/transportes/editar.php +++ b/views/transportes/editar.php @@ -41,13 +41,19 @@
- +
+ + +
- - + +
+ + +

@@ -59,15 +65,15 @@
- +
- @@ -80,38 +86,70 @@
+ + + + + + + diff --git a/views/transportes/importar_masivo.php b/views/transportes/importar_masivo.php index df0a5b5..0fd59d1 100644 --- a/views/transportes/importar_masivo.php +++ b/views/transportes/importar_masivo.php @@ -57,7 +57,7 @@
-

Descarga la plantilla y llena las columnas: contenedor, identificador_fiscal, id_transportista.

+

Descarga la plantilla y llena las columnas: contenedor, identificación_fiscal, id_transportista.

📥 Descargar plantilla
diff --git a/views/transportistas/alta.php b/views/transportistas/alta.php index 999cb39..64a2330 100644 --- a/views/transportistas/alta.php +++ b/views/transportistas/alta.php @@ -48,7 +48,7 @@
- +
@@ -57,7 +57,7 @@
- +
@@ -89,7 +89,7 @@
- +
@@ -123,7 +123,6 @@ 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) { @@ -151,11 +150,6 @@ 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' @@ -179,12 +173,7 @@ 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' }); + Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El código caat es obligatorio.', confirmButtonColor: '#dc3545' }); document.getElementById('caat').focus(); return; } diff --git a/views/transportistas/editar.php b/views/transportistas/editar.php index b65ec91..641a585 100644 --- a/views/transportistas/editar.php +++ b/views/transportistas/editar.php @@ -63,7 +63,7 @@ id="telefono" class="form-control" maxlength="11" required>
- +
@@ -140,7 +140,6 @@ 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) { @@ -168,11 +167,6 @@ 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' @@ -196,12 +190,7 @@ 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' }); + Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El código caat es obligatorio.', confirmButtonColor: '#dc3545' }); document.getElementById('caat').focus(); return; } diff --git a/views/transportistas/lista.php b/views/transportistas/lista.php index e3662e0..d2ed633 100644 --- a/views/transportistas/lista.php +++ b/views/transportistas/lista.php @@ -67,29 +67,30 @@ $('#transportistas-table').DataTable({ serverSide: true, processing: true, + searchDelay: 500, // espera 500 ms antes de lanzar la búsqueda + deferRender: true, // renderiza las filas sólo cuando tiene los datos ajax: { url: '/IMPORTADORES/transportistas/ajax_lista', type: 'GET' }, columns: [ - { data: 0 }, - { data: 1 }, - { data: 2 }, - { data: 3 }, - { data: 4 }, - { data: 5 }, + { data: 0 }, // ID + { data: 1 }, // Código + { data: 2 }, // Nombre + { data: 3 }, // RFC + { data: 4 }, // Ciudad + { data: 5 }, // Fecha { data: null, orderable: false, searchable: false, - render: function(row) { - const id = row[0]; - return ` + render: function(data, type, row) { + const id = row[0]; + return ` ✏️ - `; + `; } - } ], language: {