Files
MVE/views/mve/lista.php

2514 lines
91 KiB
PHP

<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>📄 Manifestación de Valor - Pedimentos</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="https://cdn.jsdelivr.net/npm/signature_pad@4.1.5/dist/signature_pad.umd.min.js"></script>
<style>
/* Modern Professional Styling */
:root {
--primary-color: #343a40;
--secondary-color: #495057;
--accent-color: #0d6efd;
--success-color: #198754;
--info-color: #0dcaf0;
--light-bg: #f8f9fa;
--card-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--card-shadow-hover: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
--border-radius: 0.75rem;
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 100vh;
}
.sidebar {
width: var(--sidebar-width, 280px);
height: 100vh;
background: linear-gradient(180deg, #343a40 0%, #495057 100%);
position: fixed;
top: 0;
left: 0;
padding-top: 56px;
z-index: 1040;
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1);
}
.sidebar .nav-link {
font-weight: 600;
color: white;
transition: var(--transition);
border-radius: 0.5rem;
margin: 0.25rem 0.75rem;
}
.sidebar .nav-link:hover, .sidebar .nav-link.active {
background: linear-gradient(135deg, #495057 0%, #6c757d 100%);
color: #fff;
transform: translateX(5px);
}
.content {
margin-top: 56px;
padding: 2rem;
position: relative;
z-index: 1;
transition: var(--transition);
}
@media (min-width: 768px) {
.content { margin-left: var(--sidebar-width, 280px); }
}
@media (max-width: 767.98px) {
.content { margin-left: 0; padding: 1rem; }
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
}
/* Enhanced Card Styling */
.card {
border-radius: var(--border-radius);
border: none;
box-shadow: var(--card-shadow);
backdrop-filter: blur(10px);
background: rgba(255, 255, 255, 0.95);
transition: var(--transition);
}
.card-hover:hover {
transform: translateY(-4px);
box-shadow: var(--card-shadow-hover);
}
/* Professional Header Styling */
.page-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: var(--border-radius);
padding: 2rem;
margin-bottom: 2rem;
color: white;
box-shadow: var(--card-shadow-hover);
}
.page-header h4 {
margin: 0;
font-weight: 700;
font-size: 1.75rem;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.page-header .subtitle {
opacity: 0.9;
margin-top: 0.5rem;
font-size: 1rem;
font-weight: 400;
}
/* DataTable Enhancements */
table.dataTable thead th {
background: linear-gradient(135deg, #343a40 0%, #495057 100%);
color: #fff;
font-weight: 600;
text-transform: uppercase;
font-size: 0.75rem;
letter-spacing: 0.5px;
padding: 1rem 0.75rem;
border: none;
}
table.dataTable tbody td {
padding: 0.875rem 0.75rem;
border-bottom: 1px solid #f1f3f4;
vertical-align: middle;
}
table.dataTable tbody tr:hover {
background-color: #f8f9fa;
transform: scale(1.002);
}
/* Animation Classes */
.fade-in-up {
animation: fadeInUp 0.8s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Enhanced Button Styling */
.btn-animated {
transition: var(--transition);
position: relative;
overflow: hidden;
font-weight: 600;
letter-spacing: 0.025em;
}
.btn-animated:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.btn-animated::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
.btn-animated:hover::before {
left: 100%;
}
/* Modern Card Grid System */
.pedimentos-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 1.5rem;
margin-top: 1.5rem;
}
@media (max-width: 576px) {
.pedimentos-grid {
grid-template-columns: 1fr;
gap: 1rem;
}
}
.pedimento-card {
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
border-radius: var(--border-radius);
padding: 1.5rem;
box-shadow: var(--card-shadow);
border: 2px solid #e9ecef;
transition: var(--transition);
position: relative;
overflow: hidden;
}
.pedimento-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, var(--accent-color), var(--success-color));
}
.pedimento-card:hover {
transform: translateY(-8px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
border-color: var(--accent-color);
}
.pedimento-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.pedimento-numero {
font-size: 1.25rem;
font-weight: 700;
color: var(--accent-color);
display: flex;
align-items: center;
}
.pedimento-status {
padding: 0.25rem 0.75rem;
border-radius: 2rem;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.status-activo {
background: linear-gradient(135deg, #d1e7dd, #a3cfbb);
color: var(--success-color);
}
.status-inactivo {
background: linear-gradient(135deg, #e2e3e5, #ced4da);
color: #6c757d;
}
.pedimento-info {
margin-bottom: 1.5rem;
}
.info-row {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid #f1f3f4;
}
.info-row:last-child {
border-bottom: none;
margin-bottom: 0;
}
.info-label {
font-weight: 600;
color: #6c757d;
font-size: 0.875rem;
}
.info-value {
font-weight: 500;
color: #495057;
}
.pedimento-actions {
display: flex;
gap: 0.75rem;
}
.btn-mve {
flex: 1;
background: linear-gradient(135deg, #17a2b8 0%, #138496 100%);
border: none;
color: white;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
font-weight: 600;
transition: var(--transition);
text-transform: uppercase;
font-size: 0.875rem;
letter-spacing: 0.5px;
}
.btn-mve:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(23, 162, 184, 0.3);
color: white;
}
/* KPI Band */
.kpi-card {
border-left: 6px solid var(--accent-color);
}
.kpi-value {
font-size: 1.6rem;
font-weight: 800;
line-height: 1;
}
.kpi-label {
color: #6c757d;
font-weight: 600;
letter-spacing: 0.02em;
}
/* Search and Filter Bar */
.filter-bar {
background: rgba(255, 255, 255, 0.95);
padding: 1.5rem;
border-radius: var(--border-radius);
box-shadow: var(--card-shadow);
margin-bottom: 2rem;
backdrop-filter: blur(10px);
}
.search-input {
border: 2px solid #e9ecef;
border-radius: 2rem;
padding: 0.75rem 1.5rem;
font-size: 1rem;
transition: var(--transition);
width: 100%;
}
.search-input:focus {
border-color: var(--accent-color);
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
outline: none;
}
.filter-select {
border: 2px solid #e9ecef;
border-radius: 0.5rem;
padding: 0.75rem 1rem;
transition: var(--transition);
}
.filter-select:focus {
border-color: var(--accent-color);
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
outline: none;
}
/* Loading and Empty States */
.loading-spinner {
display: flex;
justify-content: center;
align-items: center;
min-height: 200px;
flex-direction: column;
}
.empty-state {
text-align: center;
padding: 3rem 1rem;
color: #6c757d;
}
.empty-state i {
font-size: 4rem;
margin-bottom: 1rem;
opacity: 0.5;
}
/* Modern Slide Panel System */
.slide-panel {
position: fixed;
top: 0;
right: -100%;
width: 70%;
max-width: 900px;
height: 100vh;
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
box-shadow: -10px 0 30px rgba(0, 0, 0, 0.2);
z-index: 99999;
transition: right 0.4s cubic-bezier(0.4, 0, 0.2, 1);
overflow-y: auto;
border-radius: 1rem 0 0 1rem;
}
.slide-panel.active {
right: 0;
}
.slide-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 99998;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
backdrop-filter: blur(5px);
}
.slide-overlay.active {
opacity: 1;
visibility: visible;
}
.panel-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 2rem;
position: sticky;
top: 0;
z-index: 10;
}
.panel-title {
margin: 0;
font-size: 1.5rem;
font-weight: 700;
}
.panel-subtitle {
margin: 0.5rem 0 0 0;
opacity: 0.9;
font-size: 1rem;
}
.panel-close {
position: absolute;
top: 1.5rem;
right: 2rem;
background: rgba(255, 255, 255, 0.2);
border: none;
color: white;
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: var(--transition);
}
.panel-close:hover {
background: rgba(255, 255, 255, 0.3);
transform: rotate(90deg);
}
.panel-body {
padding: 2rem;
}
.panel-section {
margin-bottom: 2rem;
}
.section-card {
background: rgba(255, 255, 255, 0.8);
border-radius: var(--border-radius);
padding: 1.5rem;
box-shadow: var(--card-shadow);
border: 2px solid #f1f3f4;
transition: var(--transition);
}
.section-card:hover {
border-color: #e9ecef;
box-shadow: var(--card-shadow-hover);
}
.section-header {
display: flex;
align-items: center;
margin-bottom: 1.5rem;
padding-bottom: 1rem;
border-bottom: 2px solid #f1f3f4;
}
.section-icon {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 1rem;
color: white;
font-weight: bold;
}
.section-info h6 {
margin: 0;
font-weight: 700;
font-size: 1.1rem;
}
.section-info small {
color: #6c757d;
font-weight: 500;
}
/* Step Navigation */
.step-nav {
display: flex;
justify-content: center;
margin: 2rem 0;
gap: 0.75rem;
flex-wrap: wrap;
}
.step-btn {
padding: 0.5rem 1rem;
border: 2px solid #e9ecef;
background: white;
border-radius: 1.5rem;
font-weight: 600;
font-size: 0.875rem;
transition: var(--transition);
cursor: pointer;
text-decoration: none;
color: #495057;
display: inline-flex;
align-items: center;
justify-content: center;
user-select: none;
white-space: nowrap;
min-width: fit-content;
}
.step-btn:focus {
outline: none;
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
}
.step-btn.active {
background: var(--accent-color);
border-color: var(--accent-color);
color: white !important;
}
.step-btn:hover:not(.active) {
border-color: var(--accent-color);
color: var(--accent-color);
transform: translateY(-2px);
}
/* Enhanced Responsive Design */
/* Large Tablets and Small Desktops */
@media (max-width: 992px) {
.slide-panel {
width: 85%;
max-width: 800px;
}
.pedimento-card {
margin-bottom: 1rem;
}
}
/* Tablets */
@media (max-width: 768px) {
.slide-panel {
width: 95%;
border-radius: 1rem 0 0 0;
}
.pedimentos-grid {
grid-template-columns: 1fr;
gap: 1rem;
}
.panel-header {
padding: 1.5rem 1rem;
}
.panel-body {
padding: 1rem;
}
.step-nav {
gap: 0.5rem;
margin: 1.5rem 0;
}
.step-btn {
padding: 0.4rem 0.75rem;
font-size: 0.75rem;
}
.step-btn i {
font-size: 0.875rem;
margin-right: 0.25rem !important;
}
/* Hacer formularios más compactos en tablets */
.section-card {
padding: 1rem;
}
.row.mb-3 {
padding: 1rem;
margin-bottom: 1rem !important;
}
/* Ajustar el grid de formularios para tablets */
.row .col-md-4, .row .col-md-6 {
margin-bottom: 1rem;
}
}
/* Mobile Phones */
@media (max-width: 576px) {
.content {
padding: 1rem;
}
.page-header {
padding: 1.5rem 1rem;
margin-bottom: 1.5rem;
}
.page-header h4 {
font-size: 1.5rem;
}
.filter-bar {
padding: 1rem;
}
.slide-panel {
width: 100%;
border-radius: 1rem 0 0 0;
right: -100%;
}
.step-nav {
flex-direction: column;
align-items: center;
gap: 0.75rem;
margin: 1rem 0;
}
.step-btn {
width: 100%;
max-width: 280px;
justify-content: center;
padding: 0.75rem 1rem;
font-size: 0.875rem;
}
/* Formularios mobile-first */
.section-card {
padding: 0.75rem;
margin-bottom: 1rem;
}
.section-header {
flex-direction: column;
text-align: center;
margin-bottom: 1rem;
}
.section-icon {
margin-right: 0;
margin-bottom: 0.5rem;
}
/* Hacer todos los campos full-width en mobile */
.row .col-md-3,
.row .col-md-4,
.row .col-md-6 {
margin-bottom: 1rem;
}
/* Pedimento cards más compactas */
.pedimento-card {
padding: 1rem;
}
.pedimento-header {
flex-direction: column;
gap: 0.5rem;
text-align: center;
}
.info-row {
flex-direction: column;
gap: 0.25rem;
}
.info-label {
font-size: 0.75rem;
}
.btn-mve {
font-size: 0.875rem;
padding: 0.75rem;
}
}
/* Extra Small Devices */
@media (max-width: 400px) {
.page-header h4 {
font-size: 1.25rem;
}
.pedimento-numero {
font-size: 1rem;
}
.step-btn {
font-size: 0.75rem;
padding: 0.5rem 0.75rem;
}
.section-card {
padding: 0.5rem;
}
}
/* Enhanced Factura Selection Styling */
.factura-item {
border: 2px solid #e9ecef;
border-radius: var(--border-radius);
padding: 1rem;
margin-bottom: 0.75rem;
cursor: pointer;
transition: var(--transition);
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
position: relative;
overflow: hidden;
}
.factura-item::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(13, 110, 253, 0.1), transparent);
transition: left 0.5s;
}
.factura-item:hover {
border-color: var(--accent-color);
box-shadow: 0 4px 12px rgba(13, 110, 253, 0.15);
transform: translateY(-2px);
}
.factura-item:hover::before {
left: 100%;
}
.factura-item.selected {
border-color: var(--success-color);
background: linear-gradient(135deg, #d1e7dd 0%, #a3cfbb 100%);
box-shadow: 0 6px 20px rgba(25, 135, 84, 0.2);
transform: translateY(-2px);
}
.factura-numero {
font-weight: 700;
color: var(--accent-color);
font-size: 1.1rem;
margin-bottom: 0.25rem;
display: flex;
align-items: center;
}
.factura-numero::before {
content: '📄';
margin-right: 0.5rem;
}
.factura-item.selected .factura-numero {
color: var(--success-color);
}
.factura-item.selected .factura-numero::before {
content: '✅';
}
.factura-details {
font-size: 0.875rem;
color: #6c757d;
font-weight: 500;
}
.factura-item.selected .factura-details {
color: #495057;
font-weight: 600;
}
/* Status Badge Enhancements */
.badge {
font-weight: 600;
padding: 0.5rem 0.75rem;
border-radius: 2rem;
font-size: 0.75rem;
letter-spacing: 0.025em;
}
/* Enhanced Form Controls */
.form-control {
border: 2px solid #e9ecef;
border-radius: 0.5rem;
padding: 0.75rem 1rem;
transition: var(--transition);
font-weight: 500;
}
.form-control:focus {
border-color: var(--accent-color);
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
outline: none;
}
.form-label {
font-weight: 600;
color: #495057;
margin-bottom: 0.5rem;
font-size: 0.875rem;
}
/* Enhanced Radio Buttons */
.form-check-input[type="radio"] {
border: 2px solid #dee2e6;
margin-top: 0.125rem;
width: 1.25rem;
height: 1.25rem;
transition: var(--transition);
}
.form-check-input[type="radio"]:checked {
background-color: var(--success-color);
border-color: var(--success-color);
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='2' fill='%23fff'/%3E%3C/svg%3E");
}
.form-check-input[type="radio"]:focus {
border-color: var(--success-color);
box-shadow: 0 0 0 0.2rem rgba(25, 135, 84, 0.25);
}
.form-check-label {
font-weight: 500;
color: #495057;
margin-left: 0.5rem;
}
.form-check-inline {
margin-right: 1rem;
}
/* Form Section Styling */
.row.mb-3 {
background: rgba(255, 255, 255, 0.7);
border-radius: 0.75rem;
padding: 1.25rem;
margin-bottom: 1.5rem !important;
border: 1px solid #f1f3f4;
transition: var(--transition);
}
.row.mb-3:hover {
background: rgba(255, 255, 255, 0.9);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
/* Modal Footer Enhancement */
.modal-footer {
background: #f8f9fa;
border-top: 2px solid #e9ecef;
padding: 1.5rem 2rem;
border-radius: 0 0 var(--border-radius) var(--border-radius);
}
.modal-footer .btn {
padding: 0.75rem 2rem;
font-weight: 600;
border-radius: 2rem;
transition: var(--transition);
}
/* Save Button Special Styling */
#btnGuardarDatosMVE {
background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
border: none;
color: white;
padding: 0.875rem 2rem;
border-radius: 2rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.3);
transition: var(--transition);
}
#btnGuardarDatosMVE:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 123, 255, 0.4);
}
/* Loading States */
.spinner-border {
width: 2rem;
height: 2rem;
border-width: 0.25rem;
}
/* Alert Enhancements */
.alert {
border-radius: 0.75rem;
border: none;
font-weight: 500;
box-shadow: var(--card-shadow);
}
.alert-warning {
background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%);
color: #856404;
}
.alert-danger {
background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%);
color: #721c24;
}
/* Ensure SweetAlert2 toasts are always on top of slide panel */
.swal2-container {
z-index: 200000 !important;
}
</style>
</head>
<body>
<div class="content">
<div class="page-header fade-in-up">
<h4>📄 Manifestación de Valor - Pedimentos</h4>
<p class="subtitle mb-0">Gestiona y solicita manifestaciones de valor para tus pedimentos de importación</p>
</div>
<!-- Barra de búsqueda y filtros -->
<div class="filter-bar fade-in-up">
<div class="row g-3 align-items-end">
<div class="col-12 col-md-6 col-lg-5">
<label class="form-label fw-bold text-muted mb-2 d-block d-md-none">Buscar:</label>
<div class="input-group">
<span class="input-group-text bg-white border-end-0" style="border: 2px solid #e9ecef; border-radius: 2rem 0 0 2rem;">
<i class="fas fa-search text-muted"></i>
</span>
<input type="text" class="search-input border-start-0" id="searchInput" placeholder="Buscar por pedimento, RFC o cliente..." style="border-radius: 0 2rem 2rem 0;">
</div>
</div>
<div class="col-6 col-md-3 col-lg-3">
<label class="form-label fw-bold text-muted mb-2 d-block d-md-none">Estado:</label>
<select class="filter-select form-select" id="statusFilter">
<option value="">Todos los estados</option>
<option value="Activo">Activos</option>
<option value="Inactivo">Inactivos</option>
</select>
</div>
<div class="col-6 col-md-3 col-lg-3">
<label class="form-label fw-bold text-muted mb-2 d-block d-md-none">Mostrar:</label>
<div class="d-flex align-items-center gap-2">
<span class="text-muted fw-bold d-none d-md-inline">Mostrar:</span>
<select class="filter-select form-select" id="perPageFilter">
<option value="10">10 por página</option>
<option value="25" selected>25 por página</option>
<option value="50">50 por página</option>
</select>
</div>
</div>
<div class="col-12 col-md-12 col-lg-4 d-flex align-items-center justify-content-lg-end">
<ul class="nav nav-pills ms-auto" id="viewTabs">
<li class="nav-item">
<a class="nav-link active" href="#" data-view="pendientes">Pendientes <span class="badge bg-secondary ms-1" id="countPend">0</span></a>
</li>
<li class="nav-item ms-2">
<a class="nav-link" href="#" data-view="completos">Completos <span class="badge bg-secondary ms-1" id="countComp">0</span></a>
</li>
</ul>
</div>
</div>
</div>
<!-- Grid de pedimentos -->
<div class="fade-in-up">
<!-- KPI Band -->
<div id="kpiBand" class="row g-3 mb-3">
<div class="col-12 col-md-4">
<div class="card p-3 kpi-card">
<div class="d-flex justify-content-between align-items-center">
<div>
<div class="kpi-label">Total (filtrados)</div>
<div id="kpiTotal" class="kpi-value">0</div>
</div>
<i class="fas fa-list text-primary" style="font-size: 1.5rem;"></i>
</div>
</div>
</div>
<div class="col-12 col-md-4">
<div class="card p-3 kpi-card" style="border-left-color:#fd7e14;">
<div class="d-flex justify-content-between align-items-center">
<div>
<div class="kpi-label">Pendientes</div>
<div id="kpiPend" class="kpi-value">0</div>
</div>
<i class="fas fa-hourglass-half text-warning" style="font-size: 1.5rem;"></i>
</div>
</div>
</div>
<div class="col-12 col-md-4">
<div class="card p-3 kpi-card" style="border-left-color:#198754;">
<div class="d-flex justify-content-between align-items-center">
<div>
<div class="kpi-label">Completos</div>
<div id="kpiComp" class="kpi-value">0</div>
</div>
<i class="fas fa-check-circle text-success" style="font-size: 1.5rem;"></i>
</div>
</div>
</div>
</div>
<div id="pedimentosContainer">
<div class="loading-spinner">
<div class="spinner-border text-primary" style="width: 3rem; height: 3rem;">
<span class="visually-hidden">Cargando...</span>
</div>
<p class="mt-3 text-muted fw-bold">Cargando pedimentos...</p>
</div>
</div>
<!-- Paginación -->
<div class="d-flex justify-content-between align-items-center mt-4" id="paginationContainer" style="display: none !important;">
<div class="text-muted">
Mostrando <span id="showingFrom">0</span> a <span id="showingTo">0</span> de <span id="totalRecords">0</span> registros <span id="viewLabel"></span>
</div>
<nav>
<ul class="pagination mb-0" id="pagination">
<!-- Pagination buttons will be generated here -->
</ul>
</nav>
</div>
</div>
</div>
<!-- Slide Panel para MVE -->
<div class="slide-overlay" id="slideOverlay"></div>
<div class="slide-panel" id="slidePanel">
<div class="panel-header">
<h4 class="panel-title">📋 Manifestación de Valor</h4>
<p class="panel-subtitle">Pedimento: <span id="panelPedimento"></span></p>
<button class="panel-close" id="panelClose">
<i class="fas fa-times"></i>
</button>
</div>
<div class="panel-body">
<!-- Selección de Facturas -->
<div class="panel-section">
<div class="section-card">
<div class="section-header">
<div class="section-icon" style="background: linear-gradient(135deg, #17a2b8, #138496);">
<i class="fas fa-file-invoice"></i>
</div>
<div class="section-info">
<h6>Facturas del Pedimento</h6>
<small>Selecciona la factura para procesar</small>
</div>
</div>
<div id="facturasContainer" style="max-height: 300px; overflow-y: auto;">
<!-- Las facturas se cargan aquí -->
</div>
</div>
</div>
<!-- Navegación por pasos -->
<div class="step-nav" id="stepNavigation" style="display: none;">
<button class="step-btn active" data-step="65" onclick="cambiarSeccionMVE('65')">
<i class="fas fa-plus-circle me-2"></i>Art. 65
</button>
<button class="step-btn" data-step="66" onclick="cambiarSeccionMVE('66')">
<i class="fas fa-minus-circle me-2"></i>Art. 66
</button>
<button class="step-btn" data-step="precio-pagado" onclick="cambiarSeccionMVE('precio-pagado')">
<i class="fas fa-money-bill-wave me-2"></i>Precio Pagado
</button>
<button class="step-btn" data-step="precio-pagar" onclick="cambiarSeccionMVE('precio-pagar')">
<i class="fas fa-credit-card me-2"></i>Precio por Pagar
</button>
<button class="step-btn" data-step="compenso-pago" onclick="cambiarSeccionMVE('compenso-pago')">
<i class="fas fa-balance-scale me-2"></i>Compenso Pago
</button>
</div>
<!-- Formularios MVE -->
<!-- Sección de Artículo 65 -->
<div class="panel-section" id="art65Panel" style="display: none;">
<div class="section-card">
<div class="section-header">
<div class="section-icon" style="background: linear-gradient(135deg, #28a745, #20c997);">
<i class="fas fa-plus"></i>
</div>
<div class="section-info">
<h6>Artículo 65 - Incrementables</h6>
<small>Conceptos que incrementan el valor en aduana</small>
</div>
</div>
<div id="art65Form">
<!-- Los formularios Art 65 se cargarán aquí dinámicamente -->
</div>
</div>
</div>
<!-- Sección de Artículo 66 -->
<div class="panel-section" id="art66Panel" style="display: none;">
<div class="section-card">
<div class="section-header">
<div class="section-icon" style="background: linear-gradient(135deg, #0d6efd, #0dcaf0);">
<i class="fas fa-minus"></i>
</div>
<div class="section-info">
<h6>Artículo 66 - Decrementables</h6>
<small>Conceptos que no forman parte del valor en aduana</small>
</div>
</div>
<div id="art66Form">
<!-- Los formularios Art 66 se cargarán aquí dinámicamente -->
</div>
</div>
</div>
<!-- Sección de Precio Pagado -->
<div class="panel-section" id="precioPagadoPanel" style="display: none;">
<div class="section-card">
<div class="section-header">
<div class="section-icon" style="background: linear-gradient(135deg, #28a745, #20c997);">
<i class="fas fa-money-bill-wave"></i>
</div>
<div class="section-info">
<h6>Precio Pagado</h6>
<small>Montos ya pagados por las mercancías</small>
</div>
</div>
<div id="precioPagadoForm">
<!-- Los formularios de Precio Pagado se cargarán aquí dinámicamente -->
</div>
</div>
</div>
<!-- Sección de Precio por Pagar -->
<div class="panel-section" id="precioPagarPanel" style="display: none;">
<div class="section-card">
<div class="section-header">
<div class="section-icon" style="background: linear-gradient(135deg, #ffc107, #fd7e14);">
<i class="fas fa-credit-card"></i>
</div>
<div class="section-info">
<h6>Precio por Pagar</h6>
<small>Montos pendientes de pago</small>
</div>
</div>
<div id="precioPagarForm">
<!-- Los formularios de Precio por Pagar se cargarán aquí dinámicamente -->
</div>
</div>
</div>
<!-- Sección de Compenso Pago -->
<div class="panel-section" id="compensoPagoPanel" style="display: none;">
<div class="section-card">
<div class="section-header">
<div class="section-icon" style="background: linear-gradient(135deg, #6f42c1, #e83e8c);">
<i class="fas fa-balance-scale"></i>
</div>
<div class="section-info">
<h6>Compenso Pago</h6>
<small>Ajustes y compensaciones de pago</small>
</div>
</div>
<div id="compensoPagoForm">
<!-- Los formularios de Compenso Pago se cargarán aquí dinámicamente -->
</div>
</div>
</div>
<!-- Acción principal -->
<div class="panel-section">
<div class="d-flex gap-3 justify-content-end">
<button type="button" class="btn btn-success btn-animated" id="btnConfirmarMVE" style="display: none;">
<i class="fas fa-paper-plane me-2"></i>Solicitar Manifestación ahora
</button>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function() {
let pedimentoSeleccionado = null;
let facturaSeleccionadaId = null;
let importadorRFC = null;
let allPedimentos = [];
let filteredPedimentos = [];
let currentPage = 1;
let perPage = 25;
let pedimentosCompletos = new Set();
let pedimentoEstados = new Map(); // pedimento_id -> { total_facturas, respondidas, completo }
let activeView = 'pendientes';
// Cargar datos iniciales
cargarPedimentos();
// Configurar eventos de filtros
$('#searchInput').on('input', function() {
filtrarPedimentos();
});
// -------- AUTOGUARDADO POR SECCIÓN ---------
const debounce = (fn, delay=600) => {
let t; return function() { clearTimeout(t); const ctx=this, args=arguments; t=setTimeout(()=>fn.apply(ctx,args), delay); };
};
// Normaliza diferentes formatos de fecha a 'YYYY-MM-DD' para inputs type="date"
function formatDateInput(val) {
if (!val) return '';
if (val instanceof Date && !isNaN(val)) {
return val.toISOString().slice(0,10);
}
let s = String(val).trim();
if (!s) return '';
// Si ya viene como YYYY-MM-DD o con hora, tomar los primeros 10
if (/^\d{4}-\d{2}-\d{2}/.test(s)) return s.slice(0,10);
// Formato YYYY/MM/DD
if (/^\d{4}\/\d{1,2}\/\d{1,2}$/.test(s)) {
const [y,m,d] = s.split('/');
return `${y}-${String(m).padStart(2,'0')}-${String(d).padStart(2,'0')}`;
}
// Formato DD/MM/YYYY o MM/DD/YYYY (intentar deducir)
if (/^\d{1,2}\/\d{1,2}\/\d{4}$/.test(s)) {
const [a,b,y] = s.split('/');
// Si el primer número > 12, asumimos DD/MM/YYYY
if (parseInt(a,10) > 12) {
return `${y}-${String(b).padStart(2,'0')}-${String(a).padStart(2,'0')}`;
}
// Si el segundo > 12, asumimos MM/DD/YYYY
if (parseInt(b,10) > 12) {
return `${y}-${String(a).padStart(2,'0')}-${String(b).padStart(2,'0')}`;
}
// Ambiguo: por defecto DD/MM/YYYY
return `${y}-${String(b).padStart(2,'0')}-${String(a).padStart(2,'0')}`;
}
// Intentar parseo general
const d = new Date(s);
if (!isNaN(d)) return d.toISOString().slice(0,10);
return '';
}
// Etiquetas legibles por sección para validar campos vacíos
const etiquetasSecciones = {
'65': {
fecha_transporte: 'Art. 65 - Fecha transporte',
importe_transporte: 'Art. 65 - Importe transporte',
fecha_descuentos: 'Art. 65 - Fecha descuentos',
importe_descuentos: 'Art. 65 - Importe descuentos',
fecha_posteriores: 'Art. 65 - Fecha gastos posteriores',
importe_posteriores: 'Art. 65 - Importe gastos posteriores',
fecha_contribuciones: 'Art. 65 - Fecha contribuciones',
importe_contribuciones: 'Art. 65 - Importe contribuciones',
fecha_pagos_vendedor: 'Art. 65 - Fecha pagos al vendedor',
importe_pagos_vendedor: 'Art. 65 - Importe pagos al vendedor'
},
'66': {
fecha_comisiones: 'Art. 66 - Fecha comisiones',
importe_comisiones: 'Art. 66 - Importe comisiones',
cargo_comisiones: 'Art. 66 - ¿Comisiones a cargo del importador?',
fecha_envases: 'Art. 66 - Fecha envases',
importe_envases: 'Art. 66 - Importe envases',
cargo_envases: 'Art. 66 - ¿Envases a cargo del importador?',
fecha_embalaje: 'Art. 66 - Fecha embalaje',
importe_embalaje: 'Art. 66 - Importe embalaje',
cargo_embalaje: 'Art. 66 - ¿Embalaje a cargo del importador?',
fecha_transporte_dec: 'Art. 66 - Fecha transporte (dec.)',
importe_transporte_dec: 'Art. 66 - Importe transporte (dec.)',
cargo_transporte_dec: 'Art. 66 - ¿Transporte (dec.) a cargo del importador?',
fecha_ingenieria: 'Art. 66 - Fecha ingeniería',
importe_ingenieria: 'Art. 66 - Importe ingeniería',
cargo_ingenieria: 'Art. 66 - ¿Ingeniería a cargo del importador?',
fecha_regalias: 'Art. 66 - Fecha regalías',
importe_regalias: 'Art. 66 - Importe regalías',
cargo_regalias: 'Art. 66 - ¿Regalías a cargo del importador?',
fecha_producto: 'Art. 66 - Fecha producto',
importe_producto: 'Art. 66 - Importe producto',
cargo_producto: 'Art. 66 - ¿Producto a cargo del importador?'
},
'precio_pagado': {
fecha_precio_pagado: 'Precio Pagado - Fecha de pago',
importe_precio_pagado: 'Precio Pagado - Importe pagado',
moneda_precio_pagado: 'Precio Pagado - Moneda',
forma_pago_precio_pagado: 'Precio Pagado - Forma de pago',
referencia_precio_pagado: 'Precio Pagado - Referencia de pago'
},
'precio_pagar': {
fecha_limite_pago: 'Precio por Pagar - Fecha límite',
importe_precio_pagar: 'Precio por Pagar - Importe pendiente',
moneda_precio_pagar: 'Precio por Pagar - Moneda',
terminos_precio_pagar: 'Precio por Pagar - Términos de pago',
observaciones_precio_pagar: 'Precio por Pagar - Observaciones'
},
'compenso': {
fecha_compenso: 'Compenso - Fecha',
importe_compenso: 'Compenso - Importe',
tipo_compenso: 'Compenso - Tipo',
motivo_compenso: 'Compenso - Motivo',
documentos_compenso: 'Compenso - Documentos',
descripcion_compenso: 'Compenso - Descripción'
}
};
function validarSecciones() {
const secciones = ['65','66','precio_pagado','precio_pagar','compenso'];
const faltantes = {};
let completos = true;
secciones.forEach(sec => {
const datos = recolectarDatos(sec);
const etiquetas = etiquetasSecciones[sec] || {};
const vacios = [];
Object.keys(etiquetas).forEach(k => {
let v = datos[k];
// Para radios cargo_*, considerar vacío si no hay Si/No
if (k.startsWith('cargo_')) {
if (!v || (v !== 'Si' && v !== 'No')) vacios.push(etiquetas[k]);
} else {
if (v === undefined || v === null || String(v).trim() === '') vacios.push(etiquetas[k]);
}
});
if (vacios.length > 0) {
completos = false;
faltantes[sec] = vacios;
}
});
return { completos, faltantes };
}
function recolectarDatos(seccion) {
if (seccion === '65') {
return {
fecha_transporte: $('#fecha_transporte').val(),
importe_transporte: $('#importe_transporte').val(),
fecha_descuentos: $('#fecha_descuentos').val(),
importe_descuentos: $('#importe_descuentos').val(),
fecha_posteriores: $('#fecha_posteriores').val(),
importe_posteriores: $('#importe_posteriores').val(),
fecha_contribuciones: $('#fecha_contribuciones').val(),
importe_contribuciones: $('#importe_contribuciones').val(),
fecha_pagos_vendedor: $('#fecha_pagos_vendedor').val(),
importe_pagos_vendedor: $('#importe_pagos_vendedor').val()
};
}
if (seccion === '66') {
return {
fecha_comisiones: $('#fecha_comisiones').val(),
importe_comisiones: $('#importe_comisiones').val(),
cargo_comisiones: $('input[name="cargo_comisiones"]:checked').val(),
fecha_envases: $('#fecha_envases').val(),
importe_envases: $('#importe_envases').val(),
cargo_envases: $('input[name="cargo_envases"]:checked').val(),
fecha_embalaje: $('#fecha_embalaje').val(),
importe_embalaje: $('#importe_embalaje').val(),
cargo_embalaje: $('input[name="cargo_embalaje"]:checked').val(),
fecha_transporte_dec: $('#fecha_transporte_dec').val(),
importe_transporte_dec: $('#importe_transporte_dec').val(),
cargo_transporte_dec: $('input[name="cargo_transporte_dec"]:checked').val(),
fecha_ingenieria: $('#fecha_ingenieria').val(),
importe_ingenieria: $('#importe_ingenieria').val(),
cargo_ingenieria: $('input[name="cargo_ingenieria"]:checked').val(),
fecha_regalias: $('#fecha_regalias').val(),
importe_regalias: $('#importe_regalias').val(),
cargo_regalias: $('input[name="cargo_regalias"]:checked').val(),
fecha_producto: $('#fecha_producto').val(),
importe_producto: $('#importe_producto').val(),
cargo_producto: $('input[name="cargo_producto"]:checked').val()
};
}
if (seccion === 'precio_pagado') {
return {
fecha_precio_pagado: $('#fecha_precio_pagado').val(),
importe_precio_pagado: $('#importe_precio_pagado').val(),
moneda_precio_pagado: $('#moneda_precio_pagado').val(),
forma_pago_precio_pagado: $('#forma_pago_precio_pagado').val(),
referencia_precio_pagado: $('#referencia_precio_pagado').val()
};
}
if (seccion === 'precio_pagar') {
return {
fecha_limite_pago: $('#fecha_limite_pago').val(),
importe_precio_pagar: $('#importe_precio_pagar').val(),
moneda_precio_pagar: $('#moneda_precio_pagar').val(),
terminos_precio_pagar: $('#terminos_precio_pagar').val(),
observaciones_precio_pagar: $('#observaciones_precio_pagar').val()
};
}
if (seccion === 'compenso') {
return {
fecha_compenso: $('#fecha_compenso').val(),
importe_compenso: $('#importe_compenso').val(),
tipo_compenso: $('#tipo_compenso').val(),
motivo_compenso: $('#motivo_compenso').val(),
documentos_compenso: $('#documentos_compenso').val(),
descripcion_compenso: $('#descripcion_compenso').val()
};
}
return {};
}
// Toast helper
const lastToastAt = { };
function toastGuardado(seccion) {
const ahora = Date.now();
if (lastToastAt[seccion] && (ahora - lastToastAt[seccion] < 1200)) return; // evita spam
lastToastAt[seccion] = ahora;
const nombres = { '65': 'Art. 65', '66': 'Art. 66', 'precio_pagado': 'Precio Pagado', 'precio_pagar': 'Precio por Pagar', 'compenso': 'Compenso Pago' };
const titulo = (nombres[seccion] || 'Sección') + ' guardado';
Swal.fire({
toast: true,
position: 'top-end',
icon: 'success',
title: titulo,
showConfirmButton: false,
timer: 1600,
timerProgressBar: true
});
}
function guardarSeccion(seccion) {
if (!facturaSeleccionadaId || !pedimentoSeleccionado) return;
const datos = recolectarDatos(seccion);
$.ajax({
url: '/IMPORTADORES/mve/ajax_guardar_seccion',
method: 'POST',
dataType: 'json',
data: {
id_factura: facturaSeleccionadaId,
id_pedimento: pedimentoSeleccionado,
seccion: seccion,
datos: JSON.stringify(datos)
},
success: function(resp) {
if (resp && resp.success) {
$(`.mve-badge[data-factura-id="${facturaSeleccionadaId}"]`).removeClass('bg-secondary').addClass('bg-success').text('MVE respondido');
toastGuardado(seccion);
}
}
});
}
// Asociar eventos de autoguardado
$(document).on('input change', '#art65Form input, #art65Form select, #art65Form textarea', debounce(()=>guardarSeccion('65')));
$(document).on('input change', '#art66Form input, #art66Form select, #art66Form textarea', debounce(()=>guardarSeccion('66')));
$(document).on('input change', '#precioPagadoForm input, #precioPagadoForm select, #precioPagadoForm textarea', debounce(()=>guardarSeccion('precio_pagado')));
$(document).on('input change', '#precioPagarForm input, #precioPagarForm select, #precioPagarForm textarea', debounce(()=>guardarSeccion('precio_pagar')));
$(document).on('input change', '#compensoPagoForm input, #compensoPagoForm select, #compensoPagoForm textarea', debounce(()=>guardarSeccion('compenso')));
$('#statusFilter').on('change', function() {
filtrarPedimentos();
});
$('#perPageFilter').on('change', function() {
perPage = parseInt($(this).val());
currentPage = 1;
renderizarPedimentos();
});
// Tabs de vista (pendientes / completos)
$(document).on('click', '#viewTabs .nav-link', function(e){
e.preventDefault();
const view = $(this).data('view');
if (view && view !== activeView) {
activeView = view;
$('#viewTabs .nav-link').removeClass('active');
$(this).addClass('active');
currentPage = 1;
renderizarPedimentos();
}
});
function cargarPedimentos() {
$('#pedimentosContainer').html(`
<div class="loading-spinner">
<div class="spinner-border text-primary" style="width: 3rem; height: 3rem;">
<span class="visually-hidden">Cargando...</span>
</div>
<p class="mt-3 text-muted fw-bold">Cargando pedimentos...</p>
</div>
`);
const reqLista = $.ajax({
url: '/IMPORTADORES/catalogo_pedimentos/ajax_lista',
method: 'GET',
data: { draw: 1, start: 0, length: 1000 },
dataType: 'json'
});
const reqEstados = $.ajax({
url: '/IMPORTADORES/cove/ajax_estado_pedimentos',
method: 'GET',
dataType: 'json'
});
$.when(reqLista, reqEstados).done(function(lResp, eResp) {
const response = lResp[0];
const estados = eResp[0];
if (response.data && response.data.length > 0) {
allPedimentos = response.data;
pedimentosCompletos = new Set();
pedimentoEstados = new Map();
if (estados && estados.success && Array.isArray(estados.data)) {
estados.data.forEach(function(row){
const pid = String(row.pedimento_id);
pedimentoEstados.set(pid, row);
if (row && (row.completo === 1 || row.completo === true)) {
pedimentosCompletos.add(pid);
}
});
}
filteredPedimentos = [...allPedimentos];
renderizarPedimentos();
} else {
mostrarEstadoVacio();
}
}).fail(function(){
$('#pedimentosContainer').html(`
<div class="empty-state">
<i class="fas fa-exclamation-triangle text-warning"></i>
<h5>Error al cargar datos</h5>
<p>No se pudieron cargar los pedimentos. Por favor, intenta de nuevo.</p>
<button class="btn btn-primary" onclick="cargarPedimentos()">Reintentar</button>
</div>
`);
});
}
function filtrarPedimentos() {
const searchTerm = $('#searchInput').val().toLowerCase();
const statusFilter = $('#statusFilter').val();
filteredPedimentos = allPedimentos.filter(pedimento => {
const matchesSearch = !searchTerm ||
pedimento[1].toLowerCase().includes(searchTerm) || // Pedimento
pedimento[2].toLowerCase().includes(searchTerm) || // RFC
pedimento[3].toLowerCase().includes(searchTerm); // Nombre
const matchesStatus = !statusFilter || pedimento[5] === statusFilter;
return matchesSearch && matchesStatus;
});
currentPage = 1;
renderizarPedimentos();
updateKPIs();
}
function getActiveList() {
// Lista filtrada por texto/estado y dividida por vista
if (activeView === 'completos') {
return filteredPedimentos.filter(p => pedimentosCompletos.has(String(p[0])));
}
// default pendientes
return filteredPedimentos.filter(p => !pedimentosCompletos.has(String(p[0])));
}
function updateCounters() {
const pendCount = filteredPedimentos.filter(p => !pedimentosCompletos.has(String(p[0]))).length;
const compCount = filteredPedimentos.filter(p => pedimentosCompletos.has(String(p[0]))).length;
$('#countPend').text(pendCount);
$('#countComp').text(compCount);
}
function updateKPIs() {
const total = filteredPedimentos.length;
const comp = filteredPedimentos.filter(p => pedimentosCompletos.has(String(p[0]))).length;
const pend = total - comp;
$('#kpiTotal').text(total);
$('#kpiPend').text(pend);
$('#kpiComp').text(comp);
}
function renderizarPedimentos() {
if (filteredPedimentos.length === 0) {
mostrarEstadoVacio();
return;
}
updateCounters();
updateKPIs();
const listaActiva = getActiveList();
if (listaActiva.length === 0) {
mostrarEstadoVacio();
return;
}
const startIndex = (currentPage - 1) * perPage;
const endIndex = startIndex + perPage;
const pedimentosPagina = listaActiva.slice(startIndex, endIndex);
function cardHtml(pedimento, completo) {
const [id, numero, rfc, cliente, fecha, status] = pedimento;
const statusClass = status === 'Activo' ? 'status-activo' : 'status-inactivo';
const badge = completo ? '<span class="badge bg-success ms-2">MVE completa</span>' : '';
const est = pedimentoEstados.get(String(id));
const avance = est ? `${est.respondidas || 0}/${est.total_facturas || 0}` : '-';
const totalF = est ? parseInt(est.total_facturas || 0, 10) : 0;
const respF = est ? parseInt(est.respondidas || 0, 10) : 0;
const pct = totalF > 0 ? Math.round((respF / totalF) * 100) : 0;
const barClass = pct === 100 ? 'bg-success' : (pct === 0 ? 'bg-secondary' : 'bg-info');
return `
<div class="pedimento-card" data-id="${id}">
<div class="pedimento-header">
<div class="pedimento-numero">
<i class="fas fa-file-alt me-2"></i>${numero}
</div>
<div class="d-flex align-items-center gap-2">
${badge}
<div class="pedimento-status ${statusClass}">${status}</div>
</div>
</div>
<div class="pedimento-info">
<div class="info-row">
<span class="info-label">RFC Cliente:</span>
<span class="info-value">${rfc}</span>
</div>
<div class="info-row">
<span class="info-label">Cliente:</span>
<span class="info-value">${cliente}</span>
</div>
<div class="info-row">
<span class="info-label">Fecha:</span>
<span class="info-value">${fecha}</span>
</div>
<div class="info-row">
<span class="info-label">Avance MVE:</span>
<span class="info-value">${avance}</span>
</div>
<div class="progress mt-1" style="height:6px;">
<div class="progress-bar ${barClass}" role="progressbar" style="width: ${pct}%;" aria-valuenow="${pct}" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
<div class="pedimento-actions">
<button class="btn-mve" onclick="abrirPanelMVE('${id}', '${numero}')">
<i class="fas fa-calculator me-2"></i>${completo ? 'Ver MVE' : 'Solicitar MVE'}
</button>
</div>
</div>`;
}
let html = '<div class="pedimentos-grid">';
pedimentosPagina.forEach(p => {
const isCompleto = pedimentosCompletos.has(String(p[0]));
html += cardHtml(p, isCompleto);
});
html += '</div>';
$('#pedimentosContainer').html(html);
// Actualizar información de paginación
actualizarPaginacion();
}
function mostrarEstadoVacio() {
$('#pedimentosContainer').html(`
<div class="empty-state">
<i class="fas fa-inbox"></i>
<h5>No hay pedimentos ${activeView === 'completos' ? 'completos' : 'pendientes'}</h5>
<p>No se encontraron pedimentos con los filtros seleccionados en esta vista.</p>
</div>
`);
$('#paginationContainer').hide();
}
function actualizarPaginacion() {
const listaActiva = getActiveList();
const total = listaActiva.length;
const totalPages = Math.ceil(total / perPage);
const startIndex = (currentPage - 1) * perPage;
const endIndex = Math.min(startIndex + perPage, total);
$('#showingFrom').text(total === 0 ? 0 : startIndex + 1);
$('#showingTo').text(endIndex);
$('#totalRecords').text(total);
$('#viewLabel').text(activeView === 'completos' ? '(completos)' : '(pendientes)');
if (totalPages <= 1) {
$('#paginationContainer').hide();
return;
}
$('#paginationContainer').show();
let paginationHtml = '';
// Botón anterior
if (currentPage > 1) {
paginationHtml += `<li class="page-item"><a class="page-link" href="#" onclick="cambiarPagina(${currentPage - 1})">Anterior</a></li>`;
}
// Números de página
for (let i = Math.max(1, currentPage - 2); i <= Math.min(totalPages, currentPage + 2); i++) {
const activeClass = i === currentPage ? 'active' : '';
paginationHtml += `<li class="page-item ${activeClass}"><a class="page-link" href="#" onclick="cambiarPagina(${i})">${i}</a></li>`;
}
// Botón siguiente
if (currentPage < totalPages) {
paginationHtml += `<li class="page-item"><a class="page-link" href="#" onclick="cambiarPagina(${currentPage + 1})">Siguiente</a></li>`;
}
$('#pagination').html(paginationHtml);
}
window.cambiarPagina = function(page) {
currentPage = page;
renderizarPedimentos();
$('html, body').animate({scrollTop: 0}, 300);
};
// Función global para cambiar sección
window.cambiarSeccionMVE = function(seccion) {
console.log('Cambiando a sección:', seccion);
$('.step-btn').removeClass('active');
$(`.step-btn[data-step="${seccion}"]`).addClass('active');
mostrarSeccion(seccion.toString());
};
// Funciones del Panel Deslizante
window.abrirPanelMVE = function(id, pedimento) {
pedimentoSeleccionado = id;
$('#panelPedimento').text(pedimento);
// Mostrar panel
$('#slideOverlay').addClass('active');
$('#slidePanel').addClass('active');
// Cargar cabecera del pedimento (para RFC del importador)
cargarHeaderPedimento(id);
// Cargar facturas
cargarFacturasPedimento(id);
// Reset panel state
$('#stepNavigation').hide();
$('#art65Panel, #art66Panel').hide();
$('#btnConfirmarMVE').hide();
facturaSeleccionadaId = null;
};
function cargarHeaderPedimento(idPedimento) {
$.ajax({
url: '/IMPORTADORES/catalogo_pedimentos/ajax_pedimento',
method: 'GET',
data: { id: idPedimento },
dataType: 'json'
}).done(function(resp){
if (resp && resp.success && resp.data) {
importadorRFC = resp.data.rfc_importador || null;
} else {
importadorRFC = null;
}
}).fail(function(){ importadorRFC = null; });
}
function cargarFacturasPedimento(idPedimento) {
$('#facturasContainer').html(`
<div class="text-center py-3">
<div class="spinner-border text-primary"></div>
<div class="mt-2">Cargando facturas...</div>
</div>
`);
// Cargar facturas y estados MVE en paralelo
const reqFacturas = $.ajax({
url: '/IMPORTADORES/catalogo_pedimentos/ajax_facturas',
method: 'GET',
data: { pedimento_id: idPedimento },
dataType: 'json'
});
const reqEstados = $.ajax({
url: '/IMPORTADORES/cove/ajax_estado_facturas',
method: 'GET',
data: { pedimento_id: idPedimento },
dataType: 'json'
});
$.when(reqFacturas, reqEstados).done(function(fResp, eResp) {
const response = fResp[0];
const estadosRaw = eResp[0];
const facturas = (response && response.success && Array.isArray(response.data)) ? response.data : [];
const estados = (estadosRaw && estadosRaw.success && Array.isArray(estadosRaw.data)) ? estadosRaw.data : [];
const mapaEstados = {};
estados.forEach(s => { mapaEstados[s.factura_id] = s; });
if (facturas.length > 0) {
let html = '';
facturas.forEach(function(f) {
const id = f.id;
const numero = f.numero_factura || '';
const fecha = (f.fecha_factura || '').toString().substring(0,10);
const montoRaw = (typeof f.valor_factura !== 'undefined') ? f.valor_factura : (typeof f.valor_dolares !== 'undefined' ? f.valor_dolares : null);
const monto = (montoRaw !== null && montoRaw !== undefined && montoRaw !== '') ? parseFloat(montoRaw).toLocaleString() : '-';
const est = mapaEstados[id];
const respondido = est && (est.respondido === 1 || est.respondido === true);
const badge = respondido
? `<span class="badge bg-success ms-2 mve-badge" data-factura-id="${id}">MVE respondido</span>`
: `<span class="badge bg-secondary ms-2 mve-badge" data-factura-id="${id}">MVE pendiente</span>`;
html += `
<div class="factura-item" data-factura-id="${id}" onclick="seleccionarFactura('${numero}', ${id})">
<div class="d-flex justify-content-between align-items-center">
<div class="factura-numero">${numero}</div>
${badge}
</div>
<div class="factura-details">Fecha: ${fecha || '-'} | Monto: $${monto}</div>
</div>
`;
});
$('#facturasContainer').html(html);
} else {
$('#facturasContainer').html(`
<div class="alert alert-warning">
<i class="fas fa-exclamation-triangle me-2"></i>
No hay facturas relacionadas a este pedimento.
</div>
`);
}
}).fail(function() {
$('#facturasContainer').html(`
<div class="alert alert-danger">
<i class="fas fa-times-circle me-2"></i>
Error al cargar facturas.
</div>
`);
});
}
// Cerrar panel
$('#panelClose, #slideOverlay').on('click', function() {
$('#slideOverlay').removeClass('active');
$('#slidePanel').removeClass('active');
});
// Prevenir cierre al hacer click dentro del panel
$('#slidePanel').on('click', function(e) {
e.stopPropagation();
});
window.seleccionarFactura = function(numeroFactura, idFactura) {
if (!idFactura || idFactura === 'undefined' || idFactura === 'null') {
Swal.fire({
icon: 'error',
title: 'Error',
text: 'ID de factura no válido.',
confirmButtonText: 'OK'
});
return;
}
facturaSeleccionadaId = parseInt(idFactura);
// Actualizar selección visual
$('.factura-item').removeClass('selected');
$(`.factura-item:contains(${numeroFactura})`).addClass('selected');
// Asegurar que todos los formularios existan antes de cargar datos
if ($('#art65Form').children().length === 0) generarFormularioArt65();
if ($('#art66Form').children().length === 0) generarFormularioArt66();
if ($('#precioPagadoForm').children().length === 0) generarFormularioPrecioPagado();
if ($('#precioPagarForm').children().length === 0) generarFormularioPrecioPagar();
if ($('#compensoPagoForm').children().length === 0) generarFormularioCompenso();
// Mostrar navegación de pasos y botón de solicitud
$('#stepNavigation').show();
$('#btnConfirmarMVE').show();
// Mostrar Art. 65 por defecto y cargar datos existentes
mostrarSeccion('65');
cargarDatosMVEFactura(facturaSeleccionadaId);
};
function mostrarSeccion(seccion) {
console.log('Showing section:', seccion);
// Ocultar todos los paneles
$('#art65Panel, #art66Panel, #precioPagadoPanel, #precioPagarPanel, #compensoPagoPanel').hide();
// Mostrar el panel correspondiente
if (seccion === '65') {
console.log('Showing Art 65 panel');
$('#art65Panel').show();
if ($('#art65Form').children().length === 0) generarFormularioArt65();
} else if (seccion === '66') {
console.log('Showing Art 66 panel');
$('#art66Panel').show();
if ($('#art66Form').children().length === 0) generarFormularioArt66();
} else if (seccion === 'precio-pagado') {
console.log('Showing Precio Pagado panel');
$('#precioPagadoPanel').show();
if ($('#precioPagadoForm').children().length === 0) generarFormularioPrecioPagado();
} else if (seccion === 'precio-pagar') {
$('#precioPagarPanel').show();
if ($('#precioPagarForm').children().length === 0) generarFormularioPrecioPagar();
} else if (seccion === 'compenso-pago') {
console.log('Showing Compenso Pago panel');
if ($('#compensoPagoForm').children().length === 0) generarFormularioCompenso();
}
// Verificar que se mostró correctamente
console.log('Panels visible:', {
art65: $('#art65Panel').is(':visible'),
art66: $('#art66Panel').is(':visible'),
precioPagado: $('#precioPagadoPanel').is(':visible'),
precioPagar: $('#precioPagarPanel').is(':visible'),
compensoPago: $('#compensoPagoPanel').is(':visible')
});
}
function generarFormularioArt65() {
const campos = [
{
label: 'Gastos de transporte, seguros y conexos (carga y descarga) erogados con posterioridad:',
fecha_id: 'fecha_transporte',
importe_id: 'importe_transporte'
},
{
label: 'Información de descuentos especiales para aplicar en el momento de la compraventa:',
fecha_id: 'fecha_descuentos',
importe_id: 'importe_descuentos'
},
{
label: 'Gastos posteriores a la importación por construcción, instalación, armado, montaje, mantenimiento o asistencia técnica:',
fecha_id: 'fecha_posteriores',
importe_id: 'importe_posteriores'
},
{
label: 'Contribuciones y cuotas compensatorias aplicables en territorio nacional:',
fecha_id: 'fecha_contribuciones',
importe_id: 'importe_contribuciones'
},
{
label: 'Pagos de importador al vendedor por dividendos u otros que no guarden relación directa con las mercancías importadas:',
fecha_id: 'fecha_pagos_vendedor',
importe_id: 'importe_pagos_vendedor'
}
];
let html = '';
campos.forEach(campo => {
html += `
<div class="row mb-3">
<div class="col-12 mb-2">
<label class="form-label fw-bold">${campo.label}</label>
</div>
<div class="col-12 col-md-6 col-lg-6">
<label class="form-label">Fecha de erogación:</label>
<input type="date" class="form-control" id="${campo.fecha_id}">
</div>
<div class="col-12 col-md-6 col-lg-6">
<label class="form-label">Importe (MN):</label>
<input type="number" step="0.01" class="form-control" id="${campo.importe_id}">
</div>
</div>
`;
});
$('#art65Form').html(html);
}
function generarFormularioArt66() {
const campos = [
{
label: 'Comisiones y gastos de corretaje, excepto comisiones de compra:',
fecha_id: 'fecha_comisiones',
importe_id: 'importe_comisiones',
cargo_name: 'cargo_comisiones'
},
{
label: 'Costo de los envases o embalajes que formen un todo con las mercancías:',
fecha_id: 'fecha_envases',
importe_id: 'importe_envases',
cargo_name: 'cargo_envases'
},
{
label: 'Gastos de embalaje (mano de obra y/o materiales):',
fecha_id: 'fecha_embalaje',
importe_id: 'importe_embalaje',
cargo_name: 'cargo_embalaje'
},
{
label: 'Gastos de transporte, seguros y conexos (como manejo, carga y descarga) erogados antes de los momentos señalados en el artículo 56, fracción I de la Ley:',
fecha_id: 'fecha_transporte_dec',
importe_id: 'importe_transporte_dec',
cargo_name: 'cargo_transporte_dec'
},
{
label: 'Trabajos de ingeniería, creación y perfeccionamiento, artísticos, diseños, planos y croquis realizados fuera del territorio nacional:',
fecha_id: 'fecha_ingenieria',
importe_id: 'importe_ingenieria',
cargo_name: 'cargo_ingenieria'
},
{
label: 'Las regalías y derechos de licencia a pagar como condición de la venta de las mercancías a importar:',
fecha_id: 'fecha_regalias',
importe_id: 'importe_regalias',
cargo_name: 'cargo_regalias'
},
{
label: 'Valor de cualquier parte del producto de la enajenación, cesión o utilización, posteriores a la importación:',
fecha_id: 'fecha_producto',
importe_id: 'importe_producto',
cargo_name: 'cargo_producto'
}
];
let html = '';
campos.forEach(campo => {
html += `
<div class="row mb-3">
<div class="col-12 mb-2">
<label class="form-label fw-bold">${campo.label}</label>
</div>
<div class="col-12 col-md-6 col-lg-4">
<label class="form-label">Fecha de erogación:</label>
<input type="date" class="form-control" id="${campo.fecha_id}">
</div>
<div class="col-12 col-md-6 col-lg-4">
<label class="form-label">Importe (MN):</label>
<input type="number" step="0.01" class="form-control" id="${campo.importe_id}">
</div>
<div class="col-12 col-lg-4">
<label class="form-label">¿Está a cargo del importador?:</label>
<div class="d-flex gap-3 flex-wrap">
<div class="form-check">
<input class="form-check-input" type="radio" name="${campo.cargo_name}" value="Si" id="${campo.cargo_name}_si">
<label class="form-check-label" for="${campo.cargo_name}_si">Sí</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="${campo.cargo_name}" value="No" id="${campo.cargo_name}_no">
<label class="form-check-label" for="${campo.cargo_name}_no">No</label>
</div>
</div>
</div>
</div>
`;
});
$('#art66Form').html(html);
}
function generarFormularioPrecioPagado() {
let html = `
<div class="row mb-3">
<div class="col-12 mb-2">
<label class="form-label fw-bold">Información del Precio Pagado:</label>
</div>
<div class="col-12 col-md-6 col-lg-4">
<label class="form-label">Fecha de pago:</label>
<input type="date" class="form-control" id="fecha_precio_pagado">
</div>
<div class="col-12 col-md-6 col-lg-4">
<label class="form-label">Importe pagado (MN):</label>
<input type="number" step="0.01" class="form-control" id="importe_precio_pagado">
</div>
<div class="col-12 col-lg-4">
<label class="form-label">Moneda:</label>
<select class="form-control" id="moneda_precio_pagado">
<option value="MXN">Peso Mexicano (MXN)</option>
<option value="USD">Dólar Americano (USD)</option>
<option value="EUR">Euro (EUR)</option>
</select>
</div>
</div>
<div class="row mb-3">
<div class="col-12 mb-2">
<label class="form-label fw-bold">Método de pago:</label>
</div>
<div class="col-md-6">
<label class="form-label">Forma de pago:</label>
<select class="form-control" id="forma_pago_precio_pagado">
<option value="">Seleccionar...</option>
<option value="transferencia">Transferencia bancaria</option>
<option value="cheque">Cheque</option>
<option value="efectivo">Efectivo</option>
<option value="carta_credito">Carta de crédito</option>
</select>
</div>
<div class="col-md-6">
<label class="form-label">Referencia de pago:</label>
<input type="text" class="form-control" id="referencia_precio_pagado" placeholder="Núm. de referencia">
</div>
</div>
`;
$('#precioPagadoForm').html(html);
}
function generarFormularioPrecioPagar() {
let html = `
<div class="row mb-3">
<div class="col-12 mb-2">
<label class="form-label fw-bold">Información del Precio por Pagar:</label>
</div>
<div class="col-md-4">
<label class="form-label">Fecha límite de pago:</label>
<input type="date" class="form-control" id="fecha_limite_pago">
</div>
<div class="col-md-4">
<label class="form-label">Importe pendiente (MN):</label>
<input type="number" step="0.01" class="form-control" id="importe_precio_pagar">
</div>
<div class="col-md-4">
<label class="form-label">Moneda:</label>
<select class="form-control" id="moneda_precio_pagar">
<option value="MXN">Peso Mexicano (MXN)</option>
<option value="USD">Dólar Americano (USD)</option>
<option value="EUR">Euro (EUR)</option>
</select>
</div>
</div>
<div class="row mb-3">
<div class="col-12 mb-2">
<label class="form-label fw-bold">Condiciones de pago:</label>
</div>
<div class="col-md-6">
<label class="form-label">Términos de pago:</label>
<select class="form-control" id="terminos_precio_pagar">
<option value="">Seleccionar...</option>
<option value="contado">Al contado</option>
<option value="credito_30">Crédito 30 días</option>
<option value="credito_60">Crédito 60 días</option>
<option value="credito_90">Crédito 90 días</option>
</select>
</div>
<div class="col-md-6">
<label class="form-label">Observaciones:</label>
<textarea class="form-control" id="observaciones_precio_pagar" rows="2" placeholder="Observaciones adicionales"></textarea>
</div>
</div>
`;
$('#precioPagarForm').html(html);
}
function generarFormularioCompenso() {
let html = `
<div class="row mb-3">
<div class="col-12 mb-2">
<label class="form-label fw-bold">Información del Compenso de Pago:</label>
</div>
<div class="col-md-4">
<label class="form-label">Fecha de compensación:</label>
<input type="date" class="form-control" id="fecha_compenso">
</div>
<div class="col-md-4">
<label class="form-label">Importe a compensar (MN):</label>
<input type="number" step="0.01" class="form-control" id="importe_compenso">
</div>
<div class="col-md-4">
<label class="form-label">Tipo de compensación:</label>
<select class="form-control" id="tipo_compenso">
<option value="">Seleccionar...</option>
<option value="descuento">Descuento comercial</option>
<option value="bonificacion">Bonificación</option>
<option value="devolucion">Devolución</option>
<option value="ajuste">Ajuste de precio</option>
</select>
</div>
</div>
<div class="row mb-3">
<div class="col-12 mb-2">
<label class="form-label fw-bold">Justificación del compenso:</label>
</div>
<div class="col-md-6">
<label class="form-label">Motivo:</label>
<select class="form-control" id="motivo_compenso">
<option value="">Seleccionar...</option>
<option value="defecto_mercancia">Defecto en mercancía</option>
<option value="error_facturacion">Error en facturación</option>
<option value="acuerdo_comercial">Acuerdo comercial</option>
<option value="otro">Otro</option>
</select>
</div>
<div class="col-md-6">
<label class="form-label">Documentos de respaldo:</label>
<input type="text" class="form-control" id="documentos_compenso" placeholder="Núm. de documento">
</div>
</div>
<div class="row mb-3">
<div class="col-12">
<label class="form-label fw-bold">Descripción detallada:</label>
<textarea class="form-control" id="descripcion_compenso" rows="3" placeholder="Descripción detallada del motivo de compensación"></textarea>
</div>
</div>
`;
$('#compensoPagoForm').html(html);
}
function cargarDatosMVEFactura(idFactura) {
$.ajax({
url: '/IMPORTADORES/mve/ajax_obtener_datos_factura',
method: 'GET',
data: { id_factura: idFactura },
dataType: 'json',
success: function(response) {
if (response.success && response.datos) {
if (response.datos.art65) {
const art65 = response.datos.art65;
$('#fecha_transporte').val(formatDateInput(art65.fecha_transporte));
$('#importe_transporte').val(art65.importe_transporte || '');
$('#fecha_descuentos').val(formatDateInput(art65.fecha_descuentos));
$('#importe_descuentos').val(art65.importe_descuentos || '');
$('#fecha_posteriores').val(formatDateInput(art65.fecha_posteriores));
$('#importe_posteriores').val(art65.importe_posteriores || '');
$('#fecha_contribuciones').val(formatDateInput(art65.fecha_contribuciones));
$('#importe_contribuciones').val(art65.importe_contribuciones || '');
$('#fecha_pagos_vendedor').val(formatDateInput(art65.fecha_pagos_vendedor));
$('#importe_pagos_vendedor').val(art65.importe_pagos_vendedor || '');
}
if (response.datos.art66) {
const art66 = response.datos.art66;
$('#fecha_comisiones').val(formatDateInput(art66.fecha_comisiones));
$('#importe_comisiones').val(art66.importe_comisiones || '');
$('input[name="cargo_comisiones"][value="' + (art66.cargo_comisiones || '') + '"]').prop('checked', true);
$('#fecha_envases').val(formatDateInput(art66.fecha_envases));
$('#importe_envases').val(art66.importe_envases || '');
$('input[name="cargo_envases"][value="' + (art66.cargo_envases || '') + '"]').prop('checked', true);
$('#fecha_embalaje').val(formatDateInput(art66.fecha_embalaje));
$('#importe_embalaje').val(art66.importe_embalaje || '');
$('input[name="cargo_embalaje"][value="' + (art66.cargo_embalaje || '') + '"]').prop('checked', true);
$('#fecha_transporte_dec').val(formatDateInput(art66.fecha_transporte_dec));
$('#importe_transporte_dec').val(art66.importe_transporte_dec || '');
$('input[name="cargo_transporte_dec"][value="' + (art66.cargo_transporte_dec || '') + '"]').prop('checked', true);
$('#fecha_ingenieria').val(formatDateInput(art66.fecha_ingenieria));
$('#importe_ingenieria').val(art66.importe_ingenieria || '');
$('input[name="cargo_ingenieria"][value="' + (art66.cargo_ingenieria || '') + '"]').prop('checked', true);
$('#fecha_regalias').val(formatDateInput(art66.fecha_regalias));
$('#importe_regalias').val(art66.importe_regalias || '');
$('input[name="cargo_regalias"][value="' + (art66.cargo_regalias || '') + '"]').prop('checked', true);
$('#fecha_producto').val(formatDateInput(art66.fecha_producto));
$('#importe_producto').val(art66.importe_producto || '');
$('input[name="cargo_producto"][value="' + (art66.cargo_producto || '') + '"]').prop('checked', true);
}
if (response.datos.precio_pagado) {
const pp = response.datos.precio_pagado;
$('#fecha_precio_pagado').val(formatDateInput(pp.fecha_precio_pagado));
$('#importe_precio_pagado').val(pp.importe_precio_pagado || '');
$('#moneda_precio_pagado').val(pp.moneda_precio_pagado || 'MXN');
$('#forma_pago_precio_pagado').val(pp.forma_pago_precio_pagado || '');
$('#referencia_precio_pagado').val(pp.referencia_precio_pagado || '');
}
if (response.datos.precio_pagar) {
const px = response.datos.precio_pagar;
$('#fecha_limite_pago').val(formatDateInput(px.fecha_limite_pago));
$('#importe_precio_pagar').val(px.importe_precio_pagar || '');
$('#moneda_precio_pagar').val(px.moneda_precio_pagar || 'MXN');
$('#terminos_precio_pagar').val(px.terminos_precio_pagar || '');
$('#observaciones_precio_pagar').val(px.observaciones_precio_pagar || '');
}
if (response.datos.compenso) {
const co = response.datos.compenso;
$('#fecha_compenso').val(formatDateInput(co.fecha_compenso));
$('#importe_compenso').val(co.importe_compenso || '');
$('#tipo_compenso').val(co.tipo_compenso || '');
$('#motivo_compenso').val(co.motivo_compenso || '');
$('#documentos_compenso').val(co.documentos_compenso || '');
$('#descripcion_compenso').val(co.descripcion_compenso || '');
}
}
}
});
}
// Botón Guardar MVE removido (autosave activo)
$('#btnConfirmarMVE').off('click').on('click', function() {
if (!facturaSeleccionadaId) {
Swal.fire({ icon: 'warning', title: 'Selecciona una factura', text: 'Primero selecciona una factura para solicitar la MVE.' });
return;
}
const { completos, faltantes } = validarSecciones();
if (completos) {
const rfcTxt = importadorRFC ? ` (RFC: ${importadorRFC})` : '';
const disclaimerHtml = `
<div class="text-start" style="font-size: 0.95rem;">
<p class="mb-2">Todos los datos están completos.</p>
<div class="p-3 border rounded" style="background:#fffef4">
<strong>Declaración del Importador${rfcTxt}:</strong>
<p class="mb-0 mt-2">
Declaro que la información presentada es verdadera, completa y libre de errores.
Reconozco que AduanaSoft actúa únicamente como plataforma de captura y no asume responsabilidad por el contenido, veracidad ni por las consecuencias derivadas de su transmisión.
</p>
</div>
</div>`;
Swal.fire({
icon: 'question',
title: 'Confirmar solicitud',
width: '60rem',
html: `
<div class="container-fluid text-start">
<div class="row g-3">
<div class="col-12 col-lg-6">
${disclaimerHtml}
</div>
<div class="col-12 col-lg-6">
<div class="mb-2"><strong>Firma autógrafa (opcional)</strong></div>
<div class="border rounded p-2" style="background:#fff">
<canvas id="firmaCanvas" style="width:100%;height:180px;border:1px dashed #ced4da;border-radius:6px;"></canvas>
<div class="d-flex justify-content-between align-items-center mt-2">
<input type="text" id="firmanteNombre" class="form-control form-control-sm me-2" placeholder="Nombre del firmante (opcional)">
<button type="button" id="btnLimpiarFirma" class="btn btn-sm btn-outline-secondary">Limpiar</button>
</div>
<small class="text-muted d-block mt-1">Puedes firmar con mouse o dedo si es pantalla táctil.</small>
</div>
</div>
</div>
</div>
`,
input: 'checkbox',
inputPlaceholder: 'Acepto y declaro bajo protesta de decir verdad',
inputValidator: (v) => !v && 'Debes aceptar la declaración para continuar',
showCancelButton: true,
confirmButtonText: 'Solicitar',
cancelButtonText: 'Cancelar',
didOpen: () => {
const canvas = document.getElementById('firmaCanvas');
// Ajuste de tamaño para alta densidad
const ratio = Math.max(window.devicePixelRatio || 1, 1);
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * ratio;
canvas.height = 180 * ratio;
canvas.getContext('2d').scale(ratio, ratio);
window.__sigPad = new SignaturePad(canvas, { backgroundColor: 'rgba(255,255,255,1)' });
document.getElementById('btnLimpiarFirma').addEventListener('click', ()=> window.__sigPad.clear());
},
preConfirm: () => {
if (!window.__sigPad || window.__sigPad.isEmpty()) {
Swal.showValidationMessage('Debes firmar para continuar');
return false;
}
return true;
}
}).then(res => {
if (res.isConfirmed) {
// Registrar acuse en backend
$.ajax({
url: '/IMPORTADORES/mve/ajax_registrar_solicitud',
method: 'POST',
dataType: 'json',
data: {
pedimento_id: pedimentoSeleccionado,
factura_id: facturaSeleccionadaId,
acepto: 1,
rfc_importador: importadorRFC,
firma_base64: (window.__sigPad && !window.__sigPad.isEmpty()) ? window.__sigPad.toDataURL('image/png') : '',
firmante: $('#firmanteNombre').val() || ''
},
}).done(function(r){
if (r && r.success) {
$('#slideOverlay').removeClass('active');
$('#slidePanel').removeClass('active');
Swal.fire({ icon: 'success', title: 'Solicitud enviada', text: 'Se ha solicitado la Manifestación de Valor para el pedimento ' + $('#panelPedimento').text(), confirmButtonText: 'OK' });
} else {
Swal.fire({ icon: 'error', title: 'No se pudo registrar', text: (r && r.message) ? r.message : 'Intenta nuevamente.' });
}
}).fail(function(){
Swal.fire({ icon: 'error', title: 'Error de red', text: 'No fue posible registrar la solicitud.' });
});
}
});
} else {
// Construir listado de faltantes
const nombres = { '65': 'Art. 65', '66': 'Art. 66', 'precio_pagado': 'Precio Pagado', 'precio_pagar': 'Precio por Pagar', 'compenso': 'Compenso Pago' };
let html = '<div class="text-start">';
Object.keys(faltantes).forEach(sec => {
html += `<div class="mb-2"><strong>${nombres[sec] || sec}:</strong><ul class="mt-1">`;
faltantes[sec].slice(0,8).forEach(f => { html += `<li>${f}</li>`; });
const extra = faltantes[sec].length - 8;
if (extra > 0) html += `<li>… y ${extra} más</li>`;
html += '</ul></div>';
});
html += '</div>';
const rfcTxt = importadorRFC ? ` (RFC: ${importadorRFC})` : '';
const disclaimerHtml = `
<div class="p-3 border rounded mt-3" style="background:#fffef4">
<strong>Declaración del Importador${rfcTxt}:</strong>
<p class="mb-0 mt-2">
Declaro que la información presentada es verdadera, completa y libre de errores.
Reconozco que AduanaSoft actúa únicamente como plataforma de captura y no asume responsabilidad por el contenido, veracidad ni por las consecuencias derivadas de su transmisión.
</p>
</div>`;
Swal.fire({
icon: 'warning',
title: 'Datos incompletos',
width: '60rem',
html: `
${html}
<div class="mt-2">Algunos campos están vacíos. ¿Deseas transmitir la solicitud así?</div>
<div class="container-fluid text-start mt-3">
<div class="row g-3">
<div class="col-12 col-lg-6">
${disclaimerHtml}
</div>
<div class="col-12 col-lg-6">
<div class="mb-2"><strong>Firma autógrafa (opcional)</strong></div>
<div class="border rounded p-2" style="background:#fff">
<canvas id="firmaCanvas" style="width:100%;height:180px;border:1px dashed #ced4da;border-radius:6px;"></canvas>
<div class="d-flex justify-content-between align-items-center mt-2">
<input type="text" id="firmanteNombre" class="form-control form-control-sm me-2" placeholder="Nombre del firmante (opcional)">
<button type="button" id="btnLimpiarFirma" class="btn btn-sm btn-outline-secondary">Limpiar</button>
</div>
<small class="text-muted d-block mt-1">Puedes firmar con mouse o dedo si es pantalla táctil.</small>
</div>
</div>
</div>
</div>
`,
input: 'checkbox',
inputPlaceholder: 'Acepto y declaro bajo protesta de decir verdad',
inputValidator: (v) => !v && 'Debes aceptar la declaración para continuar',
showCancelButton: true,
confirmButtonText: 'Transmitir de todos modos',
cancelButtonText: 'Completar datos',
didOpen: () => {
const canvas = document.getElementById('firmaCanvas');
const ratio = Math.max(window.devicePixelRatio || 1, 1);
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * ratio;
canvas.height = 180 * ratio;
canvas.getContext('2d').scale(ratio, ratio);
window.__sigPad = new SignaturePad(canvas, { backgroundColor: 'rgba(255,255,255,1)' });
document.getElementById('btnLimpiarFirma').addEventListener('click', ()=> window.__sigPad.clear());
},
preConfirm: () => {
if (!window.__sigPad || window.__sigPad.isEmpty()) {
Swal.showValidationMessage('Debes firmar para continuar');
return false;
}
return true;
}
}).then(res => {
if (res.isConfirmed) {
// Registrar acuse en backend (aun con faltantes)
$.ajax({
url: '/IMPORTADORES/mve/ajax_registrar_solicitud',
method: 'POST',
dataType: 'json',
data: {
pedimento_id: pedimentoSeleccionado,
factura_id: facturaSeleccionadaId,
acepto: 1,
rfc_importador: importadorRFC,
firma_base64: (window.__sigPad && !window.__sigPad.isEmpty()) ? window.__sigPad.toDataURL('image/png') : '',
firmante: $('#firmanteNombre').val() || ''
},
}).done(function(r){
if (r && r.success) {
$('#slideOverlay').removeClass('active');
$('#slidePanel').removeClass('active');
Swal.fire({ icon: 'success', title: 'Solicitud enviada', text: 'Se ha solicitado la Manifestación de Valor para el pedimento ' + $('#panelPedimento').text(), confirmButtonText: 'OK' });
} else {
Swal.fire({ icon: 'error', title: 'No se pudo registrar', text: (r && r.message) ? r.message : 'Intenta nuevamente.' });
}
}).fail(function(){
Swal.fire({ icon: 'error', title: 'Error de red', text: 'No fue posible registrar la solicitud.' });
});
}
});
}
});
});
</script>
</body>
</html>