This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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>';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user