ALTA PROVEEDORES
LISTADO Y ALTA DE PROVEEDORES
This commit is contained in:
2
.env
2
.env
@@ -1,4 +1,4 @@
|
||||
DB_HOST=DESKTOP-22T88B6
|
||||
DB_HOST=LOCALHOST
|
||||
DB_DATABASE=Importaciones_HC
|
||||
DB_USERNAME=sa
|
||||
DB_PASSWORD=Soluciones01
|
||||
|
||||
@@ -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
128
views/proveedores/crear.php
Normal 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>
|
||||
@@ -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,31 +26,32 @@
|
||||
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 {
|
||||
color: #ccc;
|
||||
padding: 12px 20px;
|
||||
}
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active {
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active {
|
||||
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 {
|
||||
@@ -59,15 +70,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>
|
||||
@@ -82,47 +96,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?',
|
||||
@@ -139,6 +118,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',
|
||||
|
||||
Reference in New Issue
Block a user