This commit is contained in:
2025-05-12 16:31:07 -06:00
4 changed files with 275 additions and 53 deletions

2
.env
View File

@@ -1,4 +1,4 @@
DB_HOST=DESKTOP-22T88B6
DB_HOST=LOCALHOST
DB_DATABASE=Importaciones_HC
DB_USERNAME=sa
DB_PASSWORD=Soluciones01

View File

@@ -180,3 +180,92 @@ function eliminar()
header('Location: /IMPORTADORES/proveedores?deleted=' . ($status===200||$status===204 ? 'ok':'error'));
exit;
}
function crear()
{
// 1) Aseguramos que el usuario esté logueado
if (empty($_SESSION['usuario_id'])) {
header('Location: /IMPORTADORES/login');
exit;
}
// 2) Cargamos la vista
include __DIR__ . '/../../views/proveedores/crear.php';
}
/**
* POST /IMPORTADORES/proveedores/guardar
* Procesa el alta de un nuevo proveedor contra la API WinSAAI.
*/
function guardar()
{
// 1) Validar sesión
if (empty($_SESSION['usuario_id'])) {
header('Location: /IMPORTADORES/login');
exit;
}
// 2) Recoger datos del formulario
$payload = [
'Clave' => trim($_POST['Clave'] ?? ''),
'Nombre' => trim($_POST['Nombre'] ?? ''),
'RFC' => trim($_POST['RFC'] ?? ''),
'Ciudad' => trim($_POST['Ciudad'] ?? ''),
'Telefono' => trim($_POST['Telefono'] ?? ''),
'Pais' => trim($_POST['Pais'] ?? ''),
'Colonia' => trim($_POST['Colonia'] ?? ''),
'Municipio' => trim($_POST['Municipio'] ?? ''),
'CodigoPostal' => trim($_POST['CodigoPostal'] ?? ''),
'EntidadFederativa' => trim($_POST['EntidadFederativa'] ?? ''),
];
// 3) Validar campos obligatorios
if ($payload['Clave']==='' || $payload['Nombre']==='' || $payload['RFC']==='' || $payload['Ciudad']==='') {
$_SESSION['flash_error'] = 'Clave, Nombre, RFC y Ciudad son obligatorios.';
header('Location: /IMPORTADORES/proveedores/crear');
exit;
}
// 4) Obtener JWT
$token = getApiToken();
if (!$token) {
$_SESSION['flash_error'] = 'No fue posible autenticar contra la API.';
header('Location: /IMPORTADORES/proveedores/crear');
exit;
}
// 5) Llamada a la API para crear el proveedor
$url = rtrim($_ENV['API_URL'] ?? '', '/') . '/api/proveedores';
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer $token",
'Content-Type: application/json'
],
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$resp = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// 6) Manejar respuesta
if ($status === 201) {
// Éxito
header('Location: /IMPORTADORES/proveedores?created=ok');
exit;
} else {
// Error, opcionalmente parsear mensaje
$msg = '';
if ($json = json_decode($resp, true)) {
$msg = $json['mensaje'] ?? $json['error'] ?? '';
}
$_SESSION['flash_error'] = "Error al crear proveedor (HTTP $status) $msg";
header('Location: /IMPORTADORES/proveedores/crear');
exit;
}
}

128
views/proveedores/crear.php Normal file
View File

