CRUD gestión de locaciones

This commit is contained in:
2025-06-09 08:50:23 -06:00
parent 2eb3ec6061
commit 55fb7c0bf4
28 changed files with 1982 additions and 234 deletions

View File

@@ -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()
@@ -270,4 +266,516 @@ 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;
}

View File

@@ -13,4 +13,37 @@ function dashboard()
$nombreImportador = $_SESSION['usuario_nombre'];
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';
}

View File

@@ -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');

View File

@@ -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()
{

View File

@@ -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>

View File

@@ -89,7 +89,7 @@ function ciudades() {
function lista()
{
include __DIR__ . '/../../views/transportistas/lista.php';
}
}
/** Descarga la plantilla CSV para carga masiva **/
function template() {