CRUD gestión de locaciones
This commit is contained in:
2
.env
2
.env
@@ -1,4 +1,4 @@
|
||||
DB_HOST=localhost
|
||||
DB_HOST=DESKTOP-22T88B6
|
||||
DB_DATABASE=Importaciones_HC
|
||||
DB_USERNAME=sa
|
||||
DB_PASSWORD=Soluciones01
|
||||
|
||||
@@ -20,13 +20,9 @@ function dashboard()
|
||||
exit;
|
||||
}
|
||||
|
||||
// Aquí puedes conectar a la BD si vas a mostrar métricas
|
||||
// Ejemplo:
|
||||
// $conn = getConnection();
|
||||
// $sql = "SELECT COUNT(*) FROM solicitudes_importadores WHERE request_status = 'pending'";
|
||||
// ...
|
||||
$nombreAgente = $_SESSION['usuario_nombre'];
|
||||
|
||||
include __DIR__ . '/../../views/agentes/dashboard.php';
|
||||
include __DIR__ . '/../../views/agentes/dashboard_agentes.php';
|
||||
}
|
||||
|
||||
function importadores_activos()
|
||||
@@ -271,3 +267,515 @@ function bitacora()
|
||||
|
||||
include __DIR__ . '/../../views/agentes/bitacora.php';
|
||||
}
|
||||
|
||||
function lista()
|
||||
{
|
||||
$conn = getConnection();
|
||||
$locaciones = [];
|
||||
|
||||
// CONSULTA CORREGIDA - Agregamos abreviatura e iso2
|
||||
$sql = "
|
||||
SELECT
|
||||
p.id_pais, p.nombre AS nombre_pais, p.iso2, p.iso3,
|
||||
e.id_estado, e.nombre AS nombre_estado, e.abreviatura,
|
||||
c.id_ciudad, c.nombre AS nombre_ciudad
|
||||
FROM paises p
|
||||
LEFT JOIN estados e ON p.id_pais = e.pais_id
|
||||
LEFT JOIN ciudades c ON e.id_estado = c.estado_id
|
||||
ORDER BY p.id_pais, e.id_estado, c.id_ciudad
|
||||
";
|
||||
|
||||
$stmt = sqlsrv_query($conn, $sql);
|
||||
|
||||
if (!$stmt) {
|
||||
$errors = sqlsrv_errors();
|
||||
error_log("Error en consulta lista(): " . print_r($errors, true));
|
||||
die("Error en la consulta");
|
||||
}
|
||||
|
||||
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
$locaciones[] = $row;
|
||||
}
|
||||
|
||||
// Obtener países para los selects del modal
|
||||
$sql_paises_select = "SELECT id_pais, nombre FROM paises ORDER BY nombre";
|
||||
$stmt_paises_select = sqlsrv_query($conn, $sql_paises_select);
|
||||
$paises = [];
|
||||
while ($row = sqlsrv_fetch_array($stmt_paises_select, SQLSRV_FETCH_ASSOC)) {
|
||||
$paises[] = $row;
|
||||
}
|
||||
|
||||
include __DIR__ . '/../../views/locaciones/gestion_locaciones.php';
|
||||
}
|
||||
|
||||
function alta()
|
||||
{
|
||||
$conn = getConnection();
|
||||
// 1) Cargar países
|
||||
$sql = "SELECT id_pais, nombre FROM paises ORDER BY nombre";
|
||||
$stmt = sqlsrv_query($conn, $sql);
|
||||
$paises = [];
|
||||
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
$paises[] = $row;
|
||||
}
|
||||
|
||||
include __DIR__ . '/../../views/locaciones/alta_locaciones.php';
|
||||
}
|
||||
|
||||
// AJAX: devuelve los estados de un país dado
|
||||
function estados() {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
$pais = $_GET['pais'] ?? '';
|
||||
$conn = getConnection();
|
||||
$sql = "SELECT id_estado, nombre FROM estados WHERE pais_id = ? ORDER BY nombre";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$pais]);
|
||||
$out = [];
|
||||
while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
$out[] = $r;
|
||||
}
|
||||
echo json_encode($out);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Guardar nuevo estado
|
||||
function guardarEstado() {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
try {
|
||||
// Debug: Verificar qué se está recibiendo
|
||||
error_log("POST data: " . print_r($_POST, true));
|
||||
|
||||
$pais_id = $_POST['pais'] ?? '';
|
||||
$entidad = trim($_POST['entidad'] ?? '');
|
||||
|
||||
// Debug: Verificar valores específicos
|
||||
error_log("pais_id: '$pais_id', entidad: '$entidad'");
|
||||
|
||||
// Validaciones más específicas
|
||||
if (empty($pais_id)) {
|
||||
echo json_encode(['success' => false, 'message' => 'Debe seleccionar un país']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (empty($entidad)) {
|
||||
echo json_encode(['success' => false, 'message' => 'Debe ingresar el nombre del estado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$conn = getConnection();
|
||||
|
||||
// Verificar que el país existe
|
||||
$sql_pais = "SELECT COUNT(*) as count FROM paises WHERE id_pais = ?";
|
||||
$stmt_pais = sqlsrv_query($conn, $sql_pais, [$pais_id]);
|
||||
$pais_exists = sqlsrv_fetch_array($stmt_pais, SQLSRV_FETCH_ASSOC);
|
||||
|
||||
if ($pais_exists['count'] == 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'El país seleccionado no existe']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Verificar si ya existe el estado en ese país
|
||||
$sql_check = "SELECT COUNT(*) as count FROM estados WHERE pais_id = ? AND nombre = ?";
|
||||
$stmt_check = sqlsrv_query($conn, $sql_check, [$pais_id, $entidad]);
|
||||
$exists = sqlsrv_fetch_array($stmt_check, SQLSRV_FETCH_ASSOC);
|
||||
|
||||
if ($exists['count'] > 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'Este estado ya existe en el país seleccionado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Insertar nuevo estado
|
||||
// Primero obtenemos el siguiente ID disponible
|
||||
$sql_max = "SELECT ISNULL(MAX(id_estado), 0) + 1 as next_id FROM estados";
|
||||
$stmt_max = sqlsrv_query($conn, $sql_max);
|
||||
$next_id_row = sqlsrv_fetch_array($stmt_max, SQLSRV_FETCH_ASSOC);
|
||||
$next_id = $next_id_row['next_id'];
|
||||
|
||||
$sql = "INSERT INTO estados (id_estado, pais_id, nombre) VALUES (?, ?, ?)";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$next_id, $pais_id, $entidad]);
|
||||
|
||||
if ($stmt) {
|
||||
echo json_encode(['success' => true, 'message' => 'Estado registrado exitosamente']);
|
||||
} else {
|
||||
// Obtener el error específico de SQL Server
|
||||
$errors = sqlsrv_errors();
|
||||
$errorMessage = 'Error al registrar el estado';
|
||||
if ($errors) {
|
||||
$errorMessage .= ': ' . $errors[0]['message'];
|
||||
}
|
||||
error_log("Error SQL: " . print_r($errors, true));
|
||||
echo json_encode(['success' => false, 'message' => $errorMessage]);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Error: ' . $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// Guardar nueva ciudad
|
||||
function guardarCiudad() {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
try {
|
||||
$estado_id = $_POST['entidad'] ?? '';
|
||||
$ciudad = trim($_POST['ciudad'] ?? '');
|
||||
|
||||
// Validaciones
|
||||
if (empty($estado_id) || empty($ciudad)) {
|
||||
echo json_encode(['success' => false, 'message' => 'Todos los campos son obligatorios']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$conn = getConnection();
|
||||
|
||||
// Verificar si ya existe la ciudad en ese estado
|
||||
$sql_check = "SELECT COUNT(*) as count FROM ciudades WHERE estado_id = ? AND nombre = ?";
|
||||
$stmt_check = sqlsrv_query($conn, $sql_check, [$estado_id, $ciudad]);
|
||||
$exists = sqlsrv_fetch_array($stmt_check, SQLSRV_FETCH_ASSOC);
|
||||
|
||||
if ($exists['count'] > 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'Esta ciudad ya existe en el estado seleccionado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Insertar nueva ciudad
|
||||
$sql = "INSERT INTO ciudades (estado_id, nombre) VALUES (?, ?)";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$estado_id, $ciudad]);
|
||||
|
||||
if ($stmt) {
|
||||
echo json_encode(['success' => true, 'message' => 'Ciudad registrada exitosamente']);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Error al registrar la ciudad']);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Error: ' . $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// Función para obtener país por estado (necesaria para el modal de edición de ciudades)
|
||||
function obtenerPaisPorEstado() {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
try {
|
||||
$estado_id = $_GET['estado'] ?? '';
|
||||
|
||||
if (empty($estado_id)) {
|
||||
echo json_encode(['success' => false, 'message' => 'ID de estado requerido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$conn = getConnection();
|
||||
$sql = "SELECT pais_id FROM estados WHERE id_estado = ?";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$estado_id]);
|
||||
|
||||
if ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
echo json_encode(['success' => true, 'pais_id' => $row['pais_id']]);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Estado no encontrado']);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Error: ' . $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// FUNCIÓN DISPATCHER PARA EDICIÓN
|
||||
function editar() {
|
||||
$tipo = $_POST['tipo'] ?? '';
|
||||
|
||||
// Log para debugging
|
||||
error_log("Función editar llamada con tipo: " . $tipo);
|
||||
error_log("POST data: " . print_r($_POST, true));
|
||||
|
||||
switch ($tipo) {
|
||||
case 'pais':
|
||||
actualizarPais();
|
||||
break;
|
||||
case 'estado':
|
||||
actualizarEstado();
|
||||
break;
|
||||
case 'ciudad':
|
||||
actualizarCiudad();
|
||||
break;
|
||||
default:
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode(['success' => false, 'message' => 'Tipo de elemento no válido: ' . $tipo]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// FUNCIÓN ESPECÍFICA PARA ACTUALIZAR PAÍSES
|
||||
function actualizarPais() {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
try {
|
||||
$id = $_POST['id'] ?? '';
|
||||
$nombre = trim($_POST['nombre'] ?? '');
|
||||
$iso2 = trim($_POST['iso2'] ?? '');
|
||||
$iso3 = trim($_POST['iso3'] ?? '');
|
||||
|
||||
// Validaciones
|
||||
if (empty($id) || empty($nombre)) {
|
||||
echo json_encode(['success' => false, 'message' => 'ID y nombre del país son obligatorios']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$conn = getConnection();
|
||||
if (!$conn) {
|
||||
echo json_encode(['success' => false, 'message' => 'Error de conexión a la base de datos']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Verificar duplicados
|
||||
$sql_check = "SELECT COUNT(*) as count FROM paises WHERE nombre = ? AND id_pais != ?";
|
||||
$stmt_check = sqlsrv_query($conn, $sql_check, [$nombre, $id]);
|
||||
|
||||
if (!$stmt_check) {
|
||||
echo json_encode(['success' => false, 'message' => 'Error al verificar duplicados']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$exists = sqlsrv_fetch_array($stmt_check, SQLSRV_FETCH_ASSOC);
|
||||
if ($exists['count'] > 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'Ya existe otro país con este nombre']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Actualizar
|
||||
$iso2_val = empty($iso2) ? null : $iso2;
|
||||
$iso3_val = empty($iso3) ? null : $iso3;
|
||||
|
||||
$sql = "UPDATE paises SET nombre = ?, iso2 = ?, iso3 = ? WHERE id_pais = ?";
|
||||
$params = [$nombre, $iso2_val, $iso3_val, $id];
|
||||
|
||||
$stmt = sqlsrv_query($conn, $sql, $params);
|
||||
|
||||
if ($stmt && sqlsrv_rows_affected($stmt) > 0) {
|
||||
echo json_encode(['success' => true, 'message' => 'País actualizado exitosamente']);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'No se pudo actualizar el país']);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Error: ' . $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// FUNCIÓN ESPECÍFICA PARA ACTUALIZAR ESTADOS
|
||||
function actualizarEstado() {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
try {
|
||||
$id = $_POST['id'] ?? '';
|
||||
$nombre = trim($_POST['nombre'] ?? '');
|
||||
$abreviatura = trim($_POST['abreviatura'] ?? '');
|
||||
$pais_id = $_POST['pais_id'] ?? '';
|
||||
|
||||
// Validaciones
|
||||
if (empty($id) || empty($nombre) || empty($pais_id)) {
|
||||
echo json_encode(['success' => false, 'message' => 'ID, nombre del estado y país son obligatorios']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$conn = getConnection();
|
||||
if (!$conn) {
|
||||
echo json_encode(['success' => false, 'message' => 'Error de conexión a la base de datos']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Verificar duplicados
|
||||
$sql_check = "SELECT COUNT(*) as count FROM estados WHERE nombre = ? AND pais_id = ? AND id_estado != ?";
|
||||
$stmt_check = sqlsrv_query($conn, $sql_check, [$nombre, $pais_id, $id]);
|
||||
|
||||
if (!$stmt_check) {
|
||||
echo json_encode(['success' => false, 'message' => 'Error al verificar duplicados']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$exists = sqlsrv_fetch_array($stmt_check, SQLSRV_FETCH_ASSOC);
|
||||
if ($exists['count'] > 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'Ya existe otro estado con este nombre en el país seleccionado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Actualizar
|
||||
$abreviatura_val = empty($abreviatura) ? null : $abreviatura;
|
||||
|
||||
$sql = "UPDATE estados SET nombre = ?, abreviatura = ?, pais_id = ? WHERE id_estado = ?";
|
||||
$params = [$nombre, $abreviatura_val, $pais_id, $id];
|
||||
|
||||
$stmt = sqlsrv_query($conn, $sql, $params);
|
||||
|
||||
if ($stmt && sqlsrv_rows_affected($stmt) > 0) {
|
||||
echo json_encode(['success' => true, 'message' => 'Estado actualizado exitosamente']);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'No se pudo actualizar el estado']);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Error: ' . $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// FUNCIÓN ESPECÍFICA PARA ACTUALIZAR CIUDADES
|
||||
function actualizarCiudad() {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
try {
|
||||
$id = $_POST['id'] ?? '';
|
||||
$nombre = trim($_POST['nombre'] ?? '');
|
||||
$estado_id = $_POST['estado_id'] ?? '';
|
||||
|
||||
// Validaciones
|
||||
if (empty($id) || empty($nombre) || empty($estado_id)) {
|
||||
echo json_encode(['success' => false, 'message' => 'ID, nombre de la ciudad y estado son obligatorios']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$conn = getConnection();
|
||||
if (!$conn) {
|
||||
echo json_encode(['success' => false, 'message' => 'Error de conexión a la base de datos']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Verificar duplicados
|
||||
$sql_check = "SELECT COUNT(*) as count FROM ciudades WHERE nombre = ? AND estado_id = ? AND id_ciudad != ?";
|
||||
$stmt_check = sqlsrv_query($conn, $sql_check, [$nombre, $estado_id, $id]);
|
||||
|
||||
if (!$stmt_check) {
|
||||
echo json_encode(['success' => false, 'message' => 'Error al verificar duplicados']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$exists = sqlsrv_fetch_array($stmt_check, SQLSRV_FETCH_ASSOC);
|
||||
if ($exists['count'] > 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'Ya existe otra ciudad con este nombre en el estado seleccionado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Actualizar
|
||||
$sql = "UPDATE ciudades SET nombre = ?, estado_id = ? WHERE id_ciudad = ?";
|
||||
$params = [$nombre, $estado_id, $id];
|
||||
|
||||
$stmt = sqlsrv_query($conn, $sql, $params);
|
||||
|
||||
if ($stmt && sqlsrv_rows_affected($stmt) > 0) {
|
||||
echo json_encode(['success' => true, 'message' => 'Ciudad actualizada exitosamente']);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'No se pudo actualizar la ciudad']);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Error: ' . $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// Función para eliminar locaciones (países, estados, ciudades)
|
||||
function eliminar() {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
try {
|
||||
// Obtener datos del JSON enviado
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$tipo = $input['tipo'] ?? '';
|
||||
$id = $input['id'] ?? '';
|
||||
|
||||
if (empty($tipo) || empty($id)) {
|
||||
echo json_encode(['success' => false, 'message' => 'Datos incompletos']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$conn = getConnection();
|
||||
|
||||
switch ($tipo) {
|
||||
case 'pais':
|
||||
// Verificar si el país tiene estados asociados
|
||||
$sql_check = "SELECT COUNT(*) as count FROM estados WHERE pais_id = ?";
|
||||
$stmt_check = sqlsrv_query($conn, $sql_check, [$id]);
|
||||
$has_estados = sqlsrv_fetch_array($stmt_check, SQLSRV_FETCH_ASSOC);
|
||||
|
||||
if ($has_estados['count'] > 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'No se puede eliminar el país porque tiene estados asociados']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Eliminar país
|
||||
$sql = "DELETE FROM paises WHERE id_pais = ?";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$id]);
|
||||
|
||||
if ($stmt) {
|
||||
echo json_encode(['success' => true, 'message' => 'País eliminado exitosamente']);
|
||||
} else {
|
||||
$errors = sqlsrv_errors();
|
||||
$errorMessage = 'Error al eliminar el país';
|
||||
if ($errors) {
|
||||
$errorMessage .= ': ' . $errors[0]['message'];
|
||||
}
|
||||
echo json_encode(['success' => false, 'message' => $errorMessage]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'estado':
|
||||
// Verificar si el estado tiene ciudades asociadas
|
||||
$sql_check = "SELECT COUNT(*) as count FROM ciudades WHERE estado_id = ?";
|
||||
$stmt_check = sqlsrv_query($conn, $sql_check, [$id]);
|
||||
$has_ciudades = sqlsrv_fetch_array($stmt_check, SQLSRV_FETCH_ASSOC);
|
||||
|
||||
if ($has_ciudades['count'] > 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'No se puede eliminar el estado porque tiene ciudades asociadas']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Eliminar estado
|
||||
$sql = "DELETE FROM estados WHERE id_estado = ?";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$id]);
|
||||
|
||||
if ($stmt) {
|
||||
echo json_encode(['success' => true, 'message' => 'Estado eliminado exitosamente']);
|
||||
} else {
|
||||
$errors = sqlsrv_errors();
|
||||
$errorMessage = 'Error al eliminar el estado';
|
||||
if ($errors) {
|
||||
$errorMessage .= ': ' . $errors[0]['message'];
|
||||
}
|
||||
echo json_encode(['success' => false, 'message' => $errorMessage]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'ciudad':
|
||||
// Verificar si la ciudad está siendo utilizada en alguna relación
|
||||
// (aquí puedes agregar más verificaciones según tu modelo de datos)
|
||||
|
||||
// Eliminar ciudad
|
||||
$sql = "DELETE FROM ciudades WHERE id_ciudad = ?";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$id]);
|
||||
|
||||
if ($stmt) {
|
||||
echo json_encode(['success' => true, 'message' => 'Ciudad eliminada exitosamente']);
|
||||
} else {
|
||||
$errors = sqlsrv_errors();
|
||||
$errorMessage = 'Error al eliminar la ciudad';
|
||||
if ($errors) {
|
||||
$errorMessage .= ': ' . $errors[0]['message'];
|
||||
}
|
||||
echo json_encode(['success' => false, 'message' => $errorMessage]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
echo json_encode(['success' => false, 'message' => 'Tipo de elemento no válido']);
|
||||
break;
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Error: ' . $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
@@ -14,3 +14,36 @@ function dashboard()
|
||||
|
||||
include __DIR__ . '/../../views/importadores/dashboard_importador.php';
|
||||
}
|
||||
|
||||
function lista()
|
||||
{
|
||||
$conn = getConnection();
|
||||
$locaciones = [];
|
||||
|
||||
// Consulta conjunta para evitar múltiples queries anidadas
|
||||
$sql = "
|
||||
SELECT
|
||||
p.id_pais, p.nombre AS nombre_pais, p.iso2, p.iso3,
|
||||
e.id_estado, e.nombre AS nombre_estado, e.abreviatura,
|
||||
c.id_ciudad, c.nombre AS nombre_ciudad
|
||||
FROM paises p
|
||||
LEFT JOIN estados e ON p.id_pais = e.pais_id
|
||||
LEFT JOIN ciudades c ON e.id_estado = c.estado_id
|
||||
ORDER BY p.id_pais, e.id_estado, c.id_ciudad
|
||||
";
|
||||
|
||||
$stmt = sqlsrv_query($conn, $sql);
|
||||
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
$locaciones[] = $row;
|
||||
}
|
||||
|
||||
// Obtener países para los selects del modal
|
||||
$sql_paises_select = "SELECT id_pais, nombre FROM paises ORDER BY nombre";
|
||||
$stmt_paises_select = sqlsrv_query($conn, $sql_paises_select);
|
||||
$paises = [];
|
||||
while ($row = sqlsrv_fetch_array($stmt_paises_select, SQLSRV_FETCH_ASSOC)) {
|
||||
$paises[] = $row;
|
||||
}
|
||||
|
||||
include __DIR__ . '/../../views/locaciones/lista.php';
|
||||
}
|
||||
@@ -156,7 +156,7 @@ function redirectByRole($tipoUsuario)
|
||||
header('Location: /IMPORTADORES/importadores/dashboard');
|
||||
break;
|
||||
case 'agente_aduanal':
|
||||
header('Location: /IMPORTADORES/AGENTES/dashboard');
|
||||
header('Location: /IMPORTADORES/agentes/dashboard');
|
||||
break;
|
||||
default:
|
||||
header('Location: /IMPORTADORES/importadores/dashboard');
|
||||
|
||||
@@ -78,8 +78,6 @@ function lista()
|
||||
}
|
||||
|
||||
/** GET /IMPORTADORES/proveedores/ajax_lista
|
||||
* Devuelve JSON para DataTables
|
||||
* GET /IMPORTADORES/proveedores/ajax_lista
|
||||
* Devuelve JSON para DataTables (siempre HTTP 200) **/
|
||||
function ajax_lista()
|
||||
{
|
||||
|
||||
@@ -754,12 +754,27 @@ function ajax_lista()
|
||||
if ($status === 200 && ($json = json_decode($resp, true)) && is_array($json)) {
|
||||
foreach ($json as $p) {
|
||||
$clave = htmlspecialchars($p['Clave'] ?? '', ENT_QUOTES);
|
||||
|
||||
// Construir dirección
|
||||
$direccion = trim(implode(', ', array_filter([
|
||||
$p['Calles'] ?? '',
|
||||
'Num. Ext: ' . ($p['NumExt'] ?? ''),
|
||||
'Num. Int: ' . ($p['NumInt'] ?? ''),
|
||||
$p['Colonia'] ?? '',
|
||||
$p['Municipio'] ?? '',
|
||||
$p['Ciudad'] ?? '',
|
||||
'C.P. ' . ($p['CodigoPostal'] ?? ''),
|
||||
$p['EntidadFederativa'] ?? '',
|
||||
$p['Pais'] ?? ''
|
||||
])));
|
||||
|
||||
$dataList[] = [
|
||||
$clave,
|
||||
htmlspecialchars($p['Nombre'] ?? '', ENT_QUOTES),
|
||||
htmlspecialchars($p['RFC'] ?? '', ENT_QUOTES),
|
||||
htmlspecialchars($p['Ciudad'] ?? '', ENT_QUOTES),
|
||||
htmlspecialchars($p['Telefono']?? '', ENT_QUOTES),
|
||||
htmlspecialchars($direccion ?? '', ENT_QUOTES),
|
||||
// Acciones
|
||||
"<a href=\"/IMPORTADORES/proveedores/editar?clave=" . rawurlencode($clave) . "\" class=\"btn btn-sm btn-primary\">✏️</a>
|
||||
<button class=\"btn btn-sm btn-danger\" onclick=\"confirmDelete('{$clave}')\">🗑️</button>"
|
||||
@@ -990,7 +1005,6 @@ function update_status() {
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
// Función para generar el PDF
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Options;
|
||||
@@ -1049,6 +1063,12 @@ function pdf() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2.5. Obtener información del proveedor desde API
|
||||
$proveedor_info = null;
|
||||
if (!empty($solicitud['proveedor_clave'])) {
|
||||
$proveedor_info = obtenerProveedorPorClave($solicitud['proveedor_clave']);
|
||||
}
|
||||
|
||||
// 3. Obtener partidas de la solicitud
|
||||
$sql = "
|
||||
SELECT
|
||||
@@ -1074,15 +1094,18 @@ function pdf() {
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
// 4. Configuración del sistema
|
||||
global $config;
|
||||
$nombre_sistema = $config['nombre_plataforma'] ?? 'Sistema Integral para Importadores de Hidrocarburos';
|
||||
$siglas = $config['siglas'] ?? 'SIIH';
|
||||
$logo_path = realpath(__DIR__ . '/../../public/assets/img/logo_siih.png');
|
||||
$logo_url = 'file://' . $logo_path;
|
||||
$config = "SELECT * FROM configuracion_sistema";
|
||||
$stmt = sqlsrv_query($conn, $config);
|
||||
|
||||
if ($stmt === false) {
|
||||
throw new Exception("Error en la consulta: " . print_r(sqlsrv_errors(), true));
|
||||
}
|
||||
|
||||
$configuracion = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
|
||||
|
||||
// 5. Generar HTML del PDF
|
||||
$html = generarHTMLPDF($solicitud, $partidas, $nombre_sistema, $siglas, $logo_url);
|
||||
$html = generarHTMLPDF($solicitud, $partidas, $configuracion);
|
||||
|
||||
// 6. Generar PDF usando DomPDF con Composer
|
||||
require_once __DIR__ . '/../../vendor/autoload.php'; // Ajusta ruta si es necesario
|
||||
@@ -1108,12 +1131,59 @@ function pdf() {
|
||||
}
|
||||
}
|
||||
|
||||
function generarHTMLPDF($solicitud, $partidas, $nombre_sistema, $siglas, $logo_url) {
|
||||
// Formatear fecha
|
||||
$fecha_expedicion = date('d/m/Y');
|
||||
$fecha_factura = $solicitud['fecha_factura']->format('d/m/Y');
|
||||
$fecha_vencimiento = (clone $solicitud['fecha_factura'])->modify('+30 days')->format('d/m/Y');
|
||||
// NUEVA FUNCIÓN: Obtener información del proveedor por clave
|
||||
function obtenerProveedorPorClave($clave) {
|
||||
// Obtener token de la API
|
||||
$token = getApiToken();
|
||||
if (!$token) {
|
||||
error_log('[obtenerProveedorPorClave] Sin token válido');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Construir URL de la API
|
||||
$apiBase = rtrim($_ENV['API_URL'] ?? '', '/');
|
||||
$url = $apiBase . '/proveedores';
|
||||
|
||||
// Ejecutar cURL para obtener todos los proveedores
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_HTTPHEADER => ["Authorization: $token", 'Accept: application/json'],
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 10,
|
||||
]);
|
||||
|
||||
$resp = curl_exec($ch);
|
||||
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($status !== 200) {
|
||||
error_log("[obtenerProveedorPorClave] Error HTTP $status al obtener proveedores");
|
||||
return null;
|
||||
}
|
||||
|
||||
$proveedores = json_decode($resp, true);
|
||||
if (!is_array($proveedores)) {
|
||||
error_log('[obtenerProveedorPorClave] Respuesta de API no es un array válido');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Buscar el proveedor por clave
|
||||
foreach ($proveedores as $proveedor) {
|
||||
// Probar tanto 'Clave' como 'CLAVE' por si acaso
|
||||
$proveedor_clave = $proveedor['CLAVE'] ?? $proveedor['Clave'] ?? null;
|
||||
if ($proveedor_clave === $clave) {
|
||||
return $proveedor;
|
||||
}
|
||||
}
|
||||
|
||||
error_log("[obtenerProveedorPorClave] Proveedor con clave '$clave' no encontrado");
|
||||
return null;
|
||||
}
|
||||
|
||||
function generarHTMLPDF($solicitud, $partidas, $configuracion) {
|
||||
// Formatear fecha
|
||||
$fecha_expedicion = $solicitud['fecha_factura']->format('d/m/Y');
|
||||
$fecha_vencimiento = $solicitud['fecha_factura']->modify('+30 days')->format('d/m/Y');
|
||||
|
||||
// Construir dirección del importador
|
||||
$direccion_completa = trim(
|
||||
@@ -1126,6 +1196,37 @@ function generarHTMLPDF($solicitud, $partidas, $nombre_sistema, $siglas, $logo_u
|
||||
($solicitud['codigo_postal'] ?? '')
|
||||
);
|
||||
|
||||
// Construir información del proveedor
|
||||
$proveedor_nombre = 'Proveedor no disponible';
|
||||
$proveedor_rfc = 'RFC no disponible';
|
||||
$proveedor_direccion = 'Dirección no disponible';
|
||||
$proveedor_telefono = 'Teléfono no disponible';
|
||||
|
||||
// Construir información del proveedor
|
||||
if ($proveedor_info) {
|
||||
// Usar nombres de campos en mayúsculas según la estructura de la tabla
|
||||
$proveedor_nombre = $proveedor_info['NOMBRE'] ?? $proveedor_info['Nombre'] ?? 'Nombre no disponible';
|
||||
$proveedor_rfc = $proveedor_info['RFC'] ?? $proveedor_info['IDENTFISCAL'] ?? 'RFC no disponible';
|
||||
$proveedor_telefono = trim($proveedor_info['TELEFONO'] ?? $proveedor_info['Telefono'] ?? 'Teléfono no disponible');
|
||||
|
||||
// Construir dirección del proveedor usando los campos correctos
|
||||
$direccion_partes = array_filter([
|
||||
$proveedor_info['CALLES'] ?? $proveedor_info['Calles'] ?? '',
|
||||
($proveedor_info['NUMEXT'] ?? $proveedor_info['NumExt'] ?? '') ? 'Num. Ext: ' . ($proveedor_info['NUMEXT'] ?? $proveedor_info['NumExt']) : '',
|
||||
($proveedor_info['NUMINT'] ?? $proveedor_info['NumInt'] ?? '') ? 'Num. Int: ' . ($proveedor_info['NUMINT'] ?? $proveedor_info['NumInt']) : '',
|
||||
$proveedor_info['COLONIA'] ?? $proveedor_info['Colonia'] ?? '',
|
||||
$proveedor_info['MUNICIPIO'] ?? $proveedor_info['Municipio'] ?? '',
|
||||
$proveedor_info['CIUDAD'] ?? $proveedor_info['Ciudad'] ?? '',
|
||||
($proveedor_info['CODIGOPOSTAL'] ?? $proveedor_info['CodigoPostal'] ?? '') ? 'C.P. ' . ($proveedor_info['CODIGOPOSTAL'] ?? $proveedor_info['CodigoPostal']) : '',
|
||||
$proveedor_info['ENTIDADFEDERATIVA'] ?? $proveedor_info['EntidadFederativa'] ?? '',
|
||||
$proveedor_info['PAIS'] ?? $proveedor_info['Pais'] ?? ''
|
||||
]);
|
||||
|
||||
if (!empty($direccion_partes)) {
|
||||
$proveedor_direccion = implode(', ', $direccion_partes);
|
||||
}
|
||||
}
|
||||
|
||||
// Calcular el total sumando todas las partidas
|
||||
$total = 0;
|
||||
foreach ($partidas as $partida) {
|
||||
@@ -1189,16 +1290,37 @@ function generarHTMLPDF($solicitud, $partidas, $nombre_sistema, $siglas, $logo_u
|
||||
return trim($resultado);
|
||||
}
|
||||
|
||||
// Obtener la moneda de la solicitud o usar MXN por defecto
|
||||
$moneda_codigo = $solicitud['tipo_moneda'] ?? 'MXN';
|
||||
|
||||
// Configuración de monedas
|
||||
$monedas_config = [
|
||||
'MXN' => ['nombre' => 'PESOS', 'sufijo' => 'M.N.', 'centavos' => 'CENTAVOS'],
|
||||
'USD' => ['nombre' => 'DÓLARES', 'sufijo' => 'USD', 'centavos' => 'CENTAVOS'],
|
||||
'EUR' => ['nombre' => 'EUROS', 'sufijo' => 'EUR', 'centavos' => 'CÉNTIMOS'],
|
||||
'CNY' => ['nombre' => 'YUANES', 'sufijo' => 'CNY', 'centavos' => 'JIAO'],
|
||||
'GBP' => ['nombre' => 'LIBRAS', 'sufijo' => 'GBP', 'centavos' => 'PENIQUES'],
|
||||
'JPY' => ['nombre' => 'YENES', 'sufijo' => 'JPY', 'centavos' => 'SEN']
|
||||
];
|
||||
|
||||
$config_moneda = $monedas_config[$moneda_codigo] ?? $monedas_config['MXN'];
|
||||
|
||||
// Convertir total a texto
|
||||
$partes = explode('.', number_format($total, 2, '.', ''));
|
||||
$pesos = (int)$partes[0];
|
||||
$centavos = (int)$partes[1];
|
||||
$enteros = (int)$partes[0];
|
||||
$decimales = (int)$partes[1];
|
||||
|
||||
$total_texto = strtoupper(numeroATexto($pesos)) . ' PESOS';
|
||||
if ($centavos > 0) {
|
||||
$total_texto .= ' CON ' . str_pad($centavos, 2, '0', STR_PAD_LEFT) . '/100 M.N.';
|
||||
$total_texto = strtoupper(numeroATexto($enteros)) . ' ' . $config_moneda['nombre'];
|
||||
|
||||
// Para JPY no se usan decimales tradicionalmente
|
||||
if ($moneda_codigo === 'JPY') {
|
||||
$total_texto .= ' ' . $config_moneda['sufijo'];
|
||||
} else {
|
||||
$total_texto .= ' 00/100 M.N.';
|
||||
if ($decimales > 0) {
|
||||
$total_texto .= ' CON ' . str_pad($decimales, 2, '0', STR_PAD_LEFT) . '/100 ' . $config_moneda['sufijo'];
|
||||
} else {
|
||||
$total_texto .= ' 00/100 ' . $config_moneda['sufijo'];
|
||||
}
|
||||
}
|
||||
|
||||
$html = '
|
||||
@@ -1209,30 +1331,38 @@ function generarHTMLPDF($solicitud, $partidas, $nombre_sistema, $siglas, $logo_u
|
||||
<title>Solicitud de Importación</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; font-size: 9px; margin: 0; padding: 15px; line-height: 1.2; }
|
||||
<!-- Encabezado -->
|
||||
.header { margin-bottom: 10px; border-bottom: 2px solid #000; padding-bottom: 10px; font-size: 10px; }
|
||||
.logo-section { width: 20%; text-align: left; vertical-align: center; }
|
||||
.logo { max-width: 100px; height: auto; }
|
||||
.company-info { width: 60%; text-align: center; vertical-align: top; }
|
||||
/** Encabezado **/
|
||||
.header { padding-bottom: 50px; }
|
||||
.logo-section { width: 20%; text-align: left; }
|
||||
.logo { max-width: 100px; height: auto; vertical-align: center; }
|
||||
.siglas { font-size: 15px; }
|
||||
.company-info { width: 60%; text-align: center; vertical-align: top; font-size: 12px; }
|
||||
.company-name { font-weight: bold; font-size: 25px; margin-bottom: 3px; }
|
||||
.invoice-info { width: 20%; text-align: right; vertical-align: top; font-size: 12px; }
|
||||
<!-- Información del Cliente -->
|
||||
.info-section { margin-bottom: 10px; border-bottom: 2px solid #000; padding-bottom: 10px; }
|
||||
.client-info { width: 75%; border: 0.5px solid #000; margin-right: 10px; }
|
||||
/** Sección de Información **/
|
||||
.info-section { border: 1px solid black; border-collapse: collapse; width: 100%; table-layout: fixed; }
|
||||
.clave-section { border: 0.5px solid black; border-collapse: collapse; width: 100%; table-layout: fixed; padding-bottom: 15px; }
|
||||
/** Información del Proveedor **/
|
||||
.proveedor-info { width: 100%; }
|
||||
.provedor-info table { width: 100%; border-collapse: collapse; table-layout: fixed; }
|
||||
.p-field { width: 100px; background-color: #d0d0d0; font-weight: bold; text-align: center; font-size: 12px; }
|
||||
.field { border-bottom: 0.5px solid #000; padding: 5px; font-size: 12px; }
|
||||
.dates-info { width: 25%; border: 0.5px solid #000; }
|
||||
.section-title { background-color: #d0d0d0; font-weight: bold; padding: 5px; text-align: center; font-size: 12px; border-right: 0.5px solid #000; }
|
||||
<!-- Partidas -->
|
||||
.products-table { border-collapse: collapse; margin-bottom: 15px; border: 1px solid #000; }
|
||||
/** Fechas **/
|
||||
.dates-info { width: 25%; border: 1px solid #000; }
|
||||
.dates-info table { width: 100%; border-collapse: collapse; table-layout: fixed; }
|
||||
.d-field { background-color: #d0d0d0; font-weight: bold; text-align: center; font-size: 12px; }
|
||||
.date { font-weight: bold; text-align: center; font-size: 12px; padding: 7.5px; }
|
||||
/** Partidas **/
|
||||
.products-table { border-collapse: collapse; border: 0.5px solid #000; }
|
||||
.products-table td { border: 0.5px solid #000; padding: 10px; text-align: center; font-size: 10px; }
|
||||
.products-table th { border: 0.5px solid #000; padding: 5px; background-color: #d0d0d0; font-weight: bold; text-align: center; }
|
||||
.text-center { text-align: center; }
|
||||
.text-right { text-align: right; }
|
||||
.font-bold { font-weight: bold; }
|
||||
<!-- Total -->
|
||||
/** Total **/
|
||||
.totals-section { float: right; width: 250px; }
|
||||
.total-row { display: flex; justify-content: space-between; margin-top: 25px; font-size: 12px; }
|
||||
<!-- Nota inferior -->
|
||||
/** Nota inferior **/
|
||||
.footer-info { }
|
||||
.footer-note { font-size: 10px; background-color: #d0d0d0; padding: 5px; border: 0.5px solid #000; }
|
||||
</style>
|
||||
@@ -1242,44 +1372,67 @@ function generarHTMLPDF($solicitud, $partidas, $nombre_sistema, $siglas, $logo_u
|
||||
<table class="header" cellspacing="0" cellpadding="0" width="100%">
|
||||
<tr>
|
||||
<td class="logo-section">
|
||||
<img src="' . $logo_url . '" alt="Logo" class="logo"><br>
|
||||
<img src="' . htmlspecialchars($configuracion['logo_url'] ?? 'assets/img/logo_siih.png') . '" alt="Logo" class="logo"><br>
|
||||
<div class="siglas"><strong>' . htmlspecialchars($configuracion['siglas'] ?? 'SIIH') . '</strong></div>
|
||||
</td>
|
||||
<td class="company-info">
|
||||
<div class="company-name">' . htmlspecialchars($nombre_sistema) . '</div>
|
||||
<div class="company-name">' . htmlspecialchars($solicitud['importador_nombre']) . '</div>
|
||||
<div>' . htmlspecialchars($direccion_completa ?: 'Dirección no disponible') . '</div>
|
||||
<div>RFC: ' . htmlspecialchars($solicitud['importador_rfc'] ?? 'No disponible') . '</div>
|
||||
<div>Tel: ' . htmlspecialchars($solicitud['telefono'] ?? 'No disponible') . '</div>
|
||||
<div>Email: ' . htmlspecialchars($solicitud['correo'] ?? 'No disponible') . '</div>
|
||||
</td>
|
||||
<td class="invoice-info">
|
||||
<div><strong>' . htmlspecialchars($configuracion['nombre_plataforma'] ?? 'Sistema Integral para Importadores de Hidrocarburos') . '</strong></div><br>
|
||||
<div class="invoice-title">Solicitud de Importación</div>
|
||||
<div><strong>No. ' . htmlspecialchars($solicitud['id_solicitud']) . '</strong></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br><br><br><br>
|
||||
|
||||
<!-- INFORMACIÓN DEL CLIENTE Y FECHAS -->
|
||||
<table class="info-section" cellspacing="0" cellpadding="0" width="100%">
|
||||
<!-- SECCIÓN DE INFORMACIÓN DEL PROVEEDOR Y FECHAS -->
|
||||
<table class="info-section" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td class="client-info">
|
||||
<div class="field"><strong>RAZÓN SOCIAL:</strong> ' . htmlspecialchars($solicitud['importador_nombre'] ?? 'No disponible') . '</div>
|
||||
<div class="field"><strong>DOMICILIO FISCAL:</strong> ' . htmlspecialchars($direccion_completa) . '</div>
|
||||
<div class="field"><strong>RFC:</strong> ' . htmlspecialchars($solicitud['importador_rfc'] ?? 'No disponible') . '</div>
|
||||
<div class="field"><strong>TELÉFONO:</strong> ' . htmlspecialchars($solicitud['telefono'] ?? 'No disponible') . '</div>
|
||||
<div class="field"><strong>RÉGIMEN FISCAL:</strong> 601-General de Ley Personas Morales</div>
|
||||
<!-- PROVEEDOR -->
|
||||
<td>
|
||||
<table class="proveedor-info" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td class="p-field">RAZÓN SOCIAL:</td>
|
||||
<td class="field">' . htmlspecialchars($proveedor_nombre) . '</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class="proveedor-info" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td class="p-field" style="height: 45px;">DIRECCIÓN:</td>
|
||||
<td class="field">' . htmlspecialchars($proveedor_direccion) . '</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class="proveedor-info" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td class="p-field">RFC:</td>
|
||||
<td class="field" style="border-bottom: none;">' . htmlspecialchars($proveedor_rfc) . '</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<!-- FECHAS -->
|
||||
<td class="dates-info">
|
||||
<table width="100%" cellspacing="0" cellpadding="2">
|
||||
<tr><td class="section-title">FECHA DE EXPEDICIÓN</td></tr>
|
||||
<tr><td class="text-center font-bold" style="font-size: 12px; border-bottom: 0.5px solid #000; padding-bottom: 15px; padding-top: 12.5px;">' . $fecha_expedicion . '</td></tr>
|
||||
<tr><td class="section-title" style="padding-top: 10px;">FECHA DE VENCIMIENTO</td></tr>
|
||||
<tr><td class="text-center font-bold" style="font-size: 12px; padding-bottom: 15px; padding-top: 12.5px;">' . $fecha_vencimiento . '</td></tr>
|
||||
<table cellspacing="0" cellpadding="2">
|
||||
<tr><td class="d-field" style="border-left: 0.5px solid black;">FECHA DE EXPEDICIÓN</td></tr>
|
||||
<tr><td class="date" style="border-bottom: 0.5px solid black;">' . $fecha_expedicion . '</td></tr>
|
||||
<tr><td class="d-field" style="border-left: 0.5px solid black;">FECHA DE VENCIMIENTO</td></tr>
|
||||
<tr><td class="date">' . $fecha_vencimiento . '</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br><br><br>
|
||||
<table class="clave-section" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td class="p-field">CLAVE:</td>
|
||||
<td class="field" style="border-bottom: none;">' . htmlspecialchars($solicitud['proveedor_clave'] ?? 'No disponible') . '</td>
|
||||
<td class="p-field">TELÉFONO:</td>
|
||||
<td class="field" style="border-bottom: none;">' . htmlspecialchars($proveedor_telefono) . '</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- TABLA DE PRODUCTOS/PARTIDAS -->
|
||||
<table class="products-table" cellspacing="0" cellpadding="0" width="100%">
|
||||
@@ -1300,12 +1453,13 @@ function generarHTMLPDF($solicitud, $partidas, $nombre_sistema, $siglas, $logo_u
|
||||
$cantidad = (float)($partida['cantidad_comercial'] ?? 0);
|
||||
$valor_partida = (float)($partida['valor_factura'] ?? 0);
|
||||
|
||||
$html .= '<tr>
|
||||
$html .= '
|
||||
<tr>
|
||||
<td>' . htmlspecialchars($partida['descripcion']) . '</td>
|
||||
<td>' . htmlspecialchars($partida['unidad_descripcion'] ?? 'Unidad de servicio (E48)') . '</td>
|
||||
<td>$' . number_format($precio_unitario > 0 ? $precio_unitario : $valor_partida, 2) . '</td>
|
||||
<td>' . $moneda_codigo . ' ' . number_format($precio_unitario > 0 ? $precio_unitario : $valor_partida, 2) . '</td>
|
||||
<td>' . number_format($cantidad > 0 ? $cantidad : 1, 0) . '</td>
|
||||
<td>$' . number_format($valor_partida, 2) . '</td>
|
||||
<td>' . $moneda_codigo . ' ' . number_format($valor_partida, 2) . '</td>
|
||||
</tr>';
|
||||
}
|
||||
|
||||
@@ -1322,7 +1476,7 @@ function generarHTMLPDF($solicitud, $partidas, $nombre_sistema, $siglas, $logo_u
|
||||
<div class="totals-section">
|
||||
<div class="total-row text-right">
|
||||
<span><strong>Total:</strong></span>
|
||||
<span><strong>$' . number_format($total, 2) . '</strong></span>
|
||||
<span><strong>' . $moneda_codigo . ' ' . number_format($total, 2) . '</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ function ciudades() {
|
||||
function lista()
|
||||
{
|
||||
include __DIR__ . '/../../views/transportistas/lista.php';
|
||||
}
|
||||
}
|
||||
|
||||
/** Descarga la plantilla CSV para carga masiva **/
|
||||
function template() {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../app/helpers/crypto.php';
|
||||
include __DIR__ . '/../partials/sidebar_agente.php';
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Dashboard | Agente Aduanal</title>
|
||||
<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>
|
||||
<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: 60px; }
|
||||
.sidebar .nav-link { color: #ccc; padding: 12px 20px; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-left: 220px; padding: 40px 20px; }
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1000; }
|
||||
.card { border-radius: 12px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- 📄 CONTENIDO -->
|
||||
<div class="content">
|
||||
<h4 class="mb-4">Panel principal del agente</h4>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm p-3">
|
||||
<h5 class="text-primary">Importadores activos</h5>
|
||||
<p>Consulta los que ya fueron autorizados.</p>
|
||||
<a href="/IMPORTADORES/AGENTES/activos" class="btn btn-primary btn-sm mt-2">Ver importadores</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm p-3">
|
||||
<h5 class="text-success">Solicitudes pendientes</h5>
|
||||
<p>Valida nuevas solicitudes de registro.</p>
|
||||
<a href="/IMPORTADORES/AGENTES/solicitudes_pendientes" class="btn btn-success btn-sm mt-2">Ver solicitudes</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm p-3">
|
||||
<h5 class="text-warning">Bitácora del sistema</h5>
|
||||
<p>Revisa los accesos y acciones recientes.</p>
|
||||
<a href="/IMPORTADORES/agentes/bitacora" class="btn btn-warning btn-sm mt-2">Ver bitácora</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
88
views/agentes/dashboard_agentes.php
Normal file
88
views/agentes/dashboard_agentes.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../app/helpers/crypto.php';
|
||||
include __DIR__ . '/../partials/sidebar_agente.php';
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Dashboard | Agente Aduanal</title>
|
||||
<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>
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
.btn-orange { background-color: orangeRed; color: white;}
|
||||
.btn-orange:hover { background-color: #ff3600; color: white;}
|
||||
.text-orange { color: orangeRed;}
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- 📄 CONTENIDO -->
|
||||
<div class="content">
|
||||
<h4 class="mb-4">📦 Panel del Agente Aduanal</h4>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm p-3">
|
||||
<h5 class="text-primary">Importadores activos</h5>
|
||||
<p>Consulta los que ya fueron autorizados.</p>
|
||||
<a href="/IMPORTADORES/AGENTES/activos" class="btn btn-primary btn-sm mt-2">Ver importadores</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm p-3">
|
||||
<h5 class="text-success">Solicitudes pendientes</h5>
|
||||
<p>Valida nuevas solicitudes de registro.</p>
|
||||
<a href="/IMPORTADORES/AGENTES/solicitudes_pendientes" class="btn btn-success btn-sm mt-2">Ver solicitudes</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm p-3">
|
||||
<h5 class="text-warning">Bitácora del sistema</h5>
|
||||
<p>Revisa los accesos y acciones recientes.</p>
|
||||
<a href="/IMPORTADORES/agentes/bitacora" class="btn btn-warning btn-sm mt-2">Ver bitácora</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm p-3">
|
||||
<h5 class="text-orange">Locaciones</h5>
|
||||
<p>Gestiona la locaciones validas para nuevos registros.</p>
|
||||
<a href="/IMPORTADORES/agentes/lista" class="btn btn-orange btn-sm mt-2">Gestionar locaciones</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm p-3">
|
||||
<h5 class="text-danger">Cerrar sesión</h5>
|
||||
<p>Salir del sistema de forma segura.</p>
|
||||
<a href="/IMPORTADORES/sistemas/logout" class="btn btn-danger btn-sm mt-2">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,7 +1,4 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../app/helpers/crypto.php';
|
||||
include __DIR__ . '/../partials/sidebar_agente.php';
|
||||
?>
|
||||
<?php include __DIR__ . '/../partials/sidebar_agente.php'; ?>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
@@ -12,47 +9,62 @@ include __DIR__ . '/../partials/sidebar_agente.php';
|
||||
<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: 60px; }
|
||||
.sidebar .nav-link { color: #ccc; padding: 12px 20px; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-left: 220px; padding: 40px 20px; }
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1000; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
.table thead th { background: #343a40; color: #fff; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content px-4 pt-5 mt-4">
|
||||
<div class="content">
|
||||
<h4>✅ Importadores Activos</h4>
|
||||
|
||||
<table class="table table-hover mt-3 align-middle">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Nombre</th>
|
||||
<th>Correo</th>
|
||||
<th>Fecha Registro</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($importadores as $i): ?>
|
||||
<tr>
|
||||
<td><?= $i['id_usuario'] ?></td>
|
||||
<td><?= htmlspecialchars(($i['nombre'])) ?></td>
|
||||
<td><?= htmlspecialchars($i['email']) ?></td>
|
||||
<td><?= isset($i['creado_en']) && $i['creado_en'] instanceof DateTime ? $i['creado_en']->format('Y-m-d H:i') : '' ?></td>
|
||||
<td>
|
||||
<?php if (isset($i['activo']) && $i['activo'] == 1): ?>
|
||||
<a href="/IMPORTADORES/agentes/toggle_estado?id=<?= $i['id_usuario'] ?>&success=1" class="btn btn-sm btn-danger">Suspender</a>
|
||||
<?php else: ?>
|
||||
<a href="/IMPORTADORES/agentes/toggle_estado?id=<?= $i['id_usuario'] ?>&success=1" class="btn btn-sm btn-success">Activar</a>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="card p-3 shadow-sm">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped" id="tabla-activos">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Nombre</th>
|
||||
<th>Correo</th>
|
||||
<th>Fecha Registro</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($importadores as $i): ?>
|
||||
<tr>
|
||||
<td><?= $i['id_usuario'] ?></td>
|
||||
<td><?= htmlspecialchars(($i['nombre'])) ?></td>
|
||||
<td><?= htmlspecialchars($i['email']) ?></td>
|
||||
<td><?= isset($i['creado_en']) && $i['creado_en'] instanceof DateTime ? $i['creado_en']->format('Y-m-d H:i') : '' ?></td>
|
||||
<td>
|
||||
<?php if (isset($i['activo']) && $i['activo'] == 1): ?>
|
||||
<a href="/IMPORTADORES/agentes/toggle_estado?id=<?= $i['id_usuario'] ?>&success=1" class="btn btn-sm btn-danger">Suspender</a>
|
||||
<?php else: ?>
|
||||
<a href="/IMPORTADORES/agentes/toggle_estado?id=<?= $i['id_usuario'] ?>&success=1" class="btn btn-sm btn-success">Activar</a>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (isset($_GET['success'])): ?>
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../app/helpers/crypto.php';
|
||||
include __DIR__ . '/../partials/sidebar_agente.php';
|
||||
?>
|
||||
<?php include __DIR__ . '/../partials/sidebar_agente.php'; ?>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
@@ -11,55 +8,70 @@ include __DIR__ . '/../partials/sidebar_agente.php';
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></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: 60px; }
|
||||
.sidebar .nav-link { color: #ccc; padding: 12px 20px; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-left: 220px; padding: 40px 20px; }
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1000; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
.table thead th { background: #343a40; color: #fff; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content px-4 pt-5 mt-4">
|
||||
<div class="content">
|
||||
<h4>📥 Solicitudes Pendientes</h4>
|
||||
|
||||
<table class="table table-hover mt-3 align-middle">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Empresa</th>
|
||||
<th>RFC</th>
|
||||
<th>Correo</th>
|
||||
<th>Teléfono</th>
|
||||
<th>Fecha</th>
|
||||
<th>Archivo SAT</th>
|
||||
<th>Acción</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($solicitudes as $s): ?>
|
||||
<tr>
|
||||
<td><?= $s['request_id'] ?></td>
|
||||
<td><?= htmlspecialchars(decrypt($s['company_name'])) ?></td>
|
||||
<td><?= htmlspecialchars(decrypt($s['rfc'])) ?></td>
|
||||
<td><?= htmlspecialchars($s['email']) ?></td>
|
||||
<td><?= htmlspecialchars($s['phone']) ?></td>
|
||||
<td><?= $s['request_date'] ? $s['request_date']->format('Y-m-d H:i') : '' ?></td>
|
||||
<td>
|
||||
<?php if (!empty($s['opinion_file'])): ?>
|
||||
<a href="/IMPORTADORES/ver_opinion.php?file=<?= urlencode($s['opinion_file']) ?>" target="_blank" class="btn btn-sm btn-outline-secondary">Ver PDF</a>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">No adjunto</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<a href="/IMPORTADORES/agentes/aprobar_solicitud?id=<?= $s['request_id'] ?>" class="btn btn-sm btn-success">Aprobar</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="card p-3 shadow-sm">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped" id="tabla-pendientes">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Empresa</th>
|
||||
<th>RFC</th>
|
||||
<th>Correo</th>
|
||||
<th>Teléfono</th>
|
||||
<th>Fecha</th>
|
||||
<th>Archivo SAT</th>
|
||||
<th>Acción</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($solicitudes as $s): ?>
|
||||
<tr>
|
||||
<td><?= $s['request_id'] ?></td>
|
||||
<td><?= htmlspecialchars(decrypt($s['company_name'])) ?></td>
|
||||
<td><?= htmlspecialchars(decrypt($s['rfc'])) ?></td>
|
||||
<td><?= htmlspecialchars($s['email']) ?></td>
|
||||
<td><?= htmlspecialchars($s['phone']) ?></td>
|
||||
<td><?= $s['request_date'] ? $s['request_date']->format('Y-m-d H:i') : '' ?></td>
|
||||
<td>
|
||||
<?php if (!empty($s['opinion_file'])): ?>
|
||||
<a href="/IMPORTADORES/ver_opinion.php?file=<?= urlencode($s['opinion_file']) ?>" target="_blank" class="btn btn-sm btn-outline-secondary">Ver PDF</a>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">No adjunto</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<a href="/IMPORTADORES/agentes/aprobar_solicitud?id=<?= $s['request_id'] ?>" class="btn btn-sm btn-success">Aprobar</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
$(document).ready(function () {
|
||||
$('#tabla-logins').DataTable({
|
||||
language: {
|
||||
url: '//cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json'
|
||||
url: 'https://cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,7 +18,10 @@
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
.btn-indigo { background-color: #6610f2; color: white;}
|
||||
.btn-indigo:hover { background-color: #520dc2; color: white;}
|
||||
.text-indigo { color: #6610f2;}
|
||||
.text-indigo { color: orangeRed;}
|
||||
.btn-orange { background-color: orangeRed; color: white;}
|
||||
.btn-orange:hover { background-color: #ff3600; color: white;}
|
||||
.text-orange { color: orangeRed;}
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@@ -92,6 +95,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm p-3">
|
||||
<h5 class="text-orange">Locaciones</h5>
|
||||
<p>Visualiza las locaciones registradas.</p>
|
||||
<a href="/IMPORTADORES/importadores/lista"
|
||||
class="btn btn-orange btn-sm mt-2 <?= str_contains($_SERVER['REQUEST_URI'], '/importadores/lista') ? 'active' : '' ?>">
|
||||
Ver locaciones
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm p-3">
|
||||
<h5 class="text-danger">Cerrar sesión</h5>
|
||||
|
||||
286
views/locaciones/alta_locaciones.php
Normal file
286
views/locaciones/alta_locaciones.php
Normal file
@@ -0,0 +1,286 @@
|
||||
<?php include __DIR__ . '/../partials/sidebar_agente.php'; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Dashboard | Agente Aduanal</title>
|
||||
<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>
|
||||
<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; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
.hide { display: none !important; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4">🗺️ Nuevas Locaciones</h4>
|
||||
<div class="row g-3">
|
||||
|
||||
<!-- Nuevo Estado -->
|
||||
<div class="col-md">
|
||||
<div class="card p-4 bg-white shadow-sm">
|
||||
<form id="estadoForm">
|
||||
<h4 class="mb-4 text-dark">➕ Nuevo Estado</h4><br>
|
||||
<!-- País -->
|
||||
<div class="col-md-12">
|
||||
<label for="paisEstado" class="form-label">País *</label>
|
||||
<select id="paisEstado" name="pais" class="form-select" required>
|
||||
<option value="">Selecciona país</option>
|
||||
<?php foreach($paises as $p): ?>
|
||||
<option value="<?= $p['id_pais'] ?>">
|
||||
<?= htmlspecialchars($p['nombre']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div><br><br><br>
|
||||
|
||||
<!-- Estado -->
|
||||
<div class="col-md-12">
|
||||
<label for="entidadEstado" class="form-label">Entidad / Provincia *</label>
|
||||
<input id="entidadEstado" name="entidad" class="form-control" required>
|
||||
</div><br><br>
|
||||
|
||||
<div class="col-md-4 d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-success w-100">
|
||||
<i class="fas fa-plus"></i> Registrar Estado
|
||||
</button>
|
||||
<a href="/IMPORTADORES/agentes/lista" class="btn btn-secondary ms-2">Cancelar</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Nueva Ciudad -->
|
||||
<div class="col-md">
|
||||
<div class="card p-4 bg-white shadow-sm">
|
||||
<form id="ciudadForm" action="/IMPORTADORES/agentes/guadarCiudad" method="POST" enctype="multipart/form-data">
|
||||
<h4 class="mb-4 text-dark">➕ Nueva Ciudad</h4>
|
||||
<!-- País -->
|
||||
<div class="col-md-12">
|
||||
<label for="paisCiudad" class="form-label">País *</label>
|
||||
<select id="paisCiudad" name="pais" class="form-select" required>
|
||||
<option value="">Selecciona país</option>
|
||||
<?php foreach($paises as $p): ?>
|
||||
<option value="<?= $p['id_pais'] ?>">
|
||||
<?= htmlspecialchars($p['nombre']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div><br>
|
||||
|
||||
<!-- Estado -->
|
||||
<div class="col-md-12">
|
||||
<label for="entidadCiudad" class="form-label">Entidad / Provincia *</label>
|
||||
<select id="estadoCiudad" name="entidad" class="form-select" required disabled>
|
||||
<option value="">Primero país…</option>
|
||||
</select>
|
||||
</div><br>
|
||||
|
||||
<!-- Ciudad -->
|
||||
<div class="col-md-12">
|
||||
<label for="nombreCiudad" class="form-label">Ciudad *</label>
|
||||
<input id="nombreCiudad" name="ciudad" class="form-control" required>
|
||||
</div><br>
|
||||
|
||||
<div class="col-md-4 d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-success w-100">
|
||||
<i class="fas fa-plus"></i> Registrar Ciudad
|
||||
</button>
|
||||
<a href="/IMPORTADORES/agentes/lista" class="btn btn-secondary ms-2">Cancelar</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Función para cargar estados
|
||||
function cargarEstados(paisId, selectElement) {
|
||||
selectElement.innerHTML = '<option>Cargando...</option>';
|
||||
selectElement.disabled = true;
|
||||
|
||||
fetch(`/IMPORTADORES/agentes/estados?pais=${paisId}`)
|
||||
.then(response => response.json())
|
||||
.then(estados => {
|
||||
selectElement.innerHTML = '<option value="">Selecciona estado</option>';
|
||||
estados.forEach(estado => {
|
||||
const option = new Option(estado.nombre, estado.id_estado);
|
||||
selectElement.add(option);
|
||||
});
|
||||
selectElement.disabled = false;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
selectElement.innerHTML = '<option value="">Error al cargar</option>';
|
||||
});
|
||||
}
|
||||
|
||||
// Evento para cargar estados cuando se selecciona país en formulario de ciudad
|
||||
document.getElementById('paisCiudad').addEventListener('change', function(e) {
|
||||
const paisId = e.target.value;
|
||||
const estadoSelect = document.getElementById('estadoCiudad');
|
||||
|
||||
if (paisId) {
|
||||
cargarEstados(paisId, estadoSelect);
|
||||
} else {
|
||||
estadoSelect.innerHTML = '<option value="">Primero selecciona país</option>';
|
||||
estadoSelect.disabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Formulario de nuevo estado
|
||||
document.getElementById('estadoForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Obtener valores directamente
|
||||
const paisSelect = document.getElementById('paisEstado');
|
||||
const entidadInput = document.getElementById('entidadEstado');
|
||||
|
||||
const paisValue = paisSelect.value;
|
||||
const entidadValue = entidadInput.value.trim();
|
||||
|
||||
// Validación en frontend
|
||||
if (!paisValue) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Campo requerido',
|
||||
text: 'Debe seleccionar un país'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entidadValue) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Campo requerido',
|
||||
text: 'Debe ingresar el nombre del estado'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Crear FormData manualmente
|
||||
const formData = new FormData();
|
||||
formData.append('pais', paisValue);
|
||||
formData.append('entidad', entidadValue);
|
||||
|
||||
const submitBtn = this.querySelector('button[type="submit"]');
|
||||
const originalText = submitBtn.innerHTML;
|
||||
|
||||
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Guardando...';
|
||||
submitBtn.disabled = true;
|
||||
|
||||
// Debug: mostrar lo que se va a enviar
|
||||
console.log('Enviando:', {
|
||||
pais: paisValue,
|
||||
entidad: entidadValue
|
||||
});
|
||||
|
||||
fetch('/IMPORTADORES/agentes/guardarEstado', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: '¡Éxito!',
|
||||
text: data.message,
|
||||
timer: 2000,
|
||||
showConfirmButton: false
|
||||
});
|
||||
this.reset();
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: data.message
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'Ocurrió un error al procesar la solicitud'
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
submitBtn.innerHTML = originalText;
|
||||
submitBtn.disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
// Formulario de nueva ciudad
|
||||
document.getElementById('ciudadForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(this);
|
||||
const submitBtn = this.querySelector('button[type="submit"]');
|
||||
const originalText = submitBtn.innerHTML;
|
||||
|
||||
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Guardando...';
|
||||
submitBtn.disabled = true;
|
||||
|
||||
fetch('/IMPORTADORES/agentes/guardarCiudad', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: '¡Éxito!',
|
||||
text: data.message,
|
||||
timer: 2000,
|
||||
showConfirmButton: false
|
||||
});
|
||||
this.reset();
|
||||
// Resetear el select de estados
|
||||
document.getElementById('estadoCiudad').innerHTML = '<option value="">Primero selecciona país</option>';
|
||||
document.getElementById('estadoCiudad').disabled = true;
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: data.message
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'Ocurrió un error al procesar la solicitud'
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
submitBtn.innerHTML = originalText;
|
||||
submitBtn.disabled = false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
492
views/locaciones/gestion_locaciones.php
Normal file
492
views/locaciones/gestion_locaciones.php
Normal file
@@ -0,0 +1,492 @@
|
||||
<?php include __DIR__ . '/../partials/sidebar_agente.php'; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Dashboard | Agente Aduanal</title>
|
||||
<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">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
|
||||
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<style>
|
||||
table.dataTable thead th { background:#343a40; color:#fff; }
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease;}
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4">🌍 Locaciones</h4>
|
||||
<a href="/IMPORTADORES/agentes/alta" class="btn btn-success mb-3">➕ Agregar Locación</a>
|
||||
<div class="card p-3 shadow-sm">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped" id="tabla-locaciones">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th>ID País</th>
|
||||
<th>País</th>
|
||||
<th>ISO3</th>
|
||||
<th>ID Estado</th>
|
||||
<th>Estado</th>
|
||||
<th>ID Ciudad</th>
|
||||
<th>Ciudad</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($locaciones as $loc): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($loc['id_pais']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['nombre_pais']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['iso3']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['id_estado']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['nombre_estado']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['id_ciudad']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['nombre_ciudad']) ?></td>
|
||||
<td>
|
||||
<!-- Botones de acción (editar/eliminar) -->
|
||||
<?php if (!empty($loc['id_ciudad'])): ?>
|
||||
<!-- Es una ciudad -->
|
||||
<button class="btn btn-sm btn-primary"
|
||||
onclick="editarItem('ciudad', <?= $loc['id_ciudad'] ?>, '<?= htmlspecialchars($loc['nombre_ciudad'], ENT_QUOTES) ?>', '', '', '', <?= $loc['id_estado'] ?>)">
|
||||
✏️
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger"
|
||||
onclick="eliminarItem('ciudad', <?= $loc['id_ciudad'] ?>, '<?= htmlspecialchars($loc['nombre_ciudad'], ENT_QUOTES) ?>')">
|
||||
🗑️
|
||||
</button>
|
||||
<?php elseif (!empty($loc['id_estado'])): ?>
|
||||
<!-- Es un estado - CORREGIDO: Obtener abreviatura correctamente -->
|
||||
<button class="btn btn-sm btn-primary"
|
||||
onclick="editarItem('estado', <?= $loc['id_estado'] ?>, '<?= htmlspecialchars($loc['nombre_estado'], ENT_QUOTES) ?>', '', '', '<?= htmlspecialchars($loc['abreviatura'] ?? '', ENT_QUOTES) ?>', <?= $loc['id_pais'] ?>)">
|
||||
✏️
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger"
|
||||
onclick="eliminarItem('estado', <?= $loc['id_estado'] ?>, '<?= htmlspecialchars($loc['nombre_estado'], ENT_QUOTES) ?>')">
|
||||
🗑️
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<!-- Es un país -->
|
||||
<button class="btn btn-sm btn-primary"
|
||||
onclick="editarItem('pais', <?= $loc['id_pais'] ?>, '<?= htmlspecialchars($loc['nombre_pais'], ENT_QUOTES) ?>', '<?= htmlspecialchars($loc['iso2'] ?? '', ENT_QUOTES) ?>', '<?= htmlspecialchars($loc['iso3'] ?? '', ENT_QUOTES) ?>')">
|
||||
✏️
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger"
|
||||
onclick="eliminarItem('pais', <?= $loc['id_pais'] ?>, '<?= htmlspecialchars($loc['nombre_pais'], ENT_QUOTES) ?>')">
|
||||
🗑️
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal para Editar -->
|
||||
<div class="modal fade" id="editarModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">✏️ Editar <span id="tipoItem"></span></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<form id="editarForm">
|
||||
<div class="modal-body">
|
||||
<input name="editTipo" type="hidden" id="editTipo">
|
||||
<input name="editId" type="hidden" id="editId">
|
||||
|
||||
<!-- Campos para País -->
|
||||
<div id="camposPais" style="display: none;">
|
||||
<div class="mb-3">
|
||||
<label for="editNombrePais" class="form-label">Nombre del País *</label>
|
||||
<input name="editNombrePais" type="text" class="form-control" id="editNombrePais">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="editIso2" class="form-label">ISO2</label>
|
||||
<input name="editIso2" type="text" class="form-control" id="editIso2" maxlength="2">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="editIso3" class="form-label">ISO3</label>
|
||||
<input name="editIso3" type="text" class="form-control" id="editIso3" maxlength="3">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Campos para Estado -->
|
||||
<div id="camposEstado" style="display: none;">
|
||||
<div class="mb-3">
|
||||
<label for="editPaisEstado" class="form-label">País *</label>
|
||||
<select name="editPaisEstado" class="form-select" id="editPaisEstado">
|
||||
<option value="">Selecciona país</option>
|
||||
<?php foreach($paises as $p): ?>
|
||||
<option value="<?= $p['id_pais'] ?>"><?= htmlspecialchars($p['nombre']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editNombreEstado" class="form-label">Nombre del Estado *</label>
|
||||
<input name="editNombreEstado" type="text" class="form-control" id="editNombreEstado">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editAbreviatura" class="form-label">Abreviatura</label>
|
||||
<input name="editAbreviatura" type="text" class="form-control" id="editAbreviatura" maxlength="10">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Campos para Ciudad -->
|
||||
<div id="camposCiudad" style="display: none;">
|
||||
<div class="mb-3">
|
||||
<label for="editPaisCiudad" class="form-label">País *</label>
|
||||
<select name="editPaisCiudad" class="form-select" id="editPaisCiudad">
|
||||
<option value="">Selecciona país</option>
|
||||
<?php foreach($paises as $p): ?>
|
||||
<option value="<?= $p['id_pais'] ?>"><?= htmlspecialchars($p['nombre']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editEstadoCiudad" class="form-label">Estado *</label>
|
||||
<select name="editEstadoCiudad" class="form-select" id="editEstadoCiudad">
|
||||
<option value="">Primero selecciona país</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editNombreCiudad" class="form-label">Nombre de la Ciudad *</label>
|
||||
<input name="editNombreCiudad" type="text" class="form-control" id="editNombreCiudad">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
|
||||
<button type="submit" class="btn btn-primary">Guardar Cambios</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('#tabla-locaciones').DataTable({
|
||||
order: [],
|
||||
language: {
|
||||
url: 'https://cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Función para cargar estados
|
||||
function cargarEstados(paisId, selectElement, estadoSeleccionado = null) {
|
||||
selectElement.innerHTML = '<option>Cargando...</option>';
|
||||
selectElement.disabled = true;
|
||||
|
||||
fetch(`/IMPORTADORES/agentes/estados?pais=${paisId}`)
|
||||
.then(response => response.json())
|
||||
.then(estados => {
|
||||
selectElement.innerHTML = '<option value="">Selecciona estado</option>';
|
||||
estados.forEach(estado => {
|
||||
const option = new Option(estado.nombre, estado.id_estado);
|
||||
if (estadoSeleccionado && estado.id_estado == estadoSeleccionado) {
|
||||
option.selected = true;
|
||||
}
|
||||
selectElement.add(option);
|
||||
});
|
||||
selectElement.disabled = false;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
selectElement.innerHTML = '<option value="">Error al cargar</option>';
|
||||
});
|
||||
}
|
||||
|
||||
// Evento para cargar estados en modal de edición
|
||||
document.getElementById('editPaisCiudad').addEventListener('change', function(e) {
|
||||
const paisId = e.target.value;
|
||||
const estadoSelect = document.getElementById('editEstadoCiudad');
|
||||
|
||||
if (paisId) {
|
||||
cargarEstados(paisId, estadoSelect);
|
||||
} else {
|
||||
estadoSelect.innerHTML = '<option value="">Primero selecciona país</option>';
|
||||
estadoSelect.disabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Función para abrir modal de edición
|
||||
function editarItem(tipo, id, nombre, iso2 = '', iso3 = '', abreviatura = '', padreId = null) {
|
||||
document.getElementById('editTipo').value = tipo;
|
||||
document.getElementById('editId').value = id;
|
||||
document.getElementById('tipoItem').textContent = tipo.charAt(0).toUpperCase() + tipo.slice(1);
|
||||
|
||||
// Ocultar todos los campos
|
||||
document.getElementById('camposPais').style.display = 'none';
|
||||
document.getElementById('camposEstado').style.display = 'none';
|
||||
document.getElementById('camposCiudad').style.display = 'none';
|
||||
|
||||
if (tipo === 'pais') {
|
||||
document.getElementById('camposPais').style.display = 'block';
|
||||
document.getElementById('editNombrePais').value = nombre;
|
||||
document.getElementById('editIso2').value = iso2;
|
||||
document.getElementById('editIso3').value = iso3;
|
||||
|
||||
} else if (tipo === 'estado') {
|
||||
document.getElementById('camposEstado').style.display = 'block';
|
||||
document.getElementById('editNombreEstado').value = nombre;
|
||||
document.getElementById('editAbreviatura').value = abreviatura;
|
||||
document.getElementById('editPaisEstado').value = padreId;
|
||||
|
||||
} else if (tipo === 'ciudad') {
|
||||
document.getElementById('camposCiudad').style.display = 'block';
|
||||
document.getElementById('editNombreCiudad').value = nombre;
|
||||
|
||||
// Necesitamos obtener el país del estado para cargar los estados
|
||||
if (padreId) {
|
||||
fetch(`/IMPORTADORES/agentes/obtenerPaisPorEstado?estado=${padreId}`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
document.getElementById('editPaisCiudad').value = data.pais_id;
|
||||
cargarEstados(data.pais_id, document.getElementById('editEstadoCiudad'), padreId);
|
||||
} else {
|
||||
console.error('Error al obtener país:', data.message);
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'No se pudo cargar la información del país' + data.message
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'Error al cargar la información'
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
new bootstrap.Modal(document.getElementById('editarModal')).show();
|
||||
}
|
||||
|
||||
// Función para eliminar item
|
||||
function eliminarItem(tipo, id, nombre) {
|
||||
Swal.fire({
|
||||
title: '¿Estás seguro?',
|
||||
text: `¿Deseas eliminar el ${tipo} "${nombre}"?`,
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#d33',
|
||||
cancelButtonColor: '#3085d6',
|
||||
confirmButtonText: 'Sí, eliminar',
|
||||
cancelButtonText: 'Cancelar'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
fetch('/IMPORTADORES/agentes/eliminar', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
tipo: tipo,
|
||||
id: id
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: '¡Eliminado!',
|
||||
text: data.message,
|
||||
timer: 2000,
|
||||
showConfirmButton: false
|
||||
}).then(() => {
|
||||
location.reload();
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: data.message
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'Ocurrió un error al eliminar'
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// REEMPLAZA tu función de submit del formulario con esta versión corregida
|
||||
document.getElementById('editarForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const tipo = document.getElementById('editTipo').value;
|
||||
const id = document.getElementById('editId').value;
|
||||
|
||||
// VALIDACIÓN BÁSICA
|
||||
if (!tipo || !id) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'Faltan datos requeridos'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let formData = new FormData();
|
||||
formData.append('tipo', tipo);
|
||||
formData.append('id', id);
|
||||
|
||||
// Determinar endpoint y datos según el tipo
|
||||
let endpoint = '';
|
||||
let isValid = true;
|
||||
|
||||
if (tipo === 'pais') {
|
||||
const nombre = document.getElementById('editNombrePais').value.trim();
|
||||
if (!nombre) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'El nombre del país es obligatorio'
|
||||
});
|
||||
return;
|
||||
}
|
||||
endpoint = '/IMPORTADORES/agentes/actualizarPais';
|
||||
formData.append('nombre', nombre);
|
||||
formData.append('iso2', document.getElementById('editIso2').value.trim());
|
||||
formData.append('iso3', document.getElementById('editIso3').value.trim());
|
||||
|
||||
} else if (tipo === 'estado') {
|
||||
const nombre = document.getElementById('editNombreEstado').value.trim();
|
||||
const paisId = document.getElementById('editPaisEstado').value;
|
||||
|
||||
if (!nombre || !paisId) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'El nombre del estado y el país son obligatorios'
|
||||
});
|
||||
return;
|
||||
}
|
||||
endpoint = '/IMPORTADORES/agentes/actualizarEstado';
|
||||
formData.append('nombre', nombre);
|
||||
formData.append('abreviatura', document.getElementById('editAbreviatura').value.trim());
|
||||
formData.append('pais_id', paisId);
|
||||
|
||||
} else if (tipo === 'ciudad') {
|
||||
const nombre = document.getElementById('editNombreCiudad').value.trim();
|
||||
const estadoId = document.getElementById('editEstadoCiudad').value;
|
||||
|
||||
if (!nombre || !estadoId) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'El nombre de la ciudad y el estado son obligatorios'
|
||||
});
|
||||
return;
|
||||
}
|
||||
endpoint = '/IMPORTADORES/agentes/actualizarCiudad';
|
||||
formData.append('nombre', nombre);
|
||||
formData.append('estado_id', estadoId);
|
||||
}
|
||||
|
||||
const submitBtn = this.querySelector('button[type="submit"]');
|
||||
const originalText = submitBtn.innerHTML;
|
||||
|
||||
// INDICADOR DE CARGA
|
||||
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Guardando...';
|
||||
submitBtn.disabled = true;
|
||||
|
||||
// ENVÍO AJAX al endpoint específico
|
||||
fetch(endpoint, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => {
|
||||
// VERIFICAR QUE LA RESPUESTA SEA OK
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: '¡Éxito!',
|
||||
text: data.message,
|
||||
timer: 2000,
|
||||
showConfirmButton: false
|
||||
}).then(() => {
|
||||
// CERRAR MODAL Y RECARGAR
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('editarModal'));
|
||||
if (modal) {
|
||||
modal.hide();
|
||||
}
|
||||
location.reload();
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: data.message || 'Error desconocido'
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error completo:', error);
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error de conexión',
|
||||
text: 'No se pudo procesar la solicitud. Verifique su conexión.'
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
// RESTAURAR BOTÓN
|
||||
submitBtn.innerHTML = originalText;
|
||||
submitBtn.disabled = false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
85
views/locaciones/lista.php
Normal file
85
views/locaciones/lista.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>🌍 Locaciones</title>
|
||||
<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">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
|
||||
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<style>
|
||||
table.dataTable thead th { background:#343a40; color:#fff; }
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease;}
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4">🗺️ Locaciones</h4>
|
||||
<div class="card p-3 shadow-sm">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped" id="tabla-locaciones">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th>ID País</th>
|
||||
<th>País</th>
|
||||
<th>ISO3</th>
|
||||
<th>ID Estado</th>
|
||||
<th>Estado</th>
|
||||
<th>ID Ciudad</th>
|
||||
<th>Ciudad</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($locaciones as $loc): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($loc['id_pais']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['nombre_pais']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['iso3']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['id_estado']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['nombre_estado']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['id_ciudad']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['nombre_ciudad']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('#tabla-locaciones').DataTable({
|
||||
order: [],
|
||||
language: {
|
||||
url: 'https://cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json'
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -119,16 +119,7 @@ if (!isset($_SESSION['email_recuperacion'])) {
|
||||
<i class="fas fa-key me-2"></i>Código de verificación
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<input type="text"
|
||||
id="codigo"
|
||||
name="codigo"
|
||||
class="form-control"
|
||||
required
|
||||
pattern="\d{6}"
|
||||
maxlength="6"
|
||||
placeholder="000000"
|
||||
autocomplete="off"
|
||||
inputmode="numeric">
|
||||
<input type="text" id="codigo" name="codigo" class="form-control" required pattern="\d{6}" maxlength="6" placeholder="000000" autocomplete="off" inputmode="numeric">
|
||||
<button type="button" class="clear-input" id="clearCode" title="Limpiar código">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
|
||||
@@ -1,30 +1,146 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'agente_aduanal') {
|
||||
header('Location: /IMPORTADORES/login');
|
||||
exit;
|
||||
}
|
||||
|
||||
$nombreAgente = $_SESSION['usuario_nombre'];
|
||||
?>
|
||||
|
||||
<style>
|
||||
.nav .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
|
||||
.nav .nav-link:hover,
|
||||
.nav .nav-link.active { background-color: #495057; color: #fff; }
|
||||
/* Estilo inverso solo para pantallas pequeñas (sidebar tipo offcanvas) */
|
||||
@media (max-width: 767.98px) {
|
||||
.nav .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.nav .nav-link:hover,
|
||||
.nav .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 🔷 NAVBAR -->
|
||||
<nav class="navbar navbar-dark bg-dark">
|
||||
<nav class="navbar navbar-dark bg-dark fixed-top">
|
||||
<div class="container-fluid">
|
||||
<!-- Botón de menú para móviles -->
|
||||
<button class="btn btn-outline-light d-md-none me-2" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu">
|
||||
☰
|
||||
</button>
|
||||
<span class="navbar-brand">SIIH | Agente Aduanal</span>
|
||||
<div class="d-flex text-white">
|
||||
Bienvenido, <?= htmlspecialchars($nombreAgente) ?>
|
||||
<a href="/IMPORTADORES/sistemas/logout" class="btn btn-outline-light btn-sm">Cerrar sesión</a>
|
||||
<div class="d-flex ms-auto text-white">
|
||||
<?php
|
||||
// Verifica si estás en el dashboard principal del importador
|
||||
$esDashboard = str_contains($_SERVER['REQUEST_URI'], '/agentes/dashboard');
|
||||
|
||||
if ($esDashboard): ?>
|
||||
Bienvenido, <?= htmlspecialchars($_SESSION['usuario_nombre']) ?>
|
||||
<?php else: ?>
|
||||
<a href="/IMPORTADORES/sistemas/logout" class="btn btn-outline-light btn-sm">Cerrar Sesión</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- 📘 SIDEBAR -->
|
||||
<div class="sidebar d-none d-lg-block">
|
||||
<!-- SIDEBAR FIJA (escritorio) -->
|
||||
<div class="d-none d-md-block bg-dark text-white position-fixed h-100 pt-5" style="width: 250px;">
|
||||
<nav class="nav flex-column">
|
||||
<a href="/IMPORTADORES/AGENTES/dashboard" class="nav-link active">📊 Dashboard</a>
|
||||
<a href="/IMPORTADORES/AGENTES/activos" class="nav-link">✅ Importadores Activos</a>
|
||||
<a href="/IMPORTADORES/AGENTES/solicitudes_pendientes" class="nav-link">📥 Solicitudes de Registro</a>
|
||||
<a href="/IMPORTADORES/AGENTES/bitacora" class="nav-link">🕓 Bitácora</a>
|
||||
|
||||
<!-- INICIO -->
|
||||
<a href="/IMPORTADORES/agentes/dashboard"
|
||||
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/dashboard' ? 'active' : '' ?>">
|
||||
🏠 Inicio
|
||||
</a>
|
||||
|
||||
<!-- IMPORTADORES ACTIVOS -->
|
||||
<a href="/IMPORTADORES/agentes/activos"
|
||||
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/activos' ? 'active' : '' ?>">
|
||||
✅ Importadores Activos
|
||||
</a>
|
||||
|
||||
<!-- SOLICITUDES PENDIENTES -->
|
||||
<a href="/IMPORTADORES/agentes/solicitudes_pendientes"
|
||||
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/solicitudes_pendientes' ? 'active' : '' ?>">
|
||||
📥 Solicitudes de Registro
|
||||
</a>
|
||||
|
||||
<!-- BITÁCORA -->
|
||||
<a href="/IMPORTADORES/agentes/bitacora"
|
||||
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/bitacora' ? 'active' : '' ?>">
|
||||
🕓 Bitácora
|
||||
</a>
|
||||
|
||||
<!-- LOCACIONES -->
|
||||
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
|
||||
<?= str_contains($_SERVER['REQUEST_URI'], '/agentes') ? 'active' : '' ?>"
|
||||
data-bs-toggle="collapse" href="#submenuLocaciones" role="button" aria-expanded="false">
|
||||
🗺️ Locaciones
|
||||
<span class="badge bg-secondary">2</span>
|
||||
</a>
|
||||
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/agentes') ? 'show' : '' ?>" id="submenuLocaciones">
|
||||
<nav class="nav flex-column ms-3">
|
||||
<a href="/IMPORTADORES/agentes/lista"
|
||||
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/lista' ? 'active' : '' ?>">
|
||||
• Ver Locaciones
|
||||
</a>
|
||||
<a href="/IMPORTADORES/agentes/alta"
|
||||
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/agentes/alta') ? 'active' : '' ?>">
|
||||
• Agregar Locaciones
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- OFFCANVAS PARA MÓVILES -->
|
||||
<div class="offcanvas offcanvas-start d-md-none" tabindex="-1" id="sidebarMenu" style="top: 75px;">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title">Menú</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="offcanvas"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
|
||||
<!-- INICIO -->
|
||||
<a href="/IMPORTADORES/agentes/dashboard"
|
||||
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/dashboard' ? 'active' : '' ?>">
|
||||
🏠 Inicio
|
||||
</a>
|
||||
|
||||
<!-- IMPORTADORES ACTIVOS -->
|
||||
<a href="/IMPORTADORES/agentes/activos"
|
||||
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/activos' ? 'active' : '' ?>">
|
||||
✅ Importadores Activos
|
||||
</a>
|
||||
|
||||
<!-- SOLICITUDES PENDIENTES -->
|
||||
<a href="/IMPORTADORES/agentes/solicitudes_pendientes"
|
||||
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/solicitudes_pendientes' ? 'active' : '' ?>">
|
||||
📥 Solicitudes de Registro
|
||||
</a>
|
||||
|
||||
<!-- BITÁCORA -->
|
||||
<a href="/IMPORTADORES/agentes/bitacora"
|
||||
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/bitacora' ? 'active' : '' ?>">
|
||||
🕓 Bitácora
|
||||
</a>
|
||||
|
||||
<!-- LOCACIONES -->
|
||||
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
|
||||
<?= str_contains($_SERVER['REQUEST_URI'], '/agentes') ? 'active' : '' ?>"
|
||||
data-bs-toggle="collapse" href="#submenuLocaciones" role="button" aria-expanded="false">
|
||||
🗺️ Locaciones
|
||||
<span class="badge bg-secondary">2</span>
|
||||
</a>
|
||||
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/agentes') ? 'show' : '' ?>" id="submenuLocaciones">
|
||||
<nav class="nav flex-column ms-3">
|
||||
<a href="/IMPORTADORES/agentes/lista"
|
||||
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/lista' ? 'active' : '' ?>">
|
||||
• Ver Locaciones
|
||||
</a>
|
||||
<a href="/IMPORTADORES/agentes/alta"
|
||||
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/agentes/alta') ? 'active' : '' ?>">
|
||||
• Agregar Locaciones
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -50,6 +50,12 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
|
||||
🏠 Inicio
|
||||
</a>
|
||||
|
||||
<!-- LOCACIONES -->
|
||||
<a href="/IMPORTADORES/importadores/lista"
|
||||
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/importadores/lista' ? 'active' : '' ?>">
|
||||
🗺️ Locaciones
|
||||
</a>
|
||||
|
||||
<!-- TRANSPORTISTAS -->
|
||||
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
|
||||
<?= str_contains($_SERVER['REQUEST_URI'], '/transportistas') ? 'active' : '' ?>"
|
||||
@@ -194,6 +200,12 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
|
||||
🏠 Inicio
|
||||
</a>
|
||||
|
||||
<!-- LOCACIONES -->
|
||||
<a href="/IMPORTADORES/importadores/lista"
|
||||
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/importadores/lista' ? 'active' : '' ?>">
|
||||
🗺️ Locaciones
|
||||
</a>
|
||||
|
||||
<!-- TRANSPORTISTAS -->
|
||||
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
|
||||
<?= str_contains($_SERVER['REQUEST_URI'], '/transportistas') ? 'active' : '' ?>"
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="proveedor_id" class="form-label">Proveedor</label>
|
||||
<select id="proveedor_id" name="proveedor_id" class="form-select searchable" required>
|
||||
<select id="proveedor_id" name="proveedor_id" class="form-select searchable">
|
||||
<option value="">Cargando proveedores...</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
</div>
|
||||
<div class="text-end mt-4">
|
||||
<button type="submit" class="btn btn-success">Guardar</button>
|
||||
<a href="/IMPORTADORES/transportes/lista" class="btn btn-secondary ms-2">Cancelar</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
</div>
|
||||
<div class="text-end mt-4">
|
||||
<button type="submit" class="btn btn-primary">Actualizar</button>
|
||||
<a href="/IMPORTADORES/transportes/lista" class="btn btn-secondary ms-2">Cancelar</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
📥 Descargar plantilla
|
||||
</a><br>
|
||||
<button type="submit" class="btn btn-primary">Importar</button>
|
||||
<a href="/IMPORTADORES/transportes/lista" class="btn btn-secondary ms-2">Cancelar</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
|
||||
<div class="text-end mt-4">
|
||||
<button class="btn btn-success px-4">Guardar Transportista</button>
|
||||
<a href="/IMPORTADORES/transportistas/lista" class="btn btn-secondary ms-2">Cancelar</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -61,6 +61,7 @@ unset($_SESSION['import_errors'], $_SESSION['import_success']);
|
||||
<input type="file" id="archivo_csv" name="archivo_csv" class="form-control" accept=".csv" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">🚀 Cargar Transportistas</button>
|
||||
<a href="/IMPORTADORES/transportistas/lista" class="btn btn-secondary ms-2">Cancelar</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
</div>
|
||||
<div class="text-end mt-4">
|
||||
<button class="btn btn-success px-4">💾 Guardar Cambios</button>
|
||||
<a href="/IMPORTADORES/transportistas/lista" class="btn btn-secondary ms-2">Cancelar</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<!-- views/transportistas/lista.php -->
|
||||
<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
Reference in New Issue
Block a user