@@ -0,0 +1,128 @@
<!-- views/proveedores/crear.php -->
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title> Registro de Proveedor</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- SweetAlert2 -->
<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; /* coincide con navbar */
z-index: 1040;
}
.sidebar .nav-link {
color: #ccc;
padding: 12px 20px;
}
.sidebar .nav-link:hover,
.sidebar .nav-link.active {
background-color: #495057;
color: #fff;
}
.content {
margin-top: 56px;
padding: 40px 20px;
background-color: #f4f6f9;
transition: margin-left .3s ease;
}
@media (min-width: 768px) {
.content { margin-left: 250px; }
}
@media (max-width: 767.98px) {
.content { margin-left: 0; }
}
.card { border-radius: 12px; }
</style>
</head>
<body>
<?php
// Mostrar alerta si hay error en sesión
if (!empty($_SESSION['flash_error'])):
?>
<script>
Swal.fire({
icon: 'error',
title: 'Error al registrar',
text: '<?= addslashes($_SESSION['flash_error']) ?>'
});
</script>
<?php
unset($_SESSION['flash_error']);
endif;
?>
<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
<div class="content">
<h4 class="mb-4"> Registro de Proveedor</h4>
<div class="card p-4 shadow-sm">
<form action="/IMPORTADORES/proveedores/guardar" method="POST">
<div class="row g-3">
<div class="col-md-4">
<label for="Clave" class="form-label">Clave *</label>
<input type="text" name="Clave" id="Clave" class="form-control" required>
</div>
<div class="col-md-8">
<label for="Nombre" class="form-label">Nombre *</label>
<input type="text" name="Nombre" id="Nombre" class="form-control" required>
</div>
<div class="col-md-4">
<label for="RFC" class="form-label">RFC *</label>
<input type="text" name="RFC" id="RFC" class="form-control" required>
</div>
<div class="col-md-4">
<label for="Ciudad" class="form-label">Ciudad *</label>
<input type="text" name="Ciudad" id="Ciudad" class="form-control" required>
</div>
<div class="col-md-4">
<label for="Telefono" class="form-label">Teléfono</label>
<input type="tel" name="Telefono" id="Telefono" class="form-control">
</div>
<div class="col-md-6">
<label for="Pais" class="form-label">País</label>
<input type="text" name="Pais" id="Pais" class="form-control">
</div>
<div class="col-md-6">
<label for="EntidadFederativa" class="form-label">Entidad Federativa</label>
<input type="text" name="EntidadFederativa" id="EntidadFederativa" class="form-control">
</div>
<div class="col-md-4">
<label for="Municipio" class="form-label">Municipio</label>
<input type="text" name="Municipio" id="Municipio" class="form-control">
</div>
<div class="col-md-4">
<label for="Colonia" class="form-label">Colonia</label>
<input type="text" name="Colonia" id="Colonia" class="form-control">
</div>
<div class="col-md-4">
<label for="CodigoPostal" class="form-label">Código Postal</label>
<input type="text" name="CodigoPostal" id="CodigoPostal" class="form-control">
</div>
</div>
<div class="mt-4 text-end">
<a href="/IMPORTADORES/proveedores" class="btn btn-secondary me-2">Cancelar</a>
<button type="submit" class="btn btn-primary">Guardar Proveedor</button>
</div>
</form>
</div>
</div>
<!-- Bootstrap JS Bundle -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@@ -1,14 +1,24 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>📄 Listado de Proveedores</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap y DataTables CSS -->
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- DataTables CSS -->
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
<!-- SweetAlert2 -->
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<style>
body { background: #f4f6f9; font-family: 'Segoe UI', sans-serif; }
table.dataTable thead th { background: #343a40; color: #fff; }
body {
font-family: 'Segoe UI', sans-serif;
background-color: #f4f6f9;
}
.sidebar {
width: 220px;
height: 100vh;
@@ -16,8 +26,8 @@
position: fixed;
top: 0;
left: 0;
padding-top: 56px; /* Alineado con la altura de la navbar */
z-index: 1040; /* Asegura que esté por encima del contenido */
padding-top: 56px; /* coincide con la navbar */
z-index: 1040;
}
.sidebar .nav-link {
font-weight: bold;
@@ -29,20 +39,20 @@
background-color: #495057;
color: #fff;
}
.content {
margin-top: 56px; /* Ajusta debajo de navbar */
padding: 40px 20px;
position: relative;
z-index: 1;
background-color: #f4f6f9; /* Asegura fondo uniforme */
transition: margin-left 0.3s ease;
}
.navbar {
position: fixed;
top: 0;
width: 100%;
z-index: 1050;
}
.content {
margin-top: 56px; /* deja espacio para la navbar */
padding: 40px 20px;
position: relative;
z-index: 1;
background-color: #f4f6f9;
transition: margin-left .3s ease;
}
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
@media (min-width: 768px) {
.content {
@@ -71,15 +81,18 @@
}
</style>
</head>
<body>
<body>
<!-- Sidebar -->
<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
<div class="content">
<br> <br>
<h4 class="mb-4">📄 Listado de Proveedores</h4>
<a href="/IMPORTADORES/proveedores/crear" class="btn btn-success mb-3"> Nuevo Proveedor</a>
<div class="card p-3 shadow-sm">
<table id="proveedores-table" class="display nowrap" style="width:100%">
<br>
<table id="proveedores-table" class="table table-striped nowrap" style="width:100%">
<thead>
<tr>
<th>Clave</th>
@@ -94,47 +107,12 @@
</div>
</div>
<!-- JS: jQuery, DataTables, Bootstrap, SweetAlert2 -->
<!-- JS: jQuery, DataTables, Bootstrap Bundle -->
<script src="https://code.jquery.com/jquery-3.6.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/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<script>
$(function(){
$('#proveedores-table').DataTable({
serverSide: true,
processing: true,
ajax: {
url: '/IMPORTADORES/proveedores/ajax_lista',
type: 'GET',
dataSrc: function(json) {
if (!json || !Array.isArray(json.data)) {
console.error('Respuesta inválida de ajax_lista:', json);
return [];
}
return json.data;
},
error: function(xhr, status, error) {
console.error('Error Ajax:', status, error, '\nResponse:', xhr.responseText);
Swal.fire({
icon: 'error',
title: 'Error al cargar proveedores',
text: 'Revisa la consola para más detalles.',
});
}
},
columns: [
{ data: 0 }, { data: 1 }, { data: 2 },
{ data: 3 }, { data: 4 }, { data: 5 }
],
language: {
url: '//cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json'
}
});
});
function confirmDelete(clave) {
Swal.fire({
title: '¿Desactivar proveedor?',
@@ -151,6 +129,33 @@
});
}
$(function(){
$('#proveedores-table').DataTable({
processing: true,
serverSide: false,
pageLength: 25,
lengthMenu: [10,25,50,100],
scrollX: true,
ajax: {
url: '/IMPORTADORES/proveedores/ajax_lista',
type: 'GET',
dataSrc: 'data'
},
columns: [
{ data: 0 },
{ data: 1 },
{ data: 2 },
{ data: 3 },
{ data: 4, className: 'text-center' },
{ data: 5, className: 'text-center' }
],
language: {
processing: '<div class="spinner-border text-primary" role="status"><span class="visually-hidden">Cargando...</span></div>',
url: '//cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json'
}
});
});
<?php if (isset($_GET['deleted']) && $_GET['deleted']==='ok'): ?>
Swal.fire({
icon: 'success',