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="= htmlspecialchars($chofer['numero_licencia']) ?>" 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 @@
= $c['id_chofer'] ?> |
= htmlspecialchars($c['nombre_completo']) ?> |
= htmlspecialchars($c['numero_licencia']) ?> |
+
= htmlspecialchars($c['numero_gafete']) ?> |
= htmlspecialchars($c['telefono']) ?> |
= htmlspecialchars($c['email']) ?> |
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 @@
@@ -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 @@
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 @@
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 @@
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 @@
+
+
+
+
+
+
+
+ • Número económico del vehículo.
+
+
+
+
+
+
+
+
+
+
+
+ • Si el medio de transporte es vehículo terrestre, se anotarán las placas de circulación del mismo.
+ • Si el medio de transporte es ferrocarril, se anotará el número de furgón o plataforma.
+ • Si el medio de transporte es marítimo, se anotará el nombre de la embarcación.
+
+
+
+
+
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 @@
-
+
-
+
+
+
+
+
+
+
+ • Número económico del vehículo.
+
+
+
+
+
+
+
+
+
+
+
+ • Si el medio de transporte es vehículo terrestre, se anotarán las placas de circulación del mismo.
+ • Si el medio de transporte es ferrocarril, se anotará el número de furgón o plataforma.
+ • Si el medio de transporte es marítimo, se anotará el nombre de la embarcación.
+
+
+
+
+
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: {
|