This commit is contained in:
2025-06-05 08:23:47 -06:00
parent bf3c2d19a3
commit 2cdbbe8a2b
2 changed files with 157 additions and 105 deletions

View File

@@ -1,6 +1,5 @@
<?php <?php
// app/controllers/proveedores.php // app/controllers/proveedores.php
session_start(); session_start();
// 1) Composer autoload (phpdotenv y demás libs) // 1) Composer autoload (phpdotenv y demás libs)
@@ -10,13 +9,9 @@ require_once __DIR__ . '/../../vendor/autoload.php';
require_once __DIR__ . '/../helpers/env.php'; require_once __DIR__ . '/../helpers/env.php';
loadEnv(); loadEnv();
/** /** Obtiene (y cachea en sesión) el JWT de la API usando las credenciales de $_ENV **/
* Obtiene (y cachea en sesión) el JWT de la API usando las credenciales de $_ENV /** Obtiene (y cachea) el JWT de la API usando credenciales de $_ENV
*/ * Gestiona expiración: asume 1 hora de vida y renueva si ha pasado. **/
/**
* Obtiene (y cachea) el JWT de la API usando credenciales de $_ENV
* Gestiona expiración: asume 1 hora de vida y renueva si ha pasado.
*/
function getApiToken(): ?string function getApiToken(): ?string
{ {
// Duración en segundos del token (60 min) // Duración en segundos del token (60 min)
@@ -66,11 +61,8 @@ function getApiToken(): ?string
return null; return null;
} }
/** GET /IMPORTADORES/proveedores
/** * Muestra la vista de listado **/
* GET /IMPORTADORES/proveedores
* Muestra la vista de listado
*/
function index() function index()
{ {
lista(); lista();
@@ -85,14 +77,10 @@ function lista()
include __DIR__ . '/../../views/proveedores/lista.php'; include __DIR__ . '/../../views/proveedores/lista.php';
} }
/** /** GET /IMPORTADORES/proveedores/ajax_lista
* GET /IMPORTADORES/proveedores/ajax_lista * Devuelve JSON para DataTables
* Devuelve JSON para DataTables * GET /IMPORTADORES/proveedores/ajax_lista
*/ * Devuelve JSON para DataTables (siempre HTTP 200) **/
/**
* GET /IMPORTADORES/proveedores/ajax_lista
* Devuelve JSON para DataTables (siempre HTTP 200)
*/
function ajax_lista() function ajax_lista()
{ {
// 1) Fijamos el header JSON // 1) Fijamos el header JSON
@@ -164,10 +152,8 @@ function ajax_lista()
echo json_encode(['data' => $dataList]); echo json_encode(['data' => $dataList]);
} }
/** /** GET /IMPORTADORES/proveedores/eliminar
* GET /IMPORTADORES/proveedores/eliminar * Llama a DELETE /api/proveedores/:clave y redirige **/
* Llama a DELETE /api/proveedores/:clave y redirige
*/
function eliminar() function eliminar()
{ {
$token = getApiToken(); $token = getApiToken();
@@ -207,10 +193,8 @@ function crear()
include __DIR__ . '/../../views/proveedores/crear.php'; include __DIR__ . '/../../views/proveedores/crear.php';
} }
/** /** POST /IMPORTADORES/proveedores/guardar
* POST /IMPORTADORES/proveedores/guardar * Procesa el alta de un nuevo proveedor contra la API WinSAAI. **/
* Procesa el alta de un nuevo proveedor contra la API WinSAAI.
*/
function guardar() function guardar()
{ {
// 1) Validar sesión // 1) Validar sesión

View File

@@ -164,7 +164,7 @@ function guardar()
$transportista_id = $_POST['transportista_id'] ?? null; $transportista_id = $_POST['transportista_id'] ?? null;
$chofer_id = $_POST['chofer_id'] ?? null; $chofer_id = $_POST['chofer_id'] ?? null;
$status = isset($_POST['status']) ? 1 : 0; $status = isset($_POST['status']) ? 1 : 0;
$proveedor_clave = trim($_POST['proveedor_id'] ?? ''); $proveedor_clave = trim($_POST['proveedor_id'] ?? '');
if (empty($num_factura) || empty($fecha) || empty($transportista_id) || empty($chofer_id)) { if (empty($num_factura) || empty($fecha) || empty($transportista_id) || empty($chofer_id)) {
die("❌ Faltan campos obligatorios."); die("❌ Faltan campos obligatorios.");
@@ -933,14 +933,80 @@ function generarHTMLPDF($solicitud, $partidas, $nombre_sistema, $siglas, $logo_u
($solicitud['codigo_postal'] ?? '') ($solicitud['codigo_postal'] ?? '')
); );
// Calcular totales // Calcular el total sumando todas las partidas
$subtotal = 0; $total = 0;
foreach ($partidas as $partida) { foreach ($partidas as $partida) {
$subtotal += (float)$partida['valor_factura']; $total += (float)($partida['valor_factura'] ?? 0);
}
// Función para convertir número a texto
function numeroATexto($numero) {
$unidades = ['', 'uno', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve'];
$decenas = ['', '', 'veinte', 'treinta', 'cuarenta', 'cincuenta', 'sesenta', 'setenta', 'ochenta', 'noventa'];
$especiales = ['diez', 'once', 'doce', 'trece', 'catorce', 'quince', 'dieciséis', 'diecisiete', 'dieciocho', 'diecinueve'];
$centenas = ['', 'ciento', 'doscientos', 'trescientos', 'cuatrocientos', 'quinientos', 'seiscientos', 'setecientos', 'ochocientos', 'novecientos'];
if ($numero == 0) return 'cero';
if ($numero == 100) return 'cien';
if ($numero == 1000) return 'mil';
if ($numero == 1000000) return 'un millón';
$resultado = '';
// Millones
if ($numero >= 1000000) {
$millones = intval($numero / 1000000);
if ($millones == 1) {
$resultado .= 'un millón ';
} else {
$resultado .= numeroATexto($millones) . ' millones ';
}
$numero %= 1000000;
}
// Miles
if ($numero >= 1000) {
$miles = intval($numero / 1000);
if ($miles == 1) {
$resultado .= 'mil ';
} else {
$resultado .= numeroATexto($miles) . ' mil ';
}
$numero %= 1000;
}
// Centenas
if ($numero >= 100) {
$resultado .= $centenas[intval($numero / 100)] . ' ';
$numero %= 100;
}
// Decenas y unidades
if ($numero >= 20) {
$resultado .= $decenas[intval($numero / 10)];
if ($numero % 10 != 0) {
$resultado .= ' y ' . $unidades[$numero % 10];
}
} elseif ($numero >= 10) {
$resultado .= $especiales[$numero - 10];
} elseif ($numero > 0) {
$resultado .= $unidades[$numero];
}
return trim($resultado);
}
// Convertir total a texto
$partes = explode('.', number_format($total, 2, '.', ''));
$pesos = (int)$partes[0];
$centavos = (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.';
} else {
$total_texto .= ' 00/100 M.N.';
} }
$descuento_pct = 0; // Puedes calcularlo si tienes descuentos
$descuento = $subtotal * ($descuento_pct / 100);
$total = $subtotal - $descuento;
$html = ' $html = '
<!DOCTYPE html> <!DOCTYPE html>
@@ -950,83 +1016,86 @@ function generarHTMLPDF($solicitud, $partidas, $nombre_sistema, $siglas, $logo_u
<title>Solicitud de Importación</title> <title>Solicitud de Importación</title>
<style> <style>
body { font-family: Arial, sans-serif; font-size: 9px; margin: 0; padding: 15px; line-height: 1.2; } body { font-family: Arial, sans-serif; font-size: 9px; margin: 0; padding: 15px; line-height: 1.2; }
.header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 10px; border-bottom: 2px solid #000; padding-bottom: 10px; } <!-- Encabezado -->
.logo-section { width: 120px; } .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; } .logo { max-width: 100px; height: auto; }
.company-info { flex: 1; text-align: center; margin: 0 20px; } .company-info { width: 60%; text-align: center; vertical-align: top; }
.company-name { font-weight: bold; font-size: 12px; margin-bottom: 3px; } .company-name { font-weight: bold; font-size: 25px; margin-bottom: 3px; }
.invoice-info { width: 180px; border: 1px solid #000; padding: 8px; } .invoice-info { width: 20%; text-align: right; vertical-align: top; font-size: 12px; }
.invoice-title { background-color: #f0f0f0; text-align: center; font-weight: bold; margin-bottom: 5px; padding: 3px; } <!-- Información del Cliente -->
.info-section { display: flex; margin-bottom: 15px; } .info-section { margin-bottom: 10px; border-bottom: 2px solid #000; padding-bottom: 10px; }
.client-info, .dates-info { flex: 1; border: 1px solid #000; margin-right: 10px; padding: 8px; } .client-info { width: 75%; border: 0.5px solid #000; margin-right: 10px; }
.dates-info { margin-right: 0; width: 200px; } .field { border-bottom: 0.5px solid #000; padding: 5px; font-size: 12px; }
.section-title { background-color: #d0d0d0; font-weight: bold; padding: 3px; margin-bottom: 5px; text-align: center; } .dates-info { width: 25%; border: 0.5px solid #000; }
.products-table { width: 100%; border-collapse: collapse; margin-bottom: 15px; border: 1px solid #000; } .section-title { background-color: #d0d0d0; font-weight: bold; padding: 5px; text-align: center; font-size: 12px; border-right: 0.5px solid #000; }
.products-table th, <!-- Partidas -->
.products-table td { border: 1px solid #000; padding: 4px; text-align: left; font-size: 8px; } .products-table { border-collapse: collapse; margin-bottom: 15px; border: 1px solid #000; }
.products-table th { background-color: #f0f0f0; font-weight: bold; text-align: center; } .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-center { text-align: center; }
.text-right { text-align: right; } .text-right { text-align: right; }
.font-bold { font-weight: bold; } .font-bold { font-weight: bold; }
.totals-section { float: right; width: 250px; margin-top: 10px; } <!-- Total -->
.total-row { display: flex; justify-content: space-between; margin-bottom: 3px; } .totals-section { float: right; width: 250px; }
.footer-info { clear: both; margin-top: 30px; font-size: 8px; background-color: #e0e0e0; padding: 5px; text-align: center; } .total-row { display: flex; justify-content: space-between; margin-top: 25px; font-size: 12px; }
<!-- Nota inferior -->
.footer-info { }
.footer-note { font-size: 10px; background-color: #d0d0d0; padding: 5px; border: 0.5px solid #000; }
</style> </style>
</head> </head>
<body> <body>
<!-- ENCABEZADO --> <!-- ENCABEZADO -->
<div class="header"> <table class="header" cellspacing="0" cellpadding="0" width="100%">
<div class="logo-section"> <tr>
<img src="' . $logo_url . '" alt="Logo" class="logo"> <td class="logo-section">
<div style="font-size: 8px; margin-top: 5px;">' . htmlspecialchars($siglas) . '</div> <img src="' . $logo_url . '" alt="Logo" class="logo"><br>
</div> </td>
<td class="company-info">
<div class="company-info"> <div class="company-name">' . htmlspecialchars($nombre_sistema) . '</div>
<div class="company-name">' . htmlspecialchars($nombre_sistema) . '</div> <div>' . htmlspecialchars($direccion_completa ?: 'Dirección no disponible') . '</div>
<div>' . htmlspecialchars($direccion_completa ?: 'Dirección no disponible') . '</div> <div>RFC: ' . htmlspecialchars($solicitud['importador_rfc'] ?? 'No disponible') . '</div>
<div>RFC: ' . htmlspecialchars($solicitud['importador_rfc'] ?? 'No disponible') . '</div> <div>Tel: ' . htmlspecialchars($solicitud['telefono'] ?? 'No disponible') . '</div>
<div>Tel: ' . htmlspecialchars($solicitud['telefono'] ?? 'No disponible') . '</div> <div>Email: ' . htmlspecialchars($solicitud['correo'] ?? 'No disponible') . '</div>
<div>Email: ' . htmlspecialchars($solicitud['correo'] ?? 'No disponible') . '</div> </td>
</div> <td class="invoice-info">
<div class="invoice-title">Solicitud de Importación</div>
<div class="invoice-info"> <div><strong>No. ' . htmlspecialchars($solicitud['id_solicitud']) . '</strong></div>
<div class="invoice-title">Solicitud de Importación</div> </td>
<div><strong>No. ' . htmlspecialchars($solicitud['id_solicitud']) . '</strong></div> </tr>
<div>Régimen simplificado de confianza (RESICO) - 626</div> </table>
</div> <br><br><br><br>
</div>
<!-- INFORMACIÓN DEL CLIENTE Y FECHAS --> <!-- INFORMACIÓN DEL CLIENTE Y FECHAS -->
<div class="info-section"> <table class="info-section" cellspacing="0" cellpadding="0" width="100%">
<div class="client-info"> <tr>
<div class="section-title">RAZÓN SOCIAL / ' . htmlspecialchars($solicitud['importador_nombre'] ?? 'No disponible') . '</div> <td class="client-info">
<div><strong>DOMICILIO FISCAL:</strong> ' . htmlspecialchars($direccion_completa) . '</div> <div class="field"><strong>RAZÓN SOCIAL:</strong> ' . htmlspecialchars($solicitud['importador_nombre'] ?? 'No disponible') . '</div>
<div style="margin-top: 10px;"> <div class="field"><strong>DOMICILIO FISCAL:</strong> ' . htmlspecialchars($direccion_completa) . '</div>
<div><strong>RFC:</strong> ' . htmlspecialchars($solicitud['importador_rfc'] ?? 'No disponible') . '</div> <div class="field"><strong>RFC:</strong> ' . htmlspecialchars($solicitud['importador_rfc'] ?? 'No disponible') . '</div>
<div><strong>TELÉFONO:</strong> ' . htmlspecialchars($solicitud['telefono'] ?? 'No disponible') . '</div> <div class="field"><strong>TELÉFONO:</strong> ' . htmlspecialchars($solicitud['telefono'] ?? 'No disponible') . '</div>
</div> <div class="field"><strong>RÉGIMEN FISCAL:</strong> 601-General de Ley Personas Morales</div>
<div style="margin-top: 10px;"> </td>
<div><strong>RÉGIMEN FISCAL:</strong> 601-General de Ley Personas Morales</div> <td class="dates-info">
</div> <table width="100%" cellspacing="0" cellpadding="2">
</div> <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>
<div class="dates-info"> <tr><td class="section-title" style="padding-top: 10px;">FECHA DE VENCIMIENTO</td></tr>
<div class="section-title">FECHA DE EXPEDICIÓN</div> <tr><td class="text-center font-bold" style="font-size: 12px; padding-bottom: 15px; padding-top: 12.5px;">' . $fecha_vencimiento . '</td></tr>
<div class="text-center font-bold" style="font-size: 12px;">' . $fecha_expedicion . '</div> </table>
<div class="section-title" style="margin-top: 10px;">FECHA DE VENCIMIENTO</div> </td>
<div class="text-center font-bold" style="font-size: 12px;">' . $fecha_vencimiento . '</div> </tr>
</div> </table>
</div> <br><br><br>
<!-- TABLA DE PRODUCTOS/PARTIDAS --> <!-- TABLA DE PRODUCTOS/PARTIDAS -->
<table class="products-table"> <table class="products-table" cellspacing="0" cellpadding="0" width="100%">
<thead> <thead>
<tr> <tr>
<th>Producto</th> <th>Producto</th>
<th>Unidad de Medida</th> <th>Unidad de Medida</th>
<th>Precio Unitario</th> <th>Precio Unitario</th>
<th>Cantidad</th> <th>Cantidad</th>
<th>Descuento</th>
<th>Total</th> <th>Total</th>
</tr> </tr>
</thead> </thead>
@@ -1041,10 +1110,9 @@ function generarHTMLPDF($solicitud, $partidas, $nombre_sistema, $siglas, $logo_u
$html .= '<tr> $html .= '<tr>
<td>' . htmlspecialchars($partida['descripcion']) . '</td> <td>' . htmlspecialchars($partida['descripcion']) . '</td>
<td>' . htmlspecialchars($partida['unidad_descripcion'] ?? 'Unidad de servicio (E48)') . '</td> <td>' . htmlspecialchars($partida['unidad_descripcion'] ?? 'Unidad de servicio (E48)') . '</td>
<td class="text-right">$' . number_format($precio_unitario > 0 ? $precio_unitario : $valor_partida, 2) . '</td> <td>$' . number_format($precio_unitario > 0 ? $precio_unitario : $valor_partida, 2) . '</td>
<td class="text-center">' . number_format($cantidad > 0 ? $cantidad : 1, 0) . '</td> <td>' . number_format($cantidad > 0 ? $cantidad : 1, 0) . '</td>
<td class="text-right">' . number_format($descuento_pct, 1) . '%</td> <td>$' . number_format($valor_partida, 2) . '</td>
<td class="text-right">$' . number_format($valor_partida, 2) . '</td>
</tr>'; </tr>';
} }
@@ -1052,19 +1120,19 @@ function generarHTMLPDF($solicitud, $partidas, $nombre_sistema, $siglas, $logo_u
</tbody> </tbody>
</table> </table>
<!-- NOTA INFERIOR -->
<div class="footer-info">
<div class="footer-note">' . $total_texto . '</div>
</div>
<!-- TOTALES --> <!-- TOTALES -->
<div class="totals-section"> <div class="totals-section">
<div class="total-row"> <div class="total-row text-right">
<span><strong>Total:</strong></span> <span><strong>Total:</strong></span>
<span><strong>$' . number_format($total, 2) . '</strong></span> <span><strong>$' . number_format($total, 2) . '</strong></span>
</div> </div>
</div> </div>
<!-- NOTA INFERIOR -->
<div class="footer-note">
Veinticinco mil seiscientos 50/100 M.N
</div>
</body> </body>
</html>'; </html>';