Preparando proyecto para migración a repositorio MVE
This commit is contained in:
4
.env
4
.env
@@ -1,7 +1,7 @@
|
||||
DB_HOST=localhost
|
||||
DB_DATABASE=Importaciones_HC
|
||||
DB_USERNAME=sa
|
||||
DB_PASSWORD=Soluciones01
|
||||
DB_USERNAME=progra
|
||||
DB_PASSWORD=soluciones
|
||||
|
||||
ENCRYPTION_KEY=6a7f92d3c8d1e5b3b0ac23ff1926a7c9
|
||||
ENCRYPTION_IV=7c9f4a2d1e3b5f7a
|
||||
|
||||
8
.env.example
Normal file
8
.env.example
Normal file
@@ -0,0 +1,8 @@
|
||||
# Copia este archivo a .env y completa tus credenciales de base de datos
|
||||
DB_HOST=localhost\\SQLEXPRESS
|
||||
DB_DATABASE=TuBaseDeDatos
|
||||
DB_USERNAME=TuUsuario
|
||||
DB_PASSWORD=TuPassword
|
||||
|
||||
# Opcional: configuración adicional
|
||||
# DB_PORT=1433
|
||||
266
agencias.txt
266
agencias.txt
@@ -1,266 +0,0 @@
|
||||
Sistemas -- Administrador General
|
||||
INSERT INTO [Importaciones_HC].[dbo].[usuarios_sistema]
|
||||
([nombre], [email], [password_hash], [tipo_usuario], [activo], [creado_en], [dos_factores], [notificaciones], [notificaciones_extra])
|
||||
VALUES (
|
||||
'4a77Tj9XtOCnvkUrxuxLCg==', -- nombre encriptado
|
||||
'QVaqkaWSvUtgAfKN51JNDkoyUiZGVQs3+ARpoGtdD/I=', -- email encriptado
|
||||
'$2y$10$.8CI5zuJ6MICQRRGqksYjOyuIYnln4xgUKJiQkZdEpLWfvUpg1f9m', -- password hash bcrypt
|
||||
'super_admin', 1, GETDATE(), 0, 0, 0
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE solicitudes_agencias(
|
||||
[request_id] [int] IDENTITY(1,1) NOT NULL,
|
||||
[agencia_name] [varchar](255) NULL,
|
||||
[rfc] [varchar](100) NULL,
|
||||
[email] [varchar](255) NULL,
|
||||
[phone] [varchar](20) NULL,
|
||||
[direccion] [varchar](100) NULL,
|
||||
[opinion_file] [varchar](500) NULL,
|
||||
[admin_name] [varchar](255) NULL,
|
||||
[admin_email] [varchar](255) NULL,
|
||||
[request_status] [varchar](20) NULL,
|
||||
[request_date] [datetime] NULL,
|
||||
[approval_date] [datetime] NULL,
|
||||
[approved_by] [int] NULL,
|
||||
[timestamp_request] [timestamp] NOT NULL
|
||||
);
|
||||
|
||||
|
||||
-- Tabla de Agencias Aduanales
|
||||
|
||||
CREATE TABLE [dbo].[agencias_aduanales](
|
||||
[id_agencia] [int] IDENTITY(1,1) NOT NULL,
|
||||
[nombre_agencia] [nvarchar](255) NOT NULL,
|
||||
[rfc_agencia] [nvarchar](13) NOT NULL,
|
||||
[direccion] [nvarchar](500) NULL,
|
||||
[telefono] [nvarchar](20) NULL,
|
||||
[email] [nvarchar](255) NULL,
|
||||
[id_administrador] [int] NULL, -- Usuario administrador de la agencia
|
||||
[activo] [bit] NOT NULL DEFAULT 1,
|
||||
[creado_en] [datetime] NOT NULL DEFAULT GETDATE(),
|
||||
[actualizado_en] [datetime] NULL,
|
||||
[creado_por] [int] NULL, -- Super admin que creó la agencia
|
||||
CONSTRAINT [PK_agencias_aduanales] PRIMARY KEY CLUSTERED ([id_agencia] ASC)
|
||||
);
|
||||
|
||||
-- Agregar foreign key para administrador de agencia
|
||||
|
||||
ALTER TABLE [dbo].[agencias_aduanales]
|
||||
ADD CONSTRAINT [FK_agencias_administrador]
|
||||
FOREIGN KEY ([id_administrador]) REFERENCES [dbo].[usuarios_sistema]([id_usuario]);
|
||||
|
||||
CREATE TABLE [dbo].[credenciales_db_agencias] (
|
||||
[id_credencial] INT IDENTITY(1,1) NOT NULL,
|
||||
[id_agencia] INT NOT NULL,
|
||||
[db_instance] NVARCHAR(255) NOT NULL,
|
||||
[db_ip] NVARCHAR(50) NOT NULL,
|
||||
[db_user] NVARCHAR(100) NOT NULL,
|
||||
[db_password] NVARCHAR(255) NOT NULL, -- Considera encriptar o manejar de forma segura
|
||||
[db_port] INT NOT NULL DEFAULT 1433, -- Default SQL Server port
|
||||
[creado_en] DATETIME NOT NULL DEFAULT GETDATE(),
|
||||
[actualizado_en] DATETIME NULL,
|
||||
|
||||
CONSTRAINT [PK_credenciales_db_agencias] PRIMARY KEY CLUSTERED ([id_credencial] ASC),
|
||||
CONSTRAINT [FK_credenciales_db_agencia]
|
||||
FOREIGN KEY ([id_agencia]) REFERENCES [dbo].[agencias_aduanales]([id_agencia])
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX [IX_credenciales_db_agencias_id_agencia]
|
||||
ON [dbo].[credenciales_db_agencias]([id_agencia]);
|
||||
|
||||
|
||||
-- Actualizar tipos de usuario válidos
|
||||
|
||||
-- Los tipos de usuario ahora serán:
|
||||
|
||||
-- 'super_admin' - Administrador del sistema completo
|
||||
-- 'admin_agencia' - Administrador de una agencia específica
|
||||
-- 'agente_aduanal' - Agente que trabaja para una agencia
|
||||
-- 'importador' - Cliente que puede trabajar con múltiples agencias
|
||||
|
||||
-- Campos de auditoría para usuarios_sistema
|
||||
ALTER TABLE [dbo].[usuarios_sistema]
|
||||
ADD [creado_por] [int] NULL;
|
||||
|
||||
|
||||
-- Tabla de relación importador-agencia (many-to-many)
|
||||
|
||||
CREATE TABLE [dbo].[importador_agencia](
|
||||
[id_relacion] [int] IDENTITY(1,1) NOT NULL,
|
||||
[id_importador] [int] NOT NULL,
|
||||
[id_agencia] [int] NOT NULL,
|
||||
[activo] [bit] NOT NULL DEFAULT 1,
|
||||
[fecha_vinculacion] [datetime] NOT NULL DEFAULT GETDATE(),
|
||||
[fecha_desvinculacion] [datetime] NULL,
|
||||
[creado_por] [int] NULL, -- ID del usuario que creó la relación
|
||||
[aprobado_por] [int] NULL, -- Admin de agencia que aprobó la vinculación
|
||||
[estado] [varchar](20) NOT NULL DEFAULT 'PENDIENTE', -- 'PENDIENTE', 'APROBADO', 'RECHAZADO'
|
||||
CONSTRAINT [PK_importador_agencia] PRIMARY KEY CLUSTERED ([id_relacion] ASC),
|
||||
CONSTRAINT [FK_importador_agencia_usuario]
|
||||
FOREIGN KEY ([id_importador]) REFERENCES [dbo].[usuarios_sistema]([id_usuario]),
|
||||
CONSTRAINT [FK_importador_agencia_agencia]
|
||||
FOREIGN KEY ([id_agencia]) REFERENCES [dbo].[agencias_aduanales]([id_agencia]),
|
||||
CONSTRAINT [FK_importador_agencia_creador]
|
||||
FOREIGN KEY ([creado_por]) REFERENCES [dbo].[usuarios_sistema]([id_usuario]),
|
||||
CONSTRAINT [FK_importador_agencia_aprobador]
|
||||
FOREIGN KEY ([aprobado_por]) REFERENCES [dbo].[usuarios_sistema]([id_usuario])
|
||||
);
|
||||
|
||||
-- Índice único para evitar duplicados activos
|
||||
|
||||
CREATE UNIQUE INDEX [IX_importador_agencia_unique]
|
||||
ON [dbo].[importador_agencia] ([id_importador], [id_agencia])
|
||||
WHERE [activo] = 1;
|
||||
|
||||
|
||||
-- Tabla de relación agente-agencia
|
||||
|
||||
CREATE TABLE [dbo].[agente_agencia](
|
||||
[id_relacion] INT IDENTITY(1,1) NOT NULL,
|
||||
[id_agente] INT NOT NULL,
|
||||
[id_agencia] INT NOT NULL,
|
||||
[fecha_asignacion] DATETIME NOT NULL DEFAULT GETDATE(),
|
||||
[activo] BIT NOT NULL DEFAULT 1,
|
||||
[asignado_por] INT NULL, -- quien lo asignó (admin agencia o super admin)
|
||||
CONSTRAINT [PK_agente_agencia] PRIMARY KEY CLUSTERED ([id_relacion]),
|
||||
CONSTRAINT [FK_agente_agencia_usuario]
|
||||
FOREIGN KEY ([id_agente]) REFERENCES [dbo].[usuarios_sistema]([id_usuario]),
|
||||
CONSTRAINT [FK_agente_agencia_agencia]
|
||||
FOREIGN KEY ([id_agencia]) REFERENCES [dbo].[agencias_aduanales]([id_agencia])
|
||||
);
|
||||
|
||||
-- Luego creas el índice filtrado:
|
||||
|
||||
CREATE UNIQUE INDEX [UQ_agente_activo]
|
||||
ON [dbo].[agente_agencia]([id_agente])
|
||||
WHERE [activo] = 1;
|
||||
|
||||
-- Índice único para evitar duplicados activos
|
||||
|
||||
CREATE UNIQUE INDEX [IX_agente_agencia_unique]
|
||||
ON [dbo].[agente_agencia] ([id_agente], [id_agencia])
|
||||
WHERE [activo] = 1;
|
||||
|
||||
|
||||
-- Tabla para solicitudes de vinculación de importadores
|
||||
|
||||
CREATE TABLE [dbo].[solicitudes_vinculacion](
|
||||
[id_solicitud] [int] IDENTITY(1,1) NOT NULL,
|
||||
[id_importador] [int] NOT NULL,
|
||||
[id_agencia] [int] NOT NULL,
|
||||
[mensaje] [nvarchar](1000) NULL,
|
||||
[estado] [varchar](20) NOT NULL DEFAULT 'PENDIENTE', -- 'PENDIENTE', 'APROBADA', 'RECHAZADA'
|
||||
[fecha_solicitud] [datetime] NOT NULL DEFAULT GETDATE(),
|
||||
[fecha_respuesta] [datetime] NULL,
|
||||
[respondido_por] [int] NULL, -- Admin de agencia que respondió
|
||||
[comentarios_respuesta] [nvarchar](1000) NULL,
|
||||
CONSTRAINT [PK_solicitudes_vinculacion] PRIMARY KEY CLUSTERED ([id_solicitud] ASC),
|
||||
CONSTRAINT [FK_solicitudes_importador]
|
||||
FOREIGN KEY ([id_importador]) REFERENCES [dbo].[usuarios_sistema]([id_usuario]),
|
||||
CONSTRAINT [FK_solicitudes_agencia]
|
||||
FOREIGN KEY ([id_agencia]) REFERENCES [dbo].[agencias_aduanales]([id_agencia]),
|
||||
CONSTRAINT [FK_solicitudes_respondido]
|
||||
FOREIGN KEY ([respondido_por]) REFERENCES [dbo].[usuarios_sistema]([id_usuario])
|
||||
);
|
||||
|
||||
|
||||
-- Tabla para auditoría de cambios de agencia
|
||||
|
||||
CREATE TABLE [dbo].[bitacora_agencias](
|
||||
[id_bitacora] [int] IDENTITY(1,1) NOT NULL,
|
||||
[id_agencia] [int] NULL,
|
||||
[nombre] [varchar](100) NULL,
|
||||
[accion] [varchar](50) NOT NULL, -- 'CREACION', 'DESACTIVACION'
|
||||
[realizado_por] [int] NOT NULL,
|
||||
[fecha] [datetime] NOT NULL DEFAULT GETDATE(),
|
||||
CONSTRAINT [PK_bitacora_agenciaS] PRIMARY KEY CLUSTERED ([id_bitacora] ASC)
|
||||
);
|
||||
|
||||
ALTER TABLE [dbo].[bitacora_agencias]
|
||||
ADD CONSTRAINT [FK_bitacora_agencias_agencia]
|
||||
FOREIGN KEY ([id_agencia]) REFERENCES [dbo].[agencias_aduanales]([id_agencia]);
|
||||
|
||||
ALTER TABLE [dbo].[bitacora_agencias]
|
||||
ADD CONSTRAINT [FK_bitacora_agencias_administrador]
|
||||
FOREIGN KEY ([realizado_por]) REFERENCES [dbo].[usuarios_sistema]([id_usuario]);
|
||||
|
||||
ALTER TABLE [dbo].[bitacora_agencias]
|
||||
ADD CONSTRAINT [CK_bitacora_agencias_accion]
|
||||
CHECK ([accion] IN ('CREACION', 'DESACTIVACION'));
|
||||
|
||||
|
||||
-- Índices adicionales para mejorar rendimiento
|
||||
|
||||
CREATE INDEX [IX_importador_agencia_importador] ON [dbo].[importador_agencia] ([id_importador]);
|
||||
|
||||
CREATE INDEX [IX_importador_agencia_agencia] ON [dbo].[importador_agencia] ([id_agencia]);
|
||||
|
||||
CREATE INDEX [IX_importador_agencia_estado] ON [dbo].[importador_agencia] ([estado], [activo]);
|
||||
|
||||
CREATE INDEX [IX_agencias_activo] ON [dbo].[agencias_aduanales] ([activo]);
|
||||
|
||||
CREATE INDEX [IX_agencias_administrador] ON [dbo].[agencias_aduanales] ([id_administrador]);
|
||||
|
||||
CREATE INDEX [IX_solicitudes_estado] ON [dbo].[solicitudes_vinculacion] ([estado], [id_agencia]);
|
||||
|
||||
|
||||
== MODIFICACIONES MULLTI-AGENCIA ==
|
||||
ALTER TABLE usuarios_sistema
|
||||
ADD id_agencia_en_uso INT NULL;
|
||||
|
||||
ALTER TABLE usuarios_sistema
|
||||
ADD CONSTRAINT FK_usuario_agencia_uso
|
||||
FOREIGN KEY (id_agencia_en_uso) REFERENCES agencias_aduanales(id_agencia);
|
||||
|
||||
|
||||
-- 1. Modificar la tabla de solicitudes para incluir la relación con agencia
|
||||
ALTER TABLE [dbo].[solicitud_importacion_factura]
|
||||
ADD [id_agencia] [int] NULL;
|
||||
|
||||
-- Agregar foreign key para la agencia
|
||||
ALTER TABLE [dbo].[solicitud_importacion_factura]
|
||||
ADD CONSTRAINT [FK_solicitud_agencia]
|
||||
FOREIGN KEY ([id_agencia]) REFERENCES [dbo].[agencias_aduanales]([id_agencia]);
|
||||
|
||||
-- 2. Crear índices para mejorar el rendimiento
|
||||
CREATE INDEX [IX_solicitud_importador_agencia]
|
||||
ON [dbo].[solicitud_importacion_factura] ([id_importador], [id_agencia]);
|
||||
|
||||
CREATE INDEX [IX_solicitud_agencia_status]
|
||||
ON [dbo].[solicitud_importacion_factura] ([id_agencia], [status]);
|
||||
|
||||
|
||||
-- 3. Agregar campos adicionales a la tabla expediente_archivos existente para mejor control
|
||||
ALTER TABLE [dbo].[expediente_archivos]
|
||||
ADD [estado_archivo] [varchar](20) NULL DEFAULT 'ACTIVO', -- ACTIVO, INACTIVO, ELIMINADO
|
||||
[subido_por] [int] NULL, -- ID del usuario que subió el archivo
|
||||
[observaciones] [nvarchar](500) NULL,
|
||||
[fecha_modificacion] [datetime] NULL;
|
||||
|
||||
-- Agregar foreign key para el usuario que subió el archivo
|
||||
ALTER TABLE [dbo].[expediente_archivos]
|
||||
ADD CONSTRAINT [FK_expediente_archivos_usuario]
|
||||
FOREIGN KEY ([subido_por]) REFERENCES [dbo].[usuarios_sistema]([id_usuario]);
|
||||
|
||||
|
||||
-- 1. Modificar la tabla de patentes para incluir la relación con agencia
|
||||
ALTER TABLE [dbo].[agentes_aduanales]
|
||||
ADD [id_agencia] [int] NULL;
|
||||
|
||||
-- Agregar foreign key para la agencia
|
||||
ALTER TABLE [dbo].[agentes_aduanales]
|
||||
ADD CONSTRAINT [FK_agente_agencia]
|
||||
FOREIGN KEY ([id_agencia]) REFERENCES [dbo].[agencias_aduanales]([id_agencia]);
|
||||
|
||||
|
||||
ALTER TABLE [dbo].[solicitud_importacion_factura]
|
||||
ADD [patente_id] INT NULL;
|
||||
|
||||
CREATE INDEX IX_solicitud_importacion_factura_patente_id
|
||||
ON [dbo].[solicitud_importacion_factura] ([patente_id]);
|
||||
|
||||
-- Opcional: Agregar foreign key constraint
|
||||
ALTER TABLE [dbo].[solicitud_importacion_factura]
|
||||
ADD CONSTRAINT FK_solicitud_patente
|
||||
FOREIGN KEY ([patente_id]) REFERENCES [dbo].[agentes_aduanales]([id_agente]);
|
||||
303
app/controllers/ImportadorPedimentos.php
Normal file
303
app/controllers/ImportadorPedimentos.php
Normal file
@@ -0,0 +1,303 @@
|
||||
<?php
|
||||
|
||||
class ImportadorPedimentos {
|
||||
private $conn;
|
||||
private $estadisticas = [
|
||||
'total_lineas' => 0,
|
||||
'pedimentos_procesados' => 0,
|
||||
'facturas_procesadas' => 0,
|
||||
'partidas_procesadas' => 0,
|
||||
'errores' => 0,
|
||||
'duplicados' => 0
|
||||
];
|
||||
|
||||
private $errores_detalle = [];
|
||||
private $pedimentos_creados = [];
|
||||
|
||||
public function __construct($connection) {
|
||||
$this->conn = $connection;
|
||||
}
|
||||
|
||||
public function procesarArchivo($archivo_path, $validar_duplicados = true, $timestamp_importacion = null) {
|
||||
$inicio_tiempo = time();
|
||||
$nombre_archivo = basename($archivo_path);
|
||||
|
||||
if ($timestamp_importacion === null) {
|
||||
$timestamp_importacion = date('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
try {
|
||||
$contenido = file_get_contents($archivo_path);
|
||||
if ($contenido === false) {
|
||||
throw new Exception("No se pudo leer el archivo");
|
||||
}
|
||||
|
||||
// Convertir encoding si es necesario
|
||||
if (!mb_check_encoding($contenido, 'UTF-8')) {
|
||||
$contenido = mb_convert_encoding($contenido, 'UTF-8', 'ISO-8859-1');
|
||||
}
|
||||
|
||||
$lineas = explode("\n", $contenido);
|
||||
$this->estadisticas['total_lineas'] = count($lineas);
|
||||
|
||||
$pedimento_actual = null;
|
||||
$facturas_pedimento = [];
|
||||
$partidas_pedimento = [];
|
||||
|
||||
foreach ($lineas as $numero_linea => $linea) {
|
||||
$linea = trim($linea);
|
||||
if (empty($linea)) continue;
|
||||
|
||||
try {
|
||||
$codigo = substr($linea, 0, 3);
|
||||
|
||||
switch ($codigo) {
|
||||
case '500':
|
||||
// Header - ignorar
|
||||
break;
|
||||
|
||||
case '501':
|
||||
// Si hay un pedimento anterior, procesarlo
|
||||
if ($pedimento_actual) {
|
||||
$this->procesarPedimento($pedimento_actual, $facturas_pedimento, $partidas_pedimento, $validar_duplicados);
|
||||
$facturas_pedimento = [];
|
||||
$partidas_pedimento = [];
|
||||
}
|
||||
$pedimento_actual = $this->parsearLinea501Real($linea, $timestamp_importacion);
|
||||
break;
|
||||
|
||||
case '505':
|
||||
if ($pedimento_actual) {
|
||||
$factura = $this->parsearLinea505Real($linea);
|
||||
$facturas_pedimento[] = $factura;
|
||||
}
|
||||
break;
|
||||
|
||||
case '551':
|
||||
if ($pedimento_actual) {
|
||||
$partida = $this->parsearLinea551Real($linea);
|
||||
$partidas_pedimento[] = $partida;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->errores_detalle[] = "Línea " . ($numero_linea + 1) . ": " . $e->getMessage();
|
||||
$this->estadisticas['errores']++;
|
||||
}
|
||||
}
|
||||
|
||||
// Procesar el último pedimento
|
||||
if ($pedimento_actual) {
|
||||
$this->procesarPedimento($pedimento_actual, $facturas_pedimento, $partidas_pedimento, $validar_duplicados);
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'estadisticas' => $this->estadisticas,
|
||||
'errores' => $this->errores_detalle,
|
||||
'pedimentos_creados' => $this->pedimentos_creados
|
||||
];
|
||||
|
||||
} catch (Exception $e) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
private function parsearLinea501Real($linea, $timestamp_importacion) {
|
||||
$campos = explode('|', $linea);
|
||||
|
||||
if (count($campos) < 10) {
|
||||
throw new Exception("Formato inválido en línea 501");
|
||||
}
|
||||
|
||||
return [
|
||||
'numero_pedimento' => trim($campos[2]),
|
||||
'patente' => trim($campos[1]),
|
||||
'aduana' => trim($campos[3]),
|
||||
'anio' => date('Y'),
|
||||
'clave_documento' => trim($campos[5]),
|
||||
'rfc_importador' => trim($campos[8]),
|
||||
'fecha_creacion' => $timestamp_importacion,
|
||||
'usuario_id' => $_SESSION['usuario_id']
|
||||
];
|
||||
}
|
||||
|
||||
private function parsearLinea505Real($linea) {
|
||||
$campos = explode('|', $linea);
|
||||
|
||||
if (count($campos) < 12) {
|
||||
throw new Exception("Formato inválido en línea 505");
|
||||
}
|
||||
|
||||
return [
|
||||
'numero_factura' => trim($campos[3]),
|
||||
'fecha_factura' => $this->convertirFechaReal(trim($campos[2])),
|
||||
'valor_dolares' => floatval(trim($campos[6])),
|
||||
'valor_factura' => floatval(trim($campos[7])),
|
||||
'cove' => trim($campos[3]),
|
||||
'moneda' => trim($campos[5]),
|
||||
'proveedor' => trim($campos[11])
|
||||
];
|
||||
}
|
||||
|
||||
private function parsearLinea551Real($linea) {
|
||||
$campos = explode('|', $linea);
|
||||
|
||||
if (count($campos) < 12) {
|
||||
throw new Exception("Formato inválido en línea 551");
|
||||
}
|
||||
|
||||
static $contador = 1;
|
||||
|
||||
return [
|
||||
'secuencia' => $contador++,
|
||||
'fraccion_arancelaria' => trim($campos[2]) ?: 'PENDIENTE',
|
||||
'descripcion' => trim($campos[5]) ?: 'DESCRIPCIÓN PENDIENTE',
|
||||
'cantidad' => floatval(trim($campos[10])) ?: 1.0,
|
||||
'unidad' => trim($campos[11]) ?: 'PZ',
|
||||
'valor_unitario' => floatval(trim($campos[6])),
|
||||
'peso_neto' => null,
|
||||
'peso_bruto' => null
|
||||
];
|
||||
}
|
||||
|
||||
private function procesarPedimento($pedimento, $facturas, $partidas, $validar_duplicados) {
|
||||
try {
|
||||
if ($validar_duplicados && $this->existePedimento($pedimento['numero_pedimento'])) {
|
||||
$this->estadisticas['duplicados']++;
|
||||
return;
|
||||
}
|
||||
|
||||
sqlsrv_begin_transaction($this->conn);
|
||||
|
||||
// Insertar pedimento
|
||||
$sql = "INSERT INTO pedimentos (numero_pedimento, patente, aduana, anio, clave_documento,
|
||||
rfc_importador, fecha_creacion, usuario_id, estado)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'activo'); SELECT SCOPE_IDENTITY() AS id;";
|
||||
|
||||
$params = [
|
||||
$pedimento['numero_pedimento'],
|
||||
$pedimento['patente'],
|
||||
$pedimento['aduana'],
|
||||
$pedimento['anio'],
|
||||
$pedimento['clave_documento'],
|
||||
$pedimento['rfc_importador'],
|
||||
$pedimento['fecha_creacion'],
|
||||
$pedimento['usuario_id']
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_query($this->conn, $sql, $params);
|
||||
if ($stmt === false) {
|
||||
throw new Exception("Error al insertar pedimento");
|
||||
}
|
||||
|
||||
sqlsrv_next_result($stmt);
|
||||
sqlsrv_fetch($stmt);
|
||||
$pedimento_id = sqlsrv_get_field($stmt, 0);
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
// Insertar facturas
|
||||
foreach ($facturas as $factura) {
|
||||
$sql = "INSERT INTO pedimento_facturas (pedimento_id, numero_factura, fecha_factura,
|
||||
valor_dolares, valor_factura, cove, moneda, proveedor)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
$params = [
|
||||
$pedimento_id,
|
||||
$factura['numero_factura'],
|
||||
$factura['fecha_factura'],
|
||||
$factura['valor_dolares'],
|
||||
$factura['valor_factura'],
|
||||
$factura['cove'],
|
||||
$factura['moneda'],
|
||||
$factura['proveedor']
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_query($this->conn, $sql, $params);
|
||||
if ($stmt === false) {
|
||||
throw new Exception("Error al insertar factura");
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
$this->estadisticas['facturas_procesadas']++;
|
||||
}
|
||||
|
||||
// Insertar partidas
|
||||
foreach ($partidas as $partida) {
|
||||
$sql = "INSERT INTO pedimento_partidas (pedimento_id, secuencia, fraccion_arancelaria,
|
||||
descripcion, cantidad, unidad, valor_unitario,
|
||||
peso_neto, peso_bruto)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
$params = [
|
||||
$pedimento_id,
|
||||
$partida['secuencia'],
|
||||
$partida['fraccion_arancelaria'],
|
||||
$partida['descripcion'],
|
||||
$partida['cantidad'],
|
||||
$partida['unidad'],
|
||||
$partida['valor_unitario'],
|
||||
$partida['peso_neto'],
|
||||
$partida['peso_bruto']
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_query($this->conn, $sql, $params);
|
||||
if ($stmt === false) {
|
||||
throw new Exception("Error al insertar partida");
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
$this->estadisticas['partidas_procesadas']++;
|
||||
}
|
||||
|
||||
sqlsrv_commit($this->conn);
|
||||
|
||||
$this->estadisticas['pedimentos_procesados']++;
|
||||
$this->pedimentos_creados[] = $pedimento['numero_pedimento'];
|
||||
|
||||
} catch (Exception $e) {
|
||||
sqlsrv_rollback($this->conn);
|
||||
$this->errores_detalle[] = "Error procesando pedimento {$pedimento['numero_pedimento']}: " . $e->getMessage();
|
||||
$this->estadisticas['errores']++;
|
||||
}
|
||||
}
|
||||
|
||||
private function existePedimento($numero_pedimento) {
|
||||
$sql = "SELECT id FROM pedimentos WHERE numero_pedimento = ?";
|
||||
$stmt = sqlsrv_query($this->conn, $sql, [$numero_pedimento]);
|
||||
if ($stmt === false) {
|
||||
return false;
|
||||
}
|
||||
$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
return $row !== false;
|
||||
}
|
||||
|
||||
private function convertirFechaReal($fecha_str) {
|
||||
if (empty($fecha_str) || strlen($fecha_str) !== 8) {
|
||||
return date('Y-m-d');
|
||||
}
|
||||
|
||||
if (substr($fecha_str, 0, 2) === '20') {
|
||||
$anio = substr($fecha_str, 0, 4);
|
||||
$mes = substr($fecha_str, 4, 2);
|
||||
$dia = substr($fecha_str, 6, 2);
|
||||
} else {
|
||||
$dia = substr($fecha_str, 0, 2);
|
||||
$mes = substr($fecha_str, 2, 2);
|
||||
$anio = substr($fecha_str, 4, 4);
|
||||
}
|
||||
|
||||
if (checkdate($mes, $dia, $anio)) {
|
||||
return "$anio-$mes-$dia";
|
||||
}
|
||||
|
||||
return date('Y-m-d');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -359,44 +359,16 @@ function ajax_lista()
|
||||
$id_usuario = $_SESSION['usuario_id'];
|
||||
$conn = getConnection();
|
||||
|
||||
// Obtener RFC del importador para filtrar solo sus pedimentos
|
||||
$sqlImportador = "SELECT rfc FROM informacion_general WHERE id_usuario = ?";
|
||||
$stmtImportador = sqlsrv_query($conn, $sqlImportador, [$id_usuario]);
|
||||
|
||||
if ($stmtImportador === false) {
|
||||
echo json_encode([
|
||||
"draw" => intval($_GET['draw'] ?? 0),
|
||||
"recordsTotal" => 0,
|
||||
"recordsFiltered" => 0,
|
||||
"data" => [],
|
||||
"error" => "Error al consultar información del importador"
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$importador = sqlsrv_fetch_array($stmtImportador, SQLSRV_FETCH_ASSOC);
|
||||
|
||||
if (!$importador) {
|
||||
echo json_encode([
|
||||
"draw" => intval($_GET['draw'] ?? 0),
|
||||
"recordsTotal" => 0,
|
||||
"recordsFiltered" => 0,
|
||||
"data" => []
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Parámetros de DataTables
|
||||
$draw = intval($_GET['draw'] ?? 0);
|
||||
$start = intval($_GET['start'] ?? 0);
|
||||
$length = intval($_GET['length'] ?? 10);
|
||||
$search = $_GET['search']['value'] ?? '';
|
||||
|
||||
// Total registros sin filtro
|
||||
$sqlTotal = "SELECT COUNT(*) AS total FROM PREVIOS_COMPARTIDOS_WS WHERE ClienteRFC = ?";
|
||||
$stmt = sqlsrv_query($conn, $sqlTotal, [$importador['rfc']]);
|
||||
|
||||
if ($stmt === false) {
|
||||
// Total registros sin filtro en nuevas tablas
|
||||
$sqlTotal = "SELECT COUNT(*) AS total FROM pedimentos WHERE usuario_id = ?";
|
||||
$stmtTotal = sqlsrv_query($conn, $sqlTotal, [$id_usuario]);
|
||||
if ($stmtTotal === false) {
|
||||
echo json_encode([
|
||||
"draw" => $draw,
|
||||
"recordsTotal" => 0,
|
||||
@@ -406,24 +378,21 @@ function ajax_lista()
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
$rowT = sqlsrv_fetch_array($stmtTotal, SQLSRV_FETCH_ASSOC);
|
||||
$recordsTotal = (int)($rowT['total'] ?? 0);
|
||||
|
||||
$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
$recordsTotal = (int)($row['total'] ?? 0);
|
||||
|
||||
// Construir condiciones de filtro
|
||||
$where = "ClienteRFC = ?";
|
||||
$params = [$importador['rfc']];
|
||||
|
||||
// Filtro y búsqueda
|
||||
$where = "p.usuario_id = ?";
|
||||
$params = [$id_usuario];
|
||||
if ($search !== '') {
|
||||
$where .= " AND (Pedimento LIKE ? OR ClienteNombre LIKE ? OR ClavePed LIKE ?)";
|
||||
$where .= " AND (p.numero_pedimento LIKE ? OR p.rfc_importador LIKE ? OR p.clave_documento LIKE ? OR p.patente LIKE ? OR p.aduana LIKE ?)";
|
||||
$like = "%{$search}%";
|
||||
$params = array_merge($params, [$like, $like, $like]);
|
||||
$params = array_merge($params, [$like, $like, $like, $like, $like]);
|
||||
}
|
||||
|
||||
// Total registros filtrados
|
||||
$sqlFiltered = "SELECT COUNT(*) AS total FROM PREVIOS_COMPARTIDOS_WS WHERE $where";
|
||||
// Total filtrado
|
||||
$sqlFiltered = "SELECT COUNT(*) AS total FROM pedimentos p WHERE $where";
|
||||
$stmtF = sqlsrv_query($conn, $sqlFiltered, $params);
|
||||
|
||||
if ($stmtF === false) {
|
||||
echo json_encode([
|
||||
"draw" => $draw,
|
||||
@@ -434,34 +403,41 @@ function ajax_lista()
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$rowF = sqlsrv_fetch_array($stmtF, SQLSRV_FETCH_ASSOC);
|
||||
$recordsFiltered = (int)($rowF['total'] ?? 0);
|
||||
|
||||
// Datos de la página
|
||||
$sqlData = "SELECT IdPrevio, Pedimento, ClienteRFC, ClienteNombre, Timestamp, Status
|
||||
FROM PREVIOS_COMPARTIDOS_WS
|
||||
// Datos paginados
|
||||
$sqlData = "SELECT p.id, p.numero_pedimento, p.rfc_importador, p.fecha_creacion, p.estado,
|
||||
ig.nombre AS nombre_importador
|
||||
FROM pedimentos p
|
||||
LEFT JOIN informacion_general ig ON ig.id_usuario = p.usuario_id
|
||||
WHERE $where
|
||||
ORDER BY Timestamp DESC
|
||||
ORDER BY p.fecha_creacion DESC
|
||||
OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
|
||||
$params[] = $start;
|
||||
$params[] = $length;
|
||||
|
||||
$stmtD = sqlsrv_query($conn, $sqlData, $params);
|
||||
$paramsData = array_merge($params, [$start, $length]);
|
||||
$stmtD = sqlsrv_query($conn, $sqlData, $paramsData);
|
||||
|
||||
$data = [];
|
||||
if ($stmtD !== false) {
|
||||
while ($r = sqlsrv_fetch_array($stmtD, SQLSRV_FETCH_ASSOC)) {
|
||||
$timestamp = $r['Timestamp'] instanceof DateTime ? $r['Timestamp']->format('Y-m-d H:i:s') : '';
|
||||
$status_text = $r['Status'] == 1 ? 'Activo' : 'Inactivo';
|
||||
$fecha = '';
|
||||
if (isset($r['fecha_creacion'])) {
|
||||
if ($r['fecha_creacion'] instanceof DateTime) {
|
||||
$fecha = $r['fecha_creacion']->format('Y-m-d H:i:s');
|
||||
} elseif (is_array($r['fecha_creacion']) && isset($r['fecha_creacion']['date'])) {
|
||||
// Por si viene como array (SQLSRV con print_r)
|
||||
$fecha = substr($r['fecha_creacion']['date'], 0, 19);
|
||||
}
|
||||
}
|
||||
$estado = strtolower((string)$r['estado']) === 'activo' || $r['estado'] === 1 ? 'Activo' : 'Inactivo';
|
||||
|
||||
$data[] = [
|
||||
$r['IdPrevio'],
|
||||
$r['Pedimento'],
|
||||
$r['ClienteRFC'],
|
||||
$r['ClienteNombre'],
|
||||
$timestamp,
|
||||
$status_text
|
||||
$r['id'],
|
||||
$r['numero_pedimento'],
|
||||
$r['rfc_importador'],
|
||||
$r['nombre_importador'] ?? '',
|
||||
$fecha,
|
||||
$estado
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -556,3 +532,156 @@ function buscar_pedimentos()
|
||||
echo json_encode($pedimentos, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve información general del pedimento (cabecera)
|
||||
* GET /IMPORTADORES/catalogo_pedimentos/ajax_pedimento?id=123
|
||||
*/
|
||||
function ajax_pedimento()
|
||||
{
|
||||
if (!($_SESSION['usuario_id'] ?? false)) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['success' => false, 'message' => 'No autorizado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id_usuario = $_SESSION['usuario_id'];
|
||||
$pedimento_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||
if ($pedimento_id <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'ID inválido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$conn = getConnection();
|
||||
$sql = "SELECT p.id, p.numero_pedimento, p.patente, p.aduana, p.anio, p.clave_documento,
|
||||
p.rfc_importador, p.fecha_creacion, p.estado
|
||||
FROM pedimentos p
|
||||
WHERE p.id = ? AND p.usuario_id = ?";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$pedimento_id, $id_usuario]);
|
||||
if ($stmt === false) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Error de base de datos']);
|
||||
exit;
|
||||
}
|
||||
$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
if (!$row) {
|
||||
echo json_encode(['success' => false, 'message' => 'No encontrado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Formatear fecha si es DateTime
|
||||
if (isset($row['fecha_creacion']) && $row['fecha_creacion'] instanceof DateTime) {
|
||||
$row['fecha_creacion'] = $row['fecha_creacion']->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'data' => $row]);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve las facturas del pedimento
|
||||
* GET /IMPORTADORES/catalogo_pedimentos/ajax_facturas?pedimento_id=123
|
||||
*/
|
||||
function ajax_facturas()
|
||||
{
|
||||
if (!($_SESSION['usuario_id'] ?? false)) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['success' => false, 'message' => 'No autorizado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id_usuario = $_SESSION['usuario_id'];
|
||||
$pedimento_id = isset($_GET['pedimento_id']) ? (int)$_GET['pedimento_id'] : 0;
|
||||
if ($pedimento_id <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'ID inválido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$conn = getConnection();
|
||||
// Verificar propiedad
|
||||
$chk = sqlsrv_query($conn, "SELECT 1 FROM pedimentos WHERE id = ? AND usuario_id = ?", [$pedimento_id, $id_usuario]);
|
||||
$own = $chk && sqlsrv_fetch_array($chk) ? true : false;
|
||||
if ($chk) sqlsrv_free_stmt($chk);
|
||||
if (!$own) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['success' => false, 'message' => 'Pedimento no encontrado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql = "SELECT id, numero_factura, fecha_factura, valor_dolares, valor_factura, cove, moneda, proveedor
|
||||
FROM pedimento_facturas
|
||||
WHERE pedimento_id = ?
|
||||
ORDER BY fecha_factura ASC, id ASC";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$pedimento_id]);
|
||||
if ($stmt === false) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Error de base de datos']);
|
||||
exit;
|
||||
}
|
||||
$items = [];
|
||||
while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
if ($r['fecha_factura'] instanceof DateTime) {
|
||||
$r['fecha_factura'] = $r['fecha_factura']->format('Y-m-d');
|
||||
}
|
||||
$items[] = $r;
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
echo json_encode(['success' => true, 'data' => $items]);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve las partidas del pedimento
|
||||
* GET /IMPORTADORES/catalogo_pedimentos/ajax_partidas?pedimento_id=123
|
||||
*/
|
||||
function ajax_partidas()
|
||||
{
|
||||
if (!($_SESSION['usuario_id'] ?? false)) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['success' => false, 'message' => 'No autorizado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id_usuario = $_SESSION['usuario_id'];
|
||||
$pedimento_id = isset($_GET['pedimento_id']) ? (int)$_GET['pedimento_id'] : 0;
|
||||
if ($pedimento_id <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'ID inválido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$conn = getConnection();
|
||||
// Verificar propiedad
|
||||
$chk = sqlsrv_query($conn, "SELECT 1 FROM pedimentos WHERE id = ? AND usuario_id = ?", [$pedimento_id, $id_usuario]);
|
||||
$own = $chk && sqlsrv_fetch_array($chk) ? true : false;
|
||||
if ($chk) sqlsrv_free_stmt($chk);
|
||||
if (!$own) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['success' => false, 'message' => 'Pedimento no encontrado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql = "SELECT id, secuencia, fraccion_arancelaria, descripcion, cantidad, unidad, valor_unitario, peso_neto, peso_bruto
|
||||
FROM pedimento_partidas
|
||||
WHERE pedimento_id = ?
|
||||
ORDER BY secuencia ASC, id ASC";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$pedimento_id]);
|
||||
if ($stmt === false) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Error de base de datos']);
|
||||
exit;
|
||||
}
|
||||
$items = [];
|
||||
while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
$items[] = $r;
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
echo json_encode(['success' => true, 'data' => $items]);
|
||||
exit;
|
||||
}
|
||||
170
app/controllers/cove.php
Normal file
170
app/controllers/cove.php
Normal file
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../config/database.php';
|
||||
require_once __DIR__ . '/../helpers/session.php';
|
||||
|
||||
/**
|
||||
* GET /IMPORTADORES/cove/ajax_estado_facturas?pedimento_id=123
|
||||
* Devuelve estado de COVE por factura del pedimento actual del usuario.
|
||||
*/
|
||||
function ajax_estado_facturas()
|
||||
{
|
||||
if (!($_SESSION['usuario_id'] ?? false)) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['success' => false, 'message' => 'No autorizado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$usuario_id = (int)$_SESSION['usuario_id'];
|
||||
$pedimento_id = isset($_GET['pedimento_id']) ? (int)$_GET['pedimento_id'] : 0;
|
||||
if ($pedimento_id <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'ID inválido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$conn = getConnection();
|
||||
// Verificar propiedad del pedimento
|
||||
$chk = sqlsrv_query($conn, "SELECT 1 FROM pedimentos WHERE id = ? AND usuario_id = ?", [$pedimento_id, $usuario_id]);
|
||||
$own = $chk && sqlsrv_fetch_array($chk) ? true : false;
|
||||
if ($chk) sqlsrv_free_stmt($chk);
|
||||
if (!$own) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['success' => false, 'message' => 'Pedimento no encontrado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql = "SELECT f.id AS factura_id,
|
||||
CASE WHEN cr.id IS NULL THEN 0 ELSE 1 END AS respondido,
|
||||
cr.estado AS estado,
|
||||
cr.fecha_actualizacion AS fecha
|
||||
FROM pedimento_facturas f
|
||||
LEFT JOIN cove_respuestas cr
|
||||
ON cr.factura_id = f.id AND cr.usuario_id = ?
|
||||
WHERE f.pedimento_id = ?
|
||||
ORDER BY f.id";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$usuario_id, $pedimento_id]);
|
||||
if ($stmt === false) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Error de base de datos']);
|
||||
exit;
|
||||
}
|
||||
$data = [];
|
||||
while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
if (isset($r['fecha']) && $r['fecha'] instanceof DateTime) {
|
||||
$r['fecha'] = $r['fecha']->format('Y-m-d H:i:s');
|
||||
}
|
||||
$data[] = $r;
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
echo json_encode(['success' => true, 'data' => $data]);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /IMPORTADORES/cove/ajax_guardar_respuestas
|
||||
* Guarda/actualiza respuestas de COVE para una factura concreta.
|
||||
* Body: pedimento_id, factura_id, respuestas (JSON string), estado (opcional)
|
||||
*/
|
||||
function ajax_guardar_respuestas()
|
||||
{
|
||||
if (!($_SESSION['usuario_id'] ?? false)) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['success' => false, 'message' => 'No autorizado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$usuario_id = (int)$_SESSION['usuario_id'];
|
||||
$pedimento_id = isset($_POST['pedimento_id']) ? (int)$_POST['pedimento_id'] : 0;
|
||||
$factura_id = isset($_POST['factura_id']) ? (int)$_POST['factura_id'] : 0;
|
||||
$respuestas = $_POST['respuestas'] ?? '{}';
|
||||
$estado = $_POST['estado'] ?? 'respondido';
|
||||
if ($pedimento_id <= 0 || $factura_id <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'Parámetros inválidos']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$conn = getConnection();
|
||||
// Verificar propiedad del pedimento
|
||||
$chk = sqlsrv_query($conn, "SELECT 1 FROM pedimentos WHERE id = ? AND usuario_id = ?", [$pedimento_id, $usuario_id]);
|
||||
$own = $chk && sqlsrv_fetch_array($chk) ? true : false;
|
||||
if ($chk) sqlsrv_free_stmt($chk);
|
||||
if (!$own) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['success' => false, 'message' => 'Pedimento no encontrado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Upsert manual: si existe update, si no insert
|
||||
$sel = sqlsrv_query($conn, "SELECT id FROM cove_respuestas WHERE factura_id = ? AND usuario_id = ?", [$factura_id, $usuario_id]);
|
||||
$row = $sel ? sqlsrv_fetch_array($sel, SQLSRV_FETCH_ASSOC) : null;
|
||||
if ($sel) sqlsrv_free_stmt($sel);
|
||||
|
||||
if ($row) {
|
||||
$sql = "UPDATE cove_respuestas
|
||||
SET respuestas = ?, estado = ?, fecha_actualizacion = SYSDATETIME()
|
||||
WHERE id = ?";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$respuestas, $estado, $row['id']]);
|
||||
} else {
|
||||
$sql = "INSERT INTO cove_respuestas (pedimento_id, factura_id, usuario_id, respuestas, estado, fecha_creacion)
|
||||
VALUES (?, ?, ?, ?, ?, SYSDATETIME())";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$pedimento_id, $factura_id, $usuario_id, $respuestas, $estado]);
|
||||
}
|
||||
|
||||
if ($stmt === false) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'No se pudieron guardar las respuestas']);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Estado agregado por pedimento: total de facturas vs respondidas/solicitadas
|
||||
function ajax_estado_pedimentos()
|
||||
{
|
||||
if (!($_SESSION['usuario_id'] ?? false)) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['success' => false, 'message' => 'No autorizado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$usuario_id = (int)$_SESSION['usuario_id'];
|
||||
$conn = getConnection();
|
||||
|
||||
$sql = "
|
||||
SELECT p.id AS pedimento_id,
|
||||
COUNT(f.id) AS total_facturas,
|
||||
SUM(CASE WHEN cr.estado IN ('respondido','solicitado') THEN 1 ELSE 0 END) AS respondidas
|
||||
FROM pedimentos p
|
||||
LEFT JOIN pedimento_facturas f ON f.pedimento_id = p.id
|
||||
LEFT JOIN cove_respuestas cr ON cr.factura_id = f.id AND cr.usuario_id = p.usuario_id
|
||||
WHERE p.usuario_id = ?
|
||||
GROUP BY p.id
|
||||
";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$usuario_id]);
|
||||
if ($stmt === false) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Error de base de datos']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = [];
|
||||
while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
$total = (int)($r['total_facturas'] ?? 0);
|
||||
$resp = (int)($r['respondidas'] ?? 0);
|
||||
$completo = ($total > 0 && $resp >= $total) ? 1 : 0;
|
||||
$data[] = [
|
||||
'pedimento_id' => (int)$r['pedimento_id'],
|
||||
'total_facturas' => $total,
|
||||
'respondidas' => $resp,
|
||||
'completo' => $completo
|
||||
];
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
echo json_encode(['success' => true, 'data' => $data]);
|
||||
exit;
|
||||
}
|
||||
22
app/controllers/debug_paths.php
Normal file
22
app/controllers/debug_paths.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
// Debug para ver rutas
|
||||
echo "<h3>Información de rutas del servidor:</h3>";
|
||||
echo "<p><strong>DOCUMENT_ROOT:</strong> " . $_SERVER['DOCUMENT_ROOT'] . "</p>";
|
||||
echo "<p><strong>SCRIPT_NAME:</strong> " . $_SERVER['SCRIPT_NAME'] . "</p>";
|
||||
echo "<p><strong>REQUEST_URI:</strong> " . $_SERVER['REQUEST_URI'] . "</p>";
|
||||
echo "<p><strong>HTTP_HOST:</strong> " . $_SERVER['HTTP_HOST'] . "</p>";
|
||||
echo "<p><strong>__FILE__:</strong> " . __FILE__ . "</p>";
|
||||
echo "<p><strong>__DIR__:</strong> " . __DIR__ . "</p>";
|
||||
|
||||
// Verificar si los archivos existen
|
||||
$test_file = __DIR__ . '/test_connection.php';
|
||||
$import_file = __DIR__ . '/importar_pedimentos.php';
|
||||
|
||||
echo "<h3>Verificación de archivos:</h3>";
|
||||
echo "<p><strong>test_connection.php:</strong> " . (file_exists($test_file) ? '✅ Existe' : '❌ No existe') . "</p>";
|
||||
echo "<p><strong>importar_pedimentos.php:</strong> " . (file_exists($import_file) ? '✅ Existe' : '❌ No existe') . "</p>";
|
||||
|
||||
echo "<h3>URLs sugeridas:</h3>";
|
||||
$base_url = "http" . (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] === "on" ? "s" : "") . "://" . $_SERVER["HTTP_HOST"];
|
||||
echo "<p><strong>Test URL:</strong> <a href='{$base_url}/IMPORTADORES/app/controllers/test_connection.php' target='_blank'>{$base_url}/IMPORTADORES/app/controllers/test_connection.php</a></p>";
|
||||
?>
|
||||
@@ -3,6 +3,29 @@ require_once __DIR__ . '/../helpers/session.php';
|
||||
require_once __DIR__ . '/../../config/database.php';
|
||||
require_once __DIR__ . '/../helpers/env.php';
|
||||
|
||||
// Formatea el pedimento como: YY-AA-PPPP-PPPPPPP
|
||||
function format_pedimento_display($anio, $aduana, $patente, $numero)
|
||||
{
|
||||
$yy = substr((string)$anio, -2);
|
||||
$ad = substr(preg_replace('/\D/', '', (string)$aduana), 0, 2);
|
||||
$pat = str_pad(preg_replace('/\D/', '', (string)$patente), 4, '0', STR_PAD_LEFT);
|
||||
$num = str_pad(preg_replace('/\D/', '', (string)$numero), 7, '0', STR_PAD_LEFT);
|
||||
$yy = $yy !== '' ? $yy : '00';
|
||||
$ad = str_pad($ad, 2, '0', STR_PAD_LEFT);
|
||||
return "$yy-$ad-$pat-$num";
|
||||
}
|
||||
|
||||
function ensure_expediente_schema($conn) {
|
||||
// Agregar columna pedimento_id si no existe (compatibilidad con esquema nuevo)
|
||||
$chk = sqlsrv_query($conn, "SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'expediente_archivos' AND COLUMN_NAME = 'pedimento_id'");
|
||||
$exists = $chk && sqlsrv_fetch_array($chk) ? true : false;
|
||||
if ($chk) sqlsrv_free_stmt($chk);
|
||||
if (!$exists) {
|
||||
@sqlsrv_query($conn, "ALTER TABLE expediente_archivos ADD pedimento_id INT NULL");
|
||||
@sqlsrv_query($conn, "CREATE INDEX IX_expediente_archivos_pedimento ON expediente_archivos(pedimento_id)");
|
||||
}
|
||||
}
|
||||
|
||||
// Mostrar tabla de expedientes
|
||||
function index()
|
||||
{
|
||||
@@ -12,29 +35,50 @@ function index()
|
||||
}
|
||||
|
||||
$conn = getConnection();
|
||||
ensure_expediente_schema($conn);
|
||||
|
||||
$id_importador = $_SESSION['usuario_id'];
|
||||
|
||||
$sql = "SELECT
|
||||
sif.id_solicitud, sif.numero_pedimento, sif.fecha_factura, sif.aduana, sif.proveedor_clave,
|
||||
COUNT(ea.id) AS total_archivos,
|
||||
ISNULL(SUM(ea.tamano_archivo), 0) AS total_tamano
|
||||
FROM solicitud_importacion_factura sif
|
||||
LEFT JOIN expediente_archivos ea
|
||||
ON ea.id_solicitud = sif.id_solicitud
|
||||
WHERE sif.numero_pedimento IS NOT NULL
|
||||
AND sif.id_importador = ?
|
||||
AND sif.status > 0
|
||||
GROUP BY sif.id_solicitud, sif.numero_pedimento, sif.fecha_factura, sif.aduana, sif.proveedor_clave
|
||||
ORDER BY sif.fecha_factura DESC
|
||||
";
|
||||
// Nuevo origen: pedimentos (cabecera) + facturas para fecha/proveedor + archivos por pedimento
|
||||
$sql = "
|
||||
WITH fact AS (
|
||||
SELECT
|
||||
f.pedimento_id,
|
||||
MAX(f.fecha_factura) AS fecha_factura,
|
||||
MIN(COALESCE(NULLIF(LTRIM(RTRIM(f.proveedor)), ''), '-')) AS proveedor
|
||||
FROM pedimento_facturas f
|
||||
GROUP BY f.pedimento_id
|
||||
), arch AS (
|
||||
SELECT pedimento_id, COUNT(*) AS total_archivos, ISNULL(SUM(tamano_archivo), 0) AS total_tamano
|
||||
FROM expediente_archivos
|
||||
WHERE pedimento_id IS NOT NULL
|
||||
GROUP BY pedimento_id
|
||||
)
|
||||
SELECT
|
||||
p.id AS pedimento_id,
|
||||
p.numero_pedimento,
|
||||
p.patente,
|
||||
p.aduana,
|
||||
p.anio,
|
||||
f.fecha_factura,
|
||||
f.proveedor,
|
||||
ISNULL(a.total_archivos, 0) AS total_archivos,
|
||||
ISNULL(a.total_tamano, 0) AS total_tamano
|
||||
FROM pedimentos p
|
||||
LEFT JOIN fact f ON f.pedimento_id = p.id
|
||||
LEFT JOIN arch a ON a.pedimento_id = p.id
|
||||
WHERE p.usuario_id = ?
|
||||
ORDER BY COALESCE(f.fecha_factura, p.fecha_creacion) DESC, p.id DESC
|
||||
";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$id_importador]);
|
||||
|
||||
$expedientes = [];
|
||||
if ($stmt) {
|
||||
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
$row['pedimento_display'] = format_pedimento_display($row['anio'] ?? '', $row['aduana'] ?? '', $row['patente'] ?? '', $row['numero_pedimento'] ?? '');
|
||||
$expedientes[] = $row;
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
}
|
||||
|
||||
include __DIR__ . '/../../views/expediente/index.php';
|
||||
@@ -45,27 +89,50 @@ function subir($id_solicitud)
|
||||
include __DIR__ . '/../../views/expediente/subir.php';
|
||||
}
|
||||
|
||||
// Vista subir para pedimento nuevo
|
||||
function subir_pedimento($pedimento_id)
|
||||
{
|
||||
if (!isset($_SESSION['usuario_id'])) { header('Location: /IMPORTADORES/login'); exit; }
|
||||
$conn = getConnection();
|
||||
// Validar propiedad del pedimento
|
||||
$chk = sqlsrv_query($conn, "SELECT 1 FROM pedimentos WHERE id = ? AND usuario_id = ?", [$pedimento_id, $_SESSION['usuario_id']]);
|
||||
if (!$chk || !sqlsrv_fetch($chk)) { http_response_code(403); echo "No autorizado"; exit; }
|
||||
if ($chk) sqlsrv_free_stmt($chk);
|
||||
$GLOBALS['pedimento_id'] = (int)$pedimento_id;
|
||||
include __DIR__ . '/../../views/expediente/subir.php';
|
||||
}
|
||||
|
||||
function subir_handler()
|
||||
{
|
||||
if (!isset($_POST['id_solicitud']) || !isset($_FILES['archivos']) || !isset($_SESSION['usuario_id'])) {
|
||||
die("❌ Solicitud inválida.");
|
||||
}
|
||||
if (!isset($_FILES['archivos']) || !isset($_SESSION['usuario_id'])) { die("❌ Solicitud inválida."); }
|
||||
|
||||
$conn = getConnection();
|
||||
ensure_expediente_schema($conn);
|
||||
|
||||
$id_solicitud = (int) $_POST['id_solicitud'];
|
||||
$id_importador = $_SESSION['usuario_id'];
|
||||
|
||||
// Validar propiedad de la solicitud
|
||||
$validStmt = sqlsrv_query($conn, "SELECT 1 FROM solicitud_importacion_factura WHERE id_solicitud = ? AND id_importador = ?", [$id_solicitud, $id_importador]);
|
||||
if (!sqlsrv_fetch($validStmt)) {
|
||||
die("❌ No tienes permisos para esta solicitud.");
|
||||
$id_solicitud = isset($_POST['id_solicitud']) ? (int) $_POST['id_solicitud'] : null;
|
||||
$pedimento_id = isset($_POST['pedimento_id']) ? (int) $_POST['pedimento_id'] : null;
|
||||
|
||||
if ($pedimento_id) {
|
||||
// Validar propiedad del pedimento
|
||||
$validStmt = sqlsrv_query($conn, "SELECT 1 FROM pedimentos WHERE id = ? AND usuario_id = ?", [$pedimento_id, $id_importador]);
|
||||
if (!$validStmt || !sqlsrv_fetch($validStmt)) { die("❌ No tienes permisos para este pedimento."); }
|
||||
if ($validStmt) sqlsrv_free_stmt($validStmt);
|
||||
} elseif ($id_solicitud) {
|
||||
// Validar propiedad de la solicitud (legacy)
|
||||
$validStmt = sqlsrv_query($conn, "SELECT 1 FROM solicitud_importacion_factura WHERE id_solicitud = ? AND id_importador = ?", [$id_solicitud, $id_importador]);
|
||||
if (!$validStmt || !sqlsrv_fetch($validStmt)) { die("❌ No tienes permisos para esta solicitud."); }
|
||||
if ($validStmt) sqlsrv_free_stmt($validStmt);
|
||||
} else {
|
||||
die("❌ Falta identificador de pedimento.");
|
||||
}
|
||||
|
||||
$archivos = $_FILES['archivos'];
|
||||
$usuario = $_SESSION['usuario_nombre'] ?? 'sistema';
|
||||
|
||||
$uploadDir = __DIR__ . '/../../uploads/expedientes/' . $id_solicitud;
|
||||
$folderKey = $pedimento_id ? ('pedimento_' . $pedimento_id) : ('solicitud_' . $id_solicitud);
|
||||
$uploadDir = __DIR__ . '/../../uploads/expedientes/' . $folderKey;
|
||||
if (!is_dir($uploadDir)) {
|
||||
mkdir($uploadDir, 0775, true);
|
||||
}
|
||||
@@ -79,19 +146,29 @@ function subir_handler()
|
||||
|
||||
move_uploaded_file($archivos['tmp_name'][$i], $rutaFinal);
|
||||
|
||||
$rutaDb = "uploads/expedientes/$id_solicitud/$nombreSeguro";
|
||||
$rutaDb = "uploads/expedientes/$folderKey/$nombreSeguro";
|
||||
$tamanoKb = round(filesize($rutaFinal) / 1024, 2);
|
||||
$tipoArchivo = mime_content_type($rutaFinal);
|
||||
|
||||
$sql = "INSERT INTO expediente_archivos
|
||||
(id_solicitud, nombre_archivo, ruta_archivo, tipo_archivo, tamano_archivo, creado_por)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
";
|
||||
$params = [$id_solicitud, $nombreOriginal, $rutaDb, $tipoArchivo, $tamanoKb, $usuario];
|
||||
if ($pedimento_id) {
|
||||
$sql = "INSERT INTO expediente_archivos
|
||||
(pedimento_id, nombre_archivo, ruta_archivo, tipo_archivo, tamano_archivo, creado_por)
|
||||
VALUES (?, ?, ?, ?, ?, ?)";
|
||||
$params = [$pedimento_id, $nombreOriginal, $rutaDb, $tipoArchivo, $tamanoKb, $usuario];
|
||||
} else {
|
||||
$sql = "INSERT INTO expediente_archivos
|
||||
(id_solicitud, nombre_archivo, ruta_archivo, tipo_archivo, tamano_archivo, creado_por)
|
||||
VALUES (?, ?, ?, ?, ?, ?)";
|
||||
$params = [$id_solicitud, $nombreOriginal, $rutaDb, $tipoArchivo, $tamanoKb, $usuario];
|
||||
}
|
||||
sqlsrv_query($conn, $sql, $params);
|
||||
}
|
||||
|
||||
header("Location: /IMPORTADORES/expediente/ver/$id_solicitud");
|
||||
if ($pedimento_id) {
|
||||
header("Location: /IMPORTADORES/expediente/ver_pedimento/$pedimento_id");
|
||||
} else {
|
||||
header("Location: /IMPORTADORES/expediente/ver/$id_solicitud");
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -119,6 +196,51 @@ function ver($id_solicitud)
|
||||
}
|
||||
}
|
||||
|
||||
$GLOBALS['pedimento'] = null; // vista usa variable opcional
|
||||
include __DIR__ . '/../../views/expediente/ver.php';
|
||||
}
|
||||
|
||||
function ver_pedimento($pedimento_id)
|
||||
{
|
||||
if (!isset($_SESSION['usuario_id'])) {
|
||||
header('Location: /IMPORTADORES/login');
|
||||
exit;
|
||||
}
|
||||
|
||||
$conn = getConnection();
|
||||
ensure_expediente_schema($conn);
|
||||
|
||||
$id_importador = $_SESSION['usuario_id'];
|
||||
|
||||
// Validar propiedad
|
||||
$chk = sqlsrv_query($conn, "SELECT 1 FROM pedimentos WHERE id = ? AND usuario_id = ?", [$pedimento_id, $id_importador]);
|
||||
if (!$chk || !sqlsrv_fetch($chk)) { http_response_code(404); echo "No autorizado o no encontrado"; exit; }
|
||||
if ($chk) sqlsrv_free_stmt($chk);
|
||||
|
||||
// Datos del pedimento para cabecera
|
||||
$stmtPed = sqlsrv_query($conn, "SELECT id, numero_pedimento, patente, aduana, anio, clave_documento, rfc_importador, fecha_creacion FROM pedimentos WHERE id = ?", [$pedimento_id]);
|
||||
$pedimento = $stmtPed ? sqlsrv_fetch_array($stmtPed, SQLSRV_FETCH_ASSOC) : null;
|
||||
if ($stmtPed) sqlsrv_free_stmt($stmtPed);
|
||||
if ($pedimento) {
|
||||
$pedimento['pedimento_display'] = format_pedimento_display($pedimento['anio'] ?? '', $pedimento['aduana'] ?? '', $pedimento['patente'] ?? '', $pedimento['numero_pedimento'] ?? '');
|
||||
}
|
||||
|
||||
$sql = "SELECT * FROM expediente_archivos WHERE pedimento_id = ? ORDER BY creado_en DESC";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$pedimento_id]);
|
||||
|
||||
$archivos = [];
|
||||
if ($stmt) {
|
||||
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
if (isset($row['creado_en']) && is_string($row['creado_en'])) {
|
||||
$row['creado_en'] = new DateTime($row['creado_en']);
|
||||
}
|
||||
$archivos[] = $row;
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
}
|
||||
|
||||
$GLOBALS['pedimento_id'] = (int)$pedimento_id;
|
||||
$GLOBALS['pedimento'] = $pedimento;
|
||||
include __DIR__ . '/../../views/expediente/ver.php';
|
||||
}
|
||||
|
||||
@@ -131,9 +253,24 @@ function ver_archivo($id_archivo)
|
||||
|
||||
$conn = getConnection();
|
||||
|
||||
$sql = "SELECT ea.*, sif.id_importador FROM expediente_archivos ea JOIN solicitud_importacion_factura sif ON sif.id_solicitud = ea.id_solicitud WHERE ea.id = ?";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$id_archivo]);
|
||||
$archivo = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
ensure_expediente_schema($conn);
|
||||
// Intentar resolver por pedimento (nuevo) y si no, por solicitud (legacy)
|
||||
$sql = "SELECT ea.*, p.usuario_id AS id_importador
|
||||
FROM expediente_archivos ea
|
||||
JOIN pedimentos p ON p.id = ea.pedimento_id
|
||||
WHERE ea.id = ? AND ea.pedimento_id IS NOT NULL";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$id_archivo]);
|
||||
$archivo = $stmt ? sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC) : null;
|
||||
if ($stmt) sqlsrv_free_stmt($stmt);
|
||||
if (!$archivo) {
|
||||
$sqlL = "SELECT ea.*, sif.id_importador
|
||||
FROM expediente_archivos ea
|
||||
JOIN solicitud_importacion_factura sif ON sif.id_solicitud = ea.id_solicitud
|
||||
WHERE ea.id = ?";
|
||||
$stmtL = sqlsrv_query($conn, $sqlL, [$id_archivo]);
|
||||
$archivo = $stmtL ? sqlsrv_fetch_array($stmtL, SQLSRV_FETCH_ASSOC) : null;
|
||||
if ($stmtL) sqlsrv_free_stmt($stmtL);
|
||||
}
|
||||
|
||||
if (!$archivo || $archivo['id_importador'] != $_SESSION['usuario_id']) {
|
||||
http_response_code(403);
|
||||
@@ -225,3 +362,49 @@ function descargar_zip($id_solicitud)
|
||||
unlink($zip_file);
|
||||
exit;
|
||||
}
|
||||
|
||||
function descargar_zip_pedimento($pedimento_id)
|
||||
{
|
||||
if (!isset($_SESSION['usuario_id'])) {
|
||||
http_response_code(403);
|
||||
echo "No autorizado.";
|
||||
exit;
|
||||
}
|
||||
|
||||
$conn = getConnection();
|
||||
ensure_expediente_schema($conn);
|
||||
|
||||
$id_importador = $_SESSION['usuario_id'];
|
||||
|
||||
// Validar propiedad
|
||||
$chk = sqlsrv_query($conn, "SELECT 1 FROM pedimentos WHERE id = ? AND usuario_id = ?", [$pedimento_id, $id_importador]);
|
||||
if (!$chk || !sqlsrv_fetch($chk)) { http_response_code(404); echo "No autorizado"; exit; }
|
||||
if ($chk) sqlsrv_free_stmt($chk);
|
||||
|
||||
$stmt = sqlsrv_query($conn, "SELECT nombre_archivo, ruta_archivo FROM expediente_archivos WHERE pedimento_id = ?", [$pedimento_id]);
|
||||
if (!$stmt) { http_response_code(500); echo "Error al consultar archivos."; exit; }
|
||||
|
||||
$archivos = [];
|
||||
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
$ruta_absoluta = __DIR__ . '/../../' . $row['ruta_archivo'];
|
||||
if (file_exists($ruta_absoluta)) {
|
||||
$archivos[] = [ 'ruta' => $ruta_absoluta, 'nombre' => $row['nombre_archivo'] ];
|
||||
}
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
if (empty($archivos)) { http_response_code(404); echo "No hay archivos válidos para comprimir."; exit; }
|
||||
|
||||
$zip_file = tempnam(sys_get_temp_dir(), 'expediente_') . '.zip';
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($zip_file, ZipArchive::CREATE) !== true) { http_response_code(500); echo "No se pudo crear el archivo ZIP."; exit; }
|
||||
foreach ($archivos as $a) { $zip->addFile($a['ruta'], $a['nombre']); }
|
||||
$zip->close();
|
||||
|
||||
header('Content-Type: application/zip');
|
||||
header('Content-Disposition: attachment; filename="expediente_pedimento_' . $pedimento_id . '.zip"');
|
||||
header('Content-Length: ' . filesize($zip_file));
|
||||
readfile($zip_file);
|
||||
unlink($zip_file);
|
||||
exit;
|
||||
}
|
||||
@@ -142,20 +142,22 @@ function obtenerCatalogosVisibles($idUsuario)
|
||||
function obtenerIconoCatalogo($nombre)
|
||||
{
|
||||
$iconos = [
|
||||
'Locaciones' => '📍',
|
||||
'Vinculación' => '🔗',
|
||||
'Transportistas' => '🚚',
|
||||
'Transportes' => '🚛',
|
||||
'Choferes' => '👨✈️',
|
||||
'Proveedores' => '🏭',
|
||||
'Productos frecuentes' => '⭐',
|
||||
'Solicitudes importación' => '📄',
|
||||
'Expediente electrónico' => '📁',
|
||||
'Configuración' => '⚙️',
|
||||
'Cerrar sesión' => '🚪'
|
||||
'Locaciones' => 'fas fa-map-marker-alt',
|
||||
'Vinculación' => 'fas fa-link',
|
||||
'Transportistas' => 'fas fa-truck',
|
||||
'Transportes' => 'fas fa-shipping-fast',
|
||||
'Choferes' => 'fas fa-user-tie',
|
||||
'Proveedores' => 'fas fa-industry',
|
||||
'Productos frecuentes' => 'fas fa-star',
|
||||
'Solicitudes importación' => 'fas fa-file-alt',
|
||||
'Expediente electrónico' => 'fas fa-folder',
|
||||
'Configuración' => 'fas fa-cog',
|
||||
'Cerrar sesión' => 'fas fa-sign-out-alt',
|
||||
'Agencias' => 'fas fa-building',
|
||||
'Importadores' => 'fas fa-boxes'
|
||||
];
|
||||
|
||||
return $iconos[$nombre] ?? '📁';
|
||||
return $iconos[$nombre] ?? 'fas fa-folder';
|
||||
}
|
||||
|
||||
// Función para el dashboard del importador
|
||||
|
||||
636
app/controllers/importar_pedimentos.php
Normal file
636
app/controllers/importar_pedimentos.php
Normal file
@@ -0,0 +1,636 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
// Headers para AJAX
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: POST');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
// Log para debugging
|
||||
error_log("=== INICIO IMPORTACIÓN ===");
|
||||
error_log("Usuario ID en sesión: " . (isset($_SESSION['usuario_id']) ? $_SESSION['usuario_id'] : 'NO_USUARIO'));
|
||||
error_log("Tipo usuario: " . (isset($_SESSION['tipo_usuario']) ? $_SESSION['tipo_usuario'] : 'NO_TIPO'));
|
||||
error_log("Método: " . $_SERVER['REQUEST_METHOD']);
|
||||
error_log("Archivos recibidos: " . print_r($_FILES, true));
|
||||
|
||||
// Verificar que el usuario esté logueado
|
||||
if (!isset($_SESSION['usuario_id'])) {
|
||||
error_log("ERROR: Usuario no autenticado - no hay usuario_id en sesión");
|
||||
http_response_code(401);
|
||||
echo json_encode(['success' => false, 'message' => 'Usuario no autenticado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Verificar que sea una petición POST
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'message' => 'Método no permitido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Verificar que se haya subido un archivo
|
||||
if (!isset($_FILES['archivo']) || $_FILES['archivo']['error'] !== UPLOAD_ERR_OK) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'No se ha recibido ningún archivo válido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
class ImportadorPedimentos {
|
||||
private $conn;
|
||||
private $estadisticas = [
|
||||
'total_lineas' => 0,
|
||||
'pedimentos_procesados' => 0,
|
||||
'facturas_procesadas' => 0,
|
||||
'partidas_procesadas' => 0,
|
||||
'errores' => 0,
|
||||
'duplicados' => 0
|
||||
];
|
||||
|
||||
private $errores_detalle = [];
|
||||
private $pedimentos_creados = [];
|
||||
|
||||
public function __construct($connection) {
|
||||
$this->conn = $connection;
|
||||
}
|
||||
|
||||
public function procesarArchivo($archivo_path, $validar_duplicados = true) {
|
||||
$inicio_tiempo = time();
|
||||
$nombre_archivo = basename($archivo_path);
|
||||
|
||||
try {
|
||||
$contenido = file_get_contents($archivo_path);
|
||||
if ($contenido === false) {
|
||||
throw new Exception("No se pudo leer el archivo");
|
||||
}
|
||||
|
||||
// Convertir encoding si es necesario (muchos archivos julianos usan Latin1)
|
||||
if (!mb_check_encoding($contenido, 'UTF-8')) {
|
||||
$contenido = mb_convert_encoding($contenido, 'UTF-8', 'ISO-8859-1');
|
||||
}
|
||||
|
||||
$lineas = explode("\n", $contenido);
|
||||
$this->estadisticas['total_lineas'] = count($lineas);
|
||||
|
||||
$pedimento_actual = null;
|
||||
$facturas_pedimento = [];
|
||||
$partidas_pedimento = [];
|
||||
|
||||
foreach ($lineas as $numero_linea => $linea) {
|
||||
$linea = trim($linea);
|
||||
if (empty($linea)) continue;
|
||||
|
||||
try {
|
||||
$codigo = substr($linea, 0, 3);
|
||||
|
||||
switch ($codigo) {
|
||||
case '500':
|
||||
// Línea de header - ignorar por ahora
|
||||
break;
|
||||
|
||||
case '501':
|
||||
// Si hay un pedimento anterior, procesarlo
|
||||
if ($pedimento_actual) {
|
||||
$this->procesarPedimento($pedimento_actual, $facturas_pedimento, $partidas_pedimento, $validar_duplicados);
|
||||
$facturas_pedimento = [];
|
||||
$partidas_pedimento = [];
|
||||
}
|
||||
$pedimento_actual = $this->parsearLinea501Real($linea);
|
||||
break;
|
||||
|
||||
case '505':
|
||||
if ($pedimento_actual) {
|
||||
try {
|
||||
$factura = $this->parsearLinea505Real($linea);
|
||||
$facturas_pedimento[] = $factura;
|
||||
error_log("Factura 505 procesada exitosamente: " . $factura['numero_factura']);
|
||||
} catch (Exception $e) {
|
||||
error_log("ERROR procesando línea 505: " . $e->getMessage());
|
||||
error_log("Línea problemática: " . $linea);
|
||||
$this->estadisticas['errores']++;
|
||||
// Continuar con la siguiente línea
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '551':
|
||||
if ($pedimento_actual) {
|
||||
try {
|
||||
$partida = $this->parsearLinea551Real($linea);
|
||||
$partidas_pedimento[] = $partida;
|
||||
error_log("Partida 551 procesada exitosamente: secuencia " . $partida['secuencia']);
|
||||
} catch (Exception $e) {
|
||||
error_log("ERROR procesando línea 551: " . $e->getMessage());
|
||||
error_log("Línea problemática: " . $linea);
|
||||
$this->estadisticas['errores']++;
|
||||
// Continuar con la siguiente línea en lugar de fallar completamente
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '506':
|
||||
case '509':
|
||||
case '510':
|
||||
case '511':
|
||||
case '553':
|
||||
case '554':
|
||||
case '556':
|
||||
case '557':
|
||||
case '558':
|
||||
case '800':
|
||||
case '801':
|
||||
// Otros códigos del formato real - ignorar por ahora
|
||||
break;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->errores_detalle[] = "Línea " . ($numero_linea + 1) . ": " . $e->getMessage();
|
||||
$this->estadisticas['errores']++;
|
||||
}
|
||||
}
|
||||
|
||||
// Procesar el último pedimento
|
||||
if ($pedimento_actual) {
|
||||
$this->procesarPedimento($pedimento_actual, $facturas_pedimento, $partidas_pedimento, $validar_duplicados);
|
||||
}
|
||||
|
||||
// Registrar log de importación
|
||||
$tiempo_procesamiento = time() - $inicio_tiempo;
|
||||
$this->registrarLog($nombre_archivo, $tiempo_procesamiento,
|
||||
count($this->errores_detalle) > 0 ? 'con_errores' : 'exitoso');
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'estadisticas' => $this->estadisticas,
|
||||
'errores' => $this->errores_detalle,
|
||||
'pedimentos_creados' => $this->pedimentos_creados
|
||||
];
|
||||
|
||||
} catch (Exception $e) {
|
||||
// Registrar log de error
|
||||
$tiempo_procesamiento = time() - $inicio_tiempo;
|
||||
$this->registrarLog($nombre_archivo, $tiempo_procesamiento, 'fallido', $e->getMessage());
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
private function registrarLog($archivo_nombre, $tiempo_procesamiento, $estado, $error_mensaje = null) {
|
||||
try {
|
||||
$sql = "INSERT INTO importacion_logs (
|
||||
usuario_id, archivo_nombre, total_lineas, pedimentos_procesados,
|
||||
facturas_procesadas, partidas_procesadas, errores, duplicados,
|
||||
tiempo_procesamiento, estado, detalles_errores
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
$detalles_errores = null;
|
||||
if (!empty($this->errores_detalle)) {
|
||||
$detalles_errores = implode("\n", $this->errores_detalle);
|
||||
} else if ($error_mensaje) {
|
||||
$detalles_errores = $error_mensaje;
|
||||
}
|
||||
|
||||
$params = [
|
||||
isset($_SESSION['usuario_id']) ? $_SESSION['usuario_id'] : null,
|
||||
$archivo_nombre,
|
||||
$this->estadisticas['total_lineas'],
|
||||
$this->estadisticas['pedimentos_procesados'],
|
||||
$this->estadisticas['facturas_procesadas'],
|
||||
$this->estadisticas['partidas_procesadas'],
|
||||
$this->estadisticas['errores'],
|
||||
$this->estadisticas['duplicados'],
|
||||
$tiempo_procesamiento,
|
||||
$estado,
|
||||
$detalles_errores
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_query($this->conn, $sql, $params);
|
||||
if ($stmt) {
|
||||
sqlsrv_free_stmt($stmt);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// No hacer nada si falla el log, no queremos interrumpir el proceso principal
|
||||
}
|
||||
}
|
||||
|
||||
private function parsearLinea501($linea) {
|
||||
// Formato: 501|numero_pedimento|patente|aduana|anio|clave_documento|rfc_importador|...
|
||||
$campos = explode('|', $linea);
|
||||
|
||||
if (count($campos) < 7) {
|
||||
throw new Exception("Formato inválido en línea 501");
|
||||
}
|
||||
|
||||
return [
|
||||
'numero_pedimento' => trim($campos[1]),
|
||||
'patente' => trim($campos[2]),
|
||||
'aduana' => trim($campos[3]),
|
||||
'anio' => trim($campos[4]),
|
||||
'clave_documento' => trim($campos[5]),
|
||||
'rfc_importador' => trim($campos[6]),
|
||||
'fecha_creacion' => date('Y-m-d H:i:s'),
|
||||
'usuario_id' => $_SESSION['usuario_id']
|
||||
];
|
||||
}
|
||||
|
||||
private function parsearLinea501Real($linea) {
|
||||
// Formato real: 501|patente|numero_pedimento|aduana|tipo|clave_documento|aduana2||rfc_importador|nombre_importador|...
|
||||
$campos = explode('|', $linea);
|
||||
|
||||
if (count($campos) < 10) {
|
||||
throw new Exception("Formato inválido en línea 501 real - campos insuficientes: " . count($campos));
|
||||
}
|
||||
|
||||
error_log("Parseando 501 real: " . $linea);
|
||||
error_log("Campos: " . print_r(array_slice($campos, 0, 10), true));
|
||||
|
||||
return [
|
||||
'numero_pedimento' => trim($campos[2]), // Campo 2: numero de pedimento
|
||||
'patente' => trim($campos[1]), // Campo 1: patente
|
||||
'aduana' => trim($campos[3]), // Campo 3: aduana
|
||||
'anio' => date('Y'), // Usar año actual
|
||||
'clave_documento' => trim($campos[5]), // Campo 5: clave documento
|
||||
'rfc_importador' => trim($campos[8]), // Campo 8: RFC importador
|
||||
'fecha_creacion' => date('Y-m-d H:i:s'),
|
||||
'usuario_id' => $_SESSION['usuario_id']
|
||||
];
|
||||
}
|
||||
|
||||
private function parsearLinea505($linea) {
|
||||
// Formato: 505|numero_factura|fecha_factura|valor_dolares|valor_factura|...
|
||||
$campos = explode('|', $linea);
|
||||
|
||||
if (count($campos) < 5) {
|
||||
throw new Exception("Formato inválido en línea 505");
|
||||
}
|
||||
|
||||
return [
|
||||
'numero_factura' => trim($campos[1]),
|
||||
'fecha_factura' => $this->convertirFecha(trim($campos[2])),
|
||||
'valor_dolares' => floatval(trim($campos[3])),
|
||||
'valor_factura' => floatval(trim($campos[4]))
|
||||
];
|
||||
}
|
||||
|
||||
private function parsearLinea505Real($linea) {
|
||||
// Formato oficial 505: 505|numero_pedimento|fecha_cfdi|numero_cfdi_cove|termino_facturacion|moneda|valor_dolares|valor_total|pais|entidad_federativa|rfc_proveedor|nombre_proveedor|calle|num_int|num_ext|cp|municipio|...
|
||||
$campos = explode('|', $linea);
|
||||
|
||||
if (count($campos) < 12) {
|
||||
throw new Exception("Formato inválido en línea 505 - campos insuficientes: " . count($campos) . " (mínimo: 12)");
|
||||
}
|
||||
|
||||
error_log("DEBUG 505 - Total campos: " . count($campos));
|
||||
error_log("DEBUG 505 - Primeros 12 campos: " . implode(' | ', array_slice($campos, 0, 12)));
|
||||
|
||||
// Extraer datos según documentación oficial SAT
|
||||
$numero_pedimento = trim($campos[1]); // Campo 1: Número de Pedimento
|
||||
$fecha_cfdi = trim($campos[2]); // Campo 2: Fecha de CFDI
|
||||
$numero_cfdi_cove = trim($campos[3]); // Campo 3: Número de CFDI (COVE) - 40 caracteres max
|
||||
$termino_facturacion = trim($campos[4]); // Campo 4: Término de Facturación - 3 caracteres
|
||||
$moneda = trim($campos[5]); // Campo 5: Moneda - 3 caracteres
|
||||
$valor_dolares = floatval(trim($campos[6])); // Campo 6: Valor Total en Dólares USD
|
||||
$valor_total = floatval(trim($campos[7])); // Campo 7: Valor Total en moneda del CFDI
|
||||
$pais = trim($campos[8]); // Campo 8: País del CFDI - 3 caracteres
|
||||
$entidad_federativa = trim($campos[9]); // Campo 9: Entidad Federativa - 3 caracteres
|
||||
$rfc_proveedor = trim($campos[10]); // Campo 10: RFC Proveedor - 30 caracteres max
|
||||
$nombre_proveedor = trim($campos[11]); // Campo 11: Nombre Proveedor - 120 caracteres max
|
||||
|
||||
error_log("DEBUG 505 - Datos oficiales extraídos:");
|
||||
error_log(" - Número CFDI/COVE: '$numero_cfdi_cove'");
|
||||
error_log(" - Fecha CFDI: '$fecha_cfdi'");
|
||||
error_log(" - Término facturación: '$termino_facturacion'");
|
||||
error_log(" - Moneda: '$moneda'");
|
||||
error_log(" - Valor USD: $valor_dolares");
|
||||
error_log(" - Valor total: $valor_total");
|
||||
error_log(" - RFC Proveedor: '$rfc_proveedor'");
|
||||
error_log(" - Nombre Proveedor: '$nombre_proveedor'");
|
||||
|
||||
return [
|
||||
'numero_factura' => $numero_cfdi_cove, // Campo 3: Número de CFDI/COVE
|
||||
'fecha_factura' => $this->convertirFechaReal($fecha_cfdi), // Campo 2: Fecha CFDI
|
||||
'valor_dolares' => $valor_dolares, // Campo 6: Valor en USD
|
||||
'valor_factura' => $valor_total, // Campo 7: Valor total en moneda CFDI
|
||||
'cove' => $numero_cfdi_cove, // Campo 3: COVE (mismo que número factura)
|
||||
'moneda' => $moneda, // Campo 5: Moneda
|
||||
'proveedor' => $nombre_proveedor // Campo 11: Nombre del proveedor
|
||||
];
|
||||
}
|
||||
|
||||
private function parsearLinea551($linea) {
|
||||
// Formato: 551|secuencia|fraccion_arancelaria|descripcion|cantidad|unidad|valor_unitario|...
|
||||
$campos = explode('|', $linea);
|
||||
|
||||
if (count($campos) < 7) {
|
||||
throw new Exception("Formato inválido en línea 551");
|
||||
}
|
||||
|
||||
return [
|
||||
'secuencia' => intval(trim($campos[1])),
|
||||
'fraccion_arancelaria' => trim($campos[2]),
|
||||
'descripcion' => trim($campos[3]),
|
||||
'cantidad' => floatval(trim($campos[4])),
|
||||
'unidad' => trim($campos[5]),
|
||||
'valor_unitario' => floatval(trim($campos[6]))
|
||||
];
|
||||
}
|
||||
|
||||
private function parsearLinea551Real($linea) {
|
||||
// Formato oficial 551: 551|numero_pedimento|fraccion_arancelaria|numero_partida|subdivision|descripcion|precio_unitario|valor_aduana|valor_comercial|valor_dolares|cantidad_umc|unidad_comercial|...
|
||||
$campos = explode('|', $linea);
|
||||
|
||||
if (count($campos) < 12) {
|
||||
throw new Exception("Formato inválido en línea 551 - campos insuficientes: " . count($campos) . " (mínimo: 12)");
|
||||
}
|
||||
|
||||
error_log("DEBUG 551 - Total campos: " . count($campos));
|
||||
error_log("DEBUG 551 - Primeros 12 campos: " . implode(' | ', array_slice($campos, 0, 12)));
|
||||
|
||||
// Extraer datos según documentación oficial
|
||||
$numero_pedimento = isset($campos[1]) ? trim($campos[1]) : '';
|
||||
$fraccion_arancelaria = isset($campos[2]) ? trim($campos[2]) : '';
|
||||
$numero_partida = isset($campos[3]) ? intval(trim($campos[3])) : 0;
|
||||
$subdivision = isset($campos[4]) ? trim($campos[4]) : '';
|
||||
$descripcion = isset($campos[5]) ? trim($campos[5]) : '';
|
||||
$precio_unitario = isset($campos[6]) ? floatval(trim($campos[6])) : 0;
|
||||
$valor_aduana = isset($campos[7]) ? floatval(trim($campos[7])) : 0;
|
||||
$valor_comercial = isset($campos[8]) ? floatval(trim($campos[8])) : 0;
|
||||
$valor_dolares = isset($campos[9]) ? floatval(trim($campos[9])) : 0;
|
||||
$cantidad_umc = isset($campos[10]) ? floatval(trim($campos[10])) : 0;
|
||||
$unidad_comercial = isset($campos[11]) ? trim($campos[11]) : '';
|
||||
|
||||
// Aplicar valores por defecto según documentación oficial
|
||||
if (empty($fraccion_arancelaria)) {
|
||||
error_log("WARNING 551: Fracción arancelaria vacía, usando valor por defecto");
|
||||
$fraccion_arancelaria = 'PENDIENTE';
|
||||
}
|
||||
|
||||
if ($numero_partida <= 0) {
|
||||
error_log("WARNING 551: Número de partida inválido, generando secuencial");
|
||||
static $contador_partida = 1;
|
||||
$numero_partida = $contador_partida++;
|
||||
}
|
||||
|
||||
if (empty($descripcion)) {
|
||||
error_log("WARNING 551: Descripción vacía, usando valor por defecto");
|
||||
$descripcion = 'DESCRIPCIÓN PENDIENTE';
|
||||
}
|
||||
|
||||
if ($cantidad_umc <= 0) {
|
||||
error_log("WARNING 551: Cantidad UMC inválida ($cantidad_umc), usando 1.0000");
|
||||
$cantidad_umc = 1.0000;
|
||||
}
|
||||
|
||||
if (empty($unidad_comercial)) {
|
||||
error_log("WARNING 551: Unidad comercial vacía, usando 'PZ'");
|
||||
$unidad_comercial = 'PZ'; // Pieza como unidad por defecto
|
||||
}
|
||||
|
||||
// Calcular precio unitario si no existe pero hay valores
|
||||
if ($precio_unitario == 0) {
|
||||
if ($valor_dolares > 0 && $cantidad_umc > 0) {
|
||||
$precio_unitario = $valor_dolares / $cantidad_umc;
|
||||
error_log("DEBUG 551: Precio unitario calculado: $precio_unitario");
|
||||
} elseif ($valor_comercial > 0 && $cantidad_umc > 0) {
|
||||
$precio_unitario = $valor_comercial / $cantidad_umc;
|
||||
error_log("DEBUG 551: Precio unitario calculado desde valor comercial: $precio_unitario");
|
||||
}
|
||||
}
|
||||
|
||||
error_log("DEBUG 551 - Datos procesados:");
|
||||
error_log(" - Partida: $numero_partida");
|
||||
error_log(" - Fracción: '$fraccion_arancelaria'");
|
||||
error_log(" - Descripción: '" . substr($descripcion, 0, 50) . "...'");
|
||||
error_log(" - Cantidad: $cantidad_umc");
|
||||
error_log(" - Unidad: '$unidad_comercial'");
|
||||
error_log(" - Precio unitario: $precio_unitario");
|
||||
error_log(" - Valor dólares: $valor_dolares");
|
||||
|
||||
return [
|
||||
'secuencia' => $numero_partida,
|
||||
'fraccion_arancelaria' => $fraccion_arancelaria,
|
||||
'descripcion' => $descripcion,
|
||||
'cantidad' => $cantidad_umc,
|
||||
'unidad' => $unidad_comercial,
|
||||
'valor_unitario' => $precio_unitario,
|
||||
'peso_neto' => null,
|
||||
'peso_bruto' => null
|
||||
];
|
||||
}
|
||||
|
||||
private function procesarPedimento($pedimento, $facturas, $partidas, $validar_duplicados) {
|
||||
try {
|
||||
error_log("Procesando pedimento: " . $pedimento['numero_pedimento']);
|
||||
error_log("Validar duplicados: " . ($validar_duplicados ? 'SÍ' : 'NO'));
|
||||
|
||||
// Validar duplicados si está habilitado
|
||||
if ($validar_duplicados && $this->existePedimento($pedimento['numero_pedimento'])) {
|
||||
error_log("Pedimento {$pedimento['numero_pedimento']} marcado como duplicado, omitiendo...");
|
||||
$this->estadisticas['duplicados']++;
|
||||
return;
|
||||
}
|
||||
|
||||
error_log("Insertando pedimento nuevo: " . $pedimento['numero_pedimento']);
|
||||
|
||||
// Iniciar transacción
|
||||
sqlsrv_begin_transaction($this->conn);
|
||||
|
||||
// Insertar pedimento
|
||||
$sql = "INSERT INTO pedimentos (numero_pedimento, patente, aduana, anio, clave_documento,
|
||||
rfc_importador, fecha_creacion, usuario_id, estado)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'activo'); SELECT SCOPE_IDENTITY() AS id;";
|
||||
|
||||
$params = [
|
||||
$pedimento['numero_pedimento'],
|
||||
$pedimento['patente'],
|
||||
$pedimento['aduana'],
|
||||
$pedimento['anio'],
|
||||
$pedimento['clave_documento'],
|
||||
$pedimento['rfc_importador'],
|
||||
$pedimento['fecha_creacion'],
|
||||
$pedimento['usuario_id']
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_query($this->conn, $sql, $params);
|
||||
if ($stmt === false) {
|
||||
throw new Exception("Error al insertar pedimento: " . print_r(sqlsrv_errors(), true));
|
||||
}
|
||||
|
||||
// Obtener el ID del pedimento insertado
|
||||
sqlsrv_next_result($stmt);
|
||||
sqlsrv_fetch($stmt);
|
||||
$pedimento_id = sqlsrv_get_field($stmt, 0);
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
// Insertar facturas
|
||||
foreach ($facturas as $factura) {
|
||||
$sql = "INSERT INTO pedimento_facturas (pedimento_id, numero_factura, fecha_factura,
|
||||
valor_dolares, valor_factura, cove)
|
||||
VALUES (?, ?, ?, ?, ?, ?)";
|
||||
|
||||
$params = [
|
||||
$pedimento_id,
|
||||
$factura['numero_factura'],
|
||||
$factura['fecha_factura'],
|
||||
$factura['valor_dolares'],
|
||||
$factura['valor_factura'],
|
||||
isset($factura['cove']) ? $factura['cove'] : null
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_query($this->conn, $sql, $params);
|
||||
if ($stmt === false) {
|
||||
throw new Exception("Error al insertar factura: " . print_r(sqlsrv_errors(), true));
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
$this->estadisticas['facturas_procesadas']++;
|
||||
}
|
||||
|
||||
// Insertar partidas
|
||||
foreach ($partidas as $partida) {
|
||||
error_log("INSERTANDO PARTIDA: " . print_r($partida, true));
|
||||
|
||||
$sql = "INSERT INTO pedimento_partidas (pedimento_id, secuencia, fraccion_arancelaria,
|
||||
descripcion, cantidad, unidad, valor_unitario,
|
||||
peso_neto, peso_bruto)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
$params = [
|
||||
$pedimento_id,
|
||||
$partida['secuencia'],
|
||||
$partida['fraccion_arancelaria'],
|
||||
$partida['descripcion'],
|
||||
$partida['cantidad'],
|
||||
$partida['unidad'],
|
||||
$partida['valor_unitario'],
|
||||
isset($partida['peso_neto']) ? $partida['peso_neto'] : null,
|
||||
isset($partida['peso_bruto']) ? $partida['peso_bruto'] : null
|
||||
];
|
||||
|
||||
error_log("PARÁMETROS SQL: " . print_r($params, true));
|
||||
|
||||
$stmt = sqlsrv_query($this->conn, $sql, $params);
|
||||
if ($stmt === false) {
|
||||
$error = print_r(sqlsrv_errors(), true);
|
||||
error_log("ERROR AL INSERTAR PARTIDA: " . $error);
|
||||
throw new Exception("Error al insertar partida: " . $error);
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
$this->estadisticas['partidas_procesadas']++;
|
||||
error_log("Partida insertada exitosamente - Total procesadas: " . $this->estadisticas['partidas_procesadas']);
|
||||
}
|
||||
|
||||
// Confirmar transacción
|
||||
sqlsrv_commit($this->conn);
|
||||
|
||||
$this->estadisticas['pedimentos_procesados']++;
|
||||
$this->pedimentos_creados[] = $pedimento['numero_pedimento'];
|
||||
|
||||
} catch (Exception $e) {
|
||||
sqlsrv_rollback($this->conn);
|
||||
$this->errores_detalle[] = "Error procesando pedimento {$pedimento['numero_pedimento']}: " . $e->getMessage();
|
||||
$this->estadisticas['errores']++;
|
||||
}
|
||||
}
|
||||
|
||||
private function existePedimento($numero_pedimento) {
|
||||
$sql = "SELECT id, fecha_creacion FROM pedimentos WHERE numero_pedimento = ?";
|
||||
$stmt = sqlsrv_query($this->conn, $sql, [$numero_pedimento]);
|
||||
if ($stmt === false) {
|
||||
error_log("Error al verificar duplicado: " . print_r(sqlsrv_errors(), true));
|
||||
return false;
|
||||
}
|
||||
$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
if ($row) {
|
||||
error_log("Pedimento duplicado encontrado: {$numero_pedimento} (ID: {$row['id']})");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function convertirFecha($fecha_str) {
|
||||
// Convertir formato DDMMYYYY a YYYY-MM-DD
|
||||
if (strlen($fecha_str) === 8) {
|
||||
$dia = substr($fecha_str, 0, 2);
|
||||
$mes = substr($fecha_str, 2, 2);
|
||||
$anio = substr($fecha_str, 4, 4);
|
||||
return "$anio-$mes-$dia";
|
||||
}
|
||||
return date('Y-m-d'); // Fecha por defecto
|
||||
}
|
||||
|
||||
private function convertirFechaReal($fecha_str) {
|
||||
// Convertir fecha del archivo real - puede venir en formato YYYYMMDD o DDMMYYYY
|
||||
if (empty($fecha_str) || strlen($fecha_str) !== 8) {
|
||||
return date('Y-m-d'); // Fecha por defecto
|
||||
}
|
||||
|
||||
// Intentar formato YYYYMMDD primero
|
||||
if (substr($fecha_str, 0, 2) === '20') {
|
||||
$anio = substr($fecha_str, 0, 4);
|
||||
$mes = substr($fecha_str, 4, 2);
|
||||
$dia = substr($fecha_str, 6, 2);
|
||||
} else {
|
||||
// Formato DDMMYYYY
|
||||
$dia = substr($fecha_str, 0, 2);
|
||||
$mes = substr($fecha_str, 2, 2);
|
||||
$anio = substr($fecha_str, 4, 4);
|
||||
}
|
||||
|
||||
// Validar fecha
|
||||
if (checkdate($mes, $dia, $anio)) {
|
||||
return "$anio-$mes-$dia";
|
||||
}
|
||||
|
||||
return date('Y-m-d'); // Fecha por defecto si no es válida
|
||||
}
|
||||
}
|
||||
|
||||
// Procesar la importación
|
||||
try {
|
||||
$archivo = $_FILES['archivo'];
|
||||
$validar_duplicados = isset($_POST['validar_duplicados']) && $_POST['validar_duplicados'] === 'on';
|
||||
|
||||
// Crear directorio temporal si no existe
|
||||
$upload_dir = __DIR__ . '/../storage/temp/';
|
||||
if (!is_dir($upload_dir)) {
|
||||
mkdir($upload_dir, 0755, true);
|
||||
}
|
||||
|
||||
// Mover archivo a directorio temporal
|
||||
$archivo_temporal = $upload_dir . 'import_' . time() . '_' . $archivo['name'];
|
||||
if (!move_uploaded_file($archivo['tmp_name'], $archivo_temporal)) {
|
||||
throw new Exception("Error al procesar el archivo subido");
|
||||
}
|
||||
|
||||
// Obtener conexión a SQL Server
|
||||
$conn = getConnection();
|
||||
|
||||
// Procesar archivo
|
||||
$importador = new ImportadorPedimentos($conn);
|
||||
$resultado = $importador->procesarArchivo($archivo_temporal, $validar_duplicados);
|
||||
|
||||
// Cerrar conexión
|
||||
sqlsrv_close($conn);
|
||||
|
||||
// Limpiar archivo temporal
|
||||
unlink($archivo_temporal);
|
||||
|
||||
// Enviar respuesta
|
||||
echo json_encode($resultado);
|
||||
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Error del servidor: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
?>
|
||||
@@ -2,38 +2,157 @@
|
||||
require_once __DIR__ . '/../../config/database.php';
|
||||
require_once __DIR__ . '/../helpers/session.php';
|
||||
|
||||
function index() {
|
||||
// Formatea el pedimento como: YY-AA-PPPP-PPPPPPP
|
||||
function mve_format_pedimento_display($anio, $aduana, $patente, $numero)
|
||||
{
|
||||
$yy = substr((string)$anio, -2);
|
||||
$ad = substr(preg_replace('/\D/', '', (string)$aduana), 0, 2);
|
||||
$pat = str_pad(preg_replace('/\D/', '', (string)$patente), 4, '0', STR_PAD_LEFT);
|
||||
$num = str_pad(preg_replace('/\D/', '', (string)$numero), 7, '0', STR_PAD_LEFT);
|
||||
$yy = $yy !== '' ? $yy : '00';
|
||||
$ad = str_pad($ad, 2, '0', STR_PAD_LEFT);
|
||||
return "$yy-$ad-$pat-$num";
|
||||
}
|
||||
|
||||
// Garantiza que expediente_archivos tenga la columna pedimento_id
|
||||
function mve_ensure_expediente_schema($conn)
|
||||
{
|
||||
$chk = sqlsrv_query($conn, "SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'expediente_archivos' AND COLUMN_NAME = 'pedimento_id'");
|
||||
$exists = $chk && sqlsrv_fetch_array($chk) ? true : false;
|
||||
if ($chk) sqlsrv_free_stmt($chk);
|
||||
if (!$exists) {
|
||||
@sqlsrv_query($conn, "ALTER TABLE expediente_archivos ADD pedimento_id INT NULL");
|
||||
@sqlsrv_query($conn, "CREATE INDEX IX_expediente_archivos_pedimento ON expediente_archivos(pedimento_id)");
|
||||
}
|
||||
}
|
||||
|
||||
// Genera documentos de prueba (Acuse y Detalle) en el expediente del pedimento
|
||||
function mve_generar_documentos_expediente($conn, $pedimento_id, $usuario_nombre = 'sistema')
|
||||
{
|
||||
mve_ensure_expediente_schema($conn);
|
||||
|
||||
// Obtener datos del pedimento para el encabezado
|
||||
$stmt = sqlsrv_query($conn, "SELECT numero_pedimento, patente, aduana, anio, rfc_importador, clave_documento, fecha_creacion FROM pedimentos WHERE id = ?", [$pedimento_id]);
|
||||
$ped = $stmt ? sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC) : null;
|
||||
if ($stmt) sqlsrv_free_stmt($stmt);
|
||||
if (!$ped) return; // nada que hacer
|
||||
|
||||
$display = mve_format_pedimento_display($ped['anio'] ?? '', $ped['aduana'] ?? '', $ped['patente'] ?? '', $ped['numero_pedimento'] ?? '');
|
||||
|
||||
// Directorio destino
|
||||
$folderKey = 'pedimento_' . (int)$pedimento_id;
|
||||
$uploadDir = __DIR__ . '/../../uploads/expedientes/' . $folderKey;
|
||||
if (!is_dir($uploadDir)) { @mkdir($uploadDir, 0775, true); }
|
||||
|
||||
// Contenidos HTML simples
|
||||
$now = date('Y-m-d H:i');
|
||||
$htmlAcuse = "<html><head><meta charset='utf-8'><style>body{font-family:sans-serif} h1{font-size:18px} .m{font-family:monospace}</style></head><body>"
|
||||
."<h1>Acuse de Manifestación de Valor</h1>"
|
||||
."<p><strong>Pedimento:</strong> <span class='m'>{$display}</span></p>"
|
||||
."<p><strong>RFC:</strong> ".htmlspecialchars($ped['rfc_importador'] ?? '-', ENT_QUOTES, 'UTF-8')."</p>"
|
||||
."<p><strong>Clave:</strong> ".htmlspecialchars($ped['clave_documento'] ?? '-', ENT_QUOTES, 'UTF-8')."</p>"
|
||||
."<p><strong>Generado:</strong> {$now}</p>"
|
||||
."<p>Documento de prueba generado automáticamente.</p>"
|
||||
."</body></html>";
|
||||
|
||||
$htmlDetalle = "<html><head><meta charset='utf-8'><style>body{font-family:sans-serif} h1{font-size:18px} .m{font-family:monospace}</style></head><body>"
|
||||
."<h1>Detalle de Manifestación de Valor</h1>"
|
||||
."<p><strong>Pedimento:</strong> <span class='m'>{$display}</span></p>"
|
||||
."<p>Este es un detalle de ejemplo para pruebas.</p>"
|
||||
."<ul><li>Sección 65/66 capturada (mock)</li><li>Precios pagados y por pagar (mock)</li><li>Compensaciones (mock)</li></ul>"
|
||||
."<p><strong>Generado:</strong> {$now}</p>"
|
||||
."</body></html>";
|
||||
|
||||
// Intentar generar PDF con Dompdf; fallback a .txt si no está disponible
|
||||
$docs = [
|
||||
[ 'nombre' => 'Acuse Manifestacion de Valor', 'html' => $htmlAcuse ],
|
||||
[ 'nombre' => 'Detalle Manifestacion de Valor', 'html' => $htmlDetalle ],
|
||||
];
|
||||
|
||||
$dompdfOk = false;
|
||||
try {
|
||||
@require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
if (class_exists('Dompdf\\Dompdf')) { $dompdfOk = true; }
|
||||
} catch (\Throwable $e) { $dompdfOk = false; }
|
||||
|
||||
foreach ($docs as $d) {
|
||||
$safeBase = preg_replace('/[^A-Za-z0-9._\- ]/', '_', $d['nombre']);
|
||||
$filename = $safeBase . '_' . time() . ($dompdfOk ? '.pdf' : '.txt');
|
||||
$fullPath = $uploadDir . '/' . $filename;
|
||||
$rutaDb = 'uploads/expedientes/' . $folderKey . '/' . $filename;
|
||||
|
||||
if ($dompdfOk) {
|
||||
try {
|
||||
$dompdf = new Dompdf\Dompdf([ 'isRemoteEnabled' => false ]);
|
||||
$dompdf->loadHtml($d['html']);
|
||||
$dompdf->setPaper('letter', 'portrait');
|
||||
$dompdf->render();
|
||||
file_put_contents($fullPath, $dompdf->output());
|
||||
$tipo = 'application/pdf';
|
||||
} catch (\Throwable $e) {
|
||||
// Fallback a texto
|
||||
file_put_contents($fullPath, strip_tags($d['html']));
|
||||
$tipo = 'text/plain';
|
||||
}
|
||||
} else {
|
||||
file_put_contents($fullPath, strip_tags($d['html']));
|
||||
$tipo = 'text/plain';
|
||||
}
|
||||
|
||||
$tamanoKb = file_exists($fullPath) ? round(filesize($fullPath) / 1024, 2) : 0;
|
||||
// Insertar en expediente_archivos
|
||||
sqlsrv_query($conn,
|
||||
"INSERT INTO expediente_archivos (pedimento_id, nombre_archivo, ruta_archivo, tipo_archivo, tamano_archivo, creado_por) VALUES (?,?,?,?,?,?)",
|
||||
[$pedimento_id, $d['nombre'] . ($dompdfOk ? '.pdf' : '.txt'), $rutaDb, $tipo, $tamanoKb, $usuario_nombre]
|
||||
);
|
||||
}
|
||||
}
|
||||
function index()
|
||||
{
|
||||
include __DIR__ . '/../../views/mve/lista.php';
|
||||
}
|
||||
|
||||
function ajax_guardar_datos_factura() {
|
||||
try {
|
||||
// Validar que el usuario esté autenticado
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
echo json_encode(['success' => false, 'message' => 'Usuario no autenticado']);
|
||||
return;
|
||||
}
|
||||
// Guarda en bloque los datos enviados (se usa por el botón Guardar)
|
||||
function ajax_guardar_datos_factura()
|
||||
{
|
||||
if (!($_SESSION['usuario_id'] ?? false)) {
|
||||
echo json_encode(['success' => false, 'message' => 'No autorizado']);
|
||||
return;
|
||||
}
|
||||
|
||||
$id_factura = $_POST['id_factura'] ?? null;
|
||||
$id_pedimento = $_POST['id_pedimento'] ?? null;
|
||||
$datos_art65 = json_decode($_POST['datos_art65'] ?? '{}', true);
|
||||
$datos_art66 = json_decode($_POST['datos_art66'] ?? '{}', true);
|
||||
$usuario_id = (int)$_SESSION['usuario_id'];
|
||||
$id_factura = isset($_POST['id_factura']) ? (int)$_POST['id_factura'] : 0;
|
||||
$id_pedimento = isset($_POST['id_pedimento']) ? (int)$_POST['id_pedimento'] : 0;
|
||||
$datos_art65 = json_decode($_POST['datos_art65'] ?? '{}', true);
|
||||
$datos_art66 = json_decode($_POST['datos_art66'] ?? '{}', true);
|
||||
$datos_precio_pagado = json_decode($_POST['datos_precio_pagado'] ?? '{}', true);
|
||||
$datos_precio_pagar = json_decode($_POST['datos_precio_pagar'] ?? '{}', true);
|
||||
$datos_compenso = json_decode($_POST['datos_compenso'] ?? '{}', true);
|
||||
|
||||
if (!$id_factura || !$id_pedimento) {
|
||||
echo json_encode(['success' => false, 'message' => 'Faltan datos requeridos']);
|
||||
return;
|
||||
}
|
||||
if ($id_factura <= 0 || $id_pedimento <= 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'Parámetros inválidos']);
|
||||
return;
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
$conn = getConnection();
|
||||
|
||||
// Verificar si ya existen datos para esta factura
|
||||
$stmt = $db->prepare("SELECT id FROM mve_facturas_datos WHERE id_pedimento = ? AND id_factura = ?");
|
||||
$stmt->execute([$id_pedimento, $id_factura]);
|
||||
$existe = $stmt->fetch();
|
||||
// Verificar propiedad del pedimento y que la factura le pertenezca
|
||||
$chk = sqlsrv_query($conn, "SELECT 1 FROM pedimentos p JOIN pedimento_facturas f ON f.pedimento_id = p.id WHERE p.id = ? AND f.id = ? AND p.usuario_id = ?", [$id_pedimento, $id_factura, $usuario_id]);
|
||||
$own = $chk && sqlsrv_fetch_array($chk) ? true : false;
|
||||
if ($chk) sqlsrv_free_stmt($chk);
|
||||
if (!$own) {
|
||||
echo json_encode(['success' => false, 'message' => 'No autorizado o no encontrado']);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($existe) {
|
||||
// Actualizar registro existente
|
||||
$sql = "UPDATE mve_facturas_datos SET
|
||||
// ¿Existe registro para esta factura?
|
||||
$sel = sqlsrv_query($conn, "SELECT id FROM mve_facturas_datos WHERE id_pedimento = ? AND id_factura = ?", [$id_pedimento, $id_factura]);
|
||||
$row = $sel ? sqlsrv_fetch_array($sel, SQLSRV_FETCH_ASSOC) : null;
|
||||
if ($sel) sqlsrv_free_stmt($sel);
|
||||
|
||||
if ($row) {
|
||||
// UPDATE
|
||||
$sql = "UPDATE mve_facturas_datos SET
|
||||
art65_fecha_transporte = ?, art65_importe_transporte = ?,
|
||||
art65_fecha_descuentos = ?, art65_importe_descuentos = ?,
|
||||
art65_fecha_posteriores = ?, art65_importe_posteriores = ?,
|
||||
@@ -48,29 +167,56 @@ function ajax_guardar_datos_factura() {
|
||||
art66_fecha_regalias = ?, art66_importe_regalias = ?, art66_cargo_regalias = ?,
|
||||
art66_fecha_producto = ?, art66_importe_producto = ?, art66_cargo_producto = ?,
|
||||
|
||||
fecha_actualizacion = NOW()
|
||||
WHERE id_pedimento = ? AND id_factura = ?";
|
||||
precio_pagado_fecha_pago = ?, precio_pagado_importe = ?, precio_pagado_moneda = ?,
|
||||
precio_pagado_forma_pago = ?, precio_pagado_referencia = ?,
|
||||
|
||||
$params = [
|
||||
$datos_art65['fecha_transporte'] ?: null, $datos_art65['importe_transporte'] ?: null,
|
||||
$datos_art65['fecha_descuentos'] ?: null, $datos_art65['importe_descuentos'] ?: null,
|
||||
$datos_art65['fecha_posteriores'] ?: null, $datos_art65['importe_posteriores'] ?: null,
|
||||
$datos_art65['fecha_contribuciones'] ?: null, $datos_art65['importe_contribuciones'] ?: null,
|
||||
$datos_art65['fecha_pagos_vendedor'] ?: null, $datos_art65['importe_pagos_vendedor'] ?: null,
|
||||
precio_pagar_fecha_limite = ?, precio_pagar_importe = ?, precio_pagar_moneda = ?,
|
||||
precio_pagar_terminos = ?, precio_pagar_observaciones = ?,
|
||||
|
||||
$datos_art66['fecha_comisiones'] ?: null, $datos_art66['importe_comisiones'] ?: null, $datos_art66['cargo_comisiones'] ?: null,
|
||||
$datos_art66['fecha_envases'] ?: null, $datos_art66['importe_envases'] ?: null, $datos_art66['cargo_envases'] ?: null,
|
||||
$datos_art66['fecha_embalaje'] ?: null, $datos_art66['importe_embalaje'] ?: null, $datos_art66['cargo_embalaje'] ?: null,
|
||||
$datos_art66['fecha_transporte_dec'] ?: null, $datos_art66['importe_transporte_dec'] ?: null, $datos_art66['cargo_transporte_dec'] ?: null,
|
||||
$datos_art66['fecha_ingenieria'] ?: null, $datos_art66['importe_ingenieria'] ?: null, $datos_art66['cargo_ingenieria'] ?: null,
|
||||
$datos_art66['fecha_regalias'] ?: null, $datos_art66['importe_regalias'] ?: null, $datos_art66['cargo_regalias'] ?: null,
|
||||
$datos_art66['fecha_producto'] ?: null, $datos_art66['importe_producto'] ?: null, $datos_art66['cargo_producto'] ?: null,
|
||||
compenso_fecha = ?, compenso_importe = ?, compenso_tipo = ?,
|
||||
compenso_motivo = ?, compenso_documentos = ?, compenso_descripcion = ?
|
||||
WHERE id = ?";
|
||||
|
||||
$id_pedimento, $id_factura
|
||||
];
|
||||
} else {
|
||||
// Crear nuevo registro
|
||||
$sql = "INSERT INTO mve_facturas_datos (
|
||||
$params = [
|
||||
$datos_art65['fecha_transporte'] ?? null, $datos_art65['importe_transporte'] ?? null,
|
||||
$datos_art65['fecha_descuentos'] ?? null, $datos_art65['importe_descuentos'] ?? null,
|
||||
$datos_art65['fecha_posteriores'] ?? null, $datos_art65['importe_posteriores'] ?? null,
|
||||
$datos_art65['fecha_contribuciones'] ?? null, $datos_art65['importe_contribuciones'] ?? null,
|
||||
$datos_art65['fecha_pagos_vendedor'] ?? null, $datos_art65['importe_pagos_vendedor'] ?? null,
|
||||
|
||||
$datos_art66['fecha_comisiones'] ?? null, $datos_art66['importe_comisiones'] ?? null, $datos_art66['cargo_comisiones'] ?? null,
|
||||
$datos_art66['fecha_envases'] ?? null, $datos_art66['importe_envases'] ?? null, $datos_art66['cargo_envases'] ?? null,
|
||||
$datos_art66['fecha_embalaje'] ?? null, $datos_art66['importe_embalaje'] ?? null, $datos_art66['cargo_embalaje'] ?? null,
|
||||
$datos_art66['fecha_transporte_dec'] ?? null, $datos_art66['importe_transporte_dec'] ?? null, $datos_art66['cargo_transporte_dec'] ?? null,
|
||||
$datos_art66['fecha_ingenieria'] ?? null, $datos_art66['importe_ingenieria'] ?? null, $datos_art66['cargo_ingenieria'] ?? null,
|
||||
$datos_art66['fecha_regalias'] ?? null, $datos_art66['importe_regalias'] ?? null, $datos_art66['cargo_regalias'] ?? null,
|
||||
$datos_art66['fecha_producto'] ?? null, $datos_art66['importe_producto'] ?? null, $datos_art66['cargo_producto'] ?? null,
|
||||
|
||||
$datos_precio_pagado['fecha_precio_pagado'] ?? null,
|
||||
$datos_precio_pagado['importe_precio_pagado'] ?? null,
|
||||
$datos_precio_pagado['moneda_precio_pagado'] ?? null,
|
||||
$datos_precio_pagado['forma_pago_precio_pagado'] ?? null,
|
||||
$datos_precio_pagado['referencia_precio_pagado'] ?? null,
|
||||
|
||||
$datos_precio_pagar['fecha_limite_pago'] ?? null,
|
||||
$datos_precio_pagar['importe_precio_pagar'] ?? null,
|
||||
$datos_precio_pagar['moneda_precio_pagar'] ?? null,
|
||||
$datos_precio_pagar['terminos_precio_pagar'] ?? null,
|
||||
$datos_precio_pagar['observaciones_precio_pagar'] ?? null,
|
||||
|
||||
$datos_compenso['fecha_compenso'] ?? null,
|
||||
$datos_compenso['importe_compenso'] ?? null,
|
||||
$datos_compenso['tipo_compenso'] ?? null,
|
||||
$datos_compenso['motivo_compenso'] ?? null,
|
||||
$datos_compenso['documentos_compenso'] ?? null,
|
||||
$datos_compenso['descripcion_compenso'] ?? null,
|
||||
$row['id']
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_query($conn, $sql, $params);
|
||||
} else {
|
||||
// INSERT
|
||||
$sql = "INSERT INTO mve_facturas_datos (
|
||||
id_pedimento, id_factura, numero_factura,
|
||||
art65_fecha_transporte, art65_importe_transporte,
|
||||
art65_fecha_descuentos, art65_importe_descuentos,
|
||||
@@ -86,105 +232,446 @@ function ajax_guardar_datos_factura() {
|
||||
art66_fecha_regalias, art66_importe_regalias, art66_cargo_regalias,
|
||||
art66_fecha_producto, art66_importe_producto, art66_cargo_producto,
|
||||
|
||||
precio_pagado_fecha_pago, precio_pagado_importe, precio_pagado_moneda,
|
||||
precio_pagado_forma_pago, precio_pagado_referencia,
|
||||
|
||||
precio_pagar_fecha_limite, precio_pagar_importe, precio_pagar_moneda,
|
||||
precio_pagar_terminos, precio_pagar_observaciones,
|
||||
|
||||
compenso_fecha, compenso_importe, compenso_tipo,
|
||||
compenso_motivo, compenso_documentos, compenso_descripcion,
|
||||
|
||||
usuario_creacion
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
|
||||
|
||||
// Obtener número de factura opcional
|
||||
$num = null;
|
||||
$nf = sqlsrv_query($conn, "SELECT numero_factura FROM pedimento_facturas WHERE id = ?", [$id_factura]);
|
||||
if ($nf && ($r = sqlsrv_fetch_array($nf, SQLSRV_FETCH_ASSOC))) { $num = $r['numero_factura']; }
|
||||
if ($nf) sqlsrv_free_stmt($nf);
|
||||
|
||||
$params = [
|
||||
$id_pedimento, $id_factura, $num,
|
||||
$datos_art65['fecha_transporte'] ?? null, $datos_art65['importe_transporte'] ?? null,
|
||||
$datos_art65['fecha_descuentos'] ?? null, $datos_art65['importe_descuentos'] ?? null,
|
||||
$datos_art65['fecha_posteriores'] ?? null, $datos_art65['importe_posteriores'] ?? null,
|
||||
$datos_art65['fecha_contribuciones'] ?? null, $datos_art65['importe_contribuciones'] ?? null,
|
||||
$datos_art65['fecha_pagos_vendedor'] ?? null, $datos_art65['importe_pagos_vendedor'] ?? null,
|
||||
|
||||
$datos_art66['fecha_comisiones'] ?? null, $datos_art66['importe_comisiones'] ?? null, $datos_art66['cargo_comisiones'] ?? null,
|
||||
$datos_art66['fecha_envases'] ?? null, $datos_art66['importe_envases'] ?? null, $datos_art66['cargo_envases'] ?? null,
|
||||
$datos_art66['fecha_embalaje'] ?? null, $datos_art66['importe_embalaje'] ?? null, $datos_art66['cargo_embalaje'] ?? null,
|
||||
$datos_art66['fecha_transporte_dec'] ?? null, $datos_art66['importe_transporte_dec'] ?? null, $datos_art66['cargo_transporte_dec'] ?? null,
|
||||
$datos_art66['fecha_ingenieria'] ?? null, $datos_art66['importe_ingenieria'] ?? null, $datos_art66['cargo_ingenieria'] ?? null,
|
||||
$datos_art66['fecha_regalias'] ?? null, $datos_art66['importe_regalias'] ?? null, $datos_art66['cargo_regalias'] ?? null,
|
||||
$datos_art66['fecha_producto'] ?? null, $datos_art66['importe_producto'] ?? null, $datos_art66['cargo_producto'] ?? null,
|
||||
|
||||
$datos_precio_pagado['fecha_precio_pagado'] ?? null,
|
||||
$datos_precio_pagado['importe_precio_pagado'] ?? null,
|
||||
$datos_precio_pagado['moneda_precio_pagado'] ?? null,
|
||||
$datos_precio_pagado['forma_pago_precio_pagado'] ?? null,
|
||||
$datos_precio_pagado['referencia_precio_pagado'] ?? null,
|
||||
|
||||
$datos_precio_pagar['fecha_limite_pago'] ?? null,
|
||||
$datos_precio_pagar['importe_precio_pagar'] ?? null,
|
||||
$datos_precio_pagar['moneda_precio_pagar'] ?? null,
|
||||
$datos_precio_pagar['terminos_precio_pagar'] ?? null,
|
||||
$datos_precio_pagar['observaciones_precio_pagar'] ?? null,
|
||||
|
||||
$datos_compenso['fecha_compenso'] ?? null,
|
||||
$datos_compenso['importe_compenso'] ?? null,
|
||||
$datos_compenso['tipo_compenso'] ?? null,
|
||||
$datos_compenso['motivo_compenso'] ?? null,
|
||||
$datos_compenso['documentos_compenso'] ?? null,
|
||||
$datos_compenso['descripcion_compenso'] ?? null,
|
||||
|
||||
(string)$usuario_id
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_query($conn, $sql, $params);
|
||||
}
|
||||
|
||||
if ($stmt === false) {
|
||||
echo json_encode(['success' => false, 'message' => 'Error al guardar']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Marcar estado de MVE/COVE como respondido (para el badge)
|
||||
upsert_cove_respuesta_min($conn, $id_pedimento, $id_factura, $usuario_id);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
}
|
||||
|
||||
// Autosave por sección: seccion in ['65','66','precio_pagado','precio_pagar','compenso']
|
||||
function ajax_guardar_seccion()
|
||||
{
|
||||
if (!($_SESSION['usuario_id'] ?? false)) {
|
||||
echo json_encode(['success' => false, 'message' => 'No autorizado']);
|
||||
return;
|
||||
}
|
||||
|
||||
$usuario_id = (int)$_SESSION['usuario_id'];
|
||||
$id_factura = isset($_POST['id_factura']) ? (int)$_POST['id_factura'] : 0;
|
||||
$id_pedimento = isset($_POST['id_pedimento']) ? (int)$_POST['id_pedimento'] : 0;
|
||||
$seccion = $_POST['seccion'] ?? '';
|
||||
$datos = json_decode($_POST['datos'] ?? '{}', true);
|
||||
if ($id_factura <= 0 || $id_pedimento <= 0 || !$seccion) {
|
||||
echo json_encode(['success' => false, 'message' => 'Parámetros inválidos']);
|
||||
return;
|
||||
}
|
||||
|
||||
$conn = getConnection();
|
||||
// Verificar propiedad
|
||||
$chk = sqlsrv_query($conn, "SELECT 1 FROM pedimentos p JOIN pedimento_facturas f ON f.pedimento_id = p.id WHERE p.id = ? AND f.id = ? AND p.usuario_id = ?", [$id_pedimento, $id_factura, $usuario_id]);
|
||||
$own = $chk && sqlsrv_fetch_array($chk) ? true : false;
|
||||
if ($chk) sqlsrv_free_stmt($chk);
|
||||
if (!$own) { echo json_encode(['success' => false, 'message' => 'No autorizado']); return; }
|
||||
|
||||
// Garantizar que exista el registro base
|
||||
$sel = sqlsrv_query($conn, "SELECT id FROM mve_facturas_datos WHERE id_pedimento = ? AND id_factura = ?", [$id_pedimento, $id_factura]);
|
||||
$row = $sel ? sqlsrv_fetch_array($sel, SQLSRV_FETCH_ASSOC) : null; if ($sel) sqlsrv_free_stmt($sel);
|
||||
if (!$row) {
|
||||
// Crear registro vacío
|
||||
$num = null; $nf = sqlsrv_query($conn, "SELECT numero_factura FROM pedimento_facturas WHERE id = ?", [$id_factura]);
|
||||
if ($nf && ($r = sqlsrv_fetch_array($nf, SQLSRV_FETCH_ASSOC))) { $num = $r['numero_factura']; }
|
||||
if ($nf) sqlsrv_free_stmt($nf);
|
||||
$ins = sqlsrv_query($conn, "INSERT INTO mve_facturas_datos (id_pedimento, id_factura, numero_factura, usuario_creacion) VALUES (?,?,?,?)", [$id_pedimento, $id_factura, $num, (string)$usuario_id]);
|
||||
if ($ins === false) { echo json_encode(['success' => false, 'message' => 'Error al iniciar registro']); return; }
|
||||
$sel2 = sqlsrv_query($conn, "SELECT id FROM mve_facturas_datos WHERE id_pedimento = ? AND id_factura = ?", [$id_pedimento, $id_factura]);
|
||||
$row = $sel2 ? sqlsrv_fetch_array($sel2, SQLSRV_FETCH_ASSOC) : null; if ($sel2) sqlsrv_free_stmt($sel2);
|
||||
}
|
||||
|
||||
if (!$row) { echo json_encode(['success' => false, 'message' => 'No se pudo crear el registro']); return; }
|
||||
|
||||
$id = (int)$row['id'];
|
||||
$sql = '';
|
||||
$params = [];
|
||||
switch ($seccion) {
|
||||
case '65':
|
||||
$sql = "UPDATE mve_facturas_datos SET
|
||||
art65_fecha_transporte = ?, art65_importe_transporte = ?,
|
||||
art65_fecha_descuentos = ?, art65_importe_descuentos = ?,
|
||||
art65_fecha_posteriores = ?, art65_importe_posteriores = ?,
|
||||
art65_fecha_contribuciones = ?, art65_importe_contribuciones = ?,
|
||||
art65_fecha_pagos_vendedor = ?, art65_importe_pagos_vendedor = ?
|
||||
WHERE id = ?";
|
||||
$params = [
|
||||
$id_pedimento, $id_factura, "FACTURA-$id_factura",
|
||||
$datos_art65['fecha_transporte'] ?: null, $datos_art65['importe_transporte'] ?: null,
|
||||
$datos_art65['fecha_descuentos'] ?: null, $datos_art65['importe_descuentos'] ?: null,
|
||||
$datos_art65['fecha_posteriores'] ?: null, $datos_art65['importe_posteriores'] ?: null,
|
||||
$datos_art65['fecha_contribuciones'] ?: null, $datos_art65['importe_contribuciones'] ?: null,
|
||||
$datos_art65['fecha_pagos_vendedor'] ?: null, $datos_art65['importe_pagos_vendedor'] ?: null,
|
||||
|
||||
$datos_art66['fecha_comisiones'] ?: null, $datos_art66['importe_comisiones'] ?: null, $datos_art66['cargo_comisiones'] ?: null,
|
||||
$datos_art66['fecha_envases'] ?: null, $datos_art66['importe_envases'] ?: null, $datos_art66['cargo_envases'] ?: null,
|
||||
$datos_art66['fecha_embalaje'] ?: null, $datos_art66['importe_embalaje'] ?: null, $datos_art66['cargo_embalaje'] ?: null,
|
||||
$datos_art66['fecha_transporte_dec'] ?: null, $datos_art66['importe_transporte_dec'] ?: null, $datos_art66['cargo_transporte_dec'] ?: null,
|
||||
$datos_art66['fecha_ingenieria'] ?: null, $datos_art66['importe_ingenieria'] ?: null, $datos_art66['cargo_ingenieria'] ?: null,
|
||||
$datos_art66['fecha_regalias'] ?: null, $datos_art66['importe_regalias'] ?: null, $datos_art66['cargo_regalias'] ?: null,
|
||||
$datos_art66['fecha_producto'] ?: null, $datos_art66['importe_producto'] ?: null, $datos_art66['cargo_producto'] ?: null,
|
||||
|
||||
$_SESSION['user_id']
|
||||
$datos['fecha_transporte'] ?? null, $datos['importe_transporte'] ?? null,
|
||||
$datos['fecha_descuentos'] ?? null, $datos['importe_descuentos'] ?? null,
|
||||
$datos['fecha_posteriores'] ?? null, $datos['importe_posteriores'] ?? null,
|
||||
$datos['fecha_contribuciones'] ?? null, $datos['importe_contribuciones'] ?? null,
|
||||
$datos['fecha_pagos_vendedor'] ?? null, $datos['importe_pagos_vendedor'] ?? null,
|
||||
$id
|
||||
];
|
||||
break;
|
||||
case '66':
|
||||
$sql = "UPDATE mve_facturas_datos SET
|
||||
art66_fecha_comisiones = ?, art66_importe_comisiones = ?, art66_cargo_comisiones = ?,
|
||||
art66_fecha_envases = ?, art66_importe_envases = ?, art66_cargo_envases = ?,
|
||||
art66_fecha_embalaje = ?, art66_importe_embalaje = ?, art66_cargo_embalaje = ?,
|
||||
art66_fecha_transporte_dec = ?, art66_importe_transporte_dec = ?, art66_cargo_transporte_dec = ?,
|
||||
art66_fecha_ingenieria = ?, art66_importe_ingenieria = ?, art66_cargo_ingenieria = ?,
|
||||
art66_fecha_regalias = ?, art66_importe_regalias = ?, art66_cargo_regalias = ?,
|
||||
art66_fecha_producto = ?, art66_importe_producto = ?, art66_cargo_producto = ?
|
||||
WHERE id = ?";
|
||||
$params = [
|
||||
$datos['fecha_comisiones'] ?? null, $datos['importe_comisiones'] ?? null, $datos['cargo_comisiones'] ?? null,
|
||||
$datos['fecha_envases'] ?? null, $datos['importe_envases'] ?? null, $datos['cargo_envases'] ?? null,
|
||||
$datos['fecha_embalaje'] ?? null, $datos['importe_embalaje'] ?? null, $datos['cargo_embalaje'] ?? null,
|
||||
$datos['fecha_transporte_dec'] ?? null, $datos['importe_transporte_dec'] ?? null, $datos['cargo_transporte_dec'] ?? null,
|
||||
$datos['fecha_ingenieria'] ?? null, $datos['importe_ingenieria'] ?? null, $datos['cargo_ingenieria'] ?? null,
|
||||
$datos['fecha_regalias'] ?? null, $datos['importe_regalias'] ?? null, $datos['cargo_regalias'] ?? null,
|
||||
$datos['fecha_producto'] ?? null, $datos['importe_producto'] ?? null, $datos['cargo_producto'] ?? null,
|
||||
$id
|
||||
];
|
||||
break;
|
||||
case 'precio_pagado':
|
||||
$sql = "UPDATE mve_facturas_datos SET
|
||||
precio_pagado_fecha_pago = ?, precio_pagado_importe = ?, precio_pagado_moneda = ?,
|
||||
precio_pagado_forma_pago = ?, precio_pagado_referencia = ?
|
||||
WHERE id = ?";
|
||||
$params = [
|
||||
$datos['fecha_precio_pagado'] ?? null,
|
||||
$datos['importe_precio_pagado'] ?? null,
|
||||
$datos['moneda_precio_pagado'] ?? null,
|
||||
$datos['forma_pago_precio_pagado'] ?? null,
|
||||
$datos['referencia_precio_pagado'] ?? null,
|
||||
$id
|
||||
];
|
||||
break;
|
||||
case 'precio_pagar':
|
||||
$sql = "UPDATE mve_facturas_datos SET
|
||||
precio_pagar_fecha_limite = ?, precio_pagar_importe = ?, precio_pagar_moneda = ?,
|
||||
precio_pagar_terminos = ?, precio_pagar_observaciones = ?
|
||||
WHERE id = ?";
|
||||
$params = [
|
||||
$datos['fecha_limite_pago'] ?? null,
|
||||
$datos['importe_precio_pagar'] ?? null,
|
||||
$datos['moneda_precio_pagar'] ?? null,
|
||||
$datos['terminos_precio_pagar'] ?? null,
|
||||
$datos['observaciones_precio_pagar'] ?? null,
|
||||
$id
|
||||
];
|
||||
break;
|
||||
case 'compenso':
|
||||
$sql = "UPDATE mve_facturas_datos SET
|
||||
compenso_fecha = ?, compenso_importe = ?, compenso_tipo = ?,
|
||||
compenso_motivo = ?, compenso_documentos = ?, compenso_descripcion = ?
|
||||
WHERE id = ?";
|
||||
$params = [
|
||||
$datos['fecha_compenso'] ?? null,
|
||||
$datos['importe_compenso'] ?? null,
|
||||
$datos['tipo_compenso'] ?? null,
|
||||
$datos['motivo_compenso'] ?? null,
|
||||
$datos['documentos_compenso'] ?? null,
|
||||
$datos['descripcion_compenso'] ?? null,
|
||||
$id
|
||||
];
|
||||
break;
|
||||
default:
|
||||
echo json_encode(['success' => false, 'message' => 'Sección inválida']);
|
||||
return;
|
||||
}
|
||||
|
||||
$stmt = sqlsrv_query($conn, $sql, $params);
|
||||
if ($stmt === false) {
|
||||
echo json_encode(['success' => false, 'message' => 'Error al guardar sección']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Marcar estado para badge
|
||||
upsert_cove_respuesta_min($conn, $id_pedimento, $id_factura, $usuario_id);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
}
|
||||
|
||||
function ajax_obtener_datos_factura()
|
||||
{
|
||||
$id_factura = isset($_GET['id_factura']) ? (int)$_GET['id_factura'] : 0;
|
||||
if ($id_factura <= 0) { echo json_encode(['success' => false, 'message' => 'ID inválido']); return; }
|
||||
|
||||
$conn = getConnection();
|
||||
$stmt = sqlsrv_query($conn, "SELECT * FROM mve_facturas_datos WHERE id_factura = ?", [$id_factura]);
|
||||
if ($stmt === false) { echo json_encode(['success' => false, 'message' => 'Error DB']); return; }
|
||||
$datos = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
if ($stmt) sqlsrv_free_stmt($stmt);
|
||||
|
||||
if ($datos) {
|
||||
// Formateador de fechas seguro para JSON
|
||||
$fmtDate = function($v) {
|
||||
if ($v instanceof DateTime) return $v->format('Y-m-d');
|
||||
if (is_string($v)) return substr($v, 0, 10);
|
||||
return $v ?: null;
|
||||
};
|
||||
if (isset($datos['fecha_actualizacion']) && $datos['fecha_actualizacion'] instanceof DateTime) {
|
||||
$datos['fecha_actualizacion'] = $datos['fecha_actualizacion']->format('Y-m-d H:i:s');
|
||||
}
|
||||
$datosEstructurados = [
|
||||
'art65' => [
|
||||
'fecha_transporte' => $fmtDate($datos['art65_fecha_transporte'] ?? null),
|
||||
'importe_transporte' => $datos['art65_importe_transporte'] ?? null,
|
||||
'fecha_descuentos' => $fmtDate($datos['art65_fecha_descuentos'] ?? null),
|
||||
'importe_descuentos' => $datos['art65_importe_descuentos'] ?? null,
|
||||
'fecha_posteriores' => $fmtDate($datos['art65_fecha_posteriores'] ?? null),
|
||||
'importe_posteriores' => $datos['art65_importe_posteriores'] ?? null,
|
||||
'fecha_contribuciones' => $fmtDate($datos['art65_fecha_contribuciones'] ?? null),
|
||||
'importe_contribuciones' => $datos['art65_importe_contribuciones'] ?? null,
|
||||
'fecha_pagos_vendedor' => $fmtDate($datos['art65_fecha_pagos_vendedor'] ?? null),
|
||||
'importe_pagos_vendedor' => $datos['art65_importe_pagos_vendedor'] ?? null,
|
||||
],
|
||||
'art66' => [
|
||||
'fecha_comisiones' => $fmtDate($datos['art66_fecha_comisiones'] ?? null),
|
||||
'importe_comisiones' => $datos['art66_importe_comisiones'] ?? null,
|
||||
'cargo_comisiones' => $datos['art66_cargo_comisiones'] ?? null,
|
||||
'fecha_envases' => $fmtDate($datos['art66_fecha_envases'] ?? null),
|
||||
'importe_envases' => $datos['art66_importe_envases'] ?? null,
|
||||
'cargo_envases' => $datos['art66_cargo_envases'] ?? null,
|
||||
'fecha_embalaje' => $fmtDate($datos['art66_fecha_embalaje'] ?? null),
|
||||
'importe_embalaje' => $datos['art66_importe_embalaje'] ?? null,
|
||||
'cargo_embalaje' => $datos['art66_cargo_embalaje'] ?? null,
|
||||
'fecha_transporte_dec' => $fmtDate($datos['art66_fecha_transporte_dec'] ?? null),
|
||||
'importe_transporte_dec' => $datos['art66_importe_transporte_dec'] ?? null,
|
||||
'cargo_transporte_dec' => $datos['art66_cargo_transporte_dec'] ?? null,
|
||||
'fecha_ingenieria' => $fmtDate($datos['art66_fecha_ingenieria'] ?? null),
|
||||
'importe_ingenieria' => $datos['art66_importe_ingenieria'] ?? null,
|
||||
'cargo_ingenieria' => $datos['art66_cargo_ingenieria'] ?? null,
|
||||
'fecha_regalias' => $fmtDate($datos['art66_fecha_regalias'] ?? null),
|
||||
'importe_regalias' => $datos['art66_importe_regalias'] ?? null,
|
||||
'cargo_regalias' => $datos['art66_cargo_regalias'] ?? null,
|
||||
'fecha_producto' => $fmtDate($datos['art66_fecha_producto'] ?? null),
|
||||
'importe_producto' => $datos['art66_importe_producto'] ?? null,
|
||||
'cargo_producto' => $datos['art66_cargo_producto'] ?? null,
|
||||
],
|
||||
'precio_pagado' => [
|
||||
'fecha_precio_pagado' => $fmtDate($datos['precio_pagado_fecha_pago'] ?? null),
|
||||
'importe_precio_pagado' => $datos['precio_pagado_importe'] ?? null,
|
||||
'moneda_precio_pagado' => $datos['precio_pagado_moneda'] ?? null,
|
||||
'forma_pago_precio_pagado' => $datos['precio_pagado_forma_pago'] ?? null,
|
||||
'referencia_precio_pagado' => $datos['precio_pagado_referencia'] ?? null,
|
||||
],
|
||||
'precio_pagar' => [
|
||||
'fecha_limite_pago' => $fmtDate($datos['precio_pagar_fecha_limite'] ?? null),
|
||||
'importe_precio_pagar' => $datos['precio_pagar_importe'] ?? null,
|
||||
'moneda_precio_pagar' => $datos['precio_pagar_moneda'] ?? null,
|
||||
'terminos_precio_pagar' => $datos['precio_pagar_terminos'] ?? null,
|
||||
'observaciones_precio_pagar' => $datos['precio_pagar_observaciones'] ?? null,
|
||||
],
|
||||
'compenso' => [
|
||||
'fecha_compenso' => $fmtDate($datos['compenso_fecha'] ?? null),
|
||||
'importe_compenso' => $datos['compenso_importe'] ?? null,
|
||||
'tipo_compenso' => $datos['compenso_tipo'] ?? null,
|
||||
'motivo_compenso' => $datos['compenso_motivo'] ?? null,
|
||||
'documentos_compenso' => $datos['compenso_documentos'] ?? null,
|
||||
'descripcion_compenso' => $datos['compenso_descripcion'] ?? null,
|
||||
]
|
||||
];
|
||||
|
||||
$stmt = $db->prepare($sql);
|
||||
$resultado = $stmt->execute($params);
|
||||
|
||||
if ($resultado) {
|
||||
echo json_encode(['success' => true, 'message' => 'Datos guardados correctamente']);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Error al guardar los datos']);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Error interno: ' . $e->getMessage()]);
|
||||
echo json_encode(['success' => true, 'datos' => $datosEstructurados]);
|
||||
} else {
|
||||
echo json_encode(['success' => true, 'datos' => null]);
|
||||
}
|
||||
}
|
||||
|
||||
function ajax_obtener_datos_factura() {
|
||||
try {
|
||||
$id_factura = $_GET['id_factura'] ?? null;
|
||||
// Utilidad: marca/crea un registro mínimo en cove_respuestas para encender el badge
|
||||
function upsert_cove_respuesta_min($conn, $pedimento_id, $factura_id, $usuario_id)
|
||||
{
|
||||
$sel = sqlsrv_query($conn, "SELECT id FROM cove_respuestas WHERE factura_id = ? AND usuario_id = ?", [$factura_id, $usuario_id]);
|
||||
$row = $sel ? sqlsrv_fetch_array($sel, SQLSRV_FETCH_ASSOC) : null; if ($sel) sqlsrv_free_stmt($sel);
|
||||
if ($row) {
|
||||
sqlsrv_query($conn, "UPDATE cove_respuestas SET estado = 'respondido', fecha_actualizacion = SYSDATETIME() WHERE id = ?", [$row['id']]);
|
||||
} else {
|
||||
sqlsrv_query($conn, "INSERT INTO cove_respuestas (pedimento_id, factura_id, usuario_id, respuestas, estado, fecha_creacion) VALUES (?,?,?,?,?,SYSDATETIME())",
|
||||
[$pedimento_id, $factura_id, $usuario_id, '{}', 'respondido']);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$id_factura) {
|
||||
echo json_encode(['success' => false, 'message' => 'ID de factura requerido']);
|
||||
// Registra la solicitud de MVE con aceptación de declaración del importador
|
||||
function ajax_registrar_solicitud()
|
||||
{
|
||||
header('Content-Type: application/json; charset=UTF-8');
|
||||
if (!($_SESSION['usuario_id'] ?? false)) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['success' => false, 'message' => 'No autorizado']);
|
||||
return;
|
||||
}
|
||||
|
||||
$usuario_id = (int)$_SESSION['usuario_id'];
|
||||
$pedimento_id = isset($_POST['pedimento_id']) ? (int)$_POST['pedimento_id'] : 0;
|
||||
$factura_id = isset($_POST['factura_id']) ? (int)$_POST['factura_id'] : 0;
|
||||
$acepto = isset($_POST['acepto']) ? (int)$_POST['acepto'] : 0;
|
||||
$rfc_importador = isset($_POST['rfc_importador']) ? trim($_POST['rfc_importador']) : null;
|
||||
$firma_base64 = isset($_POST['firma_base64']) ? $_POST['firma_base64'] : null;
|
||||
$firmante = isset($_POST['firmante']) ? trim($_POST['firmante']) : null;
|
||||
$ip = $_SERVER['REMOTE_ADDR'] ?? null;
|
||||
|
||||
if ($pedimento_id <= 0 || $factura_id <= 0) { echo json_encode(['success' => false, 'message' => 'Parámetros inválidos']); return; }
|
||||
if ($acepto !== 1) { echo json_encode(['success' => false, 'message' => 'Debes aceptar la declaración']); return; }
|
||||
|
||||
$conn = getConnection();
|
||||
|
||||
// Verificar propiedad y relación factura-pedimento
|
||||
$chk = sqlsrv_query($conn, "SELECT 1 FROM pedimentos p JOIN pedimento_facturas f ON f.pedimento_id = p.id WHERE p.id = ? AND f.id = ? AND p.usuario_id = ?", [$pedimento_id, $factura_id, $usuario_id]);
|
||||
$ok = $chk && sqlsrv_fetch_array($chk) ? true : false; if ($chk) sqlsrv_free_stmt($chk);
|
||||
if (!$ok) { echo json_encode(['success' => false, 'message' => 'No autorizado o relación inválida']); return; }
|
||||
|
||||
// Asegurar tabla mve_solicitudes
|
||||
$sqlEnsure = "
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[mve_solicitudes]') AND type in (N'U'))
|
||||
BEGIN
|
||||
CREATE TABLE dbo.mve_solicitudes (
|
||||
id INT IDENTITY(1,1) PRIMARY KEY,
|
||||
pedimento_id INT NOT NULL,
|
||||
factura_id INT NOT NULL,
|
||||
usuario_id INT NOT NULL,
|
||||
acepto_declaracion BIT NOT NULL,
|
||||
rfc_importador NVARCHAR(20) NULL,
|
||||
ip NVARCHAR(64) NULL,
|
||||
fecha_solicitud DATETIME2 NOT NULL DEFAULT SYSDATETIME(),
|
||||
comentario NVARCHAR(400) NULL,
|
||||
firma_base64 NVARCHAR(MAX) NULL,
|
||||
firmante NVARCHAR(200) NULL
|
||||
);
|
||||
CREATE INDEX IX_mve_solicitudes_factura ON dbo.mve_solicitudes(factura_id);
|
||||
END;
|
||||
-- Ensure all expected columns exist on legacy tables
|
||||
IF COL_LENGTH('dbo.mve_solicitudes','pedimento_id') IS NULL ALTER TABLE dbo.mve_solicitudes ADD pedimento_id INT NULL;
|
||||
IF COL_LENGTH('dbo.mve_solicitudes','factura_id') IS NULL ALTER TABLE dbo.mve_solicitudes ADD factura_id INT NULL;
|
||||
IF COL_LENGTH('dbo.mve_solicitudes','usuario_id') IS NULL ALTER TABLE dbo.mve_solicitudes ADD usuario_id INT NULL;
|
||||
IF COL_LENGTH('dbo.mve_solicitudes','acepto_declaracion') IS NULL ALTER TABLE dbo.mve_solicitudes ADD acepto_declaracion BIT NULL;
|
||||
IF COL_LENGTH('dbo.mve_solicitudes','rfc_importador') IS NULL ALTER TABLE dbo.mve_solicitudes ADD rfc_importador NVARCHAR(20) NULL;
|
||||
IF COL_LENGTH('dbo.mve_solicitudes','ip') IS NULL ALTER TABLE dbo.mve_solicitudes ADD ip NVARCHAR(64) NULL;
|
||||
IF COL_LENGTH('dbo.mve_solicitudes','fecha_solicitud') IS NULL ALTER TABLE dbo.mve_solicitudes ADD fecha_solicitud DATETIME2 NULL;
|
||||
IF COL_LENGTH('dbo.mve_solicitudes','comentario') IS NULL ALTER TABLE dbo.mve_solicitudes ADD comentario NVARCHAR(400) NULL;
|
||||
IF COL_LENGTH('dbo.mve_solicitudes', 'firma_base64') IS NULL ALTER TABLE dbo.mve_solicitudes ADD firma_base64 NVARCHAR(MAX) NULL;
|
||||
IF COL_LENGTH('dbo.mve_solicitudes', 'firmante') IS NULL ALTER TABLE dbo.mve_solicitudes ADD firmante NVARCHAR(200) NULL;
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_mve_solicitudes_factura' AND object_id = OBJECT_ID('dbo.mve_solicitudes'))
|
||||
CREATE INDEX IX_mve_solicitudes_factura ON dbo.mve_solicitudes(factura_id);
|
||||
";
|
||||
$ensureOk = sqlsrv_query($conn, $sqlEnsure);
|
||||
if ($ensureOk === false) {
|
||||
$err = sqlsrv_errors();
|
||||
echo json_encode(['success' => false, 'message' => 'Error al preparar tabla de solicitudes', 'detail' => $err]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Determinar columnas legacy (id_*) y nuevas (*_id) disponibles
|
||||
$has_id_ped = false; $has_id_fac = false; $has_id_usr = false;
|
||||
$chkCols = sqlsrv_query($conn, "SELECT name FROM sys.columns WHERE object_id = OBJECT_ID('dbo.mve_solicitudes') AND name IN ('id_pedimento','id_factura','id_usuario','pedimento_id','factura_id','usuario_id')");
|
||||
if ($chkCols) {
|
||||
while ($c = sqlsrv_fetch_array($chkCols, SQLSRV_FETCH_ASSOC)) {
|
||||
if ($c['name'] === 'id_pedimento') $has_id_ped = true;
|
||||
if ($c['name'] === 'id_factura') $has_id_fac = true;
|
||||
if ($c['name'] === 'id_usuario') $has_id_usr = true;
|
||||
}
|
||||
sqlsrv_free_stmt($chkCols);
|
||||
}
|
||||
|
||||
// Validar firma obligatoria
|
||||
if (!$firma_base64 || trim($firma_base64) === '') {
|
||||
echo json_encode(['success' => false, 'message' => 'La firma es obligatoria']);
|
||||
return;
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
$stmt = $db->prepare("SELECT * FROM mve_facturas_datos WHERE id_factura = ?");
|
||||
$stmt->execute([$id_factura]);
|
||||
$datos = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
// Construir INSERT dinámico para soportar ambos esquemas
|
||||
$cols = [];
|
||||
$vals = [];
|
||||
$paramsIns = [];
|
||||
// IDs
|
||||
$cols[] = 'pedimento_id'; $vals[] = '?'; $paramsIns[] = $pedimento_id;
|
||||
if ($has_id_ped) { $cols[] = 'id_pedimento'; $vals[] = '?'; $paramsIns[] = $pedimento_id; }
|
||||
$cols[] = 'factura_id'; $vals[] = '?'; $paramsIns[] = $factura_id;
|
||||
if ($has_id_fac) { $cols[] = 'id_factura'; $vals[] = '?'; $paramsIns[] = $factura_id; }
|
||||
$cols[] = 'usuario_id'; $vals[] = '?'; $paramsIns[] = $usuario_id;
|
||||
if ($has_id_usr) { $cols[] = 'id_usuario'; $vals[] = '?'; $paramsIns[] = $usuario_id; }
|
||||
// Resto de columnas
|
||||
$cols = array_merge($cols, ['acepto_declaracion','rfc_importador','ip','firma_base64','firmante']);
|
||||
$vals = array_merge($vals, array_fill(0, 5, '?'));
|
||||
$paramsIns = array_merge($paramsIns, [1, $rfc_importador, $ip, $firma_base64, $firmante]);
|
||||
|
||||
if ($datos) {
|
||||
// Estructurar datos para el frontend
|
||||
$datosEstructurados = [
|
||||
'art65' => [
|
||||
'fecha_transporte' => $datos['art65_fecha_transporte'],
|
||||
'importe_transporte' => $datos['art65_importe_transporte'],
|
||||
'fecha_descuentos' => $datos['art65_fecha_descuentos'],
|
||||
'importe_descuentos' => $datos['art65_importe_descuentos'],
|
||||
'fecha_posteriores' => $datos['art65_fecha_posteriores'],
|
||||
'importe_posteriores' => $datos['art65_importe_posteriores'],
|
||||
'fecha_contribuciones' => $datos['art65_fecha_contribuciones'],
|
||||
'importe_contribuciones' => $datos['art65_importe_contribuciones'],
|
||||
'fecha_pagos_vendedor' => $datos['art65_fecha_pagos_vendedor'],
|
||||
'importe_pagos_vendedor' => $datos['art65_importe_pagos_vendedor']
|
||||
],
|
||||
'art66' => [
|
||||
'fecha_comisiones' => $datos['art66_fecha_comisiones'],
|
||||
'importe_comisiones' => $datos['art66_importe_comisiones'],
|
||||
'cargo_comisiones' => $datos['art66_cargo_comisiones'],
|
||||
'fecha_envases' => $datos['art66_fecha_envases'],
|
||||
'importe_envases' => $datos['art66_importe_envases'],
|
||||
'cargo_envases' => $datos['art66_cargo_envases'],
|
||||
'fecha_embalaje' => $datos['art66_fecha_embalaje'],
|
||||
'importe_embalaje' => $datos['art66_importe_embalaje'],
|
||||
'cargo_embalaje' => $datos['art66_cargo_embalaje'],
|
||||
'fecha_transporte_dec' => $datos['art66_fecha_transporte_dec'],
|
||||
'importe_transporte_dec' => $datos['art66_importe_transporte_dec'],
|
||||
'cargo_transporte_dec' => $datos['art66_cargo_transporte_dec'],
|
||||
'fecha_ingenieria' => $datos['art66_fecha_ingenieria'],
|
||||
'importe_ingenieria' => $datos['art66_importe_ingenieria'],
|
||||
'cargo_ingenieria' => $datos['art66_cargo_ingenieria'],
|
||||
'fecha_regalias' => $datos['art66_fecha_regalias'],
|
||||
'importe_regalias' => $datos['art66_importe_regalias'],
|
||||
'cargo_regalias' => $datos['art66_cargo_regalias'],
|
||||
'fecha_producto' => $datos['art66_fecha_producto'],
|
||||
'importe_producto' => $datos['art66_importe_producto'],
|
||||
'cargo_producto' => $datos['art66_cargo_producto']
|
||||
]
|
||||
];
|
||||
|
||||
echo json_encode(['success' => true, 'datos' => $datosEstructurados]);
|
||||
} else {
|
||||
echo json_encode(['success' => true, 'datos' => null]);
|
||||
$sqlIns = 'INSERT INTO mve_solicitudes (' . implode(',', $cols) . ') VALUES (' . implode(',', $vals) . ')';
|
||||
$ins = sqlsrv_query($conn, $sqlIns, $paramsIns);
|
||||
if ($ins === false) {
|
||||
$err = sqlsrv_errors();
|
||||
$msg = 'No se pudo registrar la solicitud';
|
||||
if ($err && isset($err[0]['message'])) { $msg .= ': ' . $err[0]['message']; }
|
||||
echo json_encode(['success' => false, 'message' => $msg]);
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Error interno: ' . $e->getMessage()]);
|
||||
}
|
||||
// Actualizar estado en cove_respuestas a 'solicitado'
|
||||
$sel = sqlsrv_query($conn, "SELECT id FROM cove_respuestas WHERE factura_id = ? AND usuario_id = ?", [$factura_id, $usuario_id]);
|
||||
$row = $sel ? sqlsrv_fetch_array($sel, SQLSRV_FETCH_ASSOC) : null; if ($sel) sqlsrv_free_stmt($sel);
|
||||
if ($row) {
|
||||
sqlsrv_query($conn, "UPDATE cove_respuestas SET estado = 'solicitado', fecha_actualizacion = SYSDATETIME() WHERE id = ?", [$row['id']]);
|
||||
} else {
|
||||
sqlsrv_query($conn, "INSERT INTO cove_respuestas (usuario_id, pedimento_id, factura_id, estado, fecha_actualizacion) VALUES (?, ?, ?, 'solicitado', SYSDATETIME())", [$usuario_id, $pedimento_id, $factura_id]);
|
||||
}
|
||||
|
||||
// Generar documentos de prueba (acuse y detalle) en el expediente del pedimento
|
||||
$usuario_nombre = $_SESSION['usuario_nombre'] ?? 'sistema';
|
||||
mve_generar_documentos_expediente($conn, $pedimento_id, $usuario_nombre);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
19
app/controllers/test_connection.php
Normal file
19
app/controllers/test_connection.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
session_start();
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Simple test endpoint
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Conexión exitosa',
|
||||
'timestamp' => date('Y-m-d H:i:s'),
|
||||
'usuario' => isset($_SESSION['usuario']) ? $_SESSION['usuario']['nombre'] ?? 'Usuario logueado' : 'No hay usuario',
|
||||
'post_data' => $_POST,
|
||||
'files_data' => isset($_FILES['archivo']) ? [
|
||||
'nombre' => $_FILES['archivo']['name'],
|
||||
'tamaño' => $_FILES['archivo']['size'],
|
||||
'tipo' => $_FILES['archivo']['type'],
|
||||
'error' => $_FILES['archivo']['error']
|
||||
] : 'No hay archivo'
|
||||
]);
|
||||
?>
|
||||
@@ -1,61 +0,0 @@
|
||||
CREATE TABLE preferencias_catalogos_usuario (
|
||||
id_preferencia INT IDENTITY(1,1) NOT NULL,
|
||||
id_usuario INT NOT NULL,
|
||||
locaciones BIT DEFAULT 0,
|
||||
vinculacion BIT DEFAULT 0,
|
||||
transportistas BIT DEFAULT 0,
|
||||
transportes BIT DEFAULT 0,
|
||||
choferes BIT DEFAULT 0,
|
||||
proveedores BIT DEFAULT 0,
|
||||
productos_frecuentes BIT DEFAULT 0,
|
||||
solicitudes_importacion BIT DEFAULT 0,
|
||||
expediente_electronico BIT DEFAULT 0,
|
||||
configuracion BIT DEFAULT 0,
|
||||
cerrar_sesion BIT DEFAULT 0,
|
||||
FOREIGN KEY (id_usuario) REFERENCES usuarios_sistema(id_usuario)
|
||||
);
|
||||
|
||||
ALTER TABLE usuarios_sistema
|
||||
ADD [preferencias_catalogos] BIT DEFAULT 1;
|
||||
|
||||
CREATE TABLE tipos_catalogos (
|
||||
id INT PRIMARY KEY IDENTITY,
|
||||
tipo_usuario VARCHAR(20) NOT NULL,
|
||||
nombre VARCHAR(255) NOT NULL,
|
||||
descripcion VARCHAR(500) NULL,
|
||||
color VARCHAR(20) NOT NULL,
|
||||
ruta VARCHAR(100) NOT NULL,
|
||||
accion VARCAHR(50) NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO tipos_catalogos (tipo_usuario, nombre, descripcion, color, ruta, accion) VALUES
|
||||
('importador', 'Locaciones', 'Visualiza las locaciones registradas.', 'lime', '/IMPORTADORES/importadores/lista', 'Ver locaciones'),
|
||||
('importador', 'Vinculación', 'Visualiza o establece nuevas vinculaciones con agencias aduanales.', 'teal', '/IMPORTADORES/vinculaciones/vinculacionesUsuario', 'Ver vinculaciones'),
|
||||
('importador', 'Transportistas', 'Agrega o consulta los choferes que colaboran contigo.', 'success', '/IMPORTADORES/transportistas/lista', 'Ver transportistas'),
|
||||
('importador', 'Transportes', 'Gestiona y carga tus unidades de transporte.', 'info', '/IMPORTADORES/transportes/lista', 'Ver transportes'),
|
||||
('importador', 'Choferes', 'Gestiona y carga a tus choferes.', 'cyan', '/IMPORTADORES/choferes/lista', 'Ver choferes'),
|
||||
('importador', 'Proveedores', 'Agrega tus proveedores.', 'primary', '/IMPORTADORES/proveedores/crear', 'Nuevo proveedor'),
|
||||
('importador', 'Productos frecuentes', 'Agrega productos frecuentes de tu interez.', 'violet', '/IMPORTADORES/productos_frecuentes/alta', 'Nuevo producto'),
|
||||
('importador', 'Solicitudes importación', 'Inicia nuevas solicitudes de pedimentos.', 'indigo', '/IMPORTADORES/solicitud_importacion/crear', 'Nueva solicitud'),
|
||||
('importador', 'Expediente electrónico', 'Revisa el expediente electrónico para tus solicitudes.', 'warning', '/IMPORTADORES/expediente/index', 'Ver expediente'),
|
||||
('importador', 'Configuración', 'Administra los datos de tu empresa y preferencias.', 'orange', '/IMPORTADORES/configuracion', 'Ir a configuración'),
|
||||
('importador', 'Cerrar sesión', 'Salir del sistema de forma segura.', 'danger', '/IMPORTADORES/sistemas/logout', 'Cerrar sesión');
|
||||
|
||||
|
||||
|
||||
UPDATE usuarios_sistema
|
||||
SET preferencias_catalogos = 1
|
||||
WHERE preferencias_catalogos IS NULL OR preferencias_catalogos = 0;
|
||||
|
||||
-- 2. Insertar en preferencias_catalogos_usuario solo para los que aún no tienen registro
|
||||
INSERT INTO preferencias_catalogos_usuario (
|
||||
id_usuario, locaciones, vinculacion, transportistas, transportes, choferes, proveedores,
|
||||
productos_frecuentes, solicitudes_importacion, expediente_electronico, configuracion, cerrar_sesion
|
||||
)
|
||||
SELECT u.id_usuario, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
|
||||
FROM usuarios_sistema u
|
||||
WHERE u.tipo_usuario = 'importador'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM preferencias_catalogos_usuario p
|
||||
WHERE p.id_usuario = u.id_usuario
|
||||
);
|
||||
@@ -1,21 +0,0 @@
|
||||
-- Tabla para claves de pedimentos configurables por usuario
|
||||
CREATE TABLE claves_pedimentos_usuario (
|
||||
id_clave_pedimento INT PRIMARY KEY IDENTITY,
|
||||
id_usuario INT NOT NULL,
|
||||
codigo VARCHAR(10) NOT NULL,
|
||||
descripcion NVARCHAR(255) NOT NULL,
|
||||
tipo_operacion VARCHAR(50), -- 'importacion', 'exportacion', etc.
|
||||
activo BIT DEFAULT 1,
|
||||
fecha_creacion DATETIME DEFAULT GETDATE(),
|
||||
fecha_modificacion DATETIME DEFAULT GETDATE(),
|
||||
FOREIGN KEY (id_usuario) REFERENCES usuarios_sistema(id_usuario),
|
||||
UNIQUE(id_usuario, codigo) -- Un usuario no puede tener códigos duplicados
|
||||
);
|
||||
|
||||
-- Índices para optimizar consultas
|
||||
CREATE INDEX IX_claves_pedimentos_usuario_id ON claves_pedimentos_usuario(id_usuario);
|
||||
CREATE INDEX IX_claves_pedimentos_activo ON claves_pedimentos_usuario(activo);
|
||||
|
||||
-- NOTA: Los datos de ejemplo se omiten porque requieren usuarios existentes
|
||||
-- Las claves se insertarán automáticamente cuando el usuario use la función
|
||||
-- "inicializar_claves_usuario()" desde la aplicación web
|
||||
@@ -1,97 +0,0 @@
|
||||
-- Tabla para configuración de Ventanilla Única
|
||||
-- Script de creación para SQL Server
|
||||
|
||||
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='configuracion_ventanilla_unica' AND xtype='U')
|
||||
BEGIN
|
||||
CREATE TABLE configuracion_ventanilla_unica (
|
||||
id_configuracion INT IDENTITY(1,1) PRIMARY KEY,
|
||||
id_usuario INT NOT NULL,
|
||||
ruta_ejecutable NVARCHAR(500) NOT NULL,
|
||||
ruta_archivo_key NVARCHAR(500) NULL,
|
||||
ruta_archivo_cer NVARCHAR(500) NULL,
|
||||
clave_fiel NVARCHAR(MAX) NULL, -- Encriptado
|
||||
rfc_usuario_vu NVARCHAR(13) NOT NULL,
|
||||
clave_webservice NVARCHAR(MAX) NULL, -- Encriptado
|
||||
fecha_creacion DATETIME2 DEFAULT GETDATE(),
|
||||
fecha_actualizacion DATETIME2 NULL,
|
||||
activo BIT DEFAULT 1,
|
||||
|
||||
-- Constraints
|
||||
CONSTRAINT FK_configuracion_vu_usuario
|
||||
FOREIGN KEY (id_usuario) REFERENCES usuarios_sistema(id_usuario)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
CONSTRAINT UQ_configuracion_vu_usuario
|
||||
UNIQUE (id_usuario)
|
||||
);
|
||||
|
||||
PRINT 'Tabla configuracion_ventanilla_unica creada exitosamente';
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
PRINT 'La tabla configuracion_ventanilla_unica ya existe';
|
||||
END
|
||||
|
||||
-- Crear índices para mejorar rendimiento
|
||||
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name='IX_configuracion_vu_usuario' AND object_id = OBJECT_ID('configuracion_ventanilla_unica'))
|
||||
BEGIN
|
||||
CREATE INDEX IX_configuracion_vu_usuario ON configuracion_ventanilla_unica(id_usuario);
|
||||
PRINT 'Índice IX_configuracion_vu_usuario creado';
|
||||
END
|
||||
|
||||
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name='IX_configuracion_vu_rfc' AND object_id = OBJECT_ID('configuracion_ventanilla_unica'))
|
||||
BEGIN
|
||||
CREATE INDEX IX_configuracion_vu_rfc ON configuracion_ventanilla_unica(rfc_usuario_vu);
|
||||
PRINT 'Índice IX_configuracion_vu_rfc creado';
|
||||
END
|
||||
|
||||
-- Agregar comentarios descriptivos
|
||||
EXEC sp_addextendedproperty
|
||||
@name = N'MS_Description',
|
||||
@value = N'Configuración de Ventanilla Única para transmisiones de Manifestación de Valor Electrónica',
|
||||
@level0type = N'SCHEMA', @level0name = N'dbo',
|
||||
@level1type = N'TABLE', @level1name = N'configuracion_ventanilla_unica';
|
||||
|
||||
EXEC sp_addextendedproperty
|
||||
@name = N'MS_Description',
|
||||
@value = N'Ruta completa al archivo ejecutable de Ventanilla Única',
|
||||
@level0type = N'SCHEMA', @level0name = N'dbo',
|
||||
@level1type = N'TABLE', @level1name = N'configuracion_ventanilla_unica',
|
||||
@level2type = N'COLUMN', @level2name = N'ruta_ejecutable';
|
||||
|
||||
EXEC sp_addextendedproperty
|
||||
@name = N'MS_Description',
|
||||
@value = N'Ruta al archivo KEY del certificado FIEL',
|
||||
@level0type = N'SCHEMA', @level0name = N'dbo',
|
||||
@level1type = N'TABLE', @level1name = N'configuracion_ventanilla_unica',
|
||||
@level2type = N'COLUMN', @level2name = N'ruta_archivo_key';
|
||||
|
||||
EXEC sp_addextendedproperty
|
||||
@name = N'MS_Description',
|
||||
@value = N'Ruta al archivo CER del certificado FIEL',
|
||||
@level0type = N'SCHEMA', @level0name = N'dbo',
|
||||
@level1type = N'TABLE', @level1name = N'configuracion_ventanilla_unica',
|
||||
@level2type = N'COLUMN', @level2name = N'ruta_archivo_cer';
|
||||
|
||||
EXEC sp_addextendedproperty
|
||||
@name = N'MS_Description',
|
||||
@value = N'Contraseña FIEL encriptada',
|
||||
@level0type = N'SCHEMA', @level0name = N'dbo',
|
||||
@level1type = N'TABLE', @level1name = N'configuracion_ventanilla_unica',
|
||||
@level2type = N'COLUMN', @level2name = N'clave_fiel';
|
||||
|
||||
EXEC sp_addextendedproperty
|
||||
@name = N'MS_Description',
|
||||
@value = N'RFC del usuario para acceso a Ventanilla Única',
|
||||
@level0type = N'SCHEMA', @level0name = N'dbo',
|
||||
@level1type = N'TABLE', @level1name = N'configuracion_ventanilla_unica',
|
||||
@level2type = N'COLUMN', @level2name = N'rfc_usuario_vu';
|
||||
|
||||
EXEC sp_addextendedproperty
|
||||
@name = N'MS_Description',
|
||||
@value = N'Contraseña del Web Service encriptada',
|
||||
@level0type = N'SCHEMA', @level0name = N'dbo',
|
||||
@level1type = N'TABLE', @level1name = N'configuracion_ventanilla_unica',
|
||||
@level2type = N'COLUMN', @level2name = N'clave_webservice';
|
||||
|
||||
PRINT 'Script de configuración de Ventanilla Única completado exitosamente';
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
// Script temporal para crear la tabla claves_pedimentos_usuario
|
||||
require_once __DIR__ . '/config/database.php';
|
||||
|
||||
try {
|
||||
$conn = getConnection();
|
||||
echo "✅ Conexión establecida exitosamente\n";
|
||||
|
||||
// Leer el script SQL
|
||||
$sqlScript = file_get_contents(__DIR__ . '/claves_pedimentos_tabla.sql');
|
||||
|
||||
if (!$sqlScript) {
|
||||
die("❌ No se pudo leer el archivo SQL\n");
|
||||
}
|
||||
|
||||
// Dividir el script en declaraciones individuales
|
||||
$statements = explode(';', $sqlScript);
|
||||
|
||||
$executed = 0;
|
||||
foreach ($statements as $statement) {
|
||||
$statement = trim($statement);
|
||||
|
||||
// Saltar declaraciones vacías y comentarios
|
||||
if (empty($statement) || strpos($statement, '--') === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
echo "Ejecutando: " . substr($statement, 0, 50) . "...\n";
|
||||
|
||||
$result = sqlsrv_query($conn, $statement);
|
||||
|
||||
if ($result === false) {
|
||||
$errors = sqlsrv_errors();
|
||||
// Si el error es que la tabla ya existe, continuamos
|
||||
if (isset($errors[0]['code']) && $errors[0]['code'] == 2714) {
|
||||
echo "⚠️ La tabla ya existe, continuando...\n";
|
||||
continue;
|
||||
}
|
||||
echo "❌ Error: " . print_r($errors, true) . "\n";
|
||||
} else {
|
||||
$executed++;
|
||||
echo "✅ Ejecutado exitosamente\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n🎉 Proceso completado. Se ejecutaron $executed declaraciones SQL.\n";
|
||||
echo "La tabla 'claves_pedimentos_usuario' debería estar creada ahora.\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
?>
|
||||
@@ -1,196 +0,0 @@
|
||||
<?php
|
||||
// Script de debug simple para templates
|
||||
session_start();
|
||||
|
||||
// Para debugging, vamos a simular una sesión válida
|
||||
if (!isset($_SESSION['usuario_id'])) {
|
||||
$_SESSION['usuario_id'] = 1; // Cambia este valor por tu ID de usuario real
|
||||
$_SESSION['id_agencia_en_uso'] = 1; // Cambia por tu ID de agencia real
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/config/database.php';
|
||||
|
||||
echo "<h2>🔍 Debug Simple de Templates</h2>";
|
||||
echo "<p><strong>Usuario actual:</strong> " . $_SESSION['usuario_id'] . "</p>";
|
||||
echo "<p><strong>Agencia actual:</strong> " . ($_SESSION['id_agencia_en_uso'] ?? 'NULL') . "</p>";
|
||||
|
||||
try {
|
||||
$conn = getConnection();
|
||||
|
||||
// 1. Ver todos los templates sin filtros
|
||||
echo "<h3>1. Todos los templates en la base de datos:</h3>";
|
||||
$sql = "SELECT id, nombre, descripcion, activo, id_usuario_creador, id_agencia,
|
||||
fecha_creacion, config_json
|
||||
FROM dbo.templates_rapidos
|
||||
ORDER BY fecha_creacion DESC";
|
||||
|
||||
$stmt = sqlsrv_query($conn, $sql);
|
||||
|
||||
if ($stmt === false) {
|
||||
$errors = sqlsrv_errors();
|
||||
echo "<div style='color: red;'>❌ Error en consulta: " . print_r($errors, true) . "</div>";
|
||||
} else {
|
||||
$count = 0;
|
||||
echo "<table border='1' style='border-collapse: collapse; width: 100%;'>";
|
||||
echo "<tr style='background: #f0f0f0;'>
|
||||
<th>ID</th>
|
||||
<th>Nombre</th>
|
||||
<th>Descripción</th>
|
||||
<th>Activo</th>
|
||||
<th>Usuario Creador</th>
|
||||
<th>Agencia</th>
|
||||
<th>Fecha Creación</th>
|
||||
<th>Tiene Config</th>
|
||||
</tr>";
|
||||
|
||||
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
$count++;
|
||||
$fecha = $row['fecha_creacion'] instanceof DateTime
|
||||
? $row['fecha_creacion']->format('Y-m-d H:i:s')
|
||||
: $row['fecha_creacion'];
|
||||
|
||||
$tieneConfig = !empty($row['config_json']) ? 'Sí' : 'No';
|
||||
|
||||
echo "<tr>";
|
||||
echo "<td>" . $row['id'] . "</td>";
|
||||
echo "<td><strong>" . htmlspecialchars($row['nombre']) . "</strong></td>";
|
||||
echo "<td>" . htmlspecialchars($row['descripcion'] ?? '') . "</td>";
|
||||
echo "<td>" . ($row['activo'] ? '✅' : '❌') . "</td>";
|
||||
echo "<td>" . ($row['id_usuario_creador'] ?? 'NULL') . "</td>";
|
||||
echo "<td>" . ($row['id_agencia'] ?? 'NULL') . "</td>";
|
||||
echo "<td>" . $fecha . "</td>";
|
||||
echo "<td>" . $tieneConfig . "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
echo "</table>";
|
||||
|
||||
echo "<p><strong>Total de templates encontrados: $count</strong></p>";
|
||||
}
|
||||
|
||||
// 2. Probar la consulta exacta del endpoint
|
||||
echo "<h3>2. Probando consulta del endpoint (con filtros):</h3>";
|
||||
$id_usuario = $_SESSION['usuario_id'];
|
||||
$id_agencia = $_SESSION['id_agencia_en_uso'];
|
||||
|
||||
$sql_endpoint = "SELECT id, nombre, descripcion, icono, config_json,
|
||||
ISNULL(veces_usado, 0) as veces_usado,
|
||||
id_usuario_creador, id_agencia
|
||||
FROM dbo.templates_rapidos
|
||||
WHERE activo = 1
|
||||
AND (id_usuario_creador = ? OR id_agencia = ? OR id_agencia IS NULL)
|
||||
ORDER BY
|
||||
CASE WHEN id_usuario_creador = ? THEN 0 ELSE 1 END,
|
||||
veces_usado DESC,
|
||||
nombre ASC";
|
||||
|
||||
$stmt_endpoint = sqlsrv_query($conn, $sql_endpoint, [$id_usuario, $id_agencia, $id_usuario]);
|
||||
|
||||
if ($stmt_endpoint === false) {
|
||||
$errors = sqlsrv_errors();
|
||||
echo "<div style='color: red;'>❌ Error en consulta endpoint: " . print_r($errors, true) . "</div>";
|
||||
} else {
|
||||
$count_endpoint = 0;
|
||||
echo "<table border='1' style='border-collapse: collapse; width: 100%;'>";
|
||||
echo "<tr style='background: #e3f2fd;'>
|
||||
<th>ID</th>
|
||||
<th>Nombre</th>
|
||||
<th>Es Mío</th>
|
||||
<th>Usuario Creador</th>
|
||||
<th>Agencia</th>
|
||||
<th>Debería Aparecer</th>
|
||||
</tr>";
|
||||
|
||||
while ($row = sqlsrv_fetch_array($stmt_endpoint, SQLSRV_FETCH_ASSOC)) {
|
||||
$count_endpoint++;
|
||||
$esMio = ($row['id_usuario_creador'] == $id_usuario) ? 'SÍ' : 'NO';
|
||||
|
||||
echo "<tr>";
|
||||
echo "<td>" . $row['id'] . "</td>";
|
||||
echo "<td><strong>" . htmlspecialchars($row['nombre']) . "</strong></td>";
|
||||
echo "<td style='color: " . ($esMio === 'SÍ' ? 'green' : 'blue') . ";'><strong>$esMio</strong></td>";
|
||||
echo "<td>" . ($row['id_usuario_creador'] ?? 'NULL') . "</td>";
|
||||
echo "<td>" . ($row['id_agencia'] ?? 'NULL') . "</td>";
|
||||
echo "<td>✅ SÍ</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
echo "</table>";
|
||||
|
||||
echo "<p><strong>Templates que deberían aparecer en el formulario: $count_endpoint</strong></p>";
|
||||
}
|
||||
|
||||
// 3. Probar el endpoint AJAX directamente
|
||||
echo "<h3>3. Prueba del endpoint AJAX:</h3>";
|
||||
echo "<p><a href='/IMPORTADORES/templates_rapidos/ajax_obtener_templates' target='_blank' style='background: #007bff; color: white; padding: 10px 15px; text-decoration: none; border-radius: 5px;'>🔗 Abrir endpoint AJAX en nueva pestaña</a></p>";
|
||||
|
||||
// 4. Verificar la sesión
|
||||
echo "<h3>4. Estado de la sesión:</h3>";
|
||||
echo "<pre>";
|
||||
echo "SESSION:\n";
|
||||
foreach ($_SESSION as $key => $value) {
|
||||
if (is_string($value) || is_numeric($value)) {
|
||||
echo " $key: $value\n";
|
||||
}
|
||||
}
|
||||
echo "</pre>";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "<div style='color: red;'>❌ <strong>Error:</strong> " . $e->getMessage() . "</div>";
|
||||
}
|
||||
?>
|
||||
|
||||
<script>
|
||||
// Script para probar el AJAX desde aquí mismo
|
||||
function probarAjax() {
|
||||
console.log('🔄 Probando AJAX...');
|
||||
|
||||
fetch('/IMPORTADORES/templates_rapidos/ajax_obtener_templates', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
console.log('📦 Status:', response.status);
|
||||
return response.text();
|
||||
})
|
||||
.then(data => {
|
||||
console.log('📄 Respuesta cruda:', data);
|
||||
try {
|
||||
const json = JSON.parse(data);
|
||||
console.log('✅ JSON parseado:', json);
|
||||
|
||||
document.getElementById('ajax-result').innerHTML = `
|
||||
<h4>Resultado del AJAX:</h4>
|
||||
<pre style="background: #f8f9fa; padding: 15px; border-radius: 5px; overflow-x: auto;">${JSON.stringify(json, null, 2)}</pre>
|
||||
`;
|
||||
} catch (e) {
|
||||
console.error('❌ Error parseando JSON:', e);
|
||||
document.getElementById('ajax-result').innerHTML = `
|
||||
<h4>Respuesta del servidor (no es JSON válido):</h4>
|
||||
<pre style="background: #fff3cd; padding: 15px; border-radius: 5px; overflow-x: auto;">${data}</pre>
|
||||
`;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('❌ Error AJAX:', error);
|
||||
document.getElementById('ajax-result').innerHTML = `
|
||||
<h4>Error en la petición:</h4>
|
||||
<pre style="background: #f8d7da; padding: 15px; border-radius: 5px;">${error.message}</pre>
|
||||
`;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<h3>5. Prueba AJAX en tiempo real:</h3>
|
||||
<button onclick="probarAjax()" style="background: #28a745; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer;">
|
||||
🧪 Probar AJAX ahora
|
||||
</button>
|
||||
<div id="ajax-result" style="margin-top: 15px;"></div>
|
||||
|
||||
<hr>
|
||||
<p><small><strong>💡 Instrucciones:</strong><br>
|
||||
1. Revisa los templates en la tabla de arriba<br>
|
||||
2. Verifica que tu usuario_id y agencia_id sean correctos<br>
|
||||
3. Haz clic en "Probar AJAX ahora" para ver la respuesta en tiempo real<br>
|
||||
4. Abre la consola del navegador (F12) para ver logs detallados</small></p>
|
||||
@@ -1,26 +0,0 @@
|
||||
CREATE TABLE verificaciones (
|
||||
id INT IDENTITY(1,1) PRIMARY KEY,
|
||||
id_usuario INT NOT NULL,
|
||||
codigo VARCHAR(10) NOT NULL,
|
||||
expiracion DATETIME NOT NULL,
|
||||
tipo VARCHAR(50) DEFAULT 'login',
|
||||
creado_en DATETIME DEFAULT GETDATE(),
|
||||
FOREIGN KEY (id_usuario) REFERENCES usuarios_sistema(id_usuario)
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE correo_extra (
|
||||
id INT IDENTITY(1,1) PRIMARY KEY,
|
||||
id_usuario INT NOT NULL,
|
||||
correo VARCHAR(255) NOT NULL,
|
||||
fecha_registro DATETIME DEFAULT GETDATE(),
|
||||
FOREIGN KEY (id_usuario) REFERENCES usuarios_sistema(id_usuario)
|
||||
);
|
||||
|
||||
CREATE TABLE correo_respaldo (
|
||||
id INT IDENTITY(1,1) PRIMARY KEY,
|
||||
id_usuario INT NOT NULL,
|
||||
correo VARCHAR(255) NOT NULL,
|
||||
fecha_registro DATETIME DEFAULT GETDATE(),
|
||||
FOREIGN KEY (id_usuario) REFERENCES usuarios_sistema(id_usuario)
|
||||
);
|
||||
@@ -1,32 +0,0 @@
|
||||
CREATE TABLE informacion_general (
|
||||
id_info INT PRIMARY KEY IDENTITY,
|
||||
id_usuario INT,
|
||||
clave VARCHAR(100),
|
||||
tipo_identificador VARCHAR(100),
|
||||
nombre NVARCHAR(255),
|
||||
rfc NVARCHAR(13),
|
||||
curp VARCHAR(18),
|
||||
calle NVARCHAR(255),
|
||||
num_exterior VARCHAR(20),
|
||||
num_interior VARCHAR(20),
|
||||
ciudad NVARCHAR(100),
|
||||
colonia NVARCHAR(100),
|
||||
pais NVARCHAR(100),
|
||||
codigo_postal VARCHAR(10),
|
||||
municipio NVARCHAR(100),
|
||||
estado NVARCHAR(100),
|
||||
telefono VARCHAR(50),
|
||||
fax VARCHAR(50),
|
||||
correo NVARCHAR(100),
|
||||
observaciones TEXT,
|
||||
FOREIGN KEY (id_usuario) REFERENCES usuarios_sistema(id_usuario)
|
||||
);
|
||||
|
||||
ALTER TABLE informacion_general
|
||||
ALTER COLUMN tipo_identificador INT;
|
||||
|
||||
ALTER TABLE informacion_general
|
||||
ALTER COLUMN num_exterior INT;
|
||||
|
||||
ALTER TABLE informacion_general
|
||||
ALTER COLUMN codigo_postal INT;
|
||||
@@ -1,90 +0,0 @@
|
||||
-- Tabla para almacenar datos de Manifestación de Valor por factura
|
||||
CREATE TABLE mve_facturas_datos (
|
||||
id INT IDENTITY(1,1) PRIMARY KEY,
|
||||
id_pedimento INT NOT NULL,
|
||||
id_factura INT NOT NULL,
|
||||
numero_factura NVARCHAR(100),
|
||||
|
||||
-- Campos Art. 65 - Incrementables
|
||||
art65_fecha_transporte DATE NULL,
|
||||
art65_importe_transporte DECIMAL(15,2) NULL,
|
||||
art65_fecha_descuentos DATE NULL,
|
||||
art65_importe_descuentos DECIMAL(15,2) NULL,
|
||||
art65_fecha_posteriores DATE NULL,
|
||||
art65_importe_posteriores DECIMAL(15,2) NULL,
|
||||
art65_fecha_contribuciones DATE NULL,
|
||||
art65_importe_contribuciones DECIMAL(15,2) NULL,
|
||||
art65_fecha_pagos_vendedor DATE NULL,
|
||||
art65_importe_pagos_vendedor DECIMAL(15,2) NULL,
|
||||
|
||||
-- Campos Art. 66 - Decrementables
|
||||
art66_fecha_comisiones DATE NULL,
|
||||
art66_importe_comisiones DECIMAL(15,2) NULL,
|
||||
art66_cargo_comisiones NVARCHAR(10) CHECK (art66_cargo_comisiones IN ('Si', 'No')) NULL,
|
||||
|
||||
art66_fecha_envases DATE NULL,
|
||||
art66_importe_envases DECIMAL(15,2) NULL,
|
||||
art66_cargo_envases NVARCHAR(10) CHECK (art66_cargo_envases IN ('Si', 'No')) NULL,
|
||||
|
||||
art66_fecha_embalaje DATE NULL,
|
||||
art66_importe_embalaje DECIMAL(15,2) NULL,
|
||||
art66_cargo_embalaje NVARCHAR(10) CHECK (art66_cargo_embalaje IN ('Si', 'No')) NULL,
|
||||
|
||||
art66_fecha_transporte_dec DATE NULL,
|
||||
art66_importe_transporte_dec DECIMAL(15,2) NULL,
|
||||
art66_cargo_transporte_dec NVARCHAR(10) CHECK (art66_cargo_transporte_dec IN ('Si', 'No')) NULL,
|
||||
|
||||
art66_fecha_ingenieria DATE NULL,
|
||||
art66_importe_ingenieria DECIMAL(15,2) NULL,
|
||||
art66_cargo_ingenieria NVARCHAR(10) CHECK (art66_cargo_ingenieria IN ('Si', 'No')) NULL,
|
||||
|
||||
art66_fecha_regalias DATE NULL,
|
||||
art66_importe_regalias DECIMAL(15,2) NULL,
|
||||
art66_cargo_regalias NVARCHAR(10) CHECK (art66_cargo_regalias IN ('Si', 'No')) NULL,
|
||||
|
||||
art66_fecha_producto DATE NULL,
|
||||
art66_importe_producto DECIMAL(15,2) NULL,
|
||||
art66_cargo_producto NVARCHAR(10) CHECK (art66_cargo_producto IN ('Si', 'No')) NULL,
|
||||
|
||||
-- Campos de control
|
||||
fecha_creacion DATETIME2 DEFAULT GETDATE(),
|
||||
fecha_actualizacion DATETIME2 DEFAULT GETDATE(),
|
||||
usuario_creacion NVARCHAR(100),
|
||||
|
||||
-- Índices y restricciones
|
||||
CONSTRAINT UQ_mve_facturas_datos_pedimento_factura UNIQUE (id_pedimento, id_factura)
|
||||
);
|
||||
|
||||
-- Crear índices separadamente
|
||||
CREATE INDEX IX_mve_facturas_datos_pedimento ON mve_facturas_datos (id_pedimento);
|
||||
CREATE INDEX IX_mve_facturas_datos_factura ON mve_facturas_datos (id_factura);
|
||||
|
||||
-- Tabla para el historial de solicitudes MVE
|
||||
CREATE TABLE mve_solicitudes (
|
||||
id INT IDENTITY(1,1) PRIMARY KEY,
|
||||
id_pedimento INT NOT NULL,
|
||||
numero_pedimento NVARCHAR(50),
|
||||
estado NVARCHAR(20) CHECK (estado IN ('Pendiente', 'En_Proceso', 'Completada', 'Rechazada')) DEFAULT 'Pendiente',
|
||||
fecha_solicitud DATETIME2 DEFAULT GETDATE(),
|
||||
fecha_respuesta DATETIME2 NULL,
|
||||
observaciones NTEXT,
|
||||
usuario_solicitud NVARCHAR(100)
|
||||
);
|
||||
|
||||
-- Crear índices para mve_solicitudes
|
||||
CREATE INDEX IX_mve_solicitudes_pedimento ON mve_solicitudes (id_pedimento);
|
||||
CREATE INDEX IX_mve_solicitudes_estado ON mve_solicitudes (estado);
|
||||
CREATE INDEX IX_mve_solicitudes_fecha_solicitud ON mve_solicitudes (fecha_solicitud);
|
||||
|
||||
-- Crear trigger para actualizar fecha_actualizacion automáticamente
|
||||
CREATE TRIGGER TR_mve_facturas_datos_update
|
||||
ON mve_facturas_datos
|
||||
AFTER UPDATE
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
UPDATE mve_facturas_datos
|
||||
SET fecha_actualizacion = GETDATE()
|
||||
FROM mve_facturas_datos m
|
||||
INNER JOIN inserted i ON m.id = i.id;
|
||||
END;
|
||||
@@ -1,38 +0,0 @@
|
||||
ALTER TABLE usuarios_sistema
|
||||
ADD notificaciones BIT DEFAULT 0,
|
||||
notificaciones_extra BIT DEFAULT 0;
|
||||
|
||||
CREATE TABLE tipos_notificaciones (
|
||||
id INT PRIMARY KEY IDENTITY,
|
||||
nombre NVARCHAR(255) NOT NULL,
|
||||
descripcion NVARCHAR(500) NULL
|
||||
);
|
||||
|
||||
INSERT INTO tipos_notificaciones (nombre, descripcion) VALUES
|
||||
('nuevas_solicitudes', 'Creación de nuevas solicitudes de importación'),
|
||||
('registro_solicitud', 'Notificación inmediata al registrar una solicitud'),
|
||||
('cambio_estado', 'Cambio de estado de solicitudes de importación'),
|
||||
('resumen_diario', 'Resumen diario de solicitudes de importación'),
|
||||
('alertas_tiempo', 'Alertas por tiempo excedido en estados críticos'),
|
||||
('documentos_expediente', 'Incorporación de documentos al expediente electrónico'),
|
||||
('nuevos_archivos', 'Aviso al agregarse nuevos archivos o documentos'),
|
||||
('intentos_fallidos', 'Intentos fallidos de acceso'),
|
||||
('bloqueo_cuenta', 'Bloqueo de cuenta');
|
||||
|
||||
CREATE TABLE preferencias_notificaciones_usuario (
|
||||
id_usuario INT PRIMARY KEY,
|
||||
nuevas_solicitudes BIT DEFAULT 0,
|
||||
registro_solicitud BIT DEFAULT 0,
|
||||
cambio_estado BIT DEFAULT 0,
|
||||
resumen_diario BIT DEFAULT 0,
|
||||
alertas_tiempo BIT DEFAULT 0,
|
||||
documentos_expediente BIT DEFAULT 0,
|
||||
nuevos_archivos BIT DEFAULT 0,
|
||||
intentos_fallidos BIT DEFAULT 0,
|
||||
bloqueo_cuenta BIT DEFAULT 0,
|
||||
FOREIGN KEY (id_usuario) REFERENCES usuarios_sistema(id_usuario)
|
||||
);
|
||||
|
||||
ALTER TABLE preferencias_notificaciones_usuario
|
||||
ADD resumen_diario_hora TIME DEFAULT '08:00:00',
|
||||
resumen_diario_dias NVARCHAR(20) DEFAULT 'L-V'; -- L-V = Lunes a Viernes, o 'TODOS' para todos los días
|
||||
@@ -1,58 +0,0 @@
|
||||
-- Tabla para el catálogo de pedimentos (previos)
|
||||
-- Esta tabla almacena los pedimentos registrados por los importadores
|
||||
|
||||
CREATE TABLE [dbo].[previos] (
|
||||
[IdPrevio] [int] IDENTITY(1,1) NOT NULL,
|
||||
[Pedimento] [nvarchar](50) NOT NULL,
|
||||
[ClienteRFC] [nvarchar](13) NOT NULL,
|
||||
[ClienteNombre] [nvarchar](255) NOT NULL,
|
||||
[ClavePed] [nvarchar](10) NOT NULL,
|
||||
[TipoOperacion] [int] DEFAULT 1, -- 1=importación, 2=exportación
|
||||
[TipoPedimento] [int] DEFAULT 1, -- 1=normal, 2=complementario, etc.
|
||||
[Regimen] [nvarchar](100) NULL,
|
||||
[Destino] [nvarchar](100) NULL,
|
||||
[FechaPedimento] [int] NULL, -- Formato YYYYMMDD
|
||||
[FechaInicio] [int] NULL, -- Formato YYYYMMDD
|
||||
[FechaFinal] [int] NULL, -- Formato YYYYMMDD
|
||||
[ArchivoFinalPrevio] [nvarchar](255) NULL,
|
||||
[AcuseCons] [nvarchar](100) NULL,
|
||||
[Tipo] [nvarchar](50) NULL,
|
||||
[Status] [int] DEFAULT 1, -- 1=activo, 0=inactivo
|
||||
[Timestamp] [datetime] DEFAULT GETDATE(),
|
||||
|
||||
CONSTRAINT [PK_previos] PRIMARY KEY CLUSTERED ([IdPrevio] ASC)
|
||||
);
|
||||
|
||||
-- Índices para mejorar el rendimiento
|
||||
CREATE INDEX [IX_previos_cliente] ON [dbo].[previos] ([ClienteRFC]);
|
||||
CREATE INDEX [IX_previos_pedimento] ON [dbo].[previos] ([Pedimento]);
|
||||
CREATE INDEX [IX_previos_status] ON [dbo].[previos] ([Status]);
|
||||
CREATE INDEX [IX_previos_timestamp] ON [dbo].[previos] ([Timestamp] DESC);
|
||||
|
||||
-- Comentarios para documentación
|
||||
EXEC sys.sp_addextendedproperty
|
||||
@name=N'MS_Description',
|
||||
@value=N'Tabla principal para el catálogo de pedimentos de importadores',
|
||||
@level0type=N'SCHEMA', @level0name=N'dbo',
|
||||
@level1type=N'TABLE', @level1name=N'previos';
|
||||
|
||||
EXEC sys.sp_addextendedproperty
|
||||
@name=N'MS_Description',
|
||||
@value=N'Número de pedimento aduanero',
|
||||
@level0type=N'SCHEMA', @level0name=N'dbo',
|
||||
@level1type=N'TABLE', @level1name=N'previos',
|
||||
@level2type=N'COLUMN', @level2name=N'Pedimento';
|
||||
|
||||
EXEC sys.sp_addextendedproperty
|
||||
@name=N'MS_Description',
|
||||
@value=N'RFC del cliente/importador',
|
||||
@level0type=N'SCHEMA', @level0name=N'dbo',
|
||||
@level1type=N'TABLE', @level1name=N'previos',
|
||||
@level2type=N'COLUMN', @level2name=N'ClienteRFC';
|
||||
|
||||
EXEC sys.sp_addextendedproperty
|
||||
@name=N'MS_Description',
|
||||
@value=N'Clave de pedimento utilizada',
|
||||
@level0type=N'SCHEMA', @level0name=N'dbo',
|
||||
@level1type=N'TABLE', @level1name=N'previos',
|
||||
@level2type=N'COLUMN', @level2name=N'ClavePed';
|
||||
@@ -1,35 +0,0 @@
|
||||
CREATE TABLE dbo.productos_frecuentes (
|
||||
id_producto_frecuente INT IDENTITY(1,1) PRIMARY KEY,
|
||||
sinonimo NVARCHAR(255) NOT NULL,
|
||||
fraccion NVARCHAR(50) NOT NULL,
|
||||
nico NVARCHAR(50) NOT NULL,
|
||||
numero_parte NVARCHAR(300) NULL,
|
||||
descripcion NVARCHAR(MAX) NULL,
|
||||
umc_id INT NOT NULL, -- FK a unidades_medida_apendice7(id)
|
||||
pais_origen_destino NVARCHAR(100) NULL,
|
||||
pais_comprador_vendedor NVARCHAR(100) NULL,
|
||||
uso_mercancia NVARCHAR(100) NULL,
|
||||
estado_mercancia NVARCHAR(100) NULL,
|
||||
vinculacion NVARCHAR(100) NULL,
|
||||
observaciones NVARCHAR(MAX) NULL,
|
||||
preferencia NVARCHAR(100) NULL,
|
||||
criterio_preferencia NVARCHAR(100) NULL,
|
||||
uso_producto NVARCHAR(100) NULL,
|
||||
descripcion_producto NVARCHAR(MAX) NULL,
|
||||
certificado_origen BIT NOT NULL DEFAULT 0,
|
||||
tipo_mercancia NVARCHAR(100) NULL,
|
||||
documento_en_original BIT NOT NULL DEFAULT 0,
|
||||
proveedor NVARCHAR(255) NULL,
|
||||
id_importador INT NOT NULL, -- FK a usuarios_sistema(id_usuario)
|
||||
fecha_alta DATETIME2 NOT NULL DEFAULT GETDATE(),
|
||||
status INT NOT NULL DEFAULT 1,
|
||||
frecuencia_uso INT NOT NULL DEFAULT 1, -- cuántas veces se ha utilizado
|
||||
|
||||
CONSTRAINT FK_prodFreq_UMC
|
||||
FOREIGN KEY (umc_id)
|
||||
REFERENCES dbo.unidades_medida_apendice7(id),
|
||||
|
||||
CONSTRAINT FK_prodFreq_Importador
|
||||
FOREIGN KEY (id_importador)
|
||||
REFERENCES dbo.usuarios_sistema(id_usuario)
|
||||
);
|
||||
@@ -1,11 +0,0 @@
|
||||
-- 1. Elimina la restricción DEFAULT
|
||||
ALTER TABLE [Importaciones_HC].[dbo].[productos_frecuentes]
|
||||
DROP CONSTRAINT [DF__productos__certi__3EDC53F0];
|
||||
|
||||
-- 2. Modifica el tipo de dato
|
||||
ALTER TABLE [Importaciones_HC].[dbo].[productos_frecuentes]
|
||||
ALTER COLUMN [certificado_origen] NVARCHAR(100);
|
||||
|
||||
-- 3. (Opcional) Si quieres volver a agregar un valor por defecto
|
||||
ALTER TABLE [Importaciones_HC].[dbo].[productos_frecuentes]
|
||||
ADD CONSTRAINT DF_productos_certificado_origen DEFAULT '' FOR [certificado_origen];
|
||||
13
public/check_session.php
Normal file
13
public/check_session.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
session_start();
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
echo json_encode([
|
||||
'sesion_activa' => isset($_SESSION['usuario']),
|
||||
'usuario_datos' => $_SESSION['usuario'] ?? null,
|
||||
'session_id' => session_id(),
|
||||
'session_status' => session_status(),
|
||||
'cookie_params' => session_get_cookie_params(),
|
||||
'todas_las_sesiones' => $_SESSION ?? []
|
||||
], JSON_PRETTY_PRINT);
|
||||
?>
|
||||
26
public/debug.php
Normal file
26
public/debug.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
echo "=== DEBUG IMPORTADOR ===\n";
|
||||
echo "POST: " . print_r($_POST, true) . "\n";
|
||||
echo "FILES: " . print_r($_FILES, true) . "\n";
|
||||
echo "SESSION: " . print_r($_SESSION ?? [], true) . "\n";
|
||||
|
||||
// Test de conexión a base de datos
|
||||
try {
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
echo "Config database cargado OK\n";
|
||||
|
||||
$conn = getConnection();
|
||||
echo "Conexión exitosa\n";
|
||||
|
||||
sqlsrv_close($conn);
|
||||
echo "Conexión cerrada OK\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "ERROR: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
echo "=== FIN DEBUG ===\n";
|
||||
?>
|
||||
152
public/debug_partidas.php
Normal file
152
public/debug_partidas.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../config/database.php';
|
||||
|
||||
if (!isset($_SESSION['usuario_id'])) {
|
||||
die("Error: No hay sesión activa");
|
||||
}
|
||||
|
||||
echo "<!DOCTYPE html>
|
||||
<html lang='es'>
|
||||
<head>
|
||||
<meta charset='UTF-8'>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
|
||||
<title>Debug - Partidas de Pedimentos</title>
|
||||
<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
|
||||
</head>
|
||||
<body>
|
||||
<div class='container mt-4'>
|
||||
<h2>Debug - Últimas Partidas Insertadas</h2>";
|
||||
|
||||
try {
|
||||
$conn = conectarBD();
|
||||
|
||||
// Obtener las últimas partidas con información del pedimento
|
||||
$sql = "SELECT TOP 20
|
||||
pp.id,
|
||||
pp.secuencia,
|
||||
pp.fraccion_arancelaria,
|
||||
pp.descripcion,
|
||||
pp.cantidad,
|
||||
pp.unidad,
|
||||
pp.valor_unitario,
|
||||
pp.valor_total,
|
||||
pp.peso_neto,
|
||||
pp.peso_bruto,
|
||||
pp.fecha_creacion,
|
||||
p.numero_pedimento
|
||||
FROM pedimento_partidas pp
|
||||
INNER JOIN pedimentos p ON pp.pedimento_id = p.id
|
||||
ORDER BY pp.fecha_creacion DESC";
|
||||
|
||||
$stmt = sqlsrv_query($conn, $sql);
|
||||
|
||||
if ($stmt === false) {
|
||||
throw new Exception("Error en consulta: " . print_r(sqlsrv_errors(), true));
|
||||
}
|
||||
|
||||
echo "<div class='table-responsive'>
|
||||
<table class='table table-striped table-hover'>
|
||||
<thead class='table-dark'>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Pedimento</th>
|
||||
<th>Secuencia</th>
|
||||
<th>Fracción</th>
|
||||
<th>Descripción</th>
|
||||
<th>Cantidad</th>
|
||||
<th>Unidad</th>
|
||||
<th>Valor Unit.</th>
|
||||
<th>Valor Total</th>
|
||||
<th>Peso Neto</th>
|
||||
<th>Peso Bruto</th>
|
||||
<th>Fecha</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>";
|
||||
|
||||
$contador = 0;
|
||||
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
$contador++;
|
||||
|
||||
echo "<tr>
|
||||
<td>{$row['id']}</td>
|
||||
<td><strong>{$row['numero_pedimento']}</strong></td>
|
||||
<td>{$row['secuencia']}</td>
|
||||
<td><code>{$row['fraccion_arancelaria']}</code></td>
|
||||
<td>" . (strlen($row['descripcion']) > 50 ? substr($row['descripcion'], 0, 50) . '...' : $row['descripcion']) . "</td>
|
||||
<td>" . number_format($row['cantidad'], 4) . "</td>
|
||||
<td>{$row['unidad']}</td>
|
||||
<td>" . number_format($row['valor_unitario'], 4) . "</td>
|
||||
<td>" . number_format($row['valor_total'], 4) . "</td>
|
||||
<td>" . ($row['peso_neto'] ? number_format($row['peso_neto'], 4) : 'NULL') . "</td>
|
||||
<td>" . ($row['peso_bruto'] ? number_format($row['peso_bruto'], 4) : 'NULL') . "</td>
|
||||
<td>{$row['fecha_creacion']->format('Y-m-d H:i:s')}</td>
|
||||
</tr>";
|
||||
}
|
||||
|
||||
echo "</tbody></table></div>";
|
||||
|
||||
if ($contador == 0) {
|
||||
echo "<div class='alert alert-warning'>No se encontraron partidas en la base de datos.</div>";
|
||||
} else {
|
||||
echo "<div class='alert alert-info'>Se encontraron $contador partidas.</div>";
|
||||
}
|
||||
|
||||
// Estadísticas adicionales
|
||||
$sql_stats = "SELECT
|
||||
COUNT(*) as total_partidas,
|
||||
COUNT(DISTINCT pedimento_id) as total_pedimentos,
|
||||
AVG(cantidad) as promedio_cantidad,
|
||||
AVG(valor_unitario) as promedio_valor_unitario
|
||||
FROM pedimento_partidas";
|
||||
|
||||
$stmt_stats = sqlsrv_query($conn, $sql_stats);
|
||||
if ($stmt_stats && $stats = sqlsrv_fetch_array($stmt_stats, SQLSRV_FETCH_ASSOC)) {
|
||||
echo "<div class='row mt-4'>
|
||||
<div class='col-md-3'>
|
||||
<div class='card text-center'>
|
||||
<div class='card-body'>
|
||||
<h5 class='card-title'>Total Partidas</h5>
|
||||
<p class='card-text display-6'>{$stats['total_partidas']}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='col-md-3'>
|
||||
<div class='card text-center'>
|
||||
<div class='card-body'>
|
||||
<h5 class='card-title'>Total Pedimentos</h5>
|
||||
<p class='card-text display-6'>{$stats['total_pedimentos']}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='col-md-3'>
|
||||
<div class='card text-center'>
|
||||
<div class='card-body'>
|
||||
<h5 class='card-title'>Promedio Cantidad</h5>
|
||||
<p class='card-text display-6'>" . number_format($stats['promedio_cantidad'], 2) . "</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='col-md-3'>
|
||||
<div class='card text-center'>
|
||||
<div class='card-body'>
|
||||
<h5 class='card-title'>Promedio Valor Unit.</h5>
|
||||
<p class='card-text display-6'>" . number_format($stats['promedio_valor_unitario'], 2) . "</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>";
|
||||
}
|
||||
|
||||
sqlsrv_close($conn);
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "<div class='alert alert-danger'>Error: " . $e->getMessage() . "</div>";
|
||||
}
|
||||
|
||||
echo "</div>
|
||||
<script src='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js'></script>
|
||||
</body>
|
||||
</html>";
|
||||
?>
|
||||
134
public/diagnostico_importacion.php
Normal file
134
public/diagnostico_importacion.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
session_start();
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
// Función de logging mejorada
|
||||
function logDebug($mensaje) {
|
||||
error_log("[IMPORT_DEBUG] " . $mensaje);
|
||||
}
|
||||
|
||||
try {
|
||||
logDebug("=== INICIO DIAGNÓSTICO ===");
|
||||
|
||||
// 1. Verificar sesión
|
||||
logDebug("Verificando sesión...");
|
||||
$usuario_info = "No hay sesión";
|
||||
if (isset($_SESSION['usuario_id'])) {
|
||||
$usuario_info = "Usuario ID: " . $_SESSION['usuario_id'] .
|
||||
", Tipo: " . ($_SESSION['tipo_usuario'] ?? 'sin_tipo');
|
||||
}
|
||||
logDebug("Estado sesión: " . $usuario_info);
|
||||
|
||||
// 2. Verificar método
|
||||
logDebug("Método HTTP: " . $_SERVER['REQUEST_METHOD']);
|
||||
|
||||
// 3. Verificar archivo
|
||||
$archivo_info = "No hay archivo";
|
||||
if (isset($_FILES['archivo'])) {
|
||||
$archivo_info = "Archivo: " . $_FILES['archivo']['name'] .
|
||||
", Tamaño: " . $_FILES['archivo']['size'] .
|
||||
", Error: " . $_FILES['archivo']['error'];
|
||||
}
|
||||
logDebug("Estado archivo: " . $archivo_info);
|
||||
|
||||
// 4. Verificar conexión a base de datos
|
||||
logDebug("Verificando conexión DB...");
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
$conn = getConnection();
|
||||
$db_info = $conn ? "Conexión exitosa" : "Error de conexión";
|
||||
logDebug("Estado DB: " . $db_info);
|
||||
|
||||
// 5. Verificar directorio temporal
|
||||
$temp_dir = __DIR__ . '/../storage/temp/';
|
||||
logDebug("Directorio temp: " . $temp_dir);
|
||||
logDebug("Existe directorio temp: " . (is_dir($temp_dir) ? "SÍ" : "NO"));
|
||||
logDebug("Permisos escritura temp: " . (is_writable($temp_dir) ? "SÍ" : "NO"));
|
||||
|
||||
// Respuesta de diagnóstico
|
||||
$respuesta = [
|
||||
'success' => true,
|
||||
'diagnostico' => 'Análisis completado',
|
||||
'timestamp' => date('Y-m-d H:i:s'),
|
||||
'checks' => [
|
||||
'sesion' => isset($_SESSION['usuario_id']),
|
||||
'usuario_id' => $_SESSION['usuario_id'] ?? null,
|
||||
'tipo_usuario' => $_SESSION['tipo_usuario'] ?? null,
|
||||
'metodo_post' => $_SERVER['REQUEST_METHOD'] === 'POST',
|
||||
'archivo_recibido' => isset($_FILES['archivo']),
|
||||
'archivo_nombre' => $_FILES['archivo']['name'] ?? null,
|
||||
'archivo_error' => $_FILES['archivo']['error'] ?? null,
|
||||
'conexion_db' => $conn !== false,
|
||||
'directorio_temp_existe' => is_dir($temp_dir),
|
||||
'directorio_temp_escribible' => is_writable($temp_dir)
|
||||
],
|
||||
'errores_potenciales' => []
|
||||
];
|
||||
|
||||
// Detectar problemas
|
||||
if (!isset($_SESSION['usuario_id'])) {
|
||||
$respuesta['errores_potenciales'][] = "No hay usuario_id en sesión";
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
$respuesta['errores_potenciales'][] = "Método no es POST";
|
||||
}
|
||||
|
||||
if (!isset($_FILES['archivo'])) {
|
||||
$respuesta['errores_potenciales'][] = "No se recibió archivo";
|
||||
} elseif ($_FILES['archivo']['error'] !== UPLOAD_ERR_OK) {
|
||||
$respuesta['errores_potenciales'][] = "Error en upload: " . $_FILES['archivo']['error'];
|
||||
}
|
||||
|
||||
if (!$conn) {
|
||||
$respuesta['errores_potenciales'][] = "Error de conexión a base de datos";
|
||||
}
|
||||
|
||||
if (!is_dir($temp_dir)) {
|
||||
$respuesta['errores_potenciales'][] = "Directorio temporal no existe";
|
||||
} elseif (!is_writable($temp_dir)) {
|
||||
$respuesta['errores_potenciales'][] = "Directorio temporal no tiene permisos de escritura";
|
||||
}
|
||||
|
||||
// Si no hay errores potenciales, intentar procesar
|
||||
if (empty($respuesta['errores_potenciales']) && isset($_FILES['archivo'])) {
|
||||
logDebug("Intentando procesar archivo...");
|
||||
|
||||
// Mover archivo temporal
|
||||
$archivo_temporal = $temp_dir . 'test_' . time() . '_' . $_FILES['archivo']['name'];
|
||||
if (move_uploaded_file($_FILES['archivo']['tmp_name'], $archivo_temporal)) {
|
||||
$respuesta['archivo_procesado'] = true;
|
||||
$respuesta['archivo_temporal'] = $archivo_temporal;
|
||||
|
||||
// Leer primeras líneas
|
||||
$contenido = file_get_contents($archivo_temporal);
|
||||
if ($contenido !== false) {
|
||||
$lineas = explode("\n", array_slice(explode("\n", $contenido), 0, 5));
|
||||
$respuesta['primeras_lineas'] = $lineas;
|
||||
$respuesta['total_lineas'] = count(explode("\n", $contenido));
|
||||
}
|
||||
|
||||
// Limpiar archivo
|
||||
unlink($archivo_temporal);
|
||||
} else {
|
||||
$respuesta['errores_potenciales'][] = "No se pudo mover archivo temporal";
|
||||
}
|
||||
}
|
||||
|
||||
logDebug("Respuesta: " . json_encode($respuesta));
|
||||
echo json_encode($respuesta, JSON_PRETTY_PRINT);
|
||||
|
||||
} catch (Exception $e) {
|
||||
logDebug("EXCEPCIÓN: " . $e->getMessage());
|
||||
logDebug("Stack trace: " . $e->getTraceAsString());
|
||||
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
], JSON_PRETTY_PRINT);
|
||||
}
|
||||
?>
|
||||
89
public/importar_funcional.php
Normal file
89
public/importar_funcional.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
// Forzar no cache
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Cache-Control: no-cache, no-store, must-revalidate');
|
||||
header('Pragma: no-cache');
|
||||
header('Expires: 0');
|
||||
|
||||
// Log de debug
|
||||
error_log("=== NUEVO CONTROLADOR FUNCIONAL ===");
|
||||
error_log("Método: " . $_SERVER['REQUEST_METHOD']);
|
||||
error_log("POST data: " . print_r($_POST, true));
|
||||
error_log("FILES data: " . print_r($_FILES, true));
|
||||
|
||||
try {
|
||||
// Verificar método
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
throw new Exception("Método no permitido: " . $_SERVER['REQUEST_METHOD']);
|
||||
}
|
||||
|
||||
// Verificar sesión (comentado temporalmente para test)
|
||||
// if (!isset($_SESSION['usuario_id'])) {
|
||||
// throw new Exception("Usuario no autenticado");
|
||||
// }
|
||||
|
||||
$validar_duplicados = isset($_POST['validar_duplicados']) && $_POST['validar_duplicados'] === 'on';
|
||||
|
||||
error_log("Validar duplicados: " . ($validar_duplicados ? 'SÍ' : 'NO'));
|
||||
|
||||
// Contar archivos recibidos
|
||||
$total_archivos = 0;
|
||||
if (isset($_FILES['archivos']) && is_array($_FILES['archivos']['name'])) {
|
||||
$total_archivos = count($_FILES['archivos']['name']);
|
||||
error_log("Archivos múltiples detectados: " . $total_archivos);
|
||||
} elseif (isset($_FILES['archivo'])) {
|
||||
$total_archivos = 1;
|
||||
error_log("Archivo individual detectado");
|
||||
}
|
||||
|
||||
error_log("Total de archivos a procesar: " . $total_archivos);
|
||||
|
||||
if ($total_archivos === 0) {
|
||||
throw new Exception("No se recibieron archivos para procesar");
|
||||
}
|
||||
|
||||
// Simulación de procesamiento (reemplazar con lógica real después)
|
||||
$estadisticas = [
|
||||
'pedimentos_procesados' => $total_archivos * 2,
|
||||
'facturas_procesadas' => $total_archivos * 3,
|
||||
'partidas_procesadas' => $total_archivos * 5,
|
||||
'duplicados' => 0,
|
||||
'errores' => 0,
|
||||
'total_lineas' => $total_archivos * 100
|
||||
];
|
||||
|
||||
$respuesta = [
|
||||
'success' => true,
|
||||
'message' => 'Archivos procesados correctamente (MODO SIMULACIÓN)',
|
||||
'estadisticas' => $estadisticas,
|
||||
'archivos_procesados' => $total_archivos,
|
||||
'archivos_con_errores' => [],
|
||||
'debug_info' => [
|
||||
'timestamp' => date('Y-m-d H:i:s'),
|
||||
'total_archivos_recibidos' => $total_archivos,
|
||||
'validar_duplicados' => $validar_duplicados
|
||||
]
|
||||
];
|
||||
|
||||
error_log("Enviando respuesta exitosa: " . json_encode($respuesta));
|
||||
echo json_encode($respuesta);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("ERROR: " . $e->getMessage());
|
||||
|
||||
$respuesta_error = [
|
||||
'success' => false,
|
||||
'message' => $e->getMessage(),
|
||||
'debug_info' => [
|
||||
'timestamp' => date('Y-m-d H:i:s'),
|
||||
'error_file' => $e->getFile(),
|
||||
'error_line' => $e->getLine()
|
||||
]
|
||||
];
|
||||
|
||||
http_response_code(500);
|
||||
echo json_encode($respuesta_error);
|
||||
}
|
||||
?>
|
||||
739
public/importar_pedimentos.php
Normal file
739
public/importar_pedimentos.php
Normal file
@@ -0,0 +1,739 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
// Headers para AJAX
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: POST');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
// Log para debugging
|
||||
error_log("=== INICIO IMPORTACIÓN ===");
|
||||
error_log("Usuario ID en sesión: " . (isset($_SESSION['usuario_id']) ? $_SESSION['usuario_id'] : 'NO_USUARIO'));
|
||||
error_log("Tipo usuario: " . (isset($_SESSION['tipo_usuario']) ? $_SESSION['tipo_usuario'] : 'NO_TIPO'));
|
||||
error_log("Método: " . $_SERVER['REQUEST_METHOD']);
|
||||
error_log("POST recibido: " . print_r($_POST, true));
|
||||
error_log("FILES recibido: " . print_r($_FILES, true));
|
||||
error_log("Tiene archivo individual: " . (isset($_FILES['archivo']) ? 'SÍ' : 'NO'));
|
||||
error_log("Tiene archivos múltiples: " . (isset($_FILES['archivos']) ? 'SÍ' : 'NO'));
|
||||
|
||||
// Verificar que el usuario esté logueado
|
||||
if (!isset($_SESSION['usuario_id'])) {
|
||||
error_log("ERROR: Usuario no autenticado - no hay usuario_id en sesión");
|
||||
http_response_code(401);
|
||||
echo json_encode(['success' => false, 'message' => 'Usuario no autenticado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Verificar que sea una petición POST
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'message' => 'Método no permitido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Verificar que se haya subido un archivo (individual o múltiple)
|
||||
if (!isset($_FILES['archivo']) && !isset($_FILES['archivos'])) {
|
||||
error_log("ERROR: No se encontraron archivos. FILES: " . print_r($_FILES, true));
|
||||
http_response_code(400);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'No se ha recibido ningún archivo válido',
|
||||
'debug' => $_FILES
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
class ImportadorPedimentos {
|
||||
private $conn;
|
||||
private $estadisticas = [
|
||||
'total_lineas' => 0,
|
||||
'pedimentos_procesados' => 0,
|
||||
'facturas_procesadas' => 0,
|
||||
'partidas_procesadas' => 0,
|
||||
'errores' => 0,
|
||||
'duplicados' => 0
|
||||
];
|
||||
|
||||
private $errores_detalle = [];
|
||||
private $pedimentos_creados = [];
|
||||
|
||||
public function __construct($connection) {
|
||||
$this->conn = $connection;
|
||||
}
|
||||
|
||||
public function procesarArchivo($archivo_path, $validar_duplicados = true) {
|
||||
// Reiniciar estadísticas y acumuladores por archivo para evitar sobreconteo entre múltiples archivos
|
||||
$this->estadisticas = [
|
||||
'total_lineas' => 0,
|
||||
'pedimentos_procesados' => 0,
|
||||
'facturas_procesadas' => 0,
|
||||
'partidas_procesadas' => 0,
|
||||
'errores' => 0,
|
||||
'duplicados' => 0
|
||||
];
|
||||
$this->errores_detalle = [];
|
||||
$this->pedimentos_creados = [];
|
||||
|
||||
$inicio_tiempo = time();
|
||||
$nombre_archivo = basename($archivo_path);
|
||||
|
||||
try {
|
||||
$contenido = file_get_contents($archivo_path);
|
||||
if ($contenido === false) {
|
||||
throw new Exception("No se pudo leer el archivo");
|
||||
}
|
||||
|
||||
// Convertir encoding si es necesario (muchos archivos julianos usan Latin1)
|
||||
if (!mb_check_encoding($contenido, 'UTF-8')) {
|
||||
$contenido = mb_convert_encoding($contenido, 'UTF-8', 'ISO-8859-1');
|
||||
}
|
||||
|
||||
$lineas = explode("\n", $contenido);
|
||||
$this->estadisticas['total_lineas'] = count($lineas);
|
||||
|
||||
$pedimento_actual = null;
|
||||
$facturas_pedimento = [];
|
||||
$partidas_pedimento = [];
|
||||
|
||||
foreach ($lineas as $numero_linea => $linea) {
|
||||
$linea = trim($linea);
|
||||
if (empty($linea)) continue;
|
||||
|
||||
try {
|
||||
$codigo = substr($linea, 0, 3);
|
||||
|
||||
switch ($codigo) {
|
||||
case '500':
|
||||
// Línea de header - ignorar por ahora
|
||||
break;
|
||||
|
||||
case '501':
|
||||
// Si hay un pedimento anterior, procesarlo
|
||||
if ($pedimento_actual) {
|
||||
$this->procesarPedimento($pedimento_actual, $facturas_pedimento, $partidas_pedimento, $validar_duplicados);
|
||||
$facturas_pedimento = [];
|
||||
$partidas_pedimento = [];
|
||||
}
|
||||
$pedimento_actual = $this->parsearLinea501Real($linea);
|
||||
break;
|
||||
|
||||
case '505':
|
||||
if ($pedimento_actual) {
|
||||
try {
|
||||
$factura = $this->parsearLinea505Real($linea);
|
||||
$facturas_pedimento[] = $factura;
|
||||
error_log("Factura 505 procesada exitosamente: " . $factura['numero_factura']);
|
||||
} catch (Exception $e) {
|
||||
error_log("ERROR procesando línea 505: " . $e->getMessage());
|
||||
error_log("Línea problemática: " . $linea);
|
||||
$this->estadisticas['errores']++;
|
||||
// Continuar con la siguiente línea
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '551':
|
||||
if ($pedimento_actual) {
|
||||
try {
|
||||
$partida = $this->parsearLinea551Real($linea);
|
||||
$partidas_pedimento[] = $partida;
|
||||
error_log("Partida 551 procesada exitosamente: secuencia " . $partida['secuencia']);
|
||||
} catch (Exception $e) {
|
||||
error_log("ERROR procesando línea 551: " . $e->getMessage());
|
||||
error_log("Línea problemática: " . $linea);
|
||||
$this->estadisticas['errores']++;
|
||||
// Continuar con la siguiente línea en lugar de fallar completamente
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '506':
|
||||
case '509':
|
||||
case '510':
|
||||
case '511':
|
||||
case '553':
|
||||
case '554':
|
||||
case '556':
|
||||
case '557':
|
||||
case '558':
|
||||
case '800':
|
||||
case '801':
|
||||
// Otros códigos del formato real - ignorar por ahora
|
||||
break;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->errores_detalle[] = "Línea " . ($numero_linea + 1) . ": " . $e->getMessage();
|
||||
$this->estadisticas['errores']++;
|
||||
}
|
||||
}
|
||||
|
||||
// Procesar el último pedimento
|
||||
if ($pedimento_actual) {
|
||||
$this->procesarPedimento($pedimento_actual, $facturas_pedimento, $partidas_pedimento, $validar_duplicados);
|
||||
}
|
||||
|
||||
// Registrar log de importación
|
||||
$tiempo_procesamiento = time() - $inicio_tiempo;
|
||||
$this->registrarLog($nombre_archivo, $tiempo_procesamiento,
|
||||
count($this->errores_detalle) > 0 ? 'con_errores' : 'exitoso');
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'estadisticas' => $this->estadisticas,
|
||||
'errores' => $this->errores_detalle,
|
||||
'pedimentos_creados' => $this->pedimentos_creados
|
||||
];
|
||||
|
||||
} catch (Exception $e) {
|
||||
// Registrar log de error
|
||||
$tiempo_procesamiento = time() - $inicio_tiempo;
|
||||
$this->registrarLog($nombre_archivo, $tiempo_procesamiento, 'fallido', $e->getMessage());
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
private function registrarLog($archivo_nombre, $tiempo_procesamiento, $estado, $error_mensaje = null) {
|
||||
try {
|
||||
$sql = "INSERT INTO importacion_logs (
|
||||
usuario_id, archivo_nombre, total_lineas, pedimentos_procesados,
|
||||
facturas_procesadas, partidas_procesadas, errores, duplicados,
|
||||
tiempo_procesamiento, estado, detalles_errores
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
$detalles_errores = null;
|
||||
if (!empty($this->errores_detalle)) {
|
||||
$detalles_errores = implode("\n", $this->errores_detalle);
|
||||
} else if ($error_mensaje) {
|
||||
$detalles_errores = $error_mensaje;
|
||||
}
|
||||
|
||||
$params = [
|
||||
isset($_SESSION['usuario_id']) ? $_SESSION['usuario_id'] : null,
|
||||
$archivo_nombre,
|
||||
$this->estadisticas['total_lineas'],
|
||||
$this->estadisticas['pedimentos_procesados'],
|
||||
$this->estadisticas['facturas_procesadas'],
|
||||
$this->estadisticas['partidas_procesadas'],
|
||||
$this->estadisticas['errores'],
|
||||
$this->estadisticas['duplicados'],
|
||||
$tiempo_procesamiento,
|
||||
$estado,
|
||||
$detalles_errores
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_query($this->conn, $sql, $params);
|
||||
if ($stmt) {
|
||||
sqlsrv_free_stmt($stmt);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// No hacer nada si falla el log, no queremos interrumpir el proceso principal
|
||||
}
|
||||
}
|
||||
|
||||
private function parsearLinea501($linea) {
|
||||
// Formato: 501|numero_pedimento|patente|aduana|anio|clave_documento|rfc_importador|...
|
||||
$campos = explode('|', $linea);
|
||||
|
||||
if (count($campos) < 7) {
|
||||
throw new Exception("Formato inválido en línea 501");
|
||||
}
|
||||
|
||||
return [
|
||||
'numero_pedimento' => trim($campos[1]),
|
||||
'patente' => trim($campos[2]),
|
||||
'aduana' => trim($campos[3]),
|
||||
'anio' => trim($campos[4]),
|
||||
'clave_documento' => trim($campos[5]),
|
||||
'rfc_importador' => trim($campos[6]),
|
||||
'fecha_creacion' => date('Y-m-d H:i:s'),
|
||||
'usuario_id' => $_SESSION['usuario_id']
|
||||
];
|
||||
}
|
||||
|
||||
private function parsearLinea501Real($linea) {
|
||||
// Formato real: 501|patente|numero_pedimento|aduana|tipo|clave_documento|aduana2||rfc_importador|nombre_importador|...
|
||||
$campos = explode('|', $linea);
|
||||
|
||||
if (count($campos) < 10) {
|
||||
throw new Exception("Formato inválido en línea 501 real - campos insuficientes: " . count($campos));
|
||||
}
|
||||
|
||||
error_log("Parseando 501 real: " . $linea);
|
||||
error_log("Campos: " . print_r(array_slice($campos, 0, 10), true));
|
||||
|
||||
return [
|
||||
'numero_pedimento' => trim($campos[2]), // Campo 2: numero de pedimento
|
||||
'patente' => trim($campos[1]), // Campo 1: patente
|
||||
'aduana' => trim($campos[3]), // Campo 3: aduana
|
||||
'anio' => date('Y'), // Usar año actual
|
||||
'clave_documento' => trim($campos[5]), // Campo 5: clave documento
|
||||
'rfc_importador' => trim($campos[8]), // Campo 8: RFC importador
|
||||
'fecha_creacion' => date('Y-m-d H:i:s'),
|
||||
'usuario_id' => $_SESSION['usuario_id']
|
||||
];
|
||||
}
|
||||
|
||||
private function parsearLinea505($linea) {
|
||||
// Formato: 505|numero_factura|fecha_factura|valor_dolares|valor_factura|...
|
||||
$campos = explode('|', $linea);
|
||||
|
||||
if (count($campos) < 5) {
|
||||
throw new Exception("Formato inválido en línea 505");
|
||||
}
|
||||
|
||||
return [
|
||||
'numero_factura' => trim($campos[1]),
|
||||
'fecha_factura' => $this->convertirFecha(trim($campos[2])),
|
||||
'valor_dolares' => floatval(trim($campos[3])),
|
||||
'valor_factura' => floatval(trim($campos[4]))
|
||||
];
|
||||
}
|
||||
|
||||
private function parsearLinea505Real($linea) {
|
||||
// Formato oficial 505: 505|numero_pedimento|fecha_cfdi|numero_cfdi_cove|termino_facturacion|moneda|valor_dolares|valor_total|pais|entidad_federativa|rfc_proveedor|nombre_proveedor|calle|num_int|num_ext|cp|municipio|...
|
||||
$campos = explode('|', $linea);
|
||||
|
||||
if (count($campos) < 12) {
|
||||
throw new Exception("Formato inválido en línea 505 - campos insuficientes: " . count($campos) . " (mínimo: 12)");
|
||||
}
|
||||
|
||||
error_log("DEBUG 505 - Total campos: " . count($campos));
|
||||
error_log("DEBUG 505 - Primeros 12 campos: " . implode(' | ', array_slice($campos, 0, 12)));
|
||||
|
||||
// Extraer datos según documentación oficial SAT
|
||||
$numero_pedimento = trim($campos[1]); // Campo 1: Número de Pedimento
|
||||
$fecha_cfdi = trim($campos[2]); // Campo 2: Fecha de CFDI
|
||||
$numero_cfdi_cove = trim($campos[3]); // Campo 3: Número de CFDI (COVE) - 40 caracteres max
|
||||
$termino_facturacion = trim($campos[4]); // Campo 4: Término de Facturación - 3 caracteres
|
||||
$moneda = trim($campos[5]); // Campo 5: Moneda - 3 caracteres
|
||||
$valor_dolares = floatval(trim($campos[6])); // Campo 6: Valor Total en Dólares USD
|
||||
$valor_total = floatval(trim($campos[7])); // Campo 7: Valor Total en moneda del CFDI
|
||||
$pais = trim($campos[8]); // Campo 8: País del CFDI - 3 caracteres
|
||||
$entidad_federativa = trim($campos[9]); // Campo 9: Entidad Federativa - 3 caracteres
|
||||
$rfc_proveedor = trim($campos[10]); // Campo 10: RFC Proveedor - 30 caracteres max
|
||||
$nombre_proveedor = trim($campos[11]); // Campo 11: Nombre Proveedor - 120 caracteres max
|
||||
|
||||
error_log("DEBUG 505 - Datos oficiales extraídos:");
|
||||
error_log(" - Número CFDI/COVE: '$numero_cfdi_cove'");
|
||||
error_log(" - Fecha CFDI: '$fecha_cfdi'");
|
||||
error_log(" - Término facturación: '$termino_facturacion'");
|
||||
error_log(" - Moneda: '$moneda'");
|
||||
error_log(" - Valor USD: $valor_dolares");
|
||||
error_log(" - Valor total: $valor_total");
|
||||
error_log(" - RFC Proveedor: '$rfc_proveedor'");
|
||||
error_log(" - Nombre Proveedor: '$nombre_proveedor'");
|
||||
|
||||
return [
|
||||
'numero_factura' => $numero_cfdi_cove, // Campo 3: Número de CFDI/COVE
|
||||
'fecha_factura' => $this->convertirFechaReal($fecha_cfdi), // Campo 2: Fecha CFDI
|
||||
'valor_dolares' => $valor_dolares, // Campo 6: Valor en USD
|
||||
'valor_factura' => $valor_total, // Campo 7: Valor total en moneda CFDI
|
||||
'cove' => $numero_cfdi_cove, // Campo 3: COVE (mismo que número factura)
|
||||
'moneda' => $moneda, // Campo 5: Moneda
|
||||
'proveedor' => $nombre_proveedor // Campo 11: Nombre del proveedor
|
||||
];
|
||||
}
|
||||
|
||||
private function parsearLinea551($linea) {
|
||||
// Formato: 551|secuencia|fraccion_arancelaria|descripcion|cantidad|unidad|valor_unitario|...
|
||||
$campos = explode('|', $linea);
|
||||
|
||||
if (count($campos) < 7) {
|
||||
throw new Exception("Formato inválido en línea 551");
|
||||
}
|
||||
|
||||
return [
|
||||
'secuencia' => intval(trim($campos[1])),
|
||||
'fraccion_arancelaria' => trim($campos[2]),
|
||||
'descripcion' => trim($campos[3]),
|
||||
'cantidad' => floatval(trim($campos[4])),
|
||||
'unidad' => trim($campos[5]),
|
||||
'valor_unitario' => floatval(trim($campos[6]))
|
||||
];
|
||||
}
|
||||
|
||||
private function parsearLinea551Real($linea) {
|
||||
// Formato oficial 551: 551|numero_pedimento|fraccion_arancelaria|numero_partida|subdivision|descripcion|precio_unitario|valor_aduana|valor_comercial|valor_dolares|cantidad_umc|unidad_comercial|...
|
||||
$campos = explode('|', $linea);
|
||||
|
||||
if (count($campos) < 12) {
|
||||
throw new Exception("Formato inválido en línea 551 - campos insuficientes: " . count($campos) . " (mínimo: 12)");
|
||||
}
|
||||
|
||||
error_log("DEBUG 551 - Total campos: " . count($campos));
|
||||
error_log("DEBUG 551 - Primeros 12 campos: " . implode(' | ', array_slice($campos, 0, 12)));
|
||||
|
||||
// Extraer datos según documentación oficial
|
||||
$numero_pedimento = isset($campos[1]) ? trim($campos[1]) : '';
|
||||
$fraccion_arancelaria = isset($campos[2]) ? trim($campos[2]) : '';
|
||||
$numero_partida = isset($campos[3]) ? intval(trim($campos[3])) : 0;
|
||||
$subdivision = isset($campos[4]) ? trim($campos[4]) : '';
|
||||
$descripcion = isset($campos[5]) ? trim($campos[5]) : '';
|
||||
$precio_unitario = isset($campos[6]) ? floatval(trim($campos[6])) : 0;
|
||||
$valor_aduana = isset($campos[7]) ? floatval(trim($campos[7])) : 0;
|
||||
$valor_comercial = isset($campos[8]) ? floatval(trim($campos[8])) : 0;
|
||||
$valor_dolares = isset($campos[9]) ? floatval(trim($campos[9])) : 0;
|
||||
$cantidad_umc = isset($campos[10]) ? floatval(trim($campos[10])) : 0;
|
||||
$unidad_comercial = isset($campos[11]) ? trim($campos[11]) : '';
|
||||
|
||||
// Aplicar valores por defecto según documentación oficial
|
||||
if (empty($fraccion_arancelaria)) {
|
||||
error_log("WARNING 551: Fracción arancelaria vacía, usando valor por defecto");
|
||||
$fraccion_arancelaria = 'PENDIENTE';
|
||||
}
|
||||
|
||||
if ($numero_partida <= 0) {
|
||||
error_log("WARNING 551: Número de partida inválido, generando secuencial");
|
||||
static $contador_partida = 1;
|
||||
$numero_partida = $contador_partida++;
|
||||
}
|
||||
|
||||
if (empty($descripcion)) {
|
||||
error_log("WARNING 551: Descripción vacía, usando valor por defecto");
|
||||
$descripcion = 'DESCRIPCIÓN PENDIENTE';
|
||||
}
|
||||
|
||||
if ($cantidad_umc <= 0) {
|
||||
error_log("WARNING 551: Cantidad UMC inválida ($cantidad_umc), usando 1.0000");
|
||||
$cantidad_umc = 1.0000;
|
||||
}
|
||||
|
||||
if (empty($unidad_comercial)) {
|
||||
error_log("WARNING 551: Unidad comercial vacía, usando 'PZ'");
|
||||
$unidad_comercial = 'PZ'; // Pieza como unidad por defecto
|
||||
}
|
||||
|
||||
// Calcular precio unitario si no existe pero hay valores
|
||||
if ($precio_unitario == 0) {
|
||||
if ($valor_dolares > 0 && $cantidad_umc > 0) {
|
||||
$precio_unitario = $valor_dolares / $cantidad_umc;
|
||||
error_log("DEBUG 551: Precio unitario calculado: $precio_unitario");
|
||||
} elseif ($valor_comercial > 0 && $cantidad_umc > 0) {
|
||||
$precio_unitario = $valor_comercial / $cantidad_umc;
|
||||
error_log("DEBUG 551: Precio unitario calculado desde valor comercial: $precio_unitario");
|
||||
}
|
||||
}
|
||||
|
||||
error_log("DEBUG 551 - Datos procesados:");
|
||||
error_log(" - Partida: $numero_partida");
|
||||
error_log(" - Fracción: '$fraccion_arancelaria'");
|
||||
error_log(" - Descripción: '" . substr($descripcion, 0, 50) . "...'");
|
||||
error_log(" - Cantidad: $cantidad_umc");
|
||||
error_log(" - Unidad: '$unidad_comercial'");
|
||||
error_log(" - Precio unitario: $precio_unitario");
|
||||
error_log(" - Valor dólares: $valor_dolares");
|
||||
|
||||
return [
|
||||
'secuencia' => $numero_partida,
|
||||
'fraccion_arancelaria' => $fraccion_arancelaria,
|
||||
'descripcion' => $descripcion,
|
||||
'cantidad' => $cantidad_umc,
|
||||
'unidad' => $unidad_comercial,
|
||||
'valor_unitario' => $precio_unitario,
|
||||
'peso_neto' => null,
|
||||
'peso_bruto' => null
|
||||
];
|
||||
}
|
||||
|
||||
private function procesarPedimento($pedimento, $facturas, $partidas, $validar_duplicados) {
|
||||
try {
|
||||
error_log("Procesando pedimento: " . $pedimento['numero_pedimento']);
|
||||
error_log("Validar duplicados: " . ($validar_duplicados ? 'SÍ' : 'NO'));
|
||||
|
||||
// Validar duplicados si está habilitado
|
||||
if ($validar_duplicados && $this->existePedimento($pedimento['numero_pedimento'])) {
|
||||
error_log("Pedimento {$pedimento['numero_pedimento']} marcado como duplicado, omitiendo...");
|
||||
$this->estadisticas['duplicados']++;
|
||||
return;
|
||||
}
|
||||
|
||||
error_log("Insertando pedimento nuevo: " . $pedimento['numero_pedimento']);
|
||||
|
||||
// Iniciar transacción
|
||||
sqlsrv_begin_transaction($this->conn);
|
||||
|
||||
// Insertar pedimento
|
||||
$sql = "INSERT INTO pedimentos (numero_pedimento, patente, aduana, anio, clave_documento,
|
||||
rfc_importador, fecha_creacion, usuario_id, estado)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'activo'); SELECT SCOPE_IDENTITY() AS id;";
|
||||
|
||||
$params = [
|
||||
$pedimento['numero_pedimento'],
|
||||
$pedimento['patente'],
|
||||
$pedimento['aduana'],
|
||||
$pedimento['anio'],
|
||||
$pedimento['clave_documento'],
|
||||
$pedimento['rfc_importador'],
|
||||
$pedimento['fecha_creacion'],
|
||||
$pedimento['usuario_id']
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_query($this->conn, $sql, $params);
|
||||
if ($stmt === false) {
|
||||
throw new Exception("Error al insertar pedimento: " . print_r(sqlsrv_errors(), true));
|
||||
}
|
||||
|
||||
// Obtener el ID del pedimento insertado
|
||||
sqlsrv_next_result($stmt);
|
||||
sqlsrv_fetch($stmt);
|
||||
$pedimento_id = sqlsrv_get_field($stmt, 0);
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
// Insertar facturas
|
||||
foreach ($facturas as $factura) {
|
||||
$sql = "INSERT INTO pedimento_facturas (pedimento_id, numero_factura, fecha_factura,
|
||||
valor_dolares, valor_factura, cove, moneda, proveedor)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
$params = [
|
||||
$pedimento_id,
|
||||
$factura['numero_factura'],
|
||||
$factura['fecha_factura'],
|
||||
$factura['valor_dolares'],
|
||||
$factura['valor_factura'],
|
||||
isset($factura['cove']) ? $factura['cove'] : null,
|
||||
isset($factura['moneda']) ? $factura['moneda'] : 'USD',
|
||||
isset($factura['proveedor']) ? $factura['proveedor'] : null
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_query($this->conn, $sql, $params);
|
||||
if ($stmt === false) {
|
||||
throw new Exception("Error al insertar factura: " . print_r(sqlsrv_errors(), true));
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
$this->estadisticas['facturas_procesadas']++;
|
||||
}
|
||||
|
||||
// Insertar partidas
|
||||
foreach ($partidas as $partida) {
|
||||
error_log("INSERTANDO PARTIDA: " . print_r($partida, true));
|
||||
|
||||
$sql = "INSERT INTO pedimento_partidas (pedimento_id, secuencia, fraccion_arancelaria,
|
||||
descripcion, cantidad, unidad, valor_unitario,
|
||||
peso_neto, peso_bruto)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
$params = [
|
||||
$pedimento_id,
|
||||
$partida['secuencia'],
|
||||
$partida['fraccion_arancelaria'],
|
||||
$partida['descripcion'],
|
||||
$partida['cantidad'],
|
||||
$partida['unidad'],
|
||||
$partida['valor_unitario'],
|
||||
isset($partida['peso_neto']) ? $partida['peso_neto'] : null,
|
||||
isset($partida['peso_bruto']) ? $partida['peso_bruto'] : null
|
||||
];
|
||||
|
||||
error_log("PARÁMETROS SQL: " . print_r($params, true));
|
||||
|
||||
$stmt = sqlsrv_query($this->conn, $sql, $params);
|
||||
if ($stmt === false) {
|
||||
$error = print_r(sqlsrv_errors(), true);
|
||||
error_log("ERROR AL INSERTAR PARTIDA: " . $error);
|
||||
throw new Exception("Error al insertar partida: " . $error);
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
$this->estadisticas['partidas_procesadas']++;
|
||||
error_log("Partida insertada exitosamente - Total procesadas: " . $this->estadisticas['partidas_procesadas']);
|
||||
}
|
||||
|
||||
// Confirmar transacción
|
||||
sqlsrv_commit($this->conn);
|
||||
|
||||
$this->estadisticas['pedimentos_procesados']++;
|
||||
$this->pedimentos_creados[] = $pedimento['numero_pedimento'];
|
||||
|
||||
} catch (Exception $e) {
|
||||
sqlsrv_rollback($this->conn);
|
||||
$this->errores_detalle[] = "Error procesando pedimento {$pedimento['numero_pedimento']}: " . $e->getMessage();
|
||||
$this->estadisticas['errores']++;
|
||||
}
|
||||
}
|
||||
|
||||
private function existePedimento($numero_pedimento) {
|
||||
$sql = "SELECT id, fecha_creacion FROM pedimentos WHERE numero_pedimento = ?";
|
||||
$stmt = sqlsrv_query($this->conn, $sql, [$numero_pedimento]);
|
||||
if ($stmt === false) {
|
||||
error_log("Error al verificar duplicado: " . print_r(sqlsrv_errors(), true));
|
||||
return false;
|
||||
}
|
||||
$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
if ($row) {
|
||||
error_log("Pedimento duplicado encontrado: {$numero_pedimento} (ID: {$row['id']})");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function convertirFecha($fecha_str) {
|
||||
// Convertir formato DDMMYYYY a YYYY-MM-DD
|
||||
if (strlen($fecha_str) === 8) {
|
||||
$dia = substr($fecha_str, 0, 2);
|
||||
$mes = substr($fecha_str, 2, 2);
|
||||
$anio = substr($fecha_str, 4, 4);
|
||||
return "$anio-$mes-$dia";
|
||||
}
|
||||
return date('Y-m-d'); // Fecha por defecto
|
||||
}
|
||||
|
||||
private function convertirFechaReal($fecha_str) {
|
||||
// Convertir fecha del archivo real - puede venir en formato YYYYMMDD o DDMMYYYY
|
||||
if (empty($fecha_str) || strlen($fecha_str) !== 8) {
|
||||
return date('Y-m-d'); // Fecha por defecto
|
||||
}
|
||||
|
||||
// Intentar formato YYYYMMDD primero
|
||||
if (substr($fecha_str, 0, 2) === '20') {
|
||||
$anio = substr($fecha_str, 0, 4);
|
||||
$mes = substr($fecha_str, 4, 2);
|
||||
$dia = substr($fecha_str, 6, 2);
|
||||
} else {
|
||||
// Formato DDMMYYYY
|
||||
$dia = substr($fecha_str, 0, 2);
|
||||
$mes = substr($fecha_str, 2, 2);
|
||||
$anio = substr($fecha_str, 4, 4);
|
||||
}
|
||||
|
||||
// Validar fecha
|
||||
if (checkdate($mes, $dia, $anio)) {
|
||||
return "$anio-$mes-$dia";
|
||||
}
|
||||
|
||||
return date('Y-m-d'); // Fecha por defecto si no es válida
|
||||
}
|
||||
}
|
||||
|
||||
// Procesamiento de archivos (individual o múltiple)
|
||||
try {
|
||||
error_log("=== PROCESAMIENTO DE ARCHIVOS ===");
|
||||
error_log("POST: " . print_r($_POST, true));
|
||||
error_log("FILES: " . print_r($_FILES, true));
|
||||
|
||||
$validar_duplicados = isset($_POST['validar_duplicados']) && $_POST['validar_duplicados'] === 'on';
|
||||
error_log("Validar duplicados: " . ($validar_duplicados ? 'SÍ' : 'NO'));
|
||||
|
||||
// Validar variables de entorno requeridas ANTES de intentar conectar (evita die() dentro de getConnection)
|
||||
$requiredEnv = ['DB_HOST','DB_DATABASE','DB_USERNAME','DB_PASSWORD'];
|
||||
foreach ($requiredEnv as $envKey) {
|
||||
if (!isset($_ENV[$envKey]) || $_ENV[$envKey] === '') {
|
||||
throw new Exception("Falta configurar la variable de entorno $envKey en el archivo .env");
|
||||
}
|
||||
}
|
||||
|
||||
// Obtener conexión a SQL Server
|
||||
$conn = getConnection();
|
||||
$importador = new ImportadorPedimentos($conn);
|
||||
|
||||
$resultado_final = null;
|
||||
|
||||
// Verificar si es archivo individual
|
||||
if (isset($_FILES['archivo'])) {
|
||||
error_log("=== PROCESAMIENTO INDIVIDUAL ===");
|
||||
$archivo_temporal = $_FILES['archivo']['tmp_name'];
|
||||
$nombre_archivo = $_FILES['archivo']['name'];
|
||||
|
||||
$resultado_final = $importador->procesarArchivo($archivo_temporal, $validar_duplicados);
|
||||
|
||||
} else if (isset($_FILES['archivos']) && is_array($_FILES['archivos']['name'])) {
|
||||
error_log("=== PROCESAMIENTO MÚLTIPLE FORMATO NORMAL ===");
|
||||
|
||||
$archivos = $_FILES['archivos'];
|
||||
$estadisticas_globales = [
|
||||
'pedimentos_procesados' => 0,
|
||||
'facturas_procesadas' => 0,
|
||||
'partidas_procesadas' => 0,
|
||||
'duplicados' => 0,
|
||||
'errores' => 0,
|
||||
'total_lineas' => 0
|
||||
];
|
||||
|
||||
$archivos_procesados = 0;
|
||||
$archivos_con_errores = [];
|
||||
|
||||
// Procesar cada archivo individualmente
|
||||
for ($i = 0; $i < count($archivos['name']); $i++) {
|
||||
if ($archivos['error'][$i] !== UPLOAD_ERR_OK) {
|
||||
$archivos_con_errores[] = [
|
||||
'archivo' => $archivos['name'][$i],
|
||||
'error' => 'Error en la subida del archivo (código: ' . $archivos['error'][$i] . ')'
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
$nombre_archivo = $archivos['name'][$i];
|
||||
$archivo_temporal = $archivos['tmp_name'][$i];
|
||||
|
||||
error_log("Procesando archivo: $nombre_archivo");
|
||||
|
||||
try {
|
||||
$resultado = $importador->procesarArchivo($archivo_temporal, $validar_duplicados);
|
||||
|
||||
if ($resultado['success']) {
|
||||
$estadisticas_globales['pedimentos_procesados'] += $resultado['estadisticas']['pedimentos_procesados'];
|
||||
$estadisticas_globales['facturas_procesadas'] += $resultado['estadisticas']['facturas_procesadas'];
|
||||
$estadisticas_globales['partidas_procesadas'] += $resultado['estadisticas']['partidas_procesadas'];
|
||||
$estadisticas_globales['duplicados'] += $resultado['estadisticas']['duplicados'];
|
||||
$estadisticas_globales['errores'] += $resultado['estadisticas']['errores'];
|
||||
$estadisticas_globales['total_lineas'] += $resultado['estadisticas']['total_lineas'];
|
||||
|
||||
$archivos_procesados++;
|
||||
error_log("Archivo procesado exitosamente: $nombre_archivo");
|
||||
} else {
|
||||
$archivos_con_errores[] = [
|
||||
'archivo' => $nombre_archivo,
|
||||
'error' => $resultado['message'] ?? 'Error desconocido'
|
||||
];
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$archivos_con_errores[] = [
|
||||
'archivo' => $nombre_archivo,
|
||||
'error' => $e->getMessage()
|
||||
];
|
||||
error_log("Excepción procesando archivo: $nombre_archivo - " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$resultado_final = [
|
||||
'success' => $archivos_procesados > 0,
|
||||
'estadisticas' => $estadisticas_globales,
|
||||
'archivos_procesados' => $archivos_procesados,
|
||||
'total_archivos' => count($archivos['name']),
|
||||
'archivos_con_errores' => $archivos_con_errores
|
||||
];
|
||||
|
||||
if ($archivos_procesados === 0) {
|
||||
$resultado_final['message'] = 'No se pudo procesar ningún archivo correctamente';
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new Exception("No se recibieron archivos para procesar");
|
||||
}
|
||||
|
||||
// Cerrar conexión
|
||||
sqlsrv_close($conn);
|
||||
|
||||
// Enviar respuesta
|
||||
echo json_encode($resultado_final);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("ERROR GENERAL: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Error del servidor: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
?>
|
||||
255
public/importar_pedimentos_simple.php
Normal file
255
public/importar_pedimentos_simple.php
Normal file
@@ -0,0 +1,255 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
// Limpiar cualquier output previo
|
||||
if (ob_get_level()) {
|
||||
ob_clean();
|
||||
}
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Cache-Control: no-cache, must-revalidate');
|
||||
|
||||
error_log("=== IMPORTACIÓN SIMPLE ===");
|
||||
error_log("POST: " . print_r($_POST, true));
|
||||
error_log("FILES: " . print_r($_FILES, true));
|
||||
error_log("Número total de archivos recibidos: " . count($_FILES));
|
||||
|
||||
if (!isset($_SESSION['usuario_id'])) {
|
||||
echo json_encode(['success' => false, 'message' => 'Usuario no autenticado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['success' => false, 'message' => 'Método no permitido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Incluir la clase ImportadorPedimentos
|
||||
include_once __DIR__ . '/../app/controllers/ImportadorPedimentos.php';
|
||||
|
||||
// Función segura para obtener conexión
|
||||
function getSafeConnection() {
|
||||
require_once __DIR__ . '/../app/helpers/env.php';
|
||||
loadEnv();
|
||||
|
||||
$serverName = $_ENV['DB_HOST'];
|
||||
$connectionOptions = [
|
||||
"Database" => $_ENV['DB_DATABASE'],
|
||||
"Uid" => $_ENV['DB_USERNAME'],
|
||||
"PWD" => $_ENV['DB_PASSWORD'],
|
||||
"CharacterSet" => "UTF-8"
|
||||
];
|
||||
|
||||
$conn = sqlsrv_connect($serverName, $connectionOptions);
|
||||
|
||||
if (!$conn) {
|
||||
$errors = sqlsrv_errors();
|
||||
$errorMsg = "Error de conexión SQL Server";
|
||||
if ($errors) {
|
||||
$errorMsg .= ": " . $errors[0]['message'];
|
||||
}
|
||||
throw new Exception($errorMsg);
|
||||
}
|
||||
|
||||
return $conn;
|
||||
}
|
||||
|
||||
try {
|
||||
$validar_duplicados = isset($_POST['validar_duplicados']) && $_POST['validar_duplicados'] === 'on';
|
||||
|
||||
// Crear timestamp único para esta importación
|
||||
$timestamp_importacion = date('Y-m-d H:i:s');
|
||||
|
||||
// Verificar conexión a la base de datos
|
||||
$conn = null;
|
||||
try {
|
||||
$conn = getSafeConnection();
|
||||
if (!$conn) {
|
||||
throw new Exception("No se pudo establecer conexión con la base de datos");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
error_log("Error de conexión a BD: " . $e->getMessage());
|
||||
throw new Exception("Error de conexión a la base de datos: " . $e->getMessage());
|
||||
}
|
||||
|
||||
$importador = new ImportadorPedimentos($conn);
|
||||
|
||||
$estadisticas_globales = [
|
||||
'pedimentos_procesados' => 0,
|
||||
'facturas_procesadas' => 0,
|
||||
'partidas_procesadas' => 0,
|
||||
'duplicados' => 0,
|
||||
'errores' => 0,
|
||||
'total_lineas' => 0
|
||||
];
|
||||
|
||||
$archivos_procesados = 0;
|
||||
$archivos_con_errores = [];
|
||||
|
||||
// Manejar archivos múltiples enviados como archivos[]
|
||||
if (isset($_FILES)) {
|
||||
error_log("INICIANDO BUCLE DE ARCHIVOS...");
|
||||
foreach ($_FILES as $key => $archivo_info) {
|
||||
error_log("Procesando key: $key");
|
||||
error_log("Archivo info: " . print_r($archivo_info, true));
|
||||
|
||||
if (is_array($archivo_info['name'])) {
|
||||
// Múltiples archivos
|
||||
for ($i = 0; $i < count($archivo_info['name']); $i++) {
|
||||
if ($archivo_info['error'][$i] !== UPLOAD_ERR_OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$nombre = $archivo_info['name'][$i];
|
||||
$tmp_name = $archivo_info['tmp_name'][$i];
|
||||
|
||||
error_log("Procesando archivo múltiple: $nombre");
|
||||
|
||||
try {
|
||||
$resultado = $importador->procesarArchivo($tmp_name, $validar_duplicados, $timestamp_importacion);
|
||||
|
||||
if ($resultado['success']) {
|
||||
$estadisticas_globales['pedimentos_procesados'] += $resultado['estadisticas']['pedimentos_procesados'];
|
||||
$estadisticas_globales['facturas_procesadas'] += $resultado['estadisticas']['facturas_procesadas'];
|
||||
$estadisticas_globales['partidas_procesadas'] += $resultado['estadisticas']['partidas_procesadas'];
|
||||
$estadisticas_globales['duplicados'] += $resultado['estadisticas']['duplicados'];
|
||||
$estadisticas_globales['errores'] += $resultado['estadisticas']['errores'];
|
||||
$estadisticas_globales['total_lineas'] += $resultado['estadisticas']['total_lineas'];
|
||||
$archivos_procesados++;
|
||||
} else {
|
||||
$archivos_con_errores[] = [
|
||||
'archivo' => $nombre,
|
||||
'error' => $resultado['message'] ?? 'Error desconocido'
|
||||
];
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$archivos_con_errores[] = [
|
||||
'archivo' => $nombre,
|
||||
'error' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Archivo individual
|
||||
if ($archivo_info['error'] === UPLOAD_ERR_OK) {
|
||||
$nombre = $archivo_info['name'];
|
||||
$tmp_name = $archivo_info['tmp_name'];
|
||||
|
||||
error_log("Procesando archivo individual: $nombre");
|
||||
|
||||
try {
|
||||
$resultado = $importador->procesarArchivo($tmp_name, $validar_duplicados, $timestamp_importacion);
|
||||
|
||||
if ($resultado['success']) {
|
||||
$estadisticas_globales = $resultado['estadisticas'];
|
||||
$archivos_procesados = 1;
|
||||
} else {
|
||||
$archivos_con_errores[] = [
|
||||
'archivo' => $nombre,
|
||||
'error' => $resultado['message'] ?? 'Error desconocido'
|
||||
];
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$archivos_con_errores[] = [
|
||||
'archivo' => $nombre,
|
||||
'error' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sqlsrv_close($conn);
|
||||
|
||||
// Verificar los datos realmente insertados en la base de datos
|
||||
$estadisticas_reales = verificarEstadisticasReales($estadisticas_globales, $timestamp_importacion);
|
||||
|
||||
echo json_encode([
|
||||
'success' => $archivos_procesados > 0,
|
||||
'estadisticas' => $estadisticas_reales,
|
||||
'archivos_procesados' => $archivos_procesados,
|
||||
'archivos_con_errores' => $archivos_con_errores,
|
||||
'message' => $archivos_procesados > 0 ? 'Archivos procesados correctamente' : 'No se pudo procesar ningún archivo'
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("ERROR GENERAL: " . $e->getMessage());
|
||||
error_log("STACK TRACE: " . $e->getTraceAsString());
|
||||
|
||||
// Asegurar que se devuelve JSON válido
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Error del servidor: ' . $e->getMessage(),
|
||||
'debug' => [
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'timestamp' => date('Y-m-d H:i:s')
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
function verificarEstadisticasReales($estadisticas_reportadas, $timestamp_importacion) {
|
||||
try {
|
||||
$conn = getSafeConnection();
|
||||
|
||||
// Obtener estadísticas reales de la base de datos para esta importación específica
|
||||
$usuario_id = $_SESSION['usuario_id'];
|
||||
|
||||
// Contar pedimentos insertados en esta sesión (últimos 2 minutos)
|
||||
$timestamp_desde = date('Y-m-d H:i:s', strtotime($timestamp_importacion . ' -2 minutes'));
|
||||
|
||||
$sql = "SELECT COUNT(*) as total FROM pedimentos WHERE usuario_id = ? AND fecha_creacion >= ?";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$usuario_id, $timestamp_desde]);
|
||||
$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
$pedimentos_reales = $row ? $row['total'] : 0;
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
// Contar facturas insertadas en esta sesión
|
||||
$sql = "SELECT COUNT(f.*) as total
|
||||
FROM pedimento_facturas f
|
||||
INNER JOIN pedimentos p ON f.pedimento_id = p.id
|
||||
WHERE p.usuario_id = ? AND p.fecha_creacion >= ?";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$usuario_id, $timestamp_desde]);
|
||||
$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
$facturas_reales = $row ? $row['total'] : 0;
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
// Contar partidas insertadas en esta sesión
|
||||
$sql = "SELECT COUNT(pa.*) as total
|
||||
FROM pedimento_partidas pa
|
||||
INNER JOIN pedimentos p ON pa.pedimento_id = p.id
|
||||
WHERE p.usuario_id = ? AND p.fecha_creacion >= ?";
|
||||
$stmt = sqlsrv_query($conn, $sql, [$usuario_id, $timestamp_desde]);
|
||||
$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
$partidas_reales = $row ? $row['total'] : 0;
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
sqlsrv_close($conn);
|
||||
|
||||
error_log("=== VERIFICACIÓN DE ESTADÍSTICAS ===");
|
||||
error_log("Timestamp importación: $timestamp_importacion");
|
||||
error_log("Timestamp desde: $timestamp_desde");
|
||||
error_log("Reportadas - Pedimentos: {$estadisticas_reportadas['pedimentos_procesados']}, Facturas: {$estadisticas_reportadas['facturas_procesadas']}, Partidas: {$estadisticas_reportadas['partidas_procesadas']}");
|
||||
error_log("Reales BD - Pedimentos: $pedimentos_reales, Facturas: $facturas_reales, Partidas: $partidas_reales");
|
||||
|
||||
// Retornar las estadísticas reales de la base de datos
|
||||
return [
|
||||
'total_lineas' => $estadisticas_reportadas['total_lineas'],
|
||||
'pedimentos_procesados' => $pedimentos_reales,
|
||||
'facturas_procesadas' => $facturas_reales,
|
||||
'partidas_procesadas' => $partidas_reales,
|
||||
'errores' => $estadisticas_reportadas['errores'],
|
||||
'duplicados' => $estadisticas_reportadas['duplicados']
|
||||
];
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error verificando estadísticas reales: " . $e->getMessage());
|
||||
// En caso de error, retornar las estadísticas originales
|
||||
return $estadisticas_reportadas;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
128
public/importar_test_simple.php
Normal file
128
public/importar_test_simple.php
Normal file
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
// Limpiar cualquier output previo
|
||||
if (ob_get_level()) {
|
||||
ob_clean();
|
||||
}
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Cache-Control: no-cache, must-revalidate');
|
||||
|
||||
error_log("=== IMPORTACIÓN SIMPLE V2 ===");
|
||||
error_log("POST: " . print_r($_POST, true));
|
||||
error_log("FILES: " . print_r($_FILES, true));
|
||||
|
||||
if (!isset($_SESSION['usuario_id'])) {
|
||||
echo json_encode(['success' => false, 'message' => 'Usuario no autenticado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['success' => false, 'message' => 'Método no permitido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Función de conexión directa
|
||||
function getDirectConnection() {
|
||||
// Cargar variables de entorno manualmente
|
||||
$env_file = __DIR__ . '/../.env';
|
||||
if (file_exists($env_file)) {
|
||||
$lines = file($env_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
foreach ($lines as $line) {
|
||||
if (strpos($line, '=') !== false && substr($line, 0, 1) !== '#') {
|
||||
list($key, $value) = explode('=', $line, 2);
|
||||
$_ENV[trim($key)] = trim($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$serverName = $_ENV['DB_HOST'] ?? 'localhost';
|
||||
$connectionOptions = [
|
||||
"Database" => $_ENV['DB_DATABASE'] ?? 'importadores',
|
||||
"Uid" => $_ENV['DB_USERNAME'] ?? 'sa',
|
||||
"PWD" => $_ENV['DB_PASSWORD'] ?? '',
|
||||
"CharacterSet" => "UTF-8"
|
||||
];
|
||||
|
||||
$conn = sqlsrv_connect($serverName, $connectionOptions);
|
||||
|
||||
if (!$conn) {
|
||||
$errors = sqlsrv_errors();
|
||||
$errorMsg = "Error de conexión SQL Server";
|
||||
if ($errors) {
|
||||
$errorMsg .= ": " . $errors[0]['message'];
|
||||
}
|
||||
throw new Exception($errorMsg);
|
||||
}
|
||||
|
||||
return $conn;
|
||||
}
|
||||
|
||||
try {
|
||||
$validar_duplicados = isset($_POST['validar_duplicados']) && $_POST['validar_duplicados'] === 'on';
|
||||
|
||||
// Crear timestamp único para esta importación
|
||||
$timestamp_importacion = date('Y-m-d H:i:s');
|
||||
|
||||
$conn = getDirectConnection();
|
||||
|
||||
$estadisticas_globales = [
|
||||
'pedimentos_procesados' => 0,
|
||||
'facturas_procesadas' => 0,
|
||||
'partidas_procesadas' => 0,
|
||||
'duplicados' => 0,
|
||||
'errores' => 0,
|
||||
'total_lineas' => 0
|
||||
];
|
||||
|
||||
$archivos_procesados = 0;
|
||||
$archivos_con_errores = [];
|
||||
|
||||
// Simulación de procesamiento (por ahora)
|
||||
if (isset($_FILES)) {
|
||||
foreach ($_FILES as $key => $archivo_info) {
|
||||
error_log("Procesando key: $key");
|
||||
|
||||
if (is_array($archivo_info['name'])) {
|
||||
// Múltiples archivos
|
||||
$archivos_procesados = count($archivo_info['name']);
|
||||
$estadisticas_globales['pedimentos_procesados'] = $archivos_procesados * 2; // Simulado
|
||||
$estadisticas_globales['facturas_procesadas'] = $archivos_procesados * 3; // Simulado
|
||||
$estadisticas_globales['partidas_procesadas'] = $archivos_procesados * 5; // Simulado
|
||||
} else {
|
||||
// Archivo individual
|
||||
$archivos_procesados = 1;
|
||||
$estadisticas_globales['pedimentos_procesados'] = 1;
|
||||
$estadisticas_globales['facturas_procesadas'] = 2;
|
||||
$estadisticas_globales['partidas_procesadas'] = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sqlsrv_close($conn);
|
||||
|
||||
echo json_encode([
|
||||
'success' => $archivos_procesados > 0,
|
||||
'estadisticas' => $estadisticas_globales,
|
||||
'archivos_procesados' => $archivos_procesados,
|
||||
'archivos_con_errores' => $archivos_con_errores,
|
||||
'message' => $archivos_procesados > 0 ? 'Archivos procesados correctamente (MODO TEST)' : 'No se pudo procesar ningún archivo'
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("ERROR GENERAL: " . $e->getMessage());
|
||||
error_log("STACK TRACE: " . $e->getTraceAsString());
|
||||
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Error del servidor: ' . $e->getMessage(),
|
||||
'debug' => [
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'timestamp' => date('Y-m-d H:i:s')
|
||||
]
|
||||
]);
|
||||
}
|
||||
?>
|
||||
66
public/limpiar_pedimentos.php
Normal file
66
public/limpiar_pedimentos.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Verificar que el usuario esté logueado
|
||||
if (!isset($_SESSION['usuario_id'])) {
|
||||
echo json_encode(['success' => false, 'message' => 'Usuario no autenticado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['success' => false, 'message' => 'Método no permitido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$conn = getConnection();
|
||||
|
||||
// Iniciar transacción
|
||||
sqlsrv_begin_transaction($conn);
|
||||
|
||||
// Eliminar en orden correcto (por las foreign keys)
|
||||
$sql1 = "DELETE FROM pedimento_partidas";
|
||||
$sql2 = "DELETE FROM pedimento_facturas";
|
||||
$sql3 = "DELETE FROM pedimentos";
|
||||
$sql4 = "DELETE FROM importacion_logs";
|
||||
|
||||
$stmt1 = sqlsrv_query($conn, $sql1);
|
||||
$stmt2 = sqlsrv_query($conn, $sql2);
|
||||
$stmt3 = sqlsrv_query($conn, $sql3);
|
||||
$stmt4 = sqlsrv_query($conn, $sql4);
|
||||
|
||||
if ($stmt1 && $stmt2 && $stmt3 && $stmt4) {
|
||||
// Resetear los IDENTITY
|
||||
sqlsrv_query($conn, "DBCC CHECKIDENT ('pedimentos', RESEED, 0)");
|
||||
sqlsrv_query($conn, "DBCC CHECKIDENT ('pedimento_facturas', RESEED, 0)");
|
||||
sqlsrv_query($conn, "DBCC CHECKIDENT ('pedimento_partidas', RESEED, 0)");
|
||||
sqlsrv_query($conn, "DBCC CHECKIDENT ('importacion_logs', RESEED, 0)");
|
||||
|
||||
sqlsrv_commit($conn);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Base de datos limpiada exitosamente',
|
||||
'timestamp' => date('Y-m-d H:i:s'),
|
||||
'usuario_id' => $_SESSION['usuario_id']
|
||||
]);
|
||||
} else {
|
||||
sqlsrv_rollback($conn);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Error al limpiar la base de datos',
|
||||
'errors' => sqlsrv_errors()
|
||||
]);
|
||||
}
|
||||
|
||||
sqlsrv_close($conn);
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
?>
|
||||
36
public/test_import.php
Normal file
36
public/test_import.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
session_start();
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
// Simple test endpoint
|
||||
$response = [
|
||||
'success' => true,
|
||||
'message' => 'Conexión exitosa desde public',
|
||||
'timestamp' => date('Y-m-d H:i:s'),
|
||||
'method' => $_SERVER['REQUEST_METHOD'],
|
||||
'usuario' => isset($_SESSION['usuario']) ? $_SESSION['usuario']['nombre'] ?? 'Usuario logueado' : 'No hay usuario',
|
||||
'post_data' => $_POST,
|
||||
'get_data' => $_GET,
|
||||
'server_info' => [
|
||||
'HTTP_HOST' => $_SERVER['HTTP_HOST'],
|
||||
'REQUEST_URI' => $_SERVER['REQUEST_URI'],
|
||||
'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME']
|
||||
]
|
||||
];
|
||||
|
||||
if (isset($_FILES['archivo'])) {
|
||||
$response['files_data'] = [
|
||||
'nombre' => $_FILES['archivo']['name'],
|
||||
'tamaño' => $_FILES['archivo']['size'],
|
||||
'tipo' => $_FILES['archivo']['type'],
|
||||
'error' => $_FILES['archivo']['error']
|
||||
];
|
||||
} else {
|
||||
$response['files_data'] = 'No hay archivo';
|
||||
}
|
||||
|
||||
echo json_encode($response, JSON_PRETTY_PRINT);
|
||||
?>
|
||||
10
public/test_json.php
Normal file
10
public/test_json.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
// Test simple para verificar respuesta JSON
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Test exitoso',
|
||||
'timestamp' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
?>
|
||||
26
public/test_ultra_simple.php
Normal file
26
public/test_ultra_simple.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
// Ultra simple test - solo devolver JSON
|
||||
header('Content-Type: application/json');
|
||||
|
||||
try {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Test básico funcionando',
|
||||
'estadisticas' => [
|
||||
'pedimentos_procesados' => 5,
|
||||
'facturas_procesadas' => 10,
|
||||
'partidas_procesadas' => 15,
|
||||
'duplicados' => 0,
|
||||
'errores' => 0,
|
||||
'total_lineas' => 100
|
||||
],
|
||||
'archivos_procesados' => 3,
|
||||
'archivos_con_errores' => []
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
?>
|
||||
46
public/verificar_pedimentos.php
Normal file
46
public/verificar_pedimentos.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
try {
|
||||
$conn = getConnection();
|
||||
|
||||
// Obtener todos los pedimentos existentes
|
||||
$sql = "SELECT numero_pedimento, fecha_creacion, usuario_id FROM pedimentos ORDER BY fecha_creacion DESC";
|
||||
$stmt = sqlsrv_query($conn, $sql);
|
||||
|
||||
$pedimentos_existentes = [];
|
||||
if ($stmt !== false) {
|
||||
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
$pedimentos_existentes[] = $row;
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
}
|
||||
|
||||
// Obtener estadísticas
|
||||
$sql_stats = "SELECT
|
||||
COUNT(*) as total_pedimentos,
|
||||
COUNT(DISTINCT usuario_id) as usuarios_distintos,
|
||||
MIN(fecha_creacion) as primer_pedimento,
|
||||
MAX(fecha_creacion) as ultimo_pedimento
|
||||
FROM pedimentos";
|
||||
$stmt_stats = sqlsrv_query($conn, $sql_stats);
|
||||
$stats = sqlsrv_fetch_array($stmt_stats, SQLSRV_FETCH_ASSOC);
|
||||
sqlsrv_free_stmt($stmt_stats);
|
||||
|
||||
sqlsrv_close($conn);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'pedimentos_existentes' => $pedimentos_existentes,
|
||||
'estadisticas' => $stats,
|
||||
'total_encontrados' => count($pedimentos_existentes)
|
||||
], JSON_PRETTY_PRINT);
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
?>
|
||||
@@ -1,27 +0,0 @@
|
||||
CREATE TABLE unidades_medida_apendice7 (
|
||||
id INT PRIMARY KEY,
|
||||
descripcion NVARCHAR(100)
|
||||
);
|
||||
|
||||
INSERT INTO unidades_medida_apendice7 (id, descripcion) VALUES
|
||||
(1, 'KILO'),
|
||||
(2, 'GRAMO'),
|
||||
(3, 'METRO LINEAL'),
|
||||
(4, 'METRO CUADRADO'),
|
||||
(5, 'METRO CUBICO'),
|
||||
(6, 'PIEZA'),
|
||||
(7, 'CABEZA'),
|
||||
(8, 'LITRO'),
|
||||
(9, 'PAR'),
|
||||
(10, 'KILOWATT'),
|
||||
(11, 'MILLAR'),
|
||||
(12, 'JUEGO'),
|
||||
(13, 'KILOWATT/HORA'),
|
||||
(14, 'TONELADA'),
|
||||
(15, 'BARRIL'),
|
||||
(16, 'GRAMO NETO'),
|
||||
(17, 'DECENAS'),
|
||||
(18, 'CIENTOS'),
|
||||
(19, 'DOCENAS'),
|
||||
(20, 'CAJA'),
|
||||
(21, 'BOTELLA');
|
||||
@@ -1,9 +0,0 @@
|
||||
CREATE TABLE recuperacion_password (
|
||||
id INT IDENTITY(1,1) PRIMARY KEY,
|
||||
email NVARCHAR(255) NOT NULL,
|
||||
codigo NVARCHAR(10) NOT NULL,
|
||||
expiracion DATETIME NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE recuperacion_password
|
||||
ADD estatus INT NOT NULL DEFAULT 0;
|
||||
@@ -1,11 +0,0 @@
|
||||
CREATE TABLE expediente_archivos (
|
||||
id INT IDENTITY(1,1) PRIMARY KEY,
|
||||
id_solicitud INT NOT NULL,
|
||||
nombre_archivo NVARCHAR(255),
|
||||
ruta_archivo NVARCHAR(500),
|
||||
tipo_archivo NVARCHAR(50),
|
||||
tamano_archivo DECIMAL(18,2),
|
||||
creado_por NVARCHAR(100),
|
||||
creado_en DATETIME DEFAULT GETDATE(),
|
||||
FOREIGN KEY (id_solicitud) REFERENCES solicitud_importacion_factura(id_solicitud)
|
||||
);
|
||||
@@ -1,83 +0,0 @@
|
||||
-- Tabla para Templates Rápidos Configurables
|
||||
CREATE TABLE dbo.templates_rapidos (
|
||||
id INT IDENTITY(1,1) PRIMARY KEY,
|
||||
nombre VARCHAR(100) NOT NULL,
|
||||
descripcion VARCHAR(255),
|
||||
icono VARCHAR(50) DEFAULT '🏢',
|
||||
activo BIT DEFAULT 1,
|
||||
|
||||
-- Configuración del template
|
||||
config_json NVARCHAR(MAX), -- JSON con la configuración completa
|
||||
|
||||
-- Campos específicos más usados (para facilitar consultas)
|
||||
tipo_moneda VARCHAR(3),
|
||||
incoterm VARCHAR(10),
|
||||
vinculacion TINYINT,
|
||||
pais_proveedor VARCHAR(10),
|
||||
tasa_preferencial VARCHAR(20),
|
||||
|
||||
-- Metadatos
|
||||
id_agencia INT,
|
||||
id_usuario_creador INT,
|
||||
fecha_creacion DATETIME DEFAULT GETDATE(),
|
||||
fecha_modificacion DATETIME DEFAULT GETDATE(),
|
||||
|
||||
-- Estadísticas de uso
|
||||
veces_usado INT DEFAULT 0,
|
||||
ultima_vez_usado DATETIME,
|
||||
|
||||
-- Índices
|
||||
INDEX IX_templates_rapidos_agencia (id_agencia, activo),
|
||||
INDEX IX_templates_rapidos_usuario (id_usuario_creador),
|
||||
INDEX IX_templates_rapidos_uso (veces_usado DESC)
|
||||
);
|
||||
|
||||
-- Insertar algunos templates por defecto
|
||||
INSERT INTO dbo.templates_rapidos (nombre, descripcion, icono, config_json, tipo_moneda, incoterm, vinculacion, pais_proveedor, tasa_preferencial, id_agencia, id_usuario_creador) VALUES
|
||||
('Importación China', 'FOB, CNY, General, Sin vinculación', '🇨🇳', '{"tipo_moneda":"CNY","incoterm":"FOB","vinculacion":"0","pais_proveedor_texto":"China","tasa_preferencial":"General"}', 'CNY', 'FOB', 0, NULL, 'General', NULL, NULL),
|
||||
('Importación USA', 'FOB, USD, TLC, Sin vinculación', '🇺🇸', '{"tipo_moneda":"USD","incoterm":"FOB","vinculacion":"0","pais_proveedor_texto":"Estados Unidos","tasa_preferencial":"TLC"}', 'USD', 'FOB', 0, NULL, 'TLC', NULL, NULL),
|
||||
('Comercializadora', 'FOB, USD, COMERCIALIZADORA, Con vinculación', '🏢', '{"tipo_moneda":"USD","incoterm":"FOB","vinculacion":"2","pais_proveedor_texto":"Estados Unidos","tasa_preferencial":"COMERCIALIZADORA"}', 'USD', 'FOB', 2, NULL, 'COMERCIALIZADORA', NULL, NULL),
|
||||
('Importación Europa', 'FOB, EUR, General, Sin vinculación', '🇪🇺', '{"tipo_moneda":"EUR","incoterm":"FOB","vinculacion":"0","pais_proveedor_texto":"Alemania","tasa_preferencial":"General"}', 'EUR', 'FOB', 0, NULL, 'General', NULL, NULL),
|
||||
('PROSEC México', 'FOB, USD, PROSEC, Sin vinculación', '🏭', '{"tipo_moneda":"USD","incoterm":"FOB","vinculacion":"0","pais_proveedor_texto":"Estados Unidos","tasa_preferencial":"PROSEC"}', 'USD', 'FOB', 0, NULL, 'PROSEC', NULL, NULL);
|
||||
|
||||
-- Crear procedimiento almacenado para obtener templates
|
||||
GO
|
||||
CREATE PROCEDURE sp_obtener_templates_rapidos
|
||||
@id_usuario INT,
|
||||
@id_agencia INT = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SELECT
|
||||
id,
|
||||
nombre,
|
||||
descripcion,
|
||||
icono,
|
||||
config_json,
|
||||
veces_usado,
|
||||
ultima_vez_usado
|
||||
FROM dbo.templates_rapidos
|
||||
WHERE activo = 1
|
||||
AND (
|
||||
id_agencia IS NULL -- Templates globales
|
||||
OR id_agencia = @id_agencia -- Templates de la agencia
|
||||
OR id_usuario_creador = @id_usuario -- Templates del usuario
|
||||
)
|
||||
ORDER BY veces_usado DESC, nombre ASC;
|
||||
END
|
||||
|
||||
-- Crear procedimiento para incrementar uso de template
|
||||
GO
|
||||
CREATE PROCEDURE sp_usar_template_rapido
|
||||
@id_template INT,
|
||||
@id_usuario INT
|
||||
AS
|
||||
BEGIN
|
||||
UPDATE dbo.templates_rapidos
|
||||
SET veces_usado = veces_usado + 1,
|
||||
ultima_vez_usado = GETDATE()
|
||||
WHERE id = @id_template;
|
||||
|
||||
-- Opcional: Registrar en bitácora de uso
|
||||
INSERT INTO dbo.bitacoras (id_usuario, accion, tabla_afectada, id_registro, detalles, fecha_accion)
|
||||
VALUES (@id_usuario, 'USAR_TEMPLATE', 'templates_rapidos', @id_template, 'Template rápido utilizado', GETDATE());
|
||||
END
|
||||
@@ -1,157 +0,0 @@
|
||||
<?php
|
||||
// Script de diagnóstico para templates
|
||||
session_start();
|
||||
|
||||
// Simular una sesión válida para testing
|
||||
$_SESSION['usuario_id'] = 1; // Ajusta según tu usuario
|
||||
$_SESSION['id_agencia_en_uso'] = 1; // Ajusta según tu agencia
|
||||
|
||||
require_once __DIR__ . '/config/database.php';
|
||||
|
||||
echo "<h2>🔍 Diagnóstico de Templates</h2>";
|
||||
|
||||
try {
|
||||
$conn = getConnection();
|
||||
echo "✅ <strong>Conexión a base de datos:</strong> OK<br><br>";
|
||||
|
||||
// 1. Verificar si existe la tabla
|
||||
echo "<h3>1. Verificando tabla templates_rapidos:</h3>";
|
||||
$sql_check = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'templates_rapidos'";
|
||||
$stmt = sqlsrv_query($conn, $sql_check);
|
||||
|
||||
if ($stmt && sqlsrv_fetch_array($stmt)) {
|
||||
echo "✅ La tabla <code>templates_rapidos</code> existe<br><br>";
|
||||
|
||||
// 2. Verificar estructura de la tabla
|
||||
echo "<h3>2. Estructura de la tabla:</h3>";
|
||||
$sql_columns = "SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_NAME = 'templates_rapidos'
|
||||
ORDER BY ORDINAL_POSITION";
|
||||
$stmt_cols = sqlsrv_query($conn, $sql_columns);
|
||||
|
||||
echo "<table border='1' style='border-collapse: collapse; margin-bottom: 20px;'>";
|
||||
echo "<tr><th>Columna</th><th>Tipo</th><th>Nullable</th></tr>";
|
||||
|
||||
$columnas_encontradas = [];
|
||||
while ($col = sqlsrv_fetch_array($stmt_cols, SQLSRV_FETCH_ASSOC)) {
|
||||
$columnas_encontradas[] = $col['COLUMN_NAME'];
|
||||
echo "<tr>";
|
||||
echo "<td>" . $col['COLUMN_NAME'] . "</td>";
|
||||
echo "<td>" . $col['DATA_TYPE'] . "</td>";
|
||||
echo "<td>" . $col['IS_NULLABLE'] . "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
echo "</table>";
|
||||
|
||||
// 3. Verificar columnas específicas que usa el código
|
||||
echo "<h3>3. Verificando columnas requeridas:</h3>";
|
||||
$columnas_requeridas = ['id', 'nombre', 'descripcion', 'icono', 'config_json', 'veces_usado', 'activo', 'id_usuario_creador', 'id_agencia'];
|
||||
|
||||
foreach ($columnas_requeridas as $col) {
|
||||
if (in_array($col, $columnas_encontradas)) {
|
||||
echo "✅ Columna <code>$col</code>: Existe<br>";
|
||||
} else {
|
||||
echo "❌ Columna <code>$col</code>: <strong>NO EXISTE</strong><br>";
|
||||
}
|
||||
}
|
||||
|
||||
echo "<br>";
|
||||
|
||||
// 4. Contar registros
|
||||
echo "<h3>4. Contando registros:</h3>";
|
||||
$sql_count = "SELECT COUNT(*) as total FROM dbo.templates_rapidos";
|
||||
$stmt_count = sqlsrv_query($conn, $sql_count);
|
||||
|
||||
if ($stmt_count && $row = sqlsrv_fetch_array($stmt_count, SQLSRV_FETCH_ASSOC)) {
|
||||
echo "📊 Total de registros en la tabla: <strong>" . $row['total'] . "</strong><br>";
|
||||
|
||||
// 4.1 Contar activos
|
||||
$sql_active = "SELECT COUNT(*) as activos FROM dbo.templates_rapidos WHERE activo = 1";
|
||||
$stmt_active = sqlsrv_query($conn, $sql_active);
|
||||
if ($stmt_active && $row_active = sqlsrv_fetch_array($stmt_active, SQLSRV_FETCH_ASSOC)) {
|
||||
echo "✅ Registros activos: <strong>" . $row_active['activos'] . "</strong><br>";
|
||||
}
|
||||
}
|
||||
|
||||
echo "<br>";
|
||||
|
||||
// 5. Probar la consulta exacta del controlador
|
||||
echo "<h3>5. Probando consulta del controlador:</h3>";
|
||||
$id_usuario = $_SESSION['usuario_id'];
|
||||
$id_agencia = $_SESSION['id_agencia_en_uso'];
|
||||
|
||||
echo "👤 ID Usuario: $id_usuario<br>";
|
||||
echo "🏢 ID Agencia: $id_agencia<br><br>";
|
||||
|
||||
$sql_controller = "SELECT id, nombre, descripcion, icono, config_json, veces_usado
|
||||
FROM dbo.templates_rapidos
|
||||
WHERE activo = 1
|
||||
AND (id_agencia IS NULL OR id_agencia = ? OR id_usuario_creador = ?)
|
||||
ORDER BY veces_usado DESC, nombre ASC";
|
||||
|
||||
echo "<strong>SQL:</strong><br>";
|
||||
echo "<code>" . str_replace('?', "'$id_agencia', '$id_usuario'", $sql_controller) . "</code><br><br>";
|
||||
|
||||
$stmt_test = sqlsrv_query($conn, $sql_controller, [$id_agencia, $id_usuario]);
|
||||
|
||||
if ($stmt_test === false) {
|
||||
echo "❌ <strong>Error en la consulta:</strong><br>";
|
||||
$errors = sqlsrv_errors();
|
||||
foreach ($errors as $error) {
|
||||
echo "- " . $error['message'] . "<br>";
|
||||
}
|
||||
} else {
|
||||
$templates = [];
|
||||
$count = 0;
|
||||
|
||||
while ($row = sqlsrv_fetch_array($stmt_test, SQLSRV_FETCH_ASSOC)) {
|
||||
$count++;
|
||||
$templates[] = $row;
|
||||
if ($count <= 3) { // Mostrar solo los primeros 3 para no saturar
|
||||
echo "📋 Template $count: <strong>" . htmlspecialchars($row['nombre']) . "</strong><br>";
|
||||
}
|
||||
}
|
||||
|
||||
echo "<br>✅ <strong>Consulta exitosa. Total encontrados: $count templates</strong><br>";
|
||||
|
||||
if ($count === 0) {
|
||||
echo "<br>⚠️ <strong>No se encontraron templates. Posibles causas:</strong><br>";
|
||||
echo "1. No hay templates creados<br>";
|
||||
echo "2. Todos los templates están inactivos (activo = 0)<br>";
|
||||
echo "3. Los templates no pertenecen a tu usuario/agencia<br>";
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Mostrar algunos registros de ejemplo
|
||||
echo "<br><h3>6. Registros de muestra (últimos 5):</h3>";
|
||||
$sql_sample = "SELECT TOP 5 id, nombre, activo, id_usuario_creador, id_agencia
|
||||
FROM dbo.templates_rapidos
|
||||
ORDER BY id DESC";
|
||||
$stmt_sample = sqlsrv_query($conn, $sql_sample);
|
||||
|
||||
if ($stmt_sample) {
|
||||
echo "<table border='1' style='border-collapse: collapse;'>";
|
||||
echo "<tr><th>ID</th><th>Nombre</th><th>Activo</th><th>Usuario</th><th>Agencia</th></tr>";
|
||||
|
||||
while ($sample = sqlsrv_fetch_array($stmt_sample, SQLSRV_FETCH_ASSOC)) {
|
||||
echo "<tr>";
|
||||
echo "<td>" . $sample['id'] . "</td>";
|
||||
echo "<td>" . htmlspecialchars($sample['nombre']) . "</td>";
|
||||
echo "<td>" . ($sample['activo'] ? '✅' : '❌') . "</td>";
|
||||
echo "<td>" . ($sample['id_usuario_creador'] ?? 'NULL') . "</td>";
|
||||
echo "<td>" . ($sample['id_agencia'] ?? 'NULL') . "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
echo "</table>";
|
||||
}
|
||||
|
||||
} else {
|
||||
echo "❌ La tabla <code>templates_rapidos</code> <strong>NO EXISTE</strong><br>";
|
||||
echo "🔧 Necesitas crear la tabla primero.";
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ <strong>Error:</strong> " . $e->getMessage();
|
||||
}
|
||||
?>
|
||||
211
uploads/expedientes/pedimento_5/68f95df6cea0d_m3726414.262
Normal file
211
uploads/expedientes/pedimento_5/68f95df6cea0d_m3726414.262
Normal file
@@ -0,0 +1,211 @@
|
||||
500|1|3726|8015881|071||
|
||||
701|3726|8015881|071|A1|14092018|8015740|3726|071|A1|14092018|
|
||||
501|3726|8015881|071|1|R1|071||AIC9010115YA|NUAJ680527HCHXLS05|19.05110|0|0|0|0||1301.5|7|7|7|9|AUTOMOTRIZ E INDUSTRIAL DE CHIHUAHUA S.A DE C.V|PERIFERICO DE LA JUVENTUD||9537|31126|CHIHUAHUA||MEX|CAI040913544|0|0|0|0|0|
|
||||
502|8015881|DMT061227879||DON MIGUEL TRANSPORTE S. DE R.L. DE C.V.|MEX|3939|0|CALLE OCTAVIO PAZ COMPLEJO INDUSTRIAL CHIHUAHUA No. 140 C.P. 31136|
|
||||
516|8015881|3939|NTh18010691|
|
||||
504|8015881|3939|55|
|
||||
505|8015881|05092018|COVE182LOBNI1|DAP|USD|7883.65|7883.65|USA||59-2402583|SAP USA TRUCK & AUTO PARTS|NW 74 AVE||5301|33166|MIAMI|
|
||||
505|8015881|05092018|COVE182LOBOH3|DAP|USD|67.50|67.50|USA||59-2402583|SAP USA TRUCK & AUTO PARTS|NW 74 AVE||5301|33166|MIAMI|
|
||||
506|8015881|1|14092018|
|
||||
506|8015881|2|14092018|
|
||||
507|8015881|ED|04281805BT2H7|||
|
||||
507|8015881|ED|04361807X9OB3|||
|
||||
509|8015881|1|8.00000|7|
|
||||
509|8015881|15|240.00000|2|
|
||||
509|8015881|21|20.00000|2|
|
||||
510|8015881|1|0|1212|
|
||||
510|8015881|15|0|240|
|
||||
702|8015881|1|0|305|
|
||||
511|8015881|1|LOS DATOS DE IDENTIFICACION, LAS MARCAS, MODELOS Y NUMEROS DE SERIE SE DECLARAN EN RELACION ANEXA DE CONFORMIDAD CON EL|
|
||||
511|8015881|2|ART 36-A DE LA LEY ADUANERA EN VIGOR Y LA REGLA 3.1.7.|
|
||||
551|8015881|84133002|1||BOMBA DE AGUA|171.46667|5144|5144|270.00|30.000|6|30.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|1|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|1|MA||||
|
||||
556|8015881|84133002|1|3|16.0000000000|1|
|
||||
557|8015881|84133002|1|3|0|830|
|
||||
558|8015881|84133002|1|1|MARCA: T&J, ID: 11003|
|
||||
551|8015881|84133002|2||BOMBA DE AGUA|303.86667|9116|9116|478.50|30.000|6|30.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|2|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|2|MA||||
|
||||
556|8015881|84133002|2|3|16.0000000000|1|
|
||||
557|8015881|84133002|2|3|0|1470|
|
||||
558|8015881|84133002|2|1|MARCA: T&J, ID: 11010|
|
||||
551|8015881|84133002|3||BOMBA DE AGUA|159.85000|3197|3197|167.80|20.000|6|20.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|3|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|3|MA||||
|
||||
556|8015881|84133002|3|3|16.0000000000|1|
|
||||
557|8015881|84133002|3|3|0|516|
|
||||
558|8015881|84133002|3|1|MARCA: T&J, ID: 11032|
|
||||
551|8015881|84133002|4||BOMBA DE AGUA|241.55000|4831|4831|253.60|20.000|6|20.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|4|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|4|MA||||
|
||||
556|8015881|84133002|4|3|16.0000000000|1|
|
||||
557|8015881|84133002|4|3|0|779|
|
||||
558|8015881|84133002|4|1|MARCA: T&J, ID: 11184|
|
||||
551|8015881|84133002|5||BOMBA DE AGUA|152.96667|4589|4589|240.90|30.000|6|30.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|5|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|5|MA||||
|
||||
556|8015881|84133002|5|3|16.0000000000|1|
|
||||
557|8015881|84133002|5|3|0|740|
|
||||
558|8015881|84133002|5|1|MARCA: T&J, ID: 12115|
|
||||
551|8015881|84133002|6||BOMBA DE AGUA|142.90000|1429|1429|75.00|10.000|6|10.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|6|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|6|MA||||
|
||||
556|8015881|84133002|6|3|16.0000000000|1|
|
||||
557|8015881|84133002|6|3|0|230|
|
||||
558|8015881|84133002|6|1|MARCA: T&J, ID: 13126|
|
||||
551|8015881|84133002|7||BOMBA DE AGUA|180.40000|2706|2706|142.05|15.000|6|15.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|7|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|7|MA||||
|
||||
556|8015881|84133002|7|3|16.0000000000|1|
|
||||
557|8015881|84133002|7|3|0|437|
|
||||
558|8015881|84133002|7|1|MARCA: T&J, ID: 13165|
|
||||
551|8015881|84133002|8||BOMBA DE AGUA|135.45000|5418|5418|284.40|40.000|6|40.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|8|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|8|MA||||
|
||||
556|8015881|84133002|8|3|16.0000000000|1|
|
||||
557|8015881|84133002|8|3|0|874|
|
||||
558|8015881|84133002|8|1|MARCA: T&J, ID: 16206|
|
||||
551|8015881|84133002|9||BOMBA DE AGUA|133.35000|5334|5334|280.00|40.000|6|40.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|9|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|9|MA||||
|
||||
556|8015881|84133002|9|3|16.0000000000|1|
|
||||
557|8015881|84133002|9|3|0|860|
|
||||
558|8015881|84133002|9|1|MARCA: T&J, ID: 16214|
|
||||
551|8015881|84133002|10||BOMBA DE AGUA|157.18000|7859|7859|412.50|50.000|6|50.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|10|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|10|MA||||
|
||||
556|8015881|84133002|10|3|16.0000000000|1|
|
||||
557|8015881|84133002|10|3|0|1267|
|
||||
558|8015881|84133002|10|1|MARCA: T&J, ID: 16503|
|
||||
551|8015881|84133002|11||BOMBA DE AGUA|152.40000|4572|4572|240.00|30.000|6|30.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|11|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|11|MA||||
|
||||
556|8015881|84133002|11|3|16.0000000000|1|
|
||||
557|8015881|84133002|11|3|0|737|
|
||||
558|8015881|84133002|11|1|MARCA: T&J, ID: 18377|
|
||||
551|8015881|84133002|12||BOMBA DE AGUA|249.40000|4988|4988|261.80|20.000|6|20.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|12|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|12|MA||||
|
||||
556|8015881|84133002|12|3|16.0000000000|1|
|
||||
557|8015881|84133002|12|3|0|804|
|
||||
558|8015881|84133002|12|1|MARCA: T&J, ID: 22093|
|
||||
551|8015881|84133002|13||BOMBA DE AGUA|166.66667|2500|2500|131.25|15.000|6|15.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|13|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|13|MA||||
|
||||
556|8015881|84133002|13|3|16.0000000000|1|
|
||||
557|8015881|84133002|13|3|0|403|
|
||||
558|8015881|84133002|13|1|MARCA: T&J, ID: 23300|
|
||||
551|8015881|84133002|14||BOMBA DE AGUA|129.36667|3881|3881|203.70|30.000|6|30.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|14|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|14|MA||||
|
||||
556|8015881|84133002|14|3|16.0000000000|1|
|
||||
557|8015881|84133002|14|3|0|626|
|
||||
558|8015881|84133002|14|1|MARCA: T&J, ID: 28065|
|
||||
551|8015881|84133002|15||BOMBA DE AGUA|389.58824|13246|13246|695.30|34.000|6|34.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|15|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|15|MA||||
|
||||
556|8015881|84133002|15|3|16.0000000000|1|
|
||||
557|8015881|84133002|15|3|0|2136|
|
||||
558|8015881|84133002|15|1|MARCA: T&J, ID: 31010|
|
||||
551|8015881|84133002|16||BOMBA DE AGUA|233.40000|3501|3501|183.75|15.000|6|15.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|16|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|16|MA||||
|
||||
556|8015881|84133002|16|3|16.0000000000|1|
|
||||
557|8015881|84133002|16|3|0|565|
|
||||
558|8015881|84133002|16|1|MARCA: T&J, ID: 32066|
|
||||
551|8015881|84133002|17||BOMBA DE AGUA|266.70000|8001|8001|420.00|30.000|6|30.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|17|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|17|MA||||
|
||||
556|8015881|84133002|17|3|16.0000000000|1|
|
||||
557|8015881|84133002|17|3|0|1290|
|
||||
558|8015881|84133002|17|1|MARCA: T&J, ID: 32102|
|
||||
551|8015881|84133002|18||BOMBA DE AGUA|325.76667|9773|9773|513.00|30.000|6|30.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|18|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|18|MA||||
|
||||
556|8015881|84133002|18|3|16.0000000000|1|
|
||||
557|8015881|84133002|18|3|0|1576|
|
||||
558|8015881|84133002|18|1|MARCA: T&J, ID: 41077|
|
||||
551|8015881|84133002|19||BOMBA DE AGUA|303.85000|6077|6077|319.00|20.000|6|20.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|19|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|19|MA||||
|
||||
556|8015881|84133002|19|3|16.0000000000|1|
|
||||
557|8015881|84133002|19|3|0|980|
|
||||
558|8015881|84133002|19|1|MARCA: T&J, ID: 42095|
|
||||
551|8015881|84133002|20||BOMBA DE AGUA|360.05000|7201|7201|378.00|20.000|6|20.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|20|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|20|MA||||
|
||||
556|8015881|84133002|20|3|16.0000000000|1|
|
||||
557|8015881|84133002|20|3|0|1161|
|
||||
558|8015881|84133002|20|1|MARCA: T&J, ID: 42108|
|
||||
551|8015881|84133002|21||BOMBA DE AGUA|321.00000|9630|9630|505.50|30.000|6|30.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|21|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|21|MA||||
|
||||
556|8015881|84133002|21|3|16.0000000000|1|
|
||||
557|8015881|84133002|21|3|0|1553|
|
||||
558|8015881|84133002|21|1|MARCA: T&J, ID: 43163|
|
||||
551|8015881|84133002|22||BOMBA DE AGUA|152.40000|3048|3048|160.00|20.000|6|20.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|22|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|22|MA||||
|
||||
556|8015881|84133002|22|3|16.0000000000|1|
|
||||
557|8015881|84133002|22|3|0|492|
|
||||
558|8015881|84133002|22|1|MARCA: T&J, ID: 11001|
|
||||
551|8015881|84133002|23||BOMBA DE AGUA|144.60000|1446|1446|75.90|10.000|6|10.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|23|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|23|MA||||
|
||||
556|8015881|84133002|23|3|16.0000000000|1|
|
||||
557|8015881|84133002|23|3|0|233|
|
||||
558|8015881|84133002|23|1|MARCA: T&J, ID: 11033|
|
||||
551|8015881|84133002|24||BOMBA DE AGUA|121.53333|1823|1823|95.70|15.000|6|15.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|24|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|24|MA||||
|
||||
556|8015881|84133002|24|3|16.0000000000|1|
|
||||
557|8015881|84133002|24|3|0|294|
|
||||
558|8015881|84133002|24|1|MARCA: T&J, ID: 11057|
|
||||
551|8015881|84133002|25||BOMBA DE AGUA|171.50000|1715|1715|90.00|10.000|6|10.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|25|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|25|MA||||
|
||||
556|8015881|84133002|25|3|16.0000000000|1|
|
||||
557|8015881|84133002|25|3|0|277|
|
||||
558|8015881|84133002|25|1|MARCA: T&J, ID: 26800|
|
||||
551|8015881|84133002|26||BOMBA DE AGUA|846.10000|8461|8461|444.10|10.000|6|10.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|26|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|26|MA||||
|
||||
556|8015881|84133002|26|3|16.0000000000|1|
|
||||
557|8015881|84133002|26|3|0|1365|
|
||||
558|8015881|84133002|26|1|MARCA: T&J, ID: 71616|
|
||||
551|8015881|84133002|27||BOMBA DE AGUA|923.00000|9230|9230|484.50|10.000|6|10.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|27|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|27|MA||||
|
||||
556|8015881|84133002|27|3|16.0000000000|1|
|
||||
557|8015881|84133002|27|3|0|1489|
|
||||
558|8015881|84133002|27|1|MARCA: T&J, ID: 71617|
|
||||
551|8015881|84133002|28||BOMBA DE AGUA|147.50000|1475|1475|77.40|10.000|6|10.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|28|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|28|MA||||
|
||||
556|8015881|84133002|28|3|16.0000000000|1|
|
||||
557|8015881|84133002|28|3|0|238|
|
||||
558|8015881|84133002|28|1|MARCA: T&J, ID: 82001|
|
||||
551|8015881|49111099|29||CATALOGOS|19.06000|953|953|50.00|50.000|6|12.50000|1||0|1||||CHN|USA|||||
|
||||
554|8015881|49111099|29|MA||||
|
||||
556|8015881|49111099|29|3|16.0000000000|1|
|
||||
556|8015881|49111099|29|6|15.0000000000|1|
|
||||
557|8015881|49111099|29|3|0|177|
|
||||
557|8015881|49111099|29|6|0|143|
|
||||
558|8015881|49111099|29|1|MARCA: T&J, ID: TJ-CWP02|
|
||||
551|8015881|48211001|30||ETIQUETAS|4.76000|238|238|12.50|50.000|6|50.00000|1||0|1||||CHN|USA|||||
|
||||
554|8015881|48211001|30|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|48211001|30|MA||||
|
||||
556|8015881|48211001|30|3|16.0000000000|1|
|
||||
557|8015881|48211001|30|3|0|38|
|
||||
558|8015881|48211001|30|1|MARCA: T&J, ID: TJ-SR01|
|
||||
551|8015881|49119199|31||POSTER|9.50000|95|95|5.00|10.000|6|0.50000|1||0|1||||CHN|USA|||||
|
||||
554|8015881|49119199|31|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|49119199|31|MA||||
|
||||
556|8015881|49119199|31|3|16.0000000000|1|
|
||||
556|8015881|49119199|31|6|15.0000000000|1|
|
||||
557|8015881|49119199|31|3|0|18|
|
||||
557|8015881|49119199|31|6|0|14|
|
||||
558|8015881|49119199|31|1|MARCA: T&J, ID: TJ-CWP04|
|
||||
800|8015881|1|ITpVy/oo8quhd9LbXLHh+XXw1615VFZ9BH20k4l+whJeWsiieHmDsqmhGcCoAN82Relm1GazjOhNREeu7qkp27AKT/VQxl3RqoHZfhXGlB/Wob2pqCAqzXXSKMzcNhIxawoViRGwPcxncLhWjP/xZsiUvvFgT9afYVDEtGsaxA0MubY+/w5feBm8Ny3Nj50LfXkeNiZlBshsuCUy7XDAktm3aguSFeCUDipmLp0XHMWPDns14WaHsUYqYw8bOAk0KNxl9C7jyd5UJAZpfruzC4sPnI/r0LKMBw46sdE//1t2rSxUjbn7/Z10sTwRtz+HbqT3h2b1hKKmuPebgVcryg==|00001000000517049032|
|
||||
801|m3726414.262|1|210|010|
|
||||
52
uploads/expedientes/pedimento_5/68f96d9552e13_m3726413.261
Normal file
52
uploads/expedientes/pedimento_5/68f96d9552e13_m3726413.261
Normal file
@@ -0,0 +1,52 @@
|
||||
500|1|3726|1010560|071||
|
||||
501|3726|1010560|071|1|A1|071||OIM060801GH5|NUAJ680527HCHXLS05|21.16500|0|0|0|0||27|7|7|7|9|OPERADORA IPC DE MEXICO S.A DE C.V.|LAZARO CARDENAS||999|64780|MONTERREY||MEX|CAI040913544|0|0|0|0|0|
|
||||
505|1010560|02112021|COVE214UAW8T3|EXW|USD|2410.95|2410.95|USA||93-1047803|RIDE DEVELOPMENT COMPANY|INDEPENDENCE HWY||4770|97351|OREGON|
|
||||
506|1010560|1|16122021|
|
||||
506|1010560|2|16122021|
|
||||
509|1010560|15|240.00000|2|
|
||||
509|1010560|23|16.00000|1|
|
||||
510|1010560|15|0|240|
|
||||
510|1010560|23|0|38|
|
||||
511|1010560|1|SIN PRECINTO FISCAL POR DE CONFORMIDAD CON EL ARTICULO 65 DEL REGLAMENTO DE LA LEY ADUANERA DEL PRESENTE COVE214U5Z1P6|
|
||||
511|1010560|2|NO SE IMPORTAN 2 PIEZAS CON UN VALOR DE $577.50 DLLS. QUEDANDO LA FACTURA CON UN VALOR TOTAL DE $1,833.45 DLLS.|
|
||||
551|1010560|63079099|1|00|CUBIERTA TEXTIL PARA JUEGO MECANICO|6111.50000|12223|12223|577.50|2.000|6|2.00000|1||0|1||||USA|USA|||||
|
||||
553|1010560|63079099|1|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1010560|63079099|1|EX||||
|
||||
554|1010560|63079099|1|TL|USA|||
|
||||
554|1010560|63079099|1|PO|12223|RIDE DEVELOPMENT COMPANY|26949|
|
||||
556|1010560|63079099|1|3|16.0000000000|1|
|
||||
557|1010560|63079099|1|3|0|1956|
|
||||
551|1010560|84136099|2|99|BOMBA HIDRAULICA|13001.00000|13001|13001|614.25|1.000|6|1.00000|6||0|1||||USA|USA|||||
|
||||
554|1010560|84136099|2|EN|U|NOM-004-ENER-2014||
|
||||
554|1010560|84136099|2|TL|USA|||
|
||||
554|1010560|84136099|2|PO|13001|RIDE DEVELOPMENT COMPANY|26949|
|
||||
554|1010560|84136099|2|ES|N|||
|
||||
554|1010560|84136099|2|MA||||
|
||||
556|1010560|84136099|2|3|16.0000000000|1|
|
||||
557|1010560|84136099|2|3|0|2080|
|
||||
558|1010560|84136099|2|1|Marca: EATON Modelo: 700-000 Serie: 0421006497|
|
||||
551|1010560|84136099|3|99|BOMBA HIDRAULICA|13001.00000|13001|13001|614.25|1.000|6|1.00000|6||0|1||||USA|USA|||||
|
||||
554|1010560|84136099|3|EN|U|NOM-004-ENER-2014||
|
||||
554|1010560|84136099|3|TL|USA|||
|
||||
554|1010560|84136099|3|PO|13001|RIDE DEVELOPMENT COMPANY|26949|
|
||||
554|1010560|84136099|3|ES|N|||
|
||||
554|1010560|84136099|3|MA||||
|
||||
556|1010560|84136099|3|3|16.0000000000|1|
|
||||
557|1010560|84136099|3|3|0|2080|
|
||||
558|1010560|84136099|3|1|Marca: EATON Modelo: 700-001 Serie: 0421006645|
|
||||
551|1010560|85452001|4|00|ESCOBILLA DE CARBON ID: KCE2-054B|186.25000|4470|4470|211.20|24.000|6|1.00000|1||0|1||||USA|USA|||||
|
||||
554|1010560|85452001|4|TL|USA|||
|
||||
554|1010560|85452001|4|PO|4470|RIDE DEVELOPMENT COMPANY|211102-1r|
|
||||
554|1010560|85452001|4|ES|N|||
|
||||
554|1010560|85452001|4|MA||||
|
||||
556|1010560|85452001|4|3|16.0000000000|1|
|
||||
557|1010560|85452001|4|3|0|715|
|
||||
551|1010560|84139113|5|99|GUIA METALICA PARA BOMBA ID: R4010|8334.00000|8334|8334|393.75|1.000|6|4.00000|1||0|1||||USA|USA|||||
|
||||
554|1010560|84139113|5|TL|USA|||
|
||||
554|1010560|84139113|5|PO|8334|RIDE DEVELOPMENT COMPANY|26949|
|
||||
554|1010560|84139113|5|ES|N|||
|
||||
554|1010560|84139113|5|MA||||
|
||||
556|1010560|84139113|5|3|16.0000000000|1|
|
||||
557|1010560|84139113|5|3|0|1333|
|
||||
800|1010560|1|enhVX2Pyk1PCqd/rl8l1mL32esx5yd51Zky9EmDVyAa2x3di6g6T17dCu+H/eaHZxP6XVQ7CnHuY6FL9ZKSXjrf/v2nsk98w8kezryrVI345bcfZSStF2KTYYQ30C7/fFZ4x3ga57N1ObhKWzkF9pzC88jR1IR1LFqn7VUnY4/BuZ75cfl8CKW5DlsmN1efeqMpK9fGoN4cTddEst7enI+U7xuevkPDcGaprL4pODtQBgLpxYgHWWfr9oap8rGJ2KAw9+a/K6ugEurouD80121p25Y9nu+B4p+8k67CCx2kw41bHo2gNArjAmDpCzfNrX9Iht2apU2EXrp83l9f5mQ==|00001000000517049032|
|
||||
801|m3726413.261|1|51|010|
|
||||
211
uploads/expedientes/pedimento_5/68f96d95539d4_m3726414.262
Normal file
211
uploads/expedientes/pedimento_5/68f96d95539d4_m3726414.262
Normal file
@@ -0,0 +1,211 @@
|
||||
500|1|3726|8015881|071||
|
||||
701|3726|8015881|071|A1|14092018|8015740|3726|071|A1|14092018|
|
||||
501|3726|8015881|071|1|R1|071||AIC9010115YA|NUAJ680527HCHXLS05|19.05110|0|0|0|0||1301.5|7|7|7|9|AUTOMOTRIZ E INDUSTRIAL DE CHIHUAHUA S.A DE C.V|PERIFERICO DE LA JUVENTUD||9537|31126|CHIHUAHUA||MEX|CAI040913544|0|0|0|0|0|
|
||||
502|8015881|DMT061227879||DON MIGUEL TRANSPORTE S. DE R.L. DE C.V.|MEX|3939|0|CALLE OCTAVIO PAZ COMPLEJO INDUSTRIAL CHIHUAHUA No. 140 C.P. 31136|
|
||||
516|8015881|3939|NTh18010691|
|
||||
504|8015881|3939|55|
|
||||
505|8015881|05092018|COVE182LOBNI1|DAP|USD|7883.65|7883.65|USA||59-2402583|SAP USA TRUCK & AUTO PARTS|NW 74 AVE||5301|33166|MIAMI|
|
||||
505|8015881|05092018|COVE182LOBOH3|DAP|USD|67.50|67.50|USA||59-2402583|SAP USA TRUCK & AUTO PARTS|NW 74 AVE||5301|33166|MIAMI|
|
||||
506|8015881|1|14092018|
|
||||
506|8015881|2|14092018|
|
||||
507|8015881|ED|04281805BT2H7|||
|
||||
507|8015881|ED|04361807X9OB3|||
|
||||
509|8015881|1|8.00000|7|
|
||||
509|8015881|15|240.00000|2|
|
||||
509|8015881|21|20.00000|2|
|
||||
510|8015881|1|0|1212|
|
||||
510|8015881|15|0|240|
|
||||
702|8015881|1|0|305|
|
||||
511|8015881|1|LOS DATOS DE IDENTIFICACION, LAS MARCAS, MODELOS Y NUMEROS DE SERIE SE DECLARAN EN RELACION ANEXA DE CONFORMIDAD CON EL|
|
||||
511|8015881|2|ART 36-A DE LA LEY ADUANERA EN VIGOR Y LA REGLA 3.1.7.|
|
||||
551|8015881|84133002|1||BOMBA DE AGUA|171.46667|5144|5144|270.00|30.000|6|30.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|1|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|1|MA||||
|
||||
556|8015881|84133002|1|3|16.0000000000|1|
|
||||
557|8015881|84133002|1|3|0|830|
|
||||
558|8015881|84133002|1|1|MARCA: T&J, ID: 11003|
|
||||
551|8015881|84133002|2||BOMBA DE AGUA|303.86667|9116|9116|478.50|30.000|6|30.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|2|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|2|MA||||
|
||||
556|8015881|84133002|2|3|16.0000000000|1|
|
||||
557|8015881|84133002|2|3|0|1470|
|
||||
558|8015881|84133002|2|1|MARCA: T&J, ID: 11010|
|
||||
551|8015881|84133002|3||BOMBA DE AGUA|159.85000|3197|3197|167.80|20.000|6|20.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|3|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|3|MA||||
|
||||
556|8015881|84133002|3|3|16.0000000000|1|
|
||||
557|8015881|84133002|3|3|0|516|
|
||||
558|8015881|84133002|3|1|MARCA: T&J, ID: 11032|
|
||||
551|8015881|84133002|4||BOMBA DE AGUA|241.55000|4831|4831|253.60|20.000|6|20.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|4|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|4|MA||||
|
||||
556|8015881|84133002|4|3|16.0000000000|1|
|
||||
557|8015881|84133002|4|3|0|779|
|
||||
558|8015881|84133002|4|1|MARCA: T&J, ID: 11184|
|
||||
551|8015881|84133002|5||BOMBA DE AGUA|152.96667|4589|4589|240.90|30.000|6|30.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|5|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|5|MA||||
|
||||
556|8015881|84133002|5|3|16.0000000000|1|
|
||||
557|8015881|84133002|5|3|0|740|
|
||||
558|8015881|84133002|5|1|MARCA: T&J, ID: 12115|
|
||||
551|8015881|84133002|6||BOMBA DE AGUA|142.90000|1429|1429|75.00|10.000|6|10.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|6|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|6|MA||||
|
||||
556|8015881|84133002|6|3|16.0000000000|1|
|
||||
557|8015881|84133002|6|3|0|230|
|
||||
558|8015881|84133002|6|1|MARCA: T&J, ID: 13126|
|
||||
551|8015881|84133002|7||BOMBA DE AGUA|180.40000|2706|2706|142.05|15.000|6|15.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|7|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|7|MA||||
|
||||
556|8015881|84133002|7|3|16.0000000000|1|
|
||||
557|8015881|84133002|7|3|0|437|
|
||||
558|8015881|84133002|7|1|MARCA: T&J, ID: 13165|
|
||||
551|8015881|84133002|8||BOMBA DE AGUA|135.45000|5418|5418|284.40|40.000|6|40.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|8|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|8|MA||||
|
||||
556|8015881|84133002|8|3|16.0000000000|1|
|
||||
557|8015881|84133002|8|3|0|874|
|
||||
558|8015881|84133002|8|1|MARCA: T&J, ID: 16206|
|
||||
551|8015881|84133002|9||BOMBA DE AGUA|133.35000|5334|5334|280.00|40.000|6|40.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|9|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|9|MA||||
|
||||
556|8015881|84133002|9|3|16.0000000000|1|
|
||||
557|8015881|84133002|9|3|0|860|
|
||||
558|8015881|84133002|9|1|MARCA: T&J, ID: 16214|
|
||||
551|8015881|84133002|10||BOMBA DE AGUA|157.18000|7859|7859|412.50|50.000|6|50.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|10|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|10|MA||||
|
||||
556|8015881|84133002|10|3|16.0000000000|1|
|
||||
557|8015881|84133002|10|3|0|1267|
|
||||
558|8015881|84133002|10|1|MARCA: T&J, ID: 16503|
|
||||
551|8015881|84133002|11||BOMBA DE AGUA|152.40000|4572|4572|240.00|30.000|6|30.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|11|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|11|MA||||
|
||||
556|8015881|84133002|11|3|16.0000000000|1|
|
||||
557|8015881|84133002|11|3|0|737|
|
||||
558|8015881|84133002|11|1|MARCA: T&J, ID: 18377|
|
||||
551|8015881|84133002|12||BOMBA DE AGUA|249.40000|4988|4988|261.80|20.000|6|20.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|12|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|12|MA||||
|
||||
556|8015881|84133002|12|3|16.0000000000|1|
|
||||
557|8015881|84133002|12|3|0|804|
|
||||
558|8015881|84133002|12|1|MARCA: T&J, ID: 22093|
|
||||
551|8015881|84133002|13||BOMBA DE AGUA|166.66667|2500|2500|131.25|15.000|6|15.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|13|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|13|MA||||
|
||||
556|8015881|84133002|13|3|16.0000000000|1|
|
||||
557|8015881|84133002|13|3|0|403|
|
||||
558|8015881|84133002|13|1|MARCA: T&J, ID: 23300|
|
||||
551|8015881|84133002|14||BOMBA DE AGUA|129.36667|3881|3881|203.70|30.000|6|30.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|14|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|14|MA||||
|
||||
556|8015881|84133002|14|3|16.0000000000|1|
|
||||
557|8015881|84133002|14|3|0|626|
|
||||
558|8015881|84133002|14|1|MARCA: T&J, ID: 28065|
|
||||
551|8015881|84133002|15||BOMBA DE AGUA|389.58824|13246|13246|695.30|34.000|6|34.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|15|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|15|MA||||
|
||||
556|8015881|84133002|15|3|16.0000000000|1|
|
||||
557|8015881|84133002|15|3|0|2136|
|
||||
558|8015881|84133002|15|1|MARCA: T&J, ID: 31010|
|
||||
551|8015881|84133002|16||BOMBA DE AGUA|233.40000|3501|3501|183.75|15.000|6|15.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|16|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|16|MA||||
|
||||
556|8015881|84133002|16|3|16.0000000000|1|
|
||||
557|8015881|84133002|16|3|0|565|
|
||||
558|8015881|84133002|16|1|MARCA: T&J, ID: 32066|
|
||||
551|8015881|84133002|17||BOMBA DE AGUA|266.70000|8001|8001|420.00|30.000|6|30.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|17|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|17|MA||||
|
||||
556|8015881|84133002|17|3|16.0000000000|1|
|
||||
557|8015881|84133002|17|3|0|1290|
|
||||
558|8015881|84133002|17|1|MARCA: T&J, ID: 32102|
|
||||
551|8015881|84133002|18||BOMBA DE AGUA|325.76667|9773|9773|513.00|30.000|6|30.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|18|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|18|MA||||
|
||||
556|8015881|84133002|18|3|16.0000000000|1|
|
||||
557|8015881|84133002|18|3|0|1576|
|
||||
558|8015881|84133002|18|1|MARCA: T&J, ID: 41077|
|
||||
551|8015881|84133002|19||BOMBA DE AGUA|303.85000|6077|6077|319.00|20.000|6|20.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|19|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|19|MA||||
|
||||
556|8015881|84133002|19|3|16.0000000000|1|
|
||||
557|8015881|84133002|19|3|0|980|
|
||||
558|8015881|84133002|19|1|MARCA: T&J, ID: 42095|
|
||||
551|8015881|84133002|20||BOMBA DE AGUA|360.05000|7201|7201|378.00|20.000|6|20.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|20|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|20|MA||||
|
||||
556|8015881|84133002|20|3|16.0000000000|1|
|
||||
557|8015881|84133002|20|3|0|1161|
|
||||
558|8015881|84133002|20|1|MARCA: T&J, ID: 42108|
|
||||
551|8015881|84133002|21||BOMBA DE AGUA|321.00000|9630|9630|505.50|30.000|6|30.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|21|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|21|MA||||
|
||||
556|8015881|84133002|21|3|16.0000000000|1|
|
||||
557|8015881|84133002|21|3|0|1553|
|
||||
558|8015881|84133002|21|1|MARCA: T&J, ID: 43163|
|
||||
551|8015881|84133002|22||BOMBA DE AGUA|152.40000|3048|3048|160.00|20.000|6|20.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|22|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|22|MA||||
|
||||
556|8015881|84133002|22|3|16.0000000000|1|
|
||||
557|8015881|84133002|22|3|0|492|
|
||||
558|8015881|84133002|22|1|MARCA: T&J, ID: 11001|
|
||||
551|8015881|84133002|23||BOMBA DE AGUA|144.60000|1446|1446|75.90|10.000|6|10.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|23|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|23|MA||||
|
||||
556|8015881|84133002|23|3|16.0000000000|1|
|
||||
557|8015881|84133002|23|3|0|233|
|
||||
558|8015881|84133002|23|1|MARCA: T&J, ID: 11033|
|
||||
551|8015881|84133002|24||BOMBA DE AGUA|121.53333|1823|1823|95.70|15.000|6|15.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|24|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|24|MA||||
|
||||
556|8015881|84133002|24|3|16.0000000000|1|
|
||||
557|8015881|84133002|24|3|0|294|
|
||||
558|8015881|84133002|24|1|MARCA: T&J, ID: 11057|
|
||||
551|8015881|84133002|25||BOMBA DE AGUA|171.50000|1715|1715|90.00|10.000|6|10.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|25|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|25|MA||||
|
||||
556|8015881|84133002|25|3|16.0000000000|1|
|
||||
557|8015881|84133002|25|3|0|277|
|
||||
558|8015881|84133002|25|1|MARCA: T&J, ID: 26800|
|
||||
551|8015881|84133002|26||BOMBA DE AGUA|846.10000|8461|8461|444.10|10.000|6|10.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|26|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|26|MA||||
|
||||
556|8015881|84133002|26|3|16.0000000000|1|
|
||||
557|8015881|84133002|26|3|0|1365|
|
||||
558|8015881|84133002|26|1|MARCA: T&J, ID: 71616|
|
||||
551|8015881|84133002|27||BOMBA DE AGUA|923.00000|9230|9230|484.50|10.000|6|10.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|27|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|27|MA||||
|
||||
556|8015881|84133002|27|3|16.0000000000|1|
|
||||
557|8015881|84133002|27|3|0|1489|
|
||||
558|8015881|84133002|27|1|MARCA: T&J, ID: 71617|
|
||||
551|8015881|84133002|28||BOMBA DE AGUA|147.50000|1475|1475|77.40|10.000|6|10.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015881|84133002|28|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|84133002|28|MA||||
|
||||
556|8015881|84133002|28|3|16.0000000000|1|
|
||||
557|8015881|84133002|28|3|0|238|
|
||||
558|8015881|84133002|28|1|MARCA: T&J, ID: 82001|
|
||||
551|8015881|49111099|29||CATALOGOS|19.06000|953|953|50.00|50.000|6|12.50000|1||0|1||||CHN|USA|||||
|
||||
554|8015881|49111099|29|MA||||
|
||||
556|8015881|49111099|29|3|16.0000000000|1|
|
||||
556|8015881|49111099|29|6|15.0000000000|1|
|
||||
557|8015881|49111099|29|3|0|177|
|
||||
557|8015881|49111099|29|6|0|143|
|
||||
558|8015881|49111099|29|1|MARCA: T&J, ID: TJ-CWP02|
|
||||
551|8015881|48211001|30||ETIQUETAS|4.76000|238|238|12.50|50.000|6|50.00000|1||0|1||||CHN|USA|||||
|
||||
554|8015881|48211001|30|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|48211001|30|MA||||
|
||||
556|8015881|48211001|30|3|16.0000000000|1|
|
||||
557|8015881|48211001|30|3|0|38|
|
||||
558|8015881|48211001|30|1|MARCA: T&J, ID: TJ-SR01|
|
||||
551|8015881|49119199|31||POSTER|9.50000|95|95|5.00|10.000|6|0.50000|1||0|1||||CHN|USA|||||
|
||||
554|8015881|49119199|31|EN|VIII|NOM-050-SCFI-2004||
|
||||
554|8015881|49119199|31|MA||||
|
||||
556|8015881|49119199|31|3|16.0000000000|1|
|
||||
556|8015881|49119199|31|6|15.0000000000|1|
|
||||
557|8015881|49119199|31|3|0|18|
|
||||
557|8015881|49119199|31|6|0|14|
|
||||
558|8015881|49119199|31|1|MARCA: T&J, ID: TJ-CWP04|
|
||||
800|8015881|1|ITpVy/oo8quhd9LbXLHh+XXw1615VFZ9BH20k4l+whJeWsiieHmDsqmhGcCoAN82Relm1GazjOhNREeu7qkp27AKT/VQxl3RqoHZfhXGlB/Wob2pqCAqzXXSKMzcNhIxawoViRGwPcxncLhWjP/xZsiUvvFgT9afYVDEtGsaxA0MubY+/w5feBm8Ny3Nj50LfXkeNiZlBshsuCUy7XDAktm3aguSFeCUDipmLp0XHMWPDns14WaHsUYqYw8bOAk0KNxl9C7jyd5UJAZpfruzC4sPnI/r0LKMBw46sdE//1t2rSxUjbn7/Z10sTwRtz+HbqT3h2b1hKKmuPebgVcryg==|00001000000517049032|
|
||||
801|m3726414.262|1|210|010|
|
||||
19
uploads/expedientes/pedimento_5/68f96d9554188_m3726428.262
Normal file
19
uploads/expedientes/pedimento_5/68f96d9554188_m3726428.262
Normal file
@@ -0,0 +1,19 @@
|
||||
500|1|3726|8015737|071||
|
||||
501|3726|8015737|071|1|A1|071||MMT110415BT6|NUAJ680527HCHXLS05|19.26800|0|0|3112|0||21313|7|7|7|9|MANSOUR MINING TECHNOLOGIES DE MEXICO S.A. DE C.V.|RUDYARD KIPLING||11311|31136|CHIHUAHUA||MEX|CAI040913544|0|0|0|0|0|
|
||||
505|8015737|11092018|COVE257QJTYU2|DAP|USD|36934.50|36934.50|USA||74-1871394|OBRIEN WIRE PRODUCTS|ALDINE WESTFIELD RD.||12800-A|77039|HOUSTON|
|
||||
506|8015737|1|13092018|
|
||||
506|8015737|2|13092018|
|
||||
509|8015737|15|240.00000|2|
|
||||
509|8015737|21|20.00000|2|
|
||||
510|8015737|15|0|240|
|
||||
511|8015737|1|SIN PRECINTO FISCAL POR|
|
||||
511|8015737|2|SE ANEXA FACTURA CON CONCEPTO DE INCREMENTABLES CON NUMERO: 182801|
|
||||
551|8015737|73143101|1||SECCIONES DE MALLA GALVANIZADA DE 6 PIES (183 cm) DE ANCHO POR 10 PIES (305 cm) DE LARGO CADA SECCION|33.39061|714766|711654|36934.50|21313.000|1|21313.00000|1||0|1||||USA|USA|||||
|
||||
553|8015737|73143101|1|C1||0814C118119732|36934.50|21313.00000|
|
||||
554|8015737|73143101|1|TL|USA|||
|
||||
554|8015737|73143101|1|PO|714766|OBRIEN WIRE PRODUCTS|44398|
|
||||
554|8015737|73143101|1|MA||||
|
||||
556|8015737|73143101|1|3|16.0000000000|1|
|
||||
557|8015737|73143101|1|3|0|114363|
|
||||
800|8015737|1|dq6wyhhSOukJ5bPyqbGqjxGUvArga/mUPXUzrjWM43XKrOJlNE3z2r3WKMviyxbMmZgQc1WkbKXdCB07mSAeA31xSSAXgZKzCD79OOW7eyvyH6dqsqUOL+ZTj4h5sJOjdQr9gmN53OkNtsvY3/lCGRqCvWpN+fc+B/bjoELIEt9pj/2kNSbfGWMq/uxkdFNWL00P+dTgDUmCy8POe59g23cTOYvXAs4M6SZEsL2Y/5r3Prw3Ypon1lYEjwQeY5RvjRLeEM/gp1A6SL+V16hkftAS/WUPtxvZ7BB5yd2BJdm3mN/sSEGCLfU9IfHxLeli8gjdUDOWNgsvVz53wNyuXg==|00001000000517049032|
|
||||
801|m3726428.262|1|18|010|
|
||||
65
uploads/expedientes/pedimento_5/68f96d9554612_m3726435.267
Normal file
65
uploads/expedientes/pedimento_5/68f96d9554612_m3726435.267
Normal file
@@ -0,0 +1,65 @@
|
||||
500|1|3726|8015877|070||
|
||||
501|3726|8015877|070|1|A1|070||MMI150217CCA|NUAJ680527HCHXLS05|17.78100|0|0|0|0||0|7|7|7|7|MULTISERVICIOS MIKZA S. DE R.L. DE C.V|PEDRO ROSALES DE LEON|A|7361|32500|JUAREZ||MEX|CAI040913544|0|0|0|0|0|
|
||||
505|8015877|07092018|COVE257R06QR7|DAP|USD|6685.20|6685.20|USA||03-0591335|GLOBAL GLOVE & SAFETY MANUFACTURING, INC.|RADIUM ST NW STE A||13915|55303|RAMSEY|
|
||||
506|8015877|1|14092017|
|
||||
506|8015877|2|14092017|
|
||||
509|8015877|1|8.00000|7|
|
||||
509|8015877|15|210.00000|2|
|
||||
509|8015877|21|20.00000|2|
|
||||
510|8015877|1|0|951|
|
||||
510|8015877|15|0|210|
|
||||
510|8015877|21|0|57|
|
||||
551|8015877|90049099|1||LENTES DE SEGURIDAD No BH2254AF|32.89583|4737|4737|266.40|144.000|6|144.00000|6||0|1||||TWN|USA|||||
|
||||
554|8015877|90049099|1|MA||||
|
||||
551|8015877|90049099|2||LENTES DE SEGURIDAD No BH22108AF|48.01042|13827|13827|777.60|288.000|6|288.00000|6||0|1||||TWN|USA|||||
|
||||
554|8015877|90049099|2|MA||||
|
||||
551|8015877|90049099|3||LENTES DE SEGURIDAD No BH2213AF|48.01042|13827|13827|777.60|288.000|6|288.00000|6||0|1||||TWN|USA|||||
|
||||
554|8015877|90049099|3|MA||||
|
||||
551|8015877|90049099|4||LENTES DE SEGURIDAD No BH2253AF|31.82639|9166|9166|515.52|288.000|6|288.00000|6||0|1||||TWN|USA|||||
|
||||
554|8015877|90049099|4|MA||||
|
||||
551|8015877|90049099|5||LENTES DE SEGURIDAD No BH2143AF|26.66667|1920|1920|108.00|72.000|6|72.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015877|90049099|5|MA||||
|
||||
551|8015877|90049099|6||LENTES DE SEGURIDAD No BH578|15.11458|4353|4353|244.80|288.000|6|288.00000|6||0|1||||TWN|USA|||||
|
||||
554|8015877|90049099|6|MA||||
|
||||
551|8015877|90049099|7||LENTES DE SEGURIDAD No BH584|15.11111|2176|2176|122.40|144.000|6|144.00000|6||0|1||||TWN|USA|||||
|
||||
554|8015877|90049099|7|MA||||
|
||||
551|8015877|90049099|8||LENTES DE SEGURIDAD No BH5410|20.44792|5889|5889|331.20|288.000|6|288.00000|6||0|1||||TWN|USA|||||
|
||||
554|8015877|90049099|8|MA||||
|
||||
551|8015877|90049099|9||LENTES DE SEGURIDAD No BH543AF|16.88889|2432|2432|136.80|144.000|6|144.00000|6||0|1||||CHN|USA|||||
|
||||
554|8015877|90049099|9|MA||||
|
||||
551|8015877|90049099|10||LENTES DE SEGURIDAD No BH516|19.56250|2817|2817|158.40|144.000|6|144.00000|6||0|1||||TWN|USA|||||
|
||||
554|8015877|90049099|10|MA||||
|
||||
551|8015877|90049099|11||LENTES DE SEGURIDAD No BH2333AF|24.36111|7016|7016|394.56|288.000|6|288.00000|6||0|1||||TWN|USA|||||
|
||||
554|8015877|90049099|11|MA||||
|
||||
551|8015877|61161099|12||GUANTES DE NAILON( CON REFUERZO DE NITRILO) ESTILO 510MFV No 510MFV-08 510MFV-M TALLA (8-M) MEDIANO|348.50000|8364|8364|470.40|24.000|19|288.00000|9||0|1||||CHN|USA|||||
|
||||
554|8015877|61161099|12|MA||||
|
||||
554|8015877|61161099|12|EN|VIII|NOM-004-SCFI-2006||
|
||||
551|8015877|61161099|13||GUANTES DE NAILON( CON REFUERZO DE NITRILO) ESTILO 510MFV No 510MFV-09 510MFV-L TALLA (9-L) GRANDE|348.50000|16728|16728|940.80|48.000|19|576.00000|9||0|1||||CHN|USA|||||
|
||||
554|8015877|61161099|13|MA||||
|
||||
554|8015877|61161099|13|EN|VIII|NOM-004-SCFI-2006||
|
||||
551|8015877|61161099|14||GUANTES DE NAILON( CON REFUERZO DE NITRILO) No 508XFT TALLA (9-L) GRANDE|302.29167|7255|7255|408.00|24.000|19|288.00000|9||0|1||||CHN|USA|||||
|
||||
554|8015877|61161099|14|MA||||
|
||||
554|8015877|61161099|14|EN|VIII|NOM-004-SCFI-2006||
|
||||
551|8015877|61161099|15||GUANTES DE POLIESTER( CON REFUERZO DE NITRILO) No 550XFT TALLA (9-L) GRANDE|153.62500|3687|3687|207.36|24.000|19|288.00000|9||0|1||||CHN|USA|||||
|
||||
554|8015877|61161099|15|MA||||
|
||||
554|8015877|61161099|15|EN|VIII|NOM-004-SCFI-2006||
|
||||
551|8015877|61161099|16||GUANTES DE NAILON( CON REFUERZO DE NITRILO) No 508XFT TALLA (8-M) MEDIANO|302.25000|3627|3627|204.00|12.000|19|144.00000|9||0|1||||CHN|USA|||||
|
||||
554|8015877|61161099|16|MA||||
|
||||
554|8015877|61161099|16|EN|VIII|NOM-004-SCFI-2006||
|
||||
551|8015877|61161099|17||GUANTES DE POLIESTER( CON REFUERZO DE NITRILO) No 550XFT TALLA (8-M) MEDIANO|153.66667|1844|1844|103.68|12.000|19|144.00000|9||0|1||||CHN|USA|||||
|
||||
554|8015877|61161099|17|MA||||
|
||||
554|8015877|61161099|17|EN|VIII|NOM-004-SCFI-2006||
|
||||
551|8015877|61161099|18||GUANTES DE NAILON( CON REFUERZO DE NITRILO) No 508XFT TALLA (7-S) PEQUE<55>O|302.25000|3627|3627|204.00|12.000|19|144.00000|9||0|1||||CHN|USA|||||
|
||||
554|8015877|61161099|18|MA||||
|
||||
554|8015877|61161099|18|EN|VIII|NOM-004-SCFI-2006||
|
||||
551|8015877|61161099|19||GUANTES DE POLIESTER( CON REFUERZO DE NITRILO) No 550XFT TALLA (7-S) PEQUE<55>O|153.66667|1844|1844|103.68|12.000|19|144.00000|9||0|1||||CHN|USA|||||
|
||||
554|8015877|61161099|19|MA||||
|
||||
554|8015877|61161099|19|EN|VIII|NOM-004-SCFI-2006||
|
||||
551|8015877|40151999|20||GUANTES DE NITRILO No 705PFE TALLA (M) MEDIANO (DE USO INDUSTRIAL, PRESENTACION CAJA DE 100 PZS)|53.35000|1067|1067|60.00|20.000|20|8.80000|1||0|1||||THA|USA|||||
|
||||
554|8015877|40151999|20|MA||||
|
||||
554|8015877|40151999|20|EN|VIII|NOM-050-SCFI-2004||
|
||||
551|8015877|40151999|21||GUANTES DE NITRILO No 705PFE TALLA (L) GRANDE (DE USO INDUSTRIAL, PRESENTACION CAJA DE 100 PZS)|53.34000|2667|2667|150.00|50.000|20|21.00000|1||0|1||||THA|USA|||||
|
||||
554|8015877|40151999|21|MA||||
|
||||
554|8015877|40151999|21|EN|VIII|NOM-050-SCFI-2004||
|
||||
800|8015877|1|HW4moEojOoyqQoVasray4Vk9WkJR9vqBcvMo+x1lfNUdLA95lcn5c8z/Ctm1F7qXZNVO2gYDK7v8TbMP3rcnQ1Y5GC/8eJZoYsn4LmOAjpy2K8gfut6kvxrIxO8/SVCp7bJrrss8Df2nHCbLqbr6ZaFPgIlbOvAFq9kRWjQVO4qEUBNYuijN9WH8L/WMtbvjJ+J68bOlOsLRwUPtlyqYldrNqSYEXv5g0tV4l8CuGVEjwiaACfBBXihsswyn3ava3kCfLgXL3TxRM5liT5HJ5lBN8RwxID4QCGbD2eNpv0vLGXcDTb7w8fBwvZX+LJRQMBqVLe/aEKt51JWYXy8MIQ==|00001000000517049032|
|
||||
801|m3726435.267|1|64|010|
|
||||
335
uploads/expedientes/pedimento_5/68f96d9554ea3_m3726898.260
Normal file
335
uploads/expedientes/pedimento_5/68f96d9554ea3_m3726898.260
Normal file
@@ -0,0 +1,335 @@
|
||||
500|1|3726|1024136|071||
|
||||
501|3726|1024136|071|1|A1|071||CCI8111293TA|NUAJ680527HCHXLS05|20.78230|10575|0|0|0||57.32|7|7|7|9|COMPA<50>IA COMERCIAL CIMACO, S.A. DE C.V.|AVENIDA HIDALGO PTE||399|27000|TORREON||MEX|CAI040913544|0|0|0|0|0|
|
||||
505|1024136|19112021|2246799|CPT|USD|78.00|78.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|21122021|122121|CPT|USD|6.06|6.06|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|19112021|2246820|CPT|USD|78.00|78.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|19112021|2246800|CPT|USD|78.00|78.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|19112021|2246822|CPT|USD|39.00|39.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|19112021|2246798|CPT|USD|39.00|39.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|02122021|2247867|CPT|USD|55.00|55.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|02122021|2247863|CPT|USD|60.00|60.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|02122021|2247865|CPT|USD|30.00|30.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|02122021|2247868|CPT|USD|174.00|174.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|02122021|2247869|CPT|USD|174.00|174.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|02122021|2247870|CPT|USD|126.00|126.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|02122021|2247872|CPT|USD|174.00|174.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|02122021|2247873|CPT|USD|174.00|174.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|02122021|2247862|CPT|USD|55.00|55.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|02122021|2247866|CPT|USD|30.00|30.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|02122021|2247871|CPT|USD|87.00|87.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|02122021|2247864|CPT|USD|55.00|55.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
505|1024136|19112021|2246821|CPT|USD|78.00|78.00|USA||113268564|PPI APPAREL GROUP|FIFTH AVENUE 2ND. FLOOR||320|10001|NEW YORK|
|
||||
506|1024136|1|23122021|
|
||||
506|1024136|2|23122021|
|
||||
509|1024136|1|352.00000|4|
|
||||
509|1024136|15|240.00000|2|
|
||||
509|1024136|23|16.00000|1|
|
||||
510|1024136|1|0|352|
|
||||
510|1024136|15|0|240|
|
||||
510|1024136|23|0|38|
|
||||
551|1024136|62121007|1|92|ROPA INTERIOR PARA DAMA BRASIER JUEGO 2 PIEZAS|135.08333|2140|1621|78.00|12.000|12|24.00000|6||0|1||||CHN|USA|||||
|
||||
553|1024136|62121007|1|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|62121007|1|EX|31|||
|
||||
556|1024136|62121007|1|3|16.0000000000|1|
|
||||
556|1024136|62121007|1|6|25.0000000000|1|
|
||||
557|1024136|62121007|1|3|0|430|
|
||||
557|1024136|62121007|1|6|0|535|
|
||||
558|1024136|62121007|1|1|ROPA INTERIOR PARA DAMA BRASIER JUEGO 2 PIEZAS 82% NAILON 18% ELASTANO SON 12 JUEGOS MARCA(S): NANETTE LEPORE MODELO(S):|
|
||||
558|1024136|62121007|1|2|2PKBRA2429 TEJIDO DE NO PUNTO|
|
||||
551|1024136|39269099|2|99|GANCHOS|0.41270|69|52|2.52|126.000|6|1.26000|1||0|1||||CHN|USA|||||
|
||||
553|1024136|39269099|2|N3||NOM-050-SCFI-2004|0.00||
|
||||
554|1024136|39269099|2|XP|A1|U||
|
||||
554|1024136|39269099|2|XP|S1|U||
|
||||
554|1024136|39269099|2|XP|S3|U||
|
||||
556|1024136|39269099|2|3|16.0000000000|1|
|
||||
557|1024136|39269099|2|3|0|13|
|
||||
558|1024136|39269099|2|1|GANCHOS PLASTICO SON 126 PIEZAS MARCA(S): NANETTE LEPORE|
|
||||
551|1024136|39269099|3|99|GANCHOS|0.41808|97|74|3.54|177.000|6|1.77000|1||0|1||||CHN|USA|||||
|
||||
553|1024136|39269099|3|N3||NOM-050-SCFI-2004|0.00||
|
||||
554|1024136|39269099|3|XP|A1|U||
|
||||
554|1024136|39269099|3|XP|S1|U||
|
||||
554|1024136|39269099|3|XP|S3|U||
|
||||
556|1024136|39269099|3|3|16.0000000000|1|
|
||||
557|1024136|39269099|3|3|0|17|
|
||||
558|1024136|39269099|3|1|GANCHOS PLASTICO SON 177 PIEZAS MARCA(S): BEBE|
|
||||
551|1024136|62121007|4|92|ROPA INTERIOR PARA DAMA BRASIER JUEGO 2 PIEZAS|135.08333|2140|1621|78.00|12.000|12|24.00000|6||0|1||||CHN|USA|||||
|
||||
553|1024136|62121007|4|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|62121007|4|EX|31|||
|
||||
556|1024136|62121007|4|3|16.0000000000|1|
|
||||
556|1024136|62121007|4|6|25.0000000000|1|
|
||||
557|1024136|62121007|4|3|0|430|
|
||||
557|1024136|62121007|4|6|0|535|
|
||||
558|1024136|62121007|4|1|ROPA INTERIOR PARA DAMA BRASIER JUEGO 2 PIEZAS 82% NAILON 18% ELASTANO SON 12 JUEGOS MARCA(S): NANETTE LEPORE MODELO(S):|
|
||||
558|1024136|62121007|4|2|2PKBRA2429 TEJIDO DE NO PUNTO|
|
||||
551|1024136|62121007|5|92|ROPA INTERIOR PARA DAMA BRASIER JUEGO 2 PIEZAS|135.08333|2140|1621|78.00|12.000|12|24.00000|6||0|1||||CHN|USA|||||
|
||||
553|1024136|62121007|5|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|62121007|5|EX|31|||
|
||||
556|1024136|62121007|5|3|16.0000000000|1|
|
||||
556|1024136|62121007|5|6|25.0000000000|1|
|
||||
557|1024136|62121007|5|3|0|430|
|
||||
557|1024136|62121007|5|6|0|535|
|
||||
558|1024136|62121007|5|1|ROPA INTERIOR PARA DAMA BRASIER JUEGO 2 PIEZAS 82% NAILON 18% ELASTANO SON 12 JUEGOS MARCA(S): NANETTE LEPORE MODELO(S):|
|
||||
558|1024136|62121007|5|2|2PKBRA2429 TEJIDO DE NO PUNTO|
|
||||
551|1024136|62121007|6|92|ROPA INTERIOR PARA DAMA BRASIER JUEGO 2 PIEZAS|135.16667|1070|811|39.00|6.000|12|12.00000|6||0|1||||CHN|USA|||||
|
||||
553|1024136|62121007|6|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|62121007|6|EX|31|||
|
||||
556|1024136|62121007|6|3|16.0000000000|1|
|
||||
556|1024136|62121007|6|6|25.0000000000|1|
|
||||
557|1024136|62121007|6|3|0|216|
|
||||
557|1024136|62121007|6|6|0|267|
|
||||
558|1024136|62121007|6|1|ROPA INTERIOR PARA DAMA BRASIER JUEGO 2 PIEZAS 82% NAILON 18% ELASTANO SON 6 JUEGOS MARCA(S): NANETTE LEPORE MODELO(S):|
|
||||
558|1024136|62121007|6|2|2PKBRA2429 TEJIDO DE NO PUNTO|
|
||||
551|1024136|62121007|7|92|ROPA INTERIOR PARA DAMA BRASIER JUEGO 2 PIEZAS|135.16667|1070|811|39.00|6.000|12|12.00000|6||0|1||||CHN|USA|||||
|
||||
553|1024136|62121007|7|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|62121007|7|EX|31|||
|
||||
556|1024136|62121007|7|3|16.0000000000|1|
|
||||
556|1024136|62121007|7|6|25.0000000000|1|
|
||||
557|1024136|62121007|7|3|0|216|
|
||||
557|1024136|62121007|7|6|0|267|
|
||||
558|1024136|62121007|7|1|ROPA INTERIOR PARA DAMA BRASIER JUEGO 2 PIEZAS 82% NAILON 18% ELASTANO SON 6 JUEGOS MARCA(S): NANETTE LEPORE MODELO(S):|
|
||||
558|1024136|62121007|7|2|2PKBRA2429 TEJIDO DE NO PUNTO|
|
||||
551|1024136|61082203|8|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS|103.90000|1372|1039|50.00|10.000|12|30.00000|6||0|1||BEBE||CHN|USA|||||
|
||||
553|1024136|61082203|8|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|8|EX|31|||
|
||||
554|1024136|61082203|8|MC|4|1||
|
||||
556|1024136|61082203|8|3|16.0000000000|1|
|
||||
556|1024136|61082203|8|6|25.0000000000|1|
|
||||
557|1024136|61082203|8|3|0|276|
|
||||
557|1024136|61082203|8|6|0|343|
|
||||
558|1024136|61082203|8|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS 94% NAILON 6% ELASTANO SON 10 JUEGOS MARCA(S): BEBE MODELO(S): 3PKBL1464 T|
|
||||
558|1024136|61082203|8|2|EJIDO DE PUNTO|
|
||||
551|1024136|61082203|9|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS|104.00000|137|104|5.00|1.000|12|3.00000|6||0|1||BEBE||CHN|USA|||||
|
||||
553|1024136|61082203|9|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|9|EX|31|||
|
||||
554|1024136|61082203|9|MC|4|1||
|
||||
556|1024136|61082203|9|3|16.0000000000|1|
|
||||
556|1024136|61082203|9|6|25.0000000000|1|
|
||||
557|1024136|61082203|9|3|0|29|
|
||||
557|1024136|61082203|9|6|0|34|
|
||||
558|1024136|61082203|9|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS 56% NAILON 35% POLIESTER 9% ELASTANO SON 1 JUEGO MARCA(S): BEBE MODELO(S):|
|
||||
558|1024136|61082203|9|2|3PKBL1464 TEJIDO DE PUNTO|
|
||||
551|1024136|61082203|10|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS|103.90909|1509|1143|55.00|11.000|12|33.00000|6||0|1||BEBE||CHN|USA|||||
|
||||
553|1024136|61082203|10|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|10|EX|31|||
|
||||
554|1024136|61082203|10|MC|4|1||
|
||||
556|1024136|61082203|10|3|16.0000000000|1|
|
||||
556|1024136|61082203|10|6|25.0000000000|1|
|
||||
557|1024136|61082203|10|3|0|303|
|
||||
557|1024136|61082203|10|6|0|377|
|
||||
558|1024136|61082203|10|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS 94% NAILON 6% ELASTANO SON 11 JUEGOS MARCA(S): BEBE MODELO(S): 3PKBL1464 T|
|
||||
558|1024136|61082203|10|2|EJIDO DE PUNTO|
|
||||
551|1024136|61082203|11|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS|104.00000|137|104|5.00|1.000|12|3.00000|6||0|1||BEBE||CHN|USA|||||
|
||||
553|1024136|61082203|11|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|11|EX|31|||
|
||||
554|1024136|61082203|11|MC|4|1||
|
||||
556|1024136|61082203|11|3|16.0000000000|1|
|
||||
556|1024136|61082203|11|6|25.0000000000|1|
|
||||
557|1024136|61082203|11|3|0|29|
|
||||
557|1024136|61082203|11|6|0|34|
|
||||
558|1024136|61082203|11|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS 56% NAILON 35% POLIESTER 9% ELASTANO SON 1 JUEGO MARCA(S): BEBE MODELO(S):|
|
||||
558|1024136|61082203|11|2|3PKBL1464 TEJIDO DE PUNTO|
|
||||
551|1024136|61082203|12|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS|104.00000|686|520|25.00|5.000|12|15.00000|6||0|1||BEBE||CHN|USA|||||
|
||||
553|1024136|61082203|12|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|12|EX|31|||
|
||||
554|1024136|61082203|12|MC|4|1||
|
||||
556|1024136|61082203|12|3|16.0000000000|1|
|
||||
556|1024136|61082203|12|6|25.0000000000|1|
|
||||
557|1024136|61082203|12|3|0|139|
|
||||
557|1024136|61082203|12|6|0|171|
|
||||
558|1024136|61082203|12|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS 94% NAILON 6% ELASTANO SON 5 JUEGOS MARCA(S): BEBE MODELO(S): 3PKBL1464 TE|
|
||||
558|1024136|61082203|12|2|JIDO DE PUNTO|
|
||||
551|1024136|61082203|13|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS|104.00000|137|104|5.00|1.000|12|3.00000|6||0|1||BEBE||CHN|USA|||||
|
||||
553|1024136|61082203|13|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|13|EX|31|||
|
||||
554|1024136|61082203|13|MC|4|1||
|
||||
556|1024136|61082203|13|3|16.0000000000|1|
|
||||
556|1024136|61082203|13|6|25.0000000000|1|
|
||||
557|1024136|61082203|13|3|0|29|
|
||||
557|1024136|61082203|13|6|0|34|
|
||||
558|1024136|61082203|13|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS 56% NAILON 35% POLIESTER 9% ELASTANO SON 1 JUEGO MARCA(S): BEBE MODELO(S):|
|
||||
558|1024136|61082203|13|2|3PKBL1464 TEJIDO DE PUNTO|
|
||||
551|1024136|62121007|14|02|ROPA INTERIOR PARA DAMA BRASIER CON ENCAJE|83.12500|2634|1995|96.00|24.000|6|24.00000|6||0|1||||CHN|USA|||||
|
||||
553|1024136|62121007|14|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|62121007|14|EX|31|||
|
||||
556|1024136|62121007|14|3|16.0000000000|1|
|
||||
556|1024136|62121007|14|6|25.0000000000|1|
|
||||
557|1024136|62121007|14|3|0|528|
|
||||
557|1024136|62121007|14|6|0|658|
|
||||
558|1024136|62121007|14|1|ROPA INTERIOR PARA DAMA BRASIER CON ENCAJE 90% NAILON 10% ELASTANO SON 24 PIEZAS MARCA(S): BEBE MODELO(S): BRA3578 TEJID|
|
||||
558|1024136|62121007|14|2|O DE NO PUNTO|
|
||||
551|1024136|61082203|15|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 5 PIEZAS|135.08333|2140|1621|78.00|12.000|12|60.00000|6||0|1||NANETTE LEPORE||CHN|USA|||||
|
||||
553|1024136|61082203|15|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|15|EX|31|||
|
||||
554|1024136|61082203|15|MC|4|1||
|
||||
556|1024136|61082203|15|3|16.0000000000|1|
|
||||
556|1024136|61082203|15|6|25.0000000000|1|
|
||||
557|1024136|61082203|15|3|0|430|
|
||||
557|1024136|61082203|15|6|0|535|
|
||||
558|1024136|61082203|15|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 5 PIEZAS 86% NAILON 14% ELASTANO SON 12 JUEGOS MARCA(S): NANETTE LEPORE MODELO(S):|
|
||||
558|1024136|61082203|15|2|5PKHP827 TEJIDO DE PUNTO|
|
||||
551|1024136|61082203|16|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 5 PIEZAS|135.08333|2140|1621|78.00|12.000|12|60.00000|6||0|1||NANETTE LEPORE||CHN|USA|||||
|
||||
553|1024136|61082203|16|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|16|EX|31|||
|
||||
554|1024136|61082203|16|MC|4|1||
|
||||
556|1024136|61082203|16|3|16.0000000000|1|
|
||||
556|1024136|61082203|16|6|25.0000000000|1|
|
||||
557|1024136|61082203|16|3|0|430|
|
||||
557|1024136|61082203|16|6|0|535|
|
||||
558|1024136|61082203|16|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 5 PIEZAS 86% NAILON 14% ELASTANO SON 12 JUEGOS MARCA(S): NANETTE LEPORE MODELO(S):|
|
||||
558|1024136|61082203|16|2|5PKHP827 TEJIDO DE PUNTO|
|
||||
551|1024136|62121007|17|02|ROPA INTERIOR PARA DAMA BRASIER CON ENCAJE|83.12500|2634|1995|96.00|24.000|6|24.00000|6||0|1||||CHN|USA|||||
|
||||
553|1024136|62121007|17|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|62121007|17|EX|31|||
|
||||
556|1024136|62121007|17|3|16.0000000000|1|
|
||||
556|1024136|62121007|17|6|25.0000000000|1|
|
||||
557|1024136|62121007|17|3|0|528|
|
||||
557|1024136|62121007|17|6|0|658|
|
||||
558|1024136|62121007|17|1|ROPA INTERIOR PARA DAMA BRASIER CON ENCAJE 90% NAILON 10% ELASTANO SON 24 PIEZAS MARCA(S): BEBE MODELO(S): BRA3578 TEJID|
|
||||
558|1024136|62121007|17|2|O DE NO PUNTO|
|
||||
551|1024136|61082203|18|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 5 PIEZAS|135.08333|2140|1621|78.00|12.000|12|60.00000|6||0|1||NANETTE LEPORE||CHN|USA|||||
|
||||
553|1024136|61082203|18|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|18|EX|31|||
|
||||
554|1024136|61082203|18|MC|4|1||
|
||||
556|1024136|61082203|18|3|16.0000000000|1|
|
||||
556|1024136|61082203|18|6|25.0000000000|1|
|
||||
557|1024136|61082203|18|3|0|430|
|
||||
557|1024136|61082203|18|6|0|535|
|
||||
558|1024136|61082203|18|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 5 PIEZAS 86% NAILON 14% ELASTANO SON 12 JUEGOS MARCA(S): NANETTE LEPORE MODELO(S):|
|
||||
558|1024136|61082203|18|2|5PKHP827 TEJIDO DE PUNTO|
|
||||
551|1024136|62121007|19|02|ROPA INTERIOR PARA DAMA BRASIER CON ENCAJE|83.16667|1317|998|48.00|12.000|6|12.00000|6||0|1||||CHN|USA|||||
|
||||
553|1024136|62121007|19|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|62121007|19|EX|31|||
|
||||
556|1024136|62121007|19|3|16.0000000000|1|
|
||||
556|1024136|62121007|19|6|25.0000000000|1|
|
||||
557|1024136|62121007|19|3|0|265|
|
||||
557|1024136|62121007|19|6|0|329|
|
||||
558|1024136|62121007|19|1|ROPA INTERIOR PARA DAMA BRASIER CON ENCAJE 90% NAILON 10% ELASTANO SON 12 PIEZAS MARCA(S): BEBE MODELO(S): BRA3578 TEJID|
|
||||
558|1024136|62121007|19|2|O DE NO PUNTO|
|
||||
551|1024136|62121007|20|02|ROPA INTERIOR PARA DAMA BRASIER CON ENCAJE|83.12500|2634|1995|96.00|24.000|6|24.00000|6||0|1||||CHN|USA|||||
|
||||
553|1024136|62121007|20|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|62121007|20|EX|31|||
|
||||
556|1024136|62121007|20|3|16.0000000000|1|
|
||||
556|1024136|62121007|20|6|25.0000000000|1|
|
||||
557|1024136|62121007|20|3|0|528|
|
||||
557|1024136|62121007|20|6|0|658|
|
||||
558|1024136|62121007|20|1|ROPA INTERIOR PARA DAMA BRASIER CON ENCAJE 90% NAILON 10% ELASTANO SON 24 PIEZAS MARCA(S): BEBE MODELO(S): BRA3578 TEJID|
|
||||
558|1024136|62121007|20|2|O DE NO PUNTO|
|
||||
551|1024136|61082203|21|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 5 PIEZAS|135.08333|2140|1621|78.00|12.000|12|60.00000|6||0|1||NANETTE LEPORE||CHN|USA|||||
|
||||
553|1024136|61082203|21|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|21|EX|31|||
|
||||
554|1024136|61082203|21|MC|4|1||
|
||||
556|1024136|61082203|21|3|16.0000000000|1|
|
||||
556|1024136|61082203|21|6|25.0000000000|1|
|
||||
557|1024136|61082203|21|3|0|430|
|
||||
557|1024136|61082203|21|6|0|535|
|
||||
558|1024136|61082203|21|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 5 PIEZAS 86% NAILON 14% ELASTANO SON 12 JUEGOS MARCA(S): NANETTE LEPORE MODELO(S):|
|
||||
558|1024136|61082203|21|2|5PKHP827 TEJIDO DE PUNTO|
|
||||
551|1024136|62121007|22|02|ROPA INTERIOR PARA DAMA BRASIER CON ENCAJE|83.12500|2634|1995|96.00|24.000|6|24.00000|6||0|1||||CHN|USA|||||
|
||||
553|1024136|62121007|22|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|62121007|22|EX|31|||
|
||||
556|1024136|62121007|22|3|16.0000000000|1|
|
||||
556|1024136|62121007|22|6|25.0000000000|1|
|
||||
557|1024136|62121007|22|3|0|528|
|
||||
557|1024136|62121007|22|6|0|658|
|
||||
558|1024136|62121007|22|1|ROPA INTERIOR PARA DAMA BRASIER CON ENCAJE 90% NAILON 10% ELASTANO SON 24 PIEZAS MARCA(S): BEBE MODELO(S): BRA3578 TEJID|
|
||||
558|1024136|62121007|22|2|O DE NO PUNTO|
|
||||
551|1024136|61082203|23|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 5 PIEZAS|135.08333|2140|1621|78.00|12.000|12|60.00000|6||0|1||NANETTE LEPORE||CHN|USA|||||
|
||||
553|1024136|61082203|23|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|23|EX|31|||
|
||||
554|1024136|61082203|23|MC|4|1||
|
||||
556|1024136|61082203|23|3|16.0000000000|1|
|
||||
556|1024136|61082203|23|6|25.0000000000|1|
|
||||
557|1024136|61082203|23|3|0|430|
|
||||
557|1024136|61082203|23|6|0|535|
|
||||
558|1024136|61082203|23|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 5 PIEZAS 86% NAILON 14% ELASTANO SON 12 JUEGOS MARCA(S): NANETTE LEPORE MODELO(S):|
|
||||
558|1024136|61082203|23|2|5PKHP827 TEJIDO DE PUNTO|
|
||||
551|1024136|61082203|24|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS|103.90000|1372|1039|50.00|10.000|12|30.00000|6||0|1||BEBE||CHN|USA|||||
|
||||
553|1024136|61082203|24|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|24|EX|31|||
|
||||
554|1024136|61082203|24|MC|4|1||
|
||||
556|1024136|61082203|24|3|16.0000000000|1|
|
||||
556|1024136|61082203|24|6|25.0000000000|1|
|
||||
557|1024136|61082203|24|3|0|276|
|
||||
557|1024136|61082203|24|6|0|343|
|
||||
558|1024136|61082203|24|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS 94% NAILON 6% ELSTANO SON 10 JUEGOS MARCA(S): BEBE MODELO(S): 3PKBL1464 TE|
|
||||
558|1024136|61082203|24|2|JIDO DE PUNTO|
|
||||
551|1024136|61082203|25|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS|104.00000|137|104|5.00|1.000|12|3.00000|6||0|1||BEBE||CHN|USA|||||
|
||||
553|1024136|61082203|25|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|25|EX|31|||
|
||||
554|1024136|61082203|25|MC|4|1||
|
||||
556|1024136|61082203|25|3|16.0000000000|1|
|
||||
556|1024136|61082203|25|6|25.0000000000|1|
|
||||
557|1024136|61082203|25|3|0|29|
|
||||
557|1024136|61082203|25|6|0|34|
|
||||
558|1024136|61082203|25|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS 56% NAILON 35% POLIESTER 9% ELASTANO SON 1 JUEGO MARCA(S): BEBE MODELO(S):|
|
||||
558|1024136|61082203|25|2|3PKBL1464 TEJIDO DE PUNTO|
|
||||
551|1024136|61082203|26|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS|104.00000|686|520|25.00|5.000|12|15.00000|6||0|1||BEBE||CHN|USA|||||
|
||||
553|1024136|61082203|26|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|26|EX|31|||
|
||||
554|1024136|61082203|26|MC|4|1||
|
||||
556|1024136|61082203|26|3|16.0000000000|1|
|
||||
556|1024136|61082203|26|6|25.0000000000|1|
|
||||
557|1024136|61082203|26|3|0|139|
|
||||
557|1024136|61082203|26|6|0|171|
|
||||
558|1024136|61082203|26|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS 94% NAILON 6% ELASTANO SON 5 JUEGOS MARCA(S): BEBE MODELO(S): 3PKBL1464 TE|
|
||||
558|1024136|61082203|26|2|JIDO DE PUNTO|
|
||||
551|1024136|61082203|27|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS|104.00000|137|104|5.00|1.000|12|3.00000|6||0|1||BEBE||CHN|USA|||||
|
||||
553|1024136|61082203|27|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|27|EX|31|||
|
||||
554|1024136|61082203|27|MC|4|1||
|
||||
556|1024136|61082203|27|3|16.0000000000|1|
|
||||
556|1024136|61082203|27|6|25.0000000000|1|
|
||||
557|1024136|61082203|27|3|0|29|
|
||||
557|1024136|61082203|27|6|0|34|
|
||||
558|1024136|61082203|27|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS 56% NAILON 35% POLIESTER 9% ELASTANO SON 1 JUEGO MARCA(S): BEBE MODELO(S):|
|
||||
558|1024136|61082203|27|2|3PKBL1464 TEJIDO DE PUNTO|
|
||||
551|1024136|62121007|28|01|ROPA INTERIOR PARA DAMA BRASIER CON ENCAJE|83.16667|1317|998|48.00|12.000|6|12.00000|6||0|1||||CHN|USA|||||
|
||||
553|1024136|62121007|28|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|62121007|28|EX|31|||
|
||||
556|1024136|62121007|28|3|16.0000000000|1|
|
||||
556|1024136|62121007|28|6|25.0000000000|1|
|
||||
557|1024136|62121007|28|3|0|265|
|
||||
557|1024136|62121007|28|6|0|329|
|
||||
558|1024136|62121007|28|1|ROPA INTERIOR PARA DAMA BRASIER CON ENCAJE 90% NAILON 10% ELASTANO SON 12 PIEZAS MARCA(S): BEBE MODELO(S): BRA3578 TEJID|
|
||||
558|1024136|62121007|28|2|O DE NO PUNTO|
|
||||
551|1024136|61082203|29|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 5 PIEZAS|135.16667|1070|811|39.00|6.000|12|30.00000|6||0|1||NANETTE LEPORE||CHN|USA|||||
|
||||
553|1024136|61082203|29|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|29|EX|31|||
|
||||
554|1024136|61082203|29|MC|4|1||
|
||||
556|1024136|61082203|29|3|16.0000000000|1|
|
||||
556|1024136|61082203|29|6|25.0000000000|1|
|
||||
557|1024136|61082203|29|3|0|216|
|
||||
557|1024136|61082203|29|6|0|267|
|
||||
558|1024136|61082203|29|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 5 PIEZAS 86% NAILON 14% ELASTANO SON 6 JUEGOS MARCA(S): NANETTE LEPORE MODELO(S): 5|
|
||||
558|1024136|61082203|29|2|PKHP827 TEJIDO DE PUNTO|
|
||||
551|1024136|61082203|30|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS|103.90000|1372|1039|50.00|10.000|12|30.00000|6||0|1||BEBE||CHN|USA|||||
|
||||
553|1024136|61082203|30|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|30|EX|31|||
|
||||
554|1024136|61082203|30|MC|4|1||
|
||||
556|1024136|61082203|30|3|16.0000000000|1|
|
||||
556|1024136|61082203|30|6|25.0000000000|1|
|
||||
557|1024136|61082203|30|3|0|276|
|
||||
557|1024136|61082203|30|6|0|343|
|
||||
558|1024136|61082203|30|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS 94% NAILON 6% ELASTANO SON 10 JUEGOS MARCA(S): BEBE MODELO(S): 3PKBL1464 T|
|
||||
558|1024136|61082203|30|2|EJIDO DE PUNTO|
|
||||
551|1024136|61082203|31|01|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS|104.00000|137|104|5.00|1.000|12|3.00000|6||0|1||BEBE||CHN|USA|||||
|
||||
553|1024136|61082203|31|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|61082203|31|EX|31|||
|
||||
554|1024136|61082203|31|MC|4|1||
|
||||
556|1024136|61082203|31|3|16.0000000000|1|
|
||||
556|1024136|61082203|31|6|25.0000000000|1|
|
||||
557|1024136|61082203|31|3|0|29|
|
||||
557|1024136|61082203|31|6|0|34|
|
||||
558|1024136|61082203|31|1|ROPA INTERIOR PARA DAMA CALZON JUEGO 3 PIEZAS 56% NAILON 35% POLIESTER 9% ELASTANO SON 1 JUEGO MARCA(S): BEBE MODELO(S):|
|
||||
558|1024136|61082203|31|2|3PKBL1464 TEJIDO DE PUNTO|
|
||||
551|1024136|62121007|32|92|ROPA INTERIOR PARA DAMA BRASIER JUEGO 2 PIEZAS|135.08333|2140|1621|78.00|12.000|12|24.00000|6||0|1||||CHN|USA|||||
|
||||
553|1024136|62121007|32|N3||NOM-004-SCFI-2006|0.00||
|
||||
554|1024136|62121007|32|EX|31|||
|
||||
556|1024136|62121007|32|3|16.0000000000|1|
|
||||
556|1024136|62121007|32|6|25.0000000000|1|
|
||||
557|1024136|62121007|32|3|0|430|
|
||||
557|1024136|62121007|32|6|0|535|
|
||||
558|1024136|62121007|32|1|ROPA INTERIOR PARA DAMA BRASIER JUEGO 2 PIEZAS 82% NAILON 18% ELASTANO SON 12 JUEGOS MARCA(S): NANETTE LEPORE MODELO(S):|
|
||||
558|1024136|62121007|32|2|2PKBRA2429 TEJIDO DE NO PUNTO|
|
||||
800|1024136|1|dkkw0EaMPAdiagXpjWqm6L/kqGSZKvXh7V6OJv22snG1YyrU5AmqQ5Qgx2KToJwNU1Cwl6Z3PBV+7TGxMYROizihPSMfP3vBnlk4OXpv0Q86npjdUv7BUXAUU0e34S7uHNaLSK+VX46LiuccAVdRwzo5M31m1UDYXbANJpPRqnVCTZ5By1SKiV7KfgXbpBapnLtogq5k6r7oeJNzXu6Rejzir23w/AFCFtKfqEvcTId61zqsd0mU+0iNGlQ3OUk99NuBsbqVJpZOXOw5Iyh3n7xwSHJvfOHFF3dtASuo0gEr7MlFTQ9gID0vqxHwx16oT5hrlFWTrk7uzgMgfmL6ng==|00001000000517049032|
|
||||
801|m3726898.260|1|334|010|
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
569
vendor/dompdf/dompdf/lib/fonts/Courier.afm.json
vendored
Normal file
569
vendor/dompdf/dompdf/lib/fonts/Courier.afm.json
vendored
Normal file
@@ -0,0 +1,569 @@
|
||||
{
|
||||
"codeToName": {
|
||||
"32": "space",
|
||||
"160": "space",
|
||||
"33": "exclam",
|
||||
"34": "quotedbl",
|
||||
"35": "numbersign",
|
||||
"36": "dollar",
|
||||
"37": "percent",
|
||||
"38": "ampersand",
|
||||
"146": "quoteright",
|
||||
"40": "parenleft",
|
||||
"41": "parenright",
|
||||
"42": "asterisk",
|
||||
"43": "plus",
|
||||
"44": "comma",
|
||||
"45": "hyphen",
|
||||
"173": "hyphen",
|
||||
"46": "period",
|
||||
"47": "slash",
|
||||
"48": "zero",
|
||||
"49": "one",
|
||||
"50": "two",
|
||||
"51": "three",
|
||||
"52": "four",
|
||||
"53": "five",
|
||||
"54": "six",
|
||||
"55": "seven",
|
||||
"56": "eight",
|
||||
"57": "nine",
|
||||
"58": "colon",
|
||||
"59": "semicolon",
|
||||
"60": "less",
|
||||
"61": "equal",
|
||||
"62": "greater",
|
||||
"63": "question",
|
||||
"64": "at",
|
||||
"65": "A",
|
||||
"66": "B",
|
||||
"67": "C",
|
||||
"68": "D",
|
||||
"69": "E",
|
||||
"70": "F",
|
||||
"71": "G",
|
||||
"72": "H",
|
||||
"73": "I",
|
||||
"74": "J",
|
||||
"75": "K",
|
||||
"76": "L",
|
||||
"77": "M",
|
||||
"78": "N",
|
||||
"79": "O",
|
||||
"80": "P",
|
||||
"81": "Q",
|
||||
"82": "R",
|
||||
"83": "S",
|
||||
"84": "T",
|
||||
"85": "U",
|
||||
"86": "V",
|
||||
"87": "W",
|
||||
"88": "X",
|
||||
"89": "Y",
|
||||
"90": "Z",
|
||||
"91": "bracketleft",
|
||||
"92": "backslash",
|
||||
"93": "bracketright",
|
||||
"94": "asciicircum",
|
||||
"95": "underscore",
|
||||
"145": "quoteleft",
|
||||
"97": "a",
|
||||
"98": "b",
|
||||
"99": "c",
|
||||
"100": "d",
|
||||
"101": "e",
|
||||
"102": "f",
|
||||
"103": "g",
|
||||
"104": "h",
|
||||
"105": "i",
|
||||
"106": "j",
|
||||
"107": "k",
|
||||
"108": "l",
|
||||
"109": "m",
|
||||
"110": "n",
|
||||
"111": "o",
|
||||
"112": "p",
|
||||
"113": "q",
|
||||
"114": "r",
|
||||
"115": "s",
|
||||
"116": "t",
|
||||
"117": "u",
|
||||
"118": "v",
|
||||
"119": "w",
|
||||
"120": "x",
|
||||
"121": "y",
|
||||
"122": "z",
|
||||
"123": "braceleft",
|
||||
"124": "bar",
|
||||
"125": "braceright",
|
||||
"126": "asciitilde",
|
||||
"161": "exclamdown",
|
||||
"162": "cent",
|
||||
"163": "sterling",
|
||||
"165": "yen",
|
||||
"131": "florin",
|
||||
"167": "section",
|
||||
"164": "currency",
|
||||
"39": "quotesingle",
|
||||
"147": "quotedblleft",
|
||||
"171": "guillemotleft",
|
||||
"139": "guilsinglleft",
|
||||
"155": "guilsinglright",
|
||||
"150": "endash",
|
||||
"134": "dagger",
|
||||
"135": "daggerdbl",
|
||||
"183": "periodcentered",
|
||||
"182": "paragraph",
|
||||
"149": "bullet",
|
||||
"130": "quotesinglbase",
|
||||
"132": "quotedblbase",
|
||||
"148": "quotedblright",
|
||||
"187": "guillemotright",
|
||||
"133": "ellipsis",
|
||||
"137": "perthousand",
|
||||
"191": "questiondown",
|
||||
"96": "grave",
|
||||
"180": "acute",
|
||||
"136": "circumflex",
|
||||
"152": "tilde",
|
||||
"175": "macron",
|
||||
"168": "dieresis",
|
||||
"184": "cedilla",
|
||||
"151": "emdash",
|
||||
"198": "AE",
|
||||
"170": "ordfeminine",
|
||||
"216": "Oslash",
|
||||
"140": "OE",
|
||||
"186": "ordmasculine",
|
||||
"230": "ae",
|
||||
"248": "oslash",
|
||||
"156": "oe",
|
||||
"223": "germandbls",
|
||||
"207": "Idieresis",
|
||||
"233": "eacute",
|
||||
"159": "Ydieresis",
|
||||
"247": "divide",
|
||||
"221": "Yacute",
|
||||
"194": "Acircumflex",
|
||||
"225": "aacute",
|
||||
"219": "Ucircumflex",
|
||||
"253": "yacute",
|
||||
"234": "ecircumflex",
|
||||
"220": "Udieresis",
|
||||
"218": "Uacute",
|
||||
"203": "Edieresis",
|
||||
"169": "copyright",
|
||||
"229": "aring",
|
||||
"224": "agrave",
|
||||
"227": "atilde",
|
||||
"154": "scaron",
|
||||
"237": "iacute",
|
||||
"251": "ucircumflex",
|
||||
"226": "acircumflex",
|
||||
"231": "ccedilla",
|
||||
"222": "Thorn",
|
||||
"179": "threesuperior",
|
||||
"210": "Ograve",
|
||||
"192": "Agrave",
|
||||
"215": "multiply",
|
||||
"250": "uacute",
|
||||
"255": "ydieresis",
|
||||
"238": "icircumflex",
|
||||
"202": "Ecircumflex",
|
||||
"228": "adieresis",
|
||||
"235": "edieresis",
|
||||
"205": "Iacute",
|
||||
"177": "plusminus",
|
||||
"166": "brokenbar",
|
||||
"174": "registered",
|
||||
"200": "Egrave",
|
||||
"142": "Zcaron",
|
||||
"208": "Eth",
|
||||
"199": "Ccedilla",
|
||||
"193": "Aacute",
|
||||
"196": "Adieresis",
|
||||
"232": "egrave",
|
||||
"211": "Oacute",
|
||||
"243": "oacute",
|
||||
"239": "idieresis",
|
||||
"212": "Ocircumflex",
|
||||
"217": "Ugrave",
|
||||
"254": "thorn",
|
||||
"178": "twosuperior",
|
||||
"214": "Odieresis",
|
||||
"181": "mu",
|
||||
"236": "igrave",
|
||||
"190": "threequarters",
|
||||
"153": "trademark",
|
||||
"204": "Igrave",
|
||||
"189": "onehalf",
|
||||
"244": "ocircumflex",
|
||||
"241": "ntilde",
|
||||
"201": "Eacute",
|
||||
"188": "onequarter",
|
||||
"138": "Scaron",
|
||||
"176": "degree",
|
||||
"242": "ograve",
|
||||
"249": "ugrave",
|
||||
"209": "Ntilde",
|
||||
"245": "otilde",
|
||||
"195": "Atilde",
|
||||
"197": "Aring",
|
||||
"213": "Otilde",
|
||||
"206": "Icircumflex",
|
||||
"172": "logicalnot",
|
||||
"246": "odieresis",
|
||||
"252": "udieresis",
|
||||
"240": "eth",
|
||||
"158": "zcaron",
|
||||
"185": "onesuperior",
|
||||
"128": "Euro"
|
||||
},
|
||||
"isUnicode": false,
|
||||
"FontName": "Courier",
|
||||
"FullName": "Courier",
|
||||
"FamilyName": "Courier",
|
||||
"Weight": "Medium",
|
||||
"ItalicAngle": "0",
|
||||
"IsFixedPitch": "true",
|
||||
"CharacterSet": "ExtendedRoman",
|
||||
"FontBBox": [
|
||||
"-23",
|
||||
"-250",
|
||||
"715",
|
||||
"805"
|
||||
],
|
||||
"UnderlinePosition": "-100",
|
||||
"UnderlineThickness": "50",
|
||||
"Version": "003.000",
|
||||
"EncodingScheme": "WinAnsiEncoding",
|
||||
"CapHeight": "562",
|
||||
"XHeight": "426",
|
||||
"Ascender": "629",
|
||||
"Descender": "-157",
|
||||
"StdHW": "51",
|
||||
"StdVW": "51",
|
||||
"StartCharMetrics": "317",
|
||||
"C": {
|
||||
"32": 600,
|
||||
"160": 600,
|
||||
"33": 600,
|
||||
"34": 600,
|
||||
"35": 600,
|
||||
"36": 600,
|
||||
"37": 600,
|
||||
"38": 600,
|
||||
"146": 600,
|
||||
"40": 600,
|
||||
"41": 600,
|
||||
"42": 600,
|
||||
"43": 600,
|
||||
"44": 600,
|
||||
"45": 600,
|
||||
"173": 600,
|
||||
"46": 600,
|
||||
"47": 600,
|
||||
"48": 600,
|
||||
"49": 600,
|
||||
"50": 600,
|
||||
"51": 600,
|
||||
"52": 600,
|
||||
"53": 600,
|
||||
"54": 600,
|
||||
"55": 600,
|
||||
"56": 600,
|
||||
"57": 600,
|
||||
"58": 600,
|
||||
"59": 600,
|
||||
"60": 600,
|
||||
"61": 600,
|
||||
"62": 600,
|
||||
"63": 600,
|
||||
"64": 600,
|
||||
"65": 600,
|
||||
"66": 600,
|
||||
"67": 600,
|
||||
"68": 600,
|
||||
"69": 600,
|
||||
"70": 600,
|
||||
"71": 600,
|
||||
"72": 600,
|
||||
"73": 600,
|
||||
"74": 600,
|
||||
"75": 600,
|
||||
"76": 600,
|
||||
"77": 600,
|
||||
"78": 600,
|
||||
"79": 600,
|
||||
"80": 600,
|
||||
"81": 600,
|
||||
"82": 600,
|
||||
"83": 600,
|
||||
"84": 600,
|
||||
"85": 600,
|
||||
"86": 600,
|
||||
"87": 600,
|
||||
"88": 600,
|
||||
"89": 600,
|
||||
"90": 600,
|
||||
"91": 600,
|
||||
"92": 600,
|
||||
"93": 600,
|
||||
"94": 600,
|
||||
"95": 600,
|
||||
"145": 600,
|
||||
"97": 600,
|
||||
"98": 600,
|
||||
"99": 600,
|
||||
"100": 600,
|
||||
"101": 600,
|
||||
"102": 600,
|
||||
"103": 600,
|
||||
"104": 600,
|
||||
"105": 600,
|
||||
"106": 600,
|
||||
"107": 600,
|
||||
"108": 600,
|
||||
"109": 600,
|
||||
"110": 600,
|
||||
"111": 600,
|
||||
"112": 600,
|
||||
"113": 600,
|
||||
"114": 600,
|
||||
"115": 600,
|
||||
"116": 600,
|
||||
"117": 600,
|
||||
"118": 600,
|
||||
"119": 600,
|
||||
"120": 600,
|
||||
"121": 600,
|
||||
"122": 600,
|
||||
"123": 600,
|
||||
"124": 600,
|
||||
"125": 600,
|
||||
"126": 600,
|
||||
"161": 600,
|
||||
"162": 600,
|
||||
"163": 600,
|
||||
"fraction": 600,
|
||||
"165": 600,
|
||||
"131": 600,
|
||||
"167": 600,
|
||||
"164": 600,
|
||||
"39": 600,
|
||||
"147": 600,
|
||||
"171": 600,
|
||||
"139": 600,
|
||||
"155": 600,
|
||||
"fi": 600,
|
||||
"fl": 600,
|
||||
"150": 600,
|
||||
"134": 600,
|
||||
"135": 600,
|
||||
"183": 600,
|
||||
"182": 600,
|
||||
"149": 600,
|
||||
"130": 600,
|
||||
"132": 600,
|
||||
"148": 600,
|
||||
"187": 600,
|
||||
"133": 600,
|
||||
"137": 600,
|
||||
"191": 600,
|
||||
"96": 600,
|
||||
"180": 600,
|
||||
"136": 600,
|
||||
"152": 600,
|
||||
"175": 600,
|
||||
"breve": 600,
|
||||
"dotaccent": 600,
|
||||
"168": 600,
|
||||
"ring": 600,
|
||||
"184": 600,
|
||||
"hungarumlaut": 600,
|
||||
"ogonek": 600,
|
||||
"caron": 600,
|
||||
"151": 600,
|
||||
"198": 600,
|
||||
"170": 600,
|
||||
"Lslash": 600,
|
||||
"216": 600,
|
||||
"140": 600,
|
||||
"186": 600,
|
||||
"230": 600,
|
||||
"dotlessi": 600,
|
||||
"lslash": 600,
|
||||
"248": 600,
|
||||
"156": 600,
|
||||
"223": 600,
|
||||
"207": 600,
|
||||
"233": 600,
|
||||
"abreve": 600,
|
||||
"uhungarumlaut": 600,
|
||||
"ecaron": 600,
|
||||
"159": 600,
|
||||
"247": 600,
|
||||
"221": 600,
|
||||
"194": 600,
|
||||
"225": 600,
|
||||
"219": 600,
|
||||
"253": 600,
|
||||
"scommaaccent": 600,
|
||||
"234": 600,
|
||||
"Uring": 600,
|
||||
"220": 600,
|
||||
"aogonek": 600,
|
||||
"218": 600,
|
||||
"uogonek": 600,
|
||||
"203": 600,
|
||||
"Dcroat": 600,
|
||||
"commaaccent": 600,
|
||||
"169": 600,
|
||||
"Emacron": 600,
|
||||
"ccaron": 600,
|
||||
"229": 600,
|
||||
"Ncommaaccent": 600,
|
||||
"lacute": 600,
|
||||
"224": 600,
|
||||
"Tcommaaccent": 600,
|
||||
"Cacute": 600,
|
||||
"227": 600,
|
||||
"Edotaccent": 600,
|
||||
"154": 600,
|
||||
"scedilla": 600,
|
||||
"237": 600,
|
||||
"lozenge": 600,
|
||||
"Rcaron": 600,
|
||||
"Gcommaaccent": 600,
|
||||
"251": 600,
|
||||
"226": 600,
|
||||
"Amacron": 600,
|
||||
"rcaron": 600,
|
||||
"231": 600,
|
||||
"Zdotaccent": 600,
|
||||
"222": 600,
|
||||
"Omacron": 600,
|
||||
"Racute": 600,
|
||||
"Sacute": 600,
|
||||
"dcaron": 600,
|
||||
"Umacron": 600,
|
||||
"uring": 600,
|
||||
"179": 600,
|
||||
"210": 600,
|
||||
"192": 600,
|
||||
"Abreve": 600,
|
||||
"215": 600,
|
||||
"250": 600,
|
||||
"Tcaron": 600,
|
||||
"partialdiff": 600,
|
||||
"255": 600,
|
||||
"Nacute": 600,
|
||||
"238": 600,
|
||||
"202": 600,
|
||||
"228": 600,
|
||||
"235": 600,
|
||||
"cacute": 600,
|
||||
"nacute": 600,
|
||||
"umacron": 600,
|
||||
"Ncaron": 600,
|
||||
"205": 600,
|
||||
"177": 600,
|
||||
"166": 600,
|
||||
"174": 600,
|
||||
"Gbreve": 600,
|
||||
"Idotaccent": 600,
|
||||
"summation": 600,
|
||||
"200": 600,
|
||||
"racute": 600,
|
||||
"omacron": 600,
|
||||
"Zacute": 600,
|
||||
"142": 600,
|
||||
"greaterequal": 600,
|
||||
"208": 600,
|
||||
"199": 600,
|
||||
"lcommaaccent": 600,
|
||||
"tcaron": 600,
|
||||
"eogonek": 600,
|
||||
"Uogonek": 600,
|
||||
"193": 600,
|
||||
"196": 600,
|
||||
"232": 600,
|
||||
"zacute": 600,
|
||||
"iogonek": 600,
|
||||
"211": 600,
|
||||
"243": 600,
|
||||
"amacron": 600,
|
||||
"sacute": 600,
|
||||
"239": 600,
|
||||
"212": 600,
|
||||
"217": 600,
|
||||
"Delta": 600,
|
||||
"254": 600,
|
||||
"178": 600,
|
||||
"214": 600,
|
||||
"181": 600,
|
||||
"236": 600,
|
||||
"ohungarumlaut": 600,
|
||||
"Eogonek": 600,
|
||||
"dcroat": 600,
|
||||
"190": 600,
|
||||
"Scedilla": 600,
|
||||
"lcaron": 600,
|
||||
"Kcommaaccent": 600,
|
||||
"Lacute": 600,
|
||||
"153": 600,
|
||||
"edotaccent": 600,
|
||||
"204": 600,
|
||||
"Imacron": 600,
|
||||
"Lcaron": 600,
|
||||
"189": 600,
|
||||
"lessequal": 600,
|
||||
"244": 600,
|
||||
"241": 600,
|
||||
"Uhungarumlaut": 600,
|
||||
"201": 600,
|
||||
"emacron": 600,
|
||||
"gbreve": 600,
|
||||
"188": 600,
|
||||
"138": 600,
|
||||
"Scommaaccent": 600,
|
||||
"Ohungarumlaut": 600,
|
||||
"176": 600,
|
||||
"242": 600,
|
||||
"Ccaron": 600,
|
||||
"249": 600,
|
||||
"radical": 600,
|
||||
"Dcaron": 600,
|
||||
"rcommaaccent": 600,
|
||||
"209": 600,
|
||||
"245": 600,
|
||||
"Rcommaaccent": 600,
|
||||
"Lcommaaccent": 600,
|
||||
"195": 600,
|
||||
"Aogonek": 600,
|
||||
"197": 600,
|
||||
"213": 600,
|
||||
"zdotaccent": 600,
|
||||
"Ecaron": 600,
|
||||
"Iogonek": 600,
|
||||
"kcommaaccent": 600,
|
||||
"minus": 600,
|
||||
"206": 600,
|
||||
"ncaron": 600,
|
||||
"tcommaaccent": 600,
|
||||
"172": 600,
|
||||
"246": 600,
|
||||
"252": 600,
|
||||
"notequal": 600,
|
||||
"gcommaaccent": 600,
|
||||
"240": 600,
|
||||
"158": 600,
|
||||
"ncommaaccent": 600,
|
||||
"185": 600,
|
||||
"imacron": 600,
|
||||
"128": 600
|
||||
},
|
||||
"CIDtoGID_Compressed": true,
|
||||
"CIDtoGID": "eJwDAAAAAAE=",
|
||||
"_version_": 6
|
||||
}
|
||||
@@ -27,36 +27,190 @@ if (empty($claves_pedimentos)) {
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>📋 Nuevo Pedimento de Importación</title>
|
||||
<title>Nuevo Pedimento de Importación</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">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Font Awesome -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<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; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease;}
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
:root {
|
||||
--primary: #2563eb;
|
||||
--primary-dark: #1d4ed8;
|
||||
--secondary: #64748b;
|
||||
--success: #059669;
|
||||
--warning: #d97706;
|
||||
--danger: #dc2626;
|
||||
--info: #0891b2;
|
||||
--light: #f8fafc;
|
||||
--dark: #0f172a;
|
||||
--border-color: #e2e8f0;
|
||||
--text-muted: #64748b;
|
||||
--bg-body: #f8fafc;
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background-color: var(--bg-body);
|
||||
color: var(--dark);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 280px;
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
color: var(--dark);
|
||||
font-weight: 600;
|
||||
font-size: 1.75rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.625rem 1.25rem;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.btn-modern:hover {
|
||||
background: var(--primary-dark);
|
||||
color: white;
|
||||
transform: translateY(-1px);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-modern.success {
|
||||
background: var(--success);
|
||||
}
|
||||
|
||||
.btn-modern.success:hover {
|
||||
background: #047857;
|
||||
}
|
||||
|
||||
.btn-modern.secondary {
|
||||
background: var(--secondary);
|
||||
}
|
||||
|
||||
.btn-modern.secondary:hover {
|
||||
background: #475569;
|
||||
}
|
||||
|
||||
.form-card {
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
margin-bottom: 1.5rem;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.form-section-title {
|
||||
color: var(--dark);
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.form-section-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
color: var(--dark);
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.form-control, .form-select {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 0.625rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.form-control:focus, .form-select:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 0.2rem rgba(37, 99, 235, 0.1);
|
||||
}
|
||||
|
||||
.alert-modern {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 1rem 1.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.alert-modern.warning {
|
||||
background: rgba(217, 119, 6, 0.1);
|
||||
color: #92400e;
|
||||
border-left: 4px solid var(--warning);
|
||||
}
|
||||
|
||||
.alert-modern.info {
|
||||
background: rgba(8, 145, 178, 0.1);
|
||||
color: #0e7490;
|
||||
border-left: 4px solid var(--info);
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
@@ -69,25 +223,28 @@ if (empty($claves_pedimentos)) {
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="animate__animated animate__fadeInDown title-glow">📋 Nuevo Pedimento de Importación</h4>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-arrow-left"></i> Regresar a Lista
|
||||
<div class="main-content">
|
||||
<!-- Page Header -->
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Nuevo Pedimento de Importación</h1>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn-modern secondary">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
Regresar a Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card fade-in-up">
|
||||
<div class="card-body">
|
||||
<form action="/IMPORTADORES/catalogo_pedimentos/guardar" method="POST" id="formPedimento">
|
||||
<div class="row">
|
||||
<!-- Información Básica -->
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-info-circle"></i> Información del Pedimento</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Form Container -->
|
||||
<form action="/IMPORTADORES/catalogo_pedimentos/guardar" method="POST" id="formPedimento" class="fade-in">
|
||||
<div class="row">
|
||||
<!-- Información del Pedimento -->
|
||||
<div class="col-lg-6">
|
||||
<div class="form-card">
|
||||
<h3 class="form-section-title">
|
||||
<div class="form-section-icon">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
</div>
|
||||
Información del Pedimento
|
||||
</h3>
|
||||
<div class="mb-3">
|
||||
<label for="pedimento" class="form-label">Número de Pedimento <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="pedimento" name="pedimento" required
|
||||
@@ -280,11 +437,13 @@ if (empty($claves_pedimentos)) {
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-times"></i> Cancelar
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn-modern secondary">
|
||||
<i class="fas fa-times"></i>
|
||||
Cancelar
|
||||
</a>
|
||||
<button type="submit" class="btn btn-success btn-animated" <?= empty($claves_pedimentos) ? 'disabled' : '' ?>>
|
||||
<i class="fas fa-save"></i> Guardar Pedimento
|
||||
<button type="submit" class="btn-modern success" <?= empty($claves_pedimentos) ? 'disabled' : '' ?>>
|
||||
<i class="fas fa-save"></i>
|
||||
Guardar Pedimento
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -294,9 +453,8 @@ if (empty($claves_pedimentos)) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -32,22 +32,169 @@ if (!empty($previo['FechaFinal'])) {
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>📋 Editar Pedimento</title>
|
||||
<title>Editar Pedimento</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">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Font Awesome -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<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; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease;}
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
:root {
|
||||
--primary: #2563eb;
|
||||
--primary-dark: #1d4ed8;
|
||||
--secondary: #64748b;
|
||||
--success: #059669;
|
||||
--warning: #d97706;
|
||||
--danger: #dc2626;
|
||||
--info: #0891b2;
|
||||
--light: #f8fafc;
|
||||
--dark: #0f172a;
|
||||
--border-color: #e2e8f0;
|
||||
--text-muted: #64748b;
|
||||
--bg-body: #f8fafc;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background-color: var(--bg-body);
|
||||
color: var(--dark);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 280px;
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
color: var(--dark);
|
||||
font-weight: 600;
|
||||
font-size: 1.75rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.625rem 1.25rem;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.btn-modern:hover {
|
||||
background: var(--primary-dark);
|
||||
color: white;
|
||||
transform: translateY(-1px);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-modern.success {
|
||||
background: var(--success);
|
||||
}
|
||||
|
||||
.btn-modern.success:hover {
|
||||
background: #047857;
|
||||
}
|
||||
|
||||
.btn-modern.secondary {
|
||||
background: var(--secondary);
|
||||
}
|
||||
|
||||
.btn-modern.secondary:hover {
|
||||
background: #475569;
|
||||
}
|
||||
|
||||
.form-card {
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
margin-bottom: 1.5rem;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.form-section-title {
|
||||
color: var(--dark);
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.form-section-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
color: var(--dark);
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.form-control, .form-select {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 0.625rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.form-control:focus, .form-select:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 0.2rem rgba(37, 99, 235, 0.1);
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@@ -77,20 +224,21 @@ if (!empty($previo['FechaFinal'])) {
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="animate__animated animate__fadeInDown title-glow">📋 Editar Pedimento #<?= htmlspecialchars($previo['IdPrevio']) ?></h4>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-arrow-left"></i> Regresar a Lista
|
||||
<div class="main-content">
|
||||
<!-- Page Header -->
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Editar Pedimento #<?= htmlspecialchars($previo['IdPrevio']) ?></h1>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn-modern secondary">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
Regresar a Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card fade-in-up">
|
||||
<div class="card-body">
|
||||
<form action="/IMPORTADORES/catalogo_pedimentos/actualizar" method="POST" id="formPedimento">
|
||||
<input type="hidden" name="id_previo" value="<?= htmlspecialchars($previo['IdPrevio']) ?>">
|
||||
<!-- Form Container -->
|
||||
<form action="/IMPORTADORES/catalogo_pedimentos/actualizar" method="POST" id="formPedimento" class="fade-in">
|
||||
<input type="hidden" name="id_previo" value="<?= htmlspecialchars($previo['IdPrevio']) ?>">
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<!-- Información Básica -->
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3">
|
||||
@@ -310,14 +458,11 @@ if (!empty($previo['FechaFinal'])) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -4,160 +4,837 @@
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>📋 Catálogo de Pedimentos</title>
|
||||
<title>Catálogo de 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">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Font Awesome -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<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; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease;}
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
:root {
|
||||
--primary: #2563eb;
|
||||
--primary-dark: #1d4ed8;
|
||||
--secondary: #64748b;
|
||||
--success: #059669;
|
||||
--warning: #d97706;
|
||||
--danger: #dc2626;
|
||||
--info: #0891b2;
|
||||
--light: #f8fafc;
|
||||
--dark: #0f172a;
|
||||
--border-color: #e2e8f0;
|
||||
--text-muted: #64748b;
|
||||
--bg-body: #f8fafc;
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
.card-hover:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
|
||||
.btn-pulse { animation: pulse 2s infinite; }
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background-color: var(--bg-body);
|
||||
color: var(--dark);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 280px;
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
color: var(--dark);
|
||||
font-weight: 600;
|
||||
font-size: 1.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
color: var(--text-muted);
|
||||
font-weight: 400;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
height: 100%;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.25rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.feature-icon.primary { background: var(--primary); }
|
||||
.feature-icon.success { background: var(--success); }
|
||||
.feature-icon.info { background: var(--info); }
|
||||
|
||||
.feature-title {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: var(--dark);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.feature-description {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 1.5rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.btn-feature {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.625rem 1.25rem;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
width: 100%;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-feature:hover {
|
||||
background: var(--primary-dark);
|
||||
color: white;
|
||||
transform: translateY(-1px);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-feature.success {
|
||||
background: var(--success);
|
||||
}
|
||||
|
||||
.btn-feature.success:hover {
|
||||
background: #047857;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-feature.info {
|
||||
background: var(--info);
|
||||
}
|
||||
|
||||
.btn-feature.info:hover {
|
||||
background: #0e7490;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.info-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 6px;
|
||||
background: rgba(37, 99, 235, 0.1);
|
||||
color: var(--primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 0.75rem;
|
||||
}
|
||||
|
||||
.info-title {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: var(--dark);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.info-text {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.feature-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.feature-list li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.feature-list li i {
|
||||
color: var(--success);
|
||||
margin-right: 0.75rem;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
/* Responsive animations */
|
||||
@media (max-width: 768px) { .card-hover:hover { transform: translateY(-4px) scale(1.01); } }
|
||||
/* Efecto de glow para elementos activos */
|
||||
.btn.active { box-shadow: 0 0 20px rgba(var(--bs-primary-rgb), 0.5); }
|
||||
/* Animación para el título */
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.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%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4 animate__animated animate__fadeInDown title-glow">📋 Catálogo de Pedimentos</h4>
|
||||
<div class="main-content">
|
||||
<!-- Page Header -->
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Catálogo de Pedimentos</h1>
|
||||
<p class="page-subtitle">Gestiona y administra todos los pedimentos de importación del sistema</p>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 col-lg-4 mb-3">
|
||||
<div class="card bg-primary text-white card-hover fade-in-up">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h5 class="card-title">Lista de Pedimentos</h5>
|
||||
<p class="card-text">Visualizar y gestionar todos los pedimentos registrados</p>
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<i class="fas fa-list fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn btn-outline-light btn-sm btn-animated">
|
||||
Ver Lista <i class="fas fa-arrow-right"></i>
|
||||
</a>
|
||||
<!-- Features Grid -->
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card fade-in" onclick="window.location.href='/IMPORTADORES/catalogo_pedimentos/lista'">
|
||||
<div class="feature-icon primary">
|
||||
<i class="fas fa-list"></i>
|
||||
</div>
|
||||
<h3 class="feature-title">Lista de Pedimentos</h3>
|
||||
<p class="feature-description">Visualiza y gestiona todos los pedimentos registrados en el sistema con herramientas de búsqueda y filtrado avanzado.</p>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn-feature">
|
||||
Ver Lista
|
||||
<i class="fas fa-arrow-right ms-2"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4 mb-3">
|
||||
<div class="card bg-success text-white card-hover fade-in-up">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h5 class="card-title">Nuevo Pedimento</h5>
|
||||
<p class="card-text">Registrar un nuevo pedimento en el sistema</p>
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<i class="fas fa-plus-circle fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/crear" class="btn btn-outline-light btn-sm btn-animated">
|
||||
Crear Nuevo <i class="fas fa-plus"></i>
|
||||
</a>
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card fade-in" onclick="window.location.href='/IMPORTADORES/catalogo_pedimentos/crear'">
|
||||
<div class="feature-icon success">
|
||||
<i class="fas fa-plus-circle"></i>
|
||||
</div>
|
||||
<h3 class="feature-title">Nuevo Pedimento</h3>
|
||||
<p class="feature-description">Registra un nuevo pedimento con toda la información requerida y documentación necesaria para el proceso de importación.</p>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/crear" class="btn-feature success">
|
||||
Crear Nuevo
|
||||
<i class="fas fa-plus ms-2"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4 mb-3">
|
||||
<div class="card bg-info text-white card-hover fade-in-up">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h5 class="card-title">Reportes</h5>
|
||||
<p class="card-text">Generar reportes y estadísticas de pedimentos</p>
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<i class="fas fa-chart-bar fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-outline-light btn-sm btn-animated" onclick="proximamente()">
|
||||
Ver Reportes <i class="fas fa-chart-line"></i>
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card fade-in">
|
||||
<div class="feature-icon info">
|
||||
<i class="fas fa-upload"></i>
|
||||
</div>
|
||||
<h3 class="feature-title">Importar Archivo</h3>
|
||||
<p class="feature-description">Carga masiva de pedimentos desde archivo de texto con formato específico del sistema aduanero.</p>
|
||||
<button class="btn-feature info" onclick="importarPedimentos()">
|
||||
Importar Datos
|
||||
<i class="fas fa-file-import ms-2"></i>
|
||||
</button>
|
||||
<div class="mt-2">
|
||||
<button class="btn btn-sm btn-outline-secondary" onclick="verificarPedimentosExistentes()" style="border-radius: 6px; font-size: 0.8rem;">
|
||||
<i class="fas fa-search me-1"></i>Ver Existentes
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger" onclick="limpiarBaseDatos()" style="border-radius: 6px; font-size: 0.8rem;">
|
||||
<i class="fas fa-trash me-1"></i>Limpiar BD
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card fade-in">
|
||||
<div class="feature-icon warning" style="background: var(--warning);">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
</div>
|
||||
<h3 class="feature-title">Reportes</h3>
|
||||
<p class="feature-description">Genera reportes detallados y estadísticas de pedimentos para análisis y seguimiento de operaciones.</p>
|
||||
<button class="btn-feature" style="background: var(--warning);" onclick="proximamente()">
|
||||
Ver Reportes
|
||||
<i class="fas fa-chart-line ms-2"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card fade-in-up">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-info-circle"></i> Información del Módulo</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">
|
||||
El catálogo de pedimentos le permite gestionar de manera eficiente todos los pedimentos
|
||||
de importación. Desde aquí puede:
|
||||
</p>
|
||||
<ul class="list-unstyled">
|
||||
<li><i class="fas fa-check text-success"></i> Registrar nuevos pedimentos con toda la información requerida</li>
|
||||
<li><i class="fas fa-check text-success"></i> Consultar y editar pedimentos existentes</li>
|
||||
<li><i class="fas fa-check text-success"></i> Realizar búsquedas avanzadas por diversos criterios</li>
|
||||
<li><i class="fas fa-check text-success"></i> Exportar información para reportes</li>
|
||||
<li><i class="fas fa-check text-success"></i> Mantener un histórico completo de operaciones</li>
|
||||
</ul>
|
||||
<!-- Information Card -->
|
||||
<div class="info-card">
|
||||
<div class="info-card-header">
|
||||
<div class="info-icon">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
</div>
|
||||
<h3 class="info-title">Información del Módulo</h3>
|
||||
</div>
|
||||
<p class="info-text">
|
||||
El catálogo de pedimentos te permite gestionar de manera eficiente todos los pedimentos
|
||||
de importación. Desde aquí puedes realizar las siguientes acciones:
|
||||
</p>
|
||||
<ul class="feature-list">
|
||||
<li>
|
||||
<i class="fas fa-check"></i>
|
||||
Registrar nuevos pedimentos con toda la información requerida
|
||||
</li>
|
||||
<li>
|
||||
<i class="fas fa-check"></i>
|
||||
Consultar y editar pedimentos existentes
|
||||
</li>
|
||||
<li>
|
||||
<i class="fas fa-check"></i>
|
||||
Realizar búsquedas avanzadas por diversos criterios
|
||||
</li>
|
||||
<li>
|
||||
<i class="fas fa-check"></i>
|
||||
Exportar información para reportes y análisis
|
||||
</li>
|
||||
<li>
|
||||
<i class="fas fa-check"></i>
|
||||
Mantener un histórico completo de operaciones
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal para Importar Archivo -->
|
||||
<div class="modal fade" id="modalImportarPedimentos" tabindex="-1" aria-labelledby="modalImportarLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content" style="border-radius: 12px; border: none;">
|
||||
<div class="modal-header" style="background: linear-gradient(135deg, var(--info) 0%, #0e7490 100%); color: white; border-radius: 12px 12px 0 0;">
|
||||
<h5 class="modal-title" id="modalImportarLabel">
|
||||
<i class="fas fa-upload me-2"></i>
|
||||
Importar Pedimentos desde Archivo
|
||||
</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body p-4">
|
||||
<div class="alert alert-info d-flex align-items-start" style="border: none; border-radius: 8px; background: rgba(8, 145, 178, 0.1); border-left: 4px solid var(--info);">
|
||||
<i class="fas fa-info-circle me-2 mt-1" style="color: var(--info);"></i>
|
||||
<div>
|
||||
<strong>Formato del archivo:</strong>
|
||||
<p class="mb-1">El sistema reconoce archivos con los siguientes códigos:</p>
|
||||
<ul class="mb-2 small">
|
||||
<li><strong>501:</strong> Datos del pedimento (número, RFC, fechas)</li>
|
||||
<li><strong>505:</strong> Información de facturas</li>
|
||||
<li><strong>551:</strong> Partidas de mercancías</li>
|
||||
</ul>
|
||||
<p class="mb-0 small text-muted">
|
||||
<i class="fas fa-calendar-day me-1"></i>
|
||||
<strong>Archivos julianos:</strong> Se aceptan extensiones de día del año (.001 a .366)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form id="formImportarPedimentos" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label for="archivoImportar" class="form-label fw-semibold">
|
||||
<i class="fas fa-file-text me-1"></i>
|
||||
Seleccionar archivos
|
||||
</label>
|
||||
<input type="file" class="form-control" id="archivoImportar" name="archivos[]" multiple required style="border-radius: 8px; border: 1px solid var(--border-color);">
|
||||
<div class="form-text">Formatos soportados: .txt, .dat, archivos julianos (.001-.366). Puedes seleccionar varios archivos.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="validarDuplicados" name="validar_duplicados" checked>
|
||||
<label class="form-check-label fw-medium" for="validarDuplicados">
|
||||
Validar pedimentos duplicados
|
||||
</label>
|
||||
<div class="form-text">Evita importar pedimentos que ya existen en el sistema</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="previewContainer" style="display: none;" class="mb-3">
|
||||
<h6 class="fw-semibold mb-2">
|
||||
<i class="fas fa-eye me-1"></i>
|
||||
Vista previa del archivo
|
||||
</h6>
|
||||
<div id="previewContent" class="p-3" style="background: #f8fafc; border-radius: 8px; border: 1px solid var(--border-color); max-height: 200px; overflow-y: auto; font-family: monospace; font-size: 0.85rem;"></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer" style="border-top: 1px solid var(--border-color); border-radius: 0 0 12px 12px;">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" style="border-radius: 8px;">
|
||||
<i class="fas fa-times me-1"></i>
|
||||
Cancelar
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" id="btnProcesarArchivo" style="border-radius: 8px; background: var(--info); border-color: var(--info);" disabled>
|
||||
<i class="fas fa-cogs me-1"></i>
|
||||
Procesar Archivos
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<script>
|
||||
// Toast helper (Bootstrap 5): muestra un toast en la esquina superior derecha
|
||||
function ensureToastContainer() {
|
||||
let container = document.getElementById('toastContainer');
|
||||
if (!container) {
|
||||
container = document.createElement('div');
|
||||
container.id = 'toastContainer';
|
||||
container.className = 'position-fixed';
|
||||
container.style.top = 'calc(var(--navbar-height, 70px) + 10px)';
|
||||
container.style.right = '16px';
|
||||
container.style.zIndex = '11000';
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
||||
function mostrarToastImportacion(estadisticas) {
|
||||
const ped = Number(estadisticas.pedimentos_procesados || 0);
|
||||
const fac = Number(estadisticas.facturas_procesadas || 0);
|
||||
const par = Number(estadisticas.partidas_procesadas || 0);
|
||||
const dup = Number(estadisticas.duplicados || 0);
|
||||
const err = Number(estadisticas.errores || 0);
|
||||
|
||||
const variant = err > 0 ? 'danger' : (dup > 0 ? 'warning' : 'success');
|
||||
const title = err > 0 ? 'Importación con advertencias' : 'Importación completada';
|
||||
|
||||
const container = ensureToastContainer();
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast align-items-center text-bg-${variant} border-0 shadow`;
|
||||
toast.setAttribute('role', 'alert');
|
||||
toast.setAttribute('aria-live', 'assertive');
|
||||
toast.setAttribute('aria-atomic', 'true');
|
||||
|
||||
toast.innerHTML = `
|
||||
<div class="d-flex">
|
||||
<div class="toast-body">
|
||||
<div class="fw-semibold mb-1"><i class="fas ${variant==='success' ? 'fa-check-circle' : (variant==='warning' ? 'fa-exclamation-triangle' : 'fa-times-circle')} me-1"></i>${title}</div>
|
||||
<div class="small">
|
||||
<span class="me-2">Pedimentos <span class="badge bg-light text-dark">${ped}</span></span>
|
||||
<span class="me-2">Facturas <span class="badge bg-light text-dark">${fac}</span></span>
|
||||
<span class="me-2">Partidas <span class="badge bg-light text-dark">${par}</span></span>
|
||||
${dup > 0 ? `<span class=\"me-2\">Duplicados <span class=\"badge bg-dark\">${dup}</span></span>` : ''}
|
||||
${err > 0 ? `<span class=\"me-2\">Errores <span class=\"badge bg-dark\">${err}</span></span>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>`;
|
||||
|
||||
container.appendChild(toast);
|
||||
const bsToast = new bootstrap.Toast(toast, { autohide: true, delay: 7000 });
|
||||
bsToast.show();
|
||||
toast.addEventListener('hidden.bs.toast', () => toast.remove());
|
||||
}
|
||||
// Base URL del proyecto (generada desde PHP)
|
||||
const BASE_URL = '<?php echo "http" . (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] === "on" ? "s" : "") . "://" . $_SERVER["HTTP_HOST"] . "/IMPORTADORES"; ?>';
|
||||
console.log('Base URL:', BASE_URL);
|
||||
|
||||
// Initialize page animations
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const fadeElements = document.querySelectorAll('.fade-in');
|
||||
fadeElements.forEach((element, index) => {
|
||||
element.style.animationDelay = (index * 0.1) + 's';
|
||||
});
|
||||
|
||||
// Add click handlers for cards
|
||||
const cards = document.querySelectorAll('.feature-card');
|
||||
cards.forEach(card => {
|
||||
card.addEventListener('mouseenter', function() {
|
||||
this.style.transform = 'translateY(-6px)';
|
||||
});
|
||||
|
||||
card.addEventListener('mouseleave', function() {
|
||||
this.style.transform = 'translateY(0)';
|
||||
});
|
||||
});
|
||||
|
||||
// Configurar eventos del modal de importación
|
||||
configurarModalImportacion();
|
||||
});
|
||||
|
||||
// Funciones para las acciones de las cards
|
||||
function irListaPedimentos() {
|
||||
window.location.href = 'lista.php';
|
||||
}
|
||||
|
||||
function irCrearPedimento() {
|
||||
window.location.href = 'crear.php';
|
||||
}
|
||||
|
||||
function importarPedimentos() {
|
||||
const modal = new bootstrap.Modal(document.getElementById('modalImportarPedimentos'));
|
||||
modal.show();
|
||||
}
|
||||
|
||||
function proximamente() {
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: 'Próximamente',
|
||||
text: 'Esta función estará disponible próximamente.',
|
||||
confirmButtonColor: '#0d6efd'
|
||||
confirmButtonColor: '#2563eb',
|
||||
customClass: {
|
||||
popup: 'rounded-3'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Configuración del modal de importación
|
||||
function configurarModalImportacion() {
|
||||
const archivoInput = document.getElementById('archivoImportar');
|
||||
const previewContainer = document.getElementById('previewContainer');
|
||||
const previewContent = document.getElementById('previewContent');
|
||||
const btnProcesar = document.getElementById('btnProcesarArchivo');
|
||||
|
||||
if (!archivoInput) return; // Si no existe el elemento, salir
|
||||
|
||||
// Manejar selección de archivos (múltiples)
|
||||
archivoInput.addEventListener('change', function(e) {
|
||||
const archivos = Array.from(e.target.files || []);
|
||||
if (archivos.length > 0) {
|
||||
// Validar extensiones de todos los archivos seleccionados
|
||||
const extensionesValidas = ['txt', 'dat'];
|
||||
for (const file of archivos) {
|
||||
const nombreArchivo = file.name.toLowerCase();
|
||||
const extension = nombreArchivo.split('.').pop();
|
||||
const esJuliana = /^\d{3}$/.test(extension); // 3 dígitos exactos
|
||||
if (!extensionesValidas.includes(extension) && !esJuliana) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Archivo no válido',
|
||||
text: `"${file.name}" no es válido. Usa .txt, .dat o extensión juliana (.001-.366)`,
|
||||
confirmButtonColor: '#dc2626',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
archivoInput.value = '';
|
||||
previewContainer.style.display = 'none';
|
||||
btnProcesar.disabled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Mostrar vista previa del PRIMER archivo y el total seleccionado
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
const contenido = e.target.result;
|
||||
mostrarVistaPrevia(contenido, archivos.length);
|
||||
btnProcesar.disabled = false;
|
||||
};
|
||||
reader.readAsText(archivos[0]);
|
||||
} else {
|
||||
previewContainer.style.display = 'none';
|
||||
btnProcesar.disabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Manejar procesamiento del archivo
|
||||
btnProcesar.addEventListener('click', function() {
|
||||
procesarArchivoImportacion();
|
||||
});
|
||||
}
|
||||
|
||||
function mostrarVistaPrevia(contenido, totalArchivos = 1) {
|
||||
const previewContainer = document.getElementById('previewContainer');
|
||||
const previewContent = document.getElementById('previewContent');
|
||||
const lineas = contenido.split('\n').slice(0, 10); // Mostrar solo las primeras 10 líneas
|
||||
|
||||
const preview = lineas.map(linea => {
|
||||
// Resaltar diferentes tipos de registros
|
||||
let clase = '';
|
||||
if (linea.startsWith('501')) {
|
||||
clase = 'style="color: #059669; font-weight: bold;"'; // Verde para pedimentos
|
||||
} else if (linea.startsWith('505')) {
|
||||
clase = 'style="color: #d97706; font-weight: bold;"'; // Naranja para facturas
|
||||
} else if (linea.startsWith('551')) {
|
||||
clase = 'style="color: #2563eb; font-weight: bold;"'; // Azul para partidas
|
||||
}
|
||||
return `<div ${clase}>${linea}</div>`;
|
||||
}).join('');
|
||||
|
||||
let extra = '';
|
||||
if (contenido.split('\n').length > 10) {
|
||||
extra += '<div style="color: #6b7280; font-style: italic;">... y más líneas</div>';
|
||||
}
|
||||
if (totalArchivos > 1) {
|
||||
const restantes = totalArchivos - 1;
|
||||
extra += `<div class="mt-2" style="color: #374151; font-weight: 600;">+ ${restantes} archivo${restantes === 1 ? '' : 's'} más seleccionado${restantes === 1 ? '' : 's'}</div>`;
|
||||
}
|
||||
previewContent.innerHTML = preview + extra;
|
||||
previewContainer.style.display = 'block';
|
||||
}
|
||||
|
||||
function procesarArchivoImportacion() {
|
||||
const formData = new FormData(document.getElementById('formImportarPedimentos'));
|
||||
|
||||
// Mostrar loading en el botón
|
||||
const btnProcesar = document.getElementById('btnProcesarArchivo');
|
||||
const textoOriginal = btnProcesar.innerHTML;
|
||||
btnProcesar.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Procesando...';
|
||||
btnProcesar.disabled = true;
|
||||
|
||||
// Llamada AJAX al backend
|
||||
const url = `${BASE_URL}/public/importar_pedimentos.php`;
|
||||
console.log('Attempting to fetch:', url);
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => {
|
||||
console.log('Response status:', response.status);
|
||||
console.log('Response headers:', response.headers);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log('Response data:', data);
|
||||
// Restaurar botón
|
||||
btnProcesar.innerHTML = textoOriginal;
|
||||
btnProcesar.disabled = false;
|
||||
|
||||
if (data.success) {
|
||||
// Verificar si es respuesta de prueba o de importación real
|
||||
if (data.estadisticas) {
|
||||
// Respuesta de importación real
|
||||
const estadisticas = data.estadisticas;
|
||||
// Mostrar toast superior derecho en lugar del modal
|
||||
mostrarToastImportacion(estadisticas);
|
||||
} else if (data.diagnostico) {
|
||||
// Respuesta de diagnóstico
|
||||
let mensaje = `🔍 Diagnóstico del Sistema:\n\n`;
|
||||
|
||||
// Mostrar checks
|
||||
if (data.checks) {
|
||||
mensaje += `📊 Estado de Componentes:\n`;
|
||||
mensaje += `• Sesión: ${data.checks.sesion ? '✅' : '❌'}\n`;
|
||||
mensaje += `• Usuario ID: ${data.checks.usuario_id || 'N/A'}\n`;
|
||||
mensaje += `• Tipo Usuario: ${data.checks.tipo_usuario || 'N/A'}\n`;
|
||||
mensaje += `• Método POST: ${data.checks.metodo_post ? '✅' : '❌'}\n`;
|
||||
mensaje += `• Archivo recibido: ${data.checks.archivo_recibido ? '✅' : '❌'}\n`;
|
||||
mensaje += `• Conexión DB: ${data.checks.conexion_db ? '✅' : '❌'}\n`;
|
||||
mensaje += `• Dir. temporal: ${data.checks.directorio_temp_existe ? '✅' : '❌'}\n\n`;
|
||||
}
|
||||
|
||||
// Mostrar errores potenciales
|
||||
if (data.errores_potenciales && data.errores_potenciales.length > 0) {
|
||||
mensaje += `⚠️ Problemas detectados:\n`;
|
||||
data.errores_potenciales.forEach(error => {
|
||||
mensaje += `• ${error}\n`;
|
||||
});
|
||||
mensaje += `\n`;
|
||||
}
|
||||
|
||||
// Información del archivo
|
||||
if (data.checks?.archivo_recibido) {
|
||||
mensaje += `📁 Archivo: ${data.checks.archivo_nombre}\n`;
|
||||
if (data.total_lineas) {
|
||||
mensaje += `📏 Total líneas: ${data.total_lineas}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
const icono = data.errores_potenciales.length > 0 ? 'warning' : 'success';
|
||||
const titulo = data.errores_potenciales.length > 0 ? 'Problemas Detectados' : 'Sistema OK';
|
||||
|
||||
Swal.fire({
|
||||
icon: icono,
|
||||
title: titulo,
|
||||
text: mensaje,
|
||||
confirmButtonColor: icono === 'warning' ? '#d97706' : '#059669',
|
||||
customClass: {
|
||||
popup: 'rounded-3'
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Respuesta de prueba genérica
|
||||
let mensaje = `✅ Conexión exitosa!\n\n`;
|
||||
mensaje += `📁 Archivo: ${data.files_data?.nombre || 'No detectado'}\n`;
|
||||
mensaje += `📏 Tamaño: ${data.files_data?.tamaño || 'N/A'} bytes\n`;
|
||||
mensaje += `🕒 Timestamp: ${data.timestamp}\n`;
|
||||
mensaje += `👤 Usuario: ${data.usuario}\n\n`;
|
||||
mensaje += `ℹ️ Esto es una prueba de conexión.`;
|
||||
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: 'Prueba de Conexión',
|
||||
text: mensaje,
|
||||
confirmButtonColor: '#2563eb',
|
||||
customClass: {
|
||||
popup: 'rounded-3'
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Mostrar error
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error en la Importación',
|
||||
text: data.message || 'Ocurrió un error al procesar el archivo',
|
||||
confirmButtonColor: '#dc2626',
|
||||
customClass: {
|
||||
popup: 'rounded-3'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Cerrar modal solo si fue exitoso
|
||||
if (data.success) {
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('modalImportarPedimentos'));
|
||||
modal.hide();
|
||||
|
||||
// Limpiar formulario
|
||||
document.getElementById('formImportarPedimentos').reset();
|
||||
document.getElementById('previewContainer').style.display = 'none';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// Restaurar botón en caso de error
|
||||
btnProcesar.innerHTML = textoOriginal;
|
||||
btnProcesar.disabled = false;
|
||||
|
||||
console.error('Error:', error);
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error de Conexión',
|
||||
text: 'No se pudo conectar con el servidor. Verifique su conexión e intente nuevamente.',
|
||||
confirmButtonColor: '#dc2626',
|
||||
customClass: {
|
||||
popup: 'rounded-3'
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Funciones para gestión de datos
|
||||
function verificarPedimentosExistentes() {
|
||||
fetch(`${BASE_URL}/public/verificar_pedimentos.php`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const stats = data.estadisticas;
|
||||
let mensaje = `📊 Pedimentos en Base de Datos:\n\n`;
|
||||
mensaje += `• Total: ${stats.total_pedimentos}\n`;
|
||||
mensaje += `• Usuarios: ${stats.usuarios_distintos}\n`;
|
||||
|
||||
if (stats.total_pedimentos > 0) {
|
||||
mensaje += `• Primer pedimento: ${stats.primer_pedimento?.date || 'N/A'}\n`;
|
||||
mensaje += `• Último pedimento: ${stats.ultimo_pedimento?.date || 'N/A'}\n\n`;
|
||||
|
||||
if (data.pedimentos_existentes.length > 0) {
|
||||
mensaje += `🔍 Últimos 5 pedimentos:\n`;
|
||||
data.pedimentos_existentes.slice(0, 5).forEach(p => {
|
||||
mensaje += `• ${p.numero_pedimento} (${p.fecha_creacion?.date || 'N/A'})\n`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: 'Pedimentos Existentes',
|
||||
text: mensaje,
|
||||
confirmButtonColor: '#2563eb'
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Error:', error));
|
||||
}
|
||||
|
||||
function limpiarBaseDatos() {
|
||||
Swal.fire({
|
||||
title: '⚠️ Confirmar Limpieza',
|
||||
text: 'Esto eliminará TODOS los pedimentos, facturas y partidas. ¿Estás seguro?',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#dc2626',
|
||||
cancelButtonColor: '#6b7280',
|
||||
confirmButtonText: 'Sí, limpiar',
|
||||
cancelButtonText: 'Cancelar'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
fetch(`${BASE_URL}/public/limpiar_pedimentos.php`, {
|
||||
method: 'POST'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Base de Datos Limpiada',
|
||||
text: 'Todos los registros han sido eliminados. Ahora puedes importar pedimentos sin duplicados.',
|
||||
confirmButtonColor: '#059669'
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error al Limpiar',
|
||||
text: data.message || 'No se pudo limpiar la base de datos',
|
||||
confirmButtonColor: '#dc2626'
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error de Conexión',
|
||||
text: 'No se pudo conectar al servidor para limpiar la BD',
|
||||
confirmButtonColor: '#dc2626'
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Limpiar modal al cerrar
|
||||
const modal = document.getElementById('modalImportarPedimentos');
|
||||
if (modal) {
|
||||
modal.addEventListener('hidden.bs.modal', function() {
|
||||
document.getElementById('formImportarPedimentos').reset();
|
||||
document.getElementById('previewContainer').style.display = 'none';
|
||||
document.getElementById('btnProcesarArchivo').disabled = true;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -4,80 +4,316 @@
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>📋 Catálogo de Pedimentos</title>
|
||||
<title>Lista de 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">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<link href="https://cdn.datatables.net/1.13.4/css/dataTables.bootstrap5.min.css" rel="stylesheet">
|
||||
<style>
|
||||
table.dataTable thead th { background:#343a40; color:#fff; }
|
||||
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; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease;}
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
:root {
|
||||
--primary: #2563eb;
|
||||
--primary-dark: #1d4ed8;
|
||||
--secondary: #64748b;
|
||||
--success: #059669;
|
||||
--warning: #d97706;
|
||||
--danger: #dc2626;
|
||||
--info: #0891b2;
|
||||
--light: #f8fafc;
|
||||
--dark: #0f172a;
|
||||
--border-color: #e2e8f0;
|
||||
--text-muted: #64748b;
|
||||
--bg-body: #f8fafc;
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
.card-hover:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
|
||||
.btn-pulse { animation: pulse 2s infinite; }
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background-color: var(--bg-body);
|
||||
color: var(--dark);
|
||||
line-height: 1.6;
|
||||
}
|
||||
/* Responsive animations */
|
||||
@media (max-width: 768px) { .card-hover:hover { transform: translateY(-4px) scale(1.01); } }
|
||||
/* Efecto de glow para elementos activos */
|
||||
.btn.active { box-shadow: 0 0 20px rgba(var(--bs-primary-rgb), 0.5); }
|
||||
/* Animación para el título */
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.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%; }
|
||||
|
||||
.main-content {
|
||||
margin-left: 280px;
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
color: var(--dark);
|
||||
font-weight: 600;
|
||||
font-size: 1.75rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.625rem 1.25rem;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.btn-modern:hover {
|
||||
background: var(--primary-dark);
|
||||
color: white;
|
||||
transform: translateY(-1px);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-modern.success {
|
||||
background: var(--success);
|
||||
}
|
||||
|
||||
.btn-modern.success:hover {
|
||||
background: #047857;
|
||||
}
|
||||
|
||||
.btn-modern.secondary {
|
||||
background: var(--secondary);
|
||||
}
|
||||
|
||||
.btn-modern.secondary:hover {
|
||||
background: #475569;
|
||||
}
|
||||
|
||||
.btn-modern.danger {
|
||||
background: var(--danger);
|
||||
}
|
||||
|
||||
.btn-modern.danger:hover {
|
||||
background: #b91c1c;
|
||||
}
|
||||
|
||||
.data-card {
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.table-container {
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table {
|
||||
margin-bottom: 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.table thead th {
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 1rem 0.75rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
border-bottom: 3px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.table tbody td {
|
||||
padding: 0.875rem 0.75rem;
|
||||
border-color: var(--border-color);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table tbody td:first-child {
|
||||
font-weight: 600;
|
||||
color: var(--primary);
|
||||
font-family: 'JetBrains Mono', 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.table tbody tr {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.table tbody tr:hover {
|
||||
background-color: rgba(37, 99, 235, 0.05);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.badge-modern {
|
||||
padding: 0.4rem 0.8rem;
|
||||
border-radius: 20px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.badge-success {
|
||||
background: linear-gradient(135deg, var(--success), #047857);
|
||||
color: white;
|
||||
box-shadow: 0 2px 4px rgba(5, 150, 105, 0.2);
|
||||
}
|
||||
|
||||
.badge-secondary {
|
||||
background: linear-gradient(135deg, var(--secondary), #475569);
|
||||
color: white;
|
||||
box-shadow: 0 2px 4px rgba(100, 116, 139, 0.2);
|
||||
}
|
||||
|
||||
.btn-action {
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 8px;
|
||||
font-size: 0.75rem;
|
||||
margin: 0 0.25rem;
|
||||
transition: all 0.2s ease;
|
||||
border: 1px solid transparent;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.btn-action:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.btn-action.edit {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-action.edit:hover {
|
||||
background: var(--primary-dark);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-action.delete {
|
||||
background: var(--danger);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-action.delete:hover {
|
||||
background: #b91c1c;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* DataTables customization */
|
||||
.dataTables_wrapper .dataTables_length,
|
||||
.dataTables_wrapper .dataTables_filter,
|
||||
.dataTables_wrapper .dataTables_info,
|
||||
.dataTables_wrapper .dataTables_paginate {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.dataTables_wrapper .dataTables_length select,
|
||||
.dataTables_wrapper .dataTables_filter input {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 0.375rem 0.75rem;
|
||||
}
|
||||
|
||||
.dataTables_wrapper .dataTables_paginate .paginate_button {
|
||||
padding: 0.375rem 0.75rem !important;
|
||||
margin: 0 0.125rem !important;
|
||||
border-radius: 6px !important;
|
||||
border: 1px solid var(--border-color) !important;
|
||||
color: var(--dark) !important;
|
||||
}
|
||||
|
||||
.dataTables_wrapper .dataTables_paginate .paginate_button.current {
|
||||
background: var(--primary) !important;
|
||||
color: white !important;
|
||||
border-color: var(--primary) !important;
|
||||
}
|
||||
|
||||
.dataTables_wrapper .dataTables_paginate .paginate_button:hover {
|
||||
background: var(--primary) !important;
|
||||
color: white !important;
|
||||
border-color: var(--primary) !important;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
/* Drawer lateral */
|
||||
.drawer { position: fixed; inset: 0; z-index: 99999; display: none; }
|
||||
.drawer.open { display: block; }
|
||||
.drawer-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0.35); z-index: 99998; }
|
||||
.drawer-panel { position: absolute; top: 0; right: -520px; width: 520px; max-width: 94vw; height: 100%; background: #fff; border-left: 1px solid var(--border-color); box-shadow: -8px 0 24px rgba(0,0,0,0.1); transition: right 0.3s ease; display: flex; flex-direction: column; z-index: 99999; }
|
||||
.drawer.open .drawer-panel { right: 0; }
|
||||
.drawer-header { padding: 1rem 1rem; border-bottom: 1px solid var(--border-color); display: flex; justify-content: space-between; align-items: center; }
|
||||
.drawer-title { font-weight: 600; font-size: 1.1rem; }
|
||||
.drawer-subtitle { color: var(--text-muted); font-size: 0.9rem; }
|
||||
.page-subtitle { color: var(--text-muted); font-weight: 500; font-size: 1rem; margin-left: 0.75rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="animate__animated animate__fadeInDown title-glow">📋 Lista de Pedimentos</h4>
|
||||
<div>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/crear" class="btn btn-success btn-animated me-2">
|
||||
<i class="fas fa-plus"></i> Nuevo Pedimento
|
||||
<div class="main-content">
|
||||
<!-- Page Header -->
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Lista de Pedimentos <span id="headerPedimentoActual" class="page-subtitle"></span></h1>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/crear" class="btn-modern success">
|
||||
<i class="fas fa-plus"></i>
|
||||
Nuevo Pedimento
|
||||
</a>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-arrow-left"></i> Regresar
|
||||
<button type="button" class="btn-modern" style="background: var(--info);" onclick="abrirModalImportar()">
|
||||
<i class="fas fa-upload"></i>
|
||||
Importar Archivos
|
||||
</button>
|
||||
<button type="button" id="btnImportarWinsaai" class="btn-modern secondary" onclick="importarDesdeWinsaai()" title="Requiere configuración en Automatizaciones (WINSAAI)">
|
||||
<i class="fas fa-cloud-download-alt"></i>
|
||||
Importar desde WINSAAI
|
||||
</button>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos" class="btn-modern secondary">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
Regresar
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card p-3 shadow-sm card-hover position-relative h-auto fade-in-up">
|
||||
<div class="table-responsive">
|
||||
<table id="tablaPedimentos" class="display nowrap" style="width:100%">
|
||||
<!-- Data Table -->
|
||||
<div class="data-card fade-in">
|
||||
<div class="table-container">
|
||||
<table id="tablaPedimentos" class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
@@ -97,16 +333,152 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<!-- Modal para Importar Archivos -->
|
||||
<div class="modal fade" id="modalImportarPedimentos" tabindex="-1" aria-labelledby="modalImportarLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content" style="border-radius: 12px; border: none;">
|
||||
<div class="modal-header" style="background: linear-gradient(135deg, var(--info) 0%, #0e7490 100%); color: white; border-radius: 12px 12px 0 0;">
|
||||
<h5 class="modal-title" id="modalImportarLabel">
|
||||
<i class="fas fa-upload me-2"></i>
|
||||
Importar Pedimentos desde Archivos
|
||||
</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body p-4">
|
||||
<div class="alert alert-info d-flex align-items-start" style="border: none; border-radius: 8px; background: rgba(8, 145, 178, 0.1); border-left: 4px solid var(--info);">
|
||||
<i class="fas fa-info-circle me-2 mt-1" style="color: var(--info);"></i>
|
||||
<div>
|
||||
<strong>Formato del archivo:</strong>
|
||||
<p class="mb-1">El sistema reconoce archivos con los siguientes códigos:</p>
|
||||
<ul class="mb-2 small">
|
||||
<li><strong>501:</strong> Datos del pedimento (número, RFC, fechas)</li>
|
||||
<li><strong>505:</strong> Información de facturas</li>
|
||||
<li><strong>551:</strong> Partidas de mercancías</li>
|
||||
</ul>
|
||||
<p class="mb-0 small text-muted">
|
||||
<i class="fas fa-calendar-day me-1"></i>
|
||||
<strong>Archivos julianos:</strong> Se aceptan extensiones de día del año (.001 a .366)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form id="formImportarPedimentos" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label for="archivoImportar" class="form-label fw-semibold">
|
||||
<i class="fas fa-file-text me-1"></i>
|
||||
Seleccionar archivos
|
||||
</label>
|
||||
<input type="file" class="form-control" id="archivoImportar" name="archivos[]" multiple required style="border-radius: 8px; border: 1px solid var(--border-color);">
|
||||
<div class="form-text">Formatos soportados: .txt, .dat, archivos julianos (.001-.366). Puedes seleccionar varios archivos.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="validarDuplicados" name="validar_duplicados" checked>
|
||||
<label class="form-check-label fw-medium" for="validarDuplicados">
|
||||
Validar pedimentos duplicados
|
||||
</label>
|
||||
<div class="form-text">Evita importar pedimentos que ya existen en el sistema</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="previewContainer" style="display: none;" class="mb-3">
|
||||
<h6 class="fw-semibold mb-2">
|
||||
<i class="fas fa-eye me-1"></i>
|
||||
Vista previa del archivo
|
||||
</h6>
|
||||
<div id="previewContent" class="p-3" style="background: #f8fafc; border-radius: 8px; border: 1px solid var(--border-color); max-height: 200px; overflow-y: auto; font-family: monospace; font-size: 0.85rem;"></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer" style="border-top: 1px solid var(--border-color); border-radius: 0 0 12px 12px;">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" style="border-radius: 8px;">
|
||||
<i class="fas fa-times me-1"></i>
|
||||
Cancelar
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" id="btnProcesarArchivo" style="border-radius: 8px; background: var(--info); border-color: var(--info);" disabled>
|
||||
<i class="fas fa-cogs me-1"></i>
|
||||
Procesar Archivos
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Drawer lateral de detalle -->
|
||||
<div id="drawerDetalle" class="drawer" aria-hidden="true">
|
||||
<div class="drawer-overlay" onclick="cerrarDetalle()"></div>
|
||||
<div class="drawer-panel">
|
||||
<div class="drawer-header">
|
||||
<div>
|
||||
<div class="drawer-title">Pedimento <span id="drawerNumero"></span></div>
|
||||
<div class="drawer-subtitle" id="drawerSubtitulo"></div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="cerrarDetalle()" aria-label="Cerrar">×</button>
|
||||
</div>
|
||||
<div class="drawer-body p-3 overflow-auto">
|
||||
<ul class="nav nav-tabs" id="detalleTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="tab-facturas" data-bs-toggle="tab" data-bs-target="#pane-facturas" type="button" role="tab">Facturas</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="tab-partidas" data-bs-toggle="tab" data-bs-target="#pane-partidas" type="button" role="tab">Partidas</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content pt-3">
|
||||
<div class="tab-pane fade show active" id="pane-facturas" role="tabpanel">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-striped align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Número</th>
|
||||
<th>Fecha</th>
|
||||
<th>Proveedor</th>
|
||||
<th>Moneda</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tbodyFacturas"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="pane-partidas" role="tabpanel">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-striped align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Fracción</th>
|
||||
<th>Descripción</th>
|
||||
<th>Cantidad</th>
|
||||
<th>UM</th>
|
||||
<th>Valor Aduana</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tbodyPartidas"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- DataTables JS -->
|
||||
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.datatables.net/1.13.4/js/dataTables.bootstrap5.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Inicializar DataTable
|
||||
// Base URL del proyecto (igual que en index)
|
||||
const BASE_URL = '<?php echo "http" . (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] === "on" ? "s" : "") . "://" . $_SERVER["HTTP_HOST"] . "/IMPORTADORES"; ?>';
|
||||
|
||||
let hasWinsaaiConfig = false;
|
||||
|
||||
$(document).ready(function() {
|
||||
// Initialize DataTable with professional styling
|
||||
$('#tablaPedimentos').DataTable({
|
||||
"processing": true,
|
||||
"serverSide": true,
|
||||
@@ -127,9 +499,9 @@
|
||||
"name": "Status",
|
||||
"render": function(data, type, row) {
|
||||
if (data === 'Activo') {
|
||||
return '<span class="badge bg-success">Activo</span>';
|
||||
return '<span class="badge-modern badge-success"><i class="fas fa-check-circle"></i>Activo</span>';
|
||||
} else {
|
||||
return '<span class="badge bg-secondary">Inactivo</span>';
|
||||
return '<span class="badge-modern badge-secondary"><i class="fas fa-pause-circle"></i>Inactivo</span>';
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -140,12 +512,20 @@
|
||||
"render": function(data, type, row) {
|
||||
const id = row[0];
|
||||
return `
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/editar?id=${id}" class="btn btn-sm btn-primary btn-animated" title="Editar">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-danger btn-animated" onclick="confirmarEliminar(${id})" title="Eliminar">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
<div class="d-flex justify-content-center gap-1">
|
||||
<button type="button" class="btn-action view" onclick="abrirDetalle(${id})" title="Ver detalle">
|
||||
<i class="fas fa-list"></i>
|
||||
<span>Detalle</span>
|
||||
</button>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/editar?id=${id}" class="btn-action edit" title="Editar pedimento">
|
||||
<i class="fas fa-edit"></i>
|
||||
<span>Editar</span>
|
||||
</a>
|
||||
<button type="button" class="btn-action delete" onclick="confirmarEliminar(${id})" title="Eliminar pedimento">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
<span>Eliminar</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@@ -156,8 +536,23 @@
|
||||
"responsive": true,
|
||||
"order": [[4, "desc"]],
|
||||
"pageLength": 25,
|
||||
"lengthMenu": [[10, 25, 50, 100], [10, 25, 50, 100]]
|
||||
"lengthMenu": [[10, 25, 50, 100], [10, 25, 50, 100]],
|
||||
"dom": "<'row'<'col-sm-6'l><'col-sm-6'f>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>"
|
||||
});
|
||||
|
||||
// Add fade-in animation
|
||||
setTimeout(() => {
|
||||
document.querySelector('.fade-in').style.opacity = '1';
|
||||
document.querySelector('.fade-in').style.transform = 'translateY(0)';
|
||||
}, 100);
|
||||
|
||||
// Configurar modal importar
|
||||
configurarModalImportacion();
|
||||
|
||||
// Cargar estado de configuración WINSAAI
|
||||
cargarConfigWinsaai();
|
||||
});
|
||||
|
||||
function confirmarEliminar(id) {
|
||||
@@ -168,7 +563,11 @@
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Sí, eliminar',
|
||||
cancelButtonText: 'Cancelar',
|
||||
confirmButtonColor: '#d33'
|
||||
confirmButtonColor: '#dc2626',
|
||||
cancelButtonColor: '#64748b',
|
||||
customClass: {
|
||||
popup: 'rounded-3'
|
||||
}
|
||||
}).then(result => {
|
||||
if (result.isConfirmed) {
|
||||
window.location = `/IMPORTADORES/catalogo_pedimentos/eliminar?id=${id}`;
|
||||
@@ -176,18 +575,378 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Mostrar alertas de éxito/error
|
||||
// Success/Error alerts
|
||||
<?php if (isset($_GET['created'])): ?>
|
||||
Swal.fire({ icon: 'success', title: 'Pedimento creado', text: 'El pedimento se creó correctamente.', confirmButtonColor: '#198754' });
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Pedimento creado',
|
||||
text: 'El pedimento se creó correctamente.',
|
||||
confirmButtonColor: '#059669',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET['updated'])): ?>
|
||||
Swal.fire({ icon: 'success', title: 'Pedimento actualizado', text: 'Los datos fueron modificados correctamente.', confirmButtonColor: '#198754' });
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Pedimento actualizado',
|
||||
text: 'Los datos fueron modificados correctamente.',
|
||||
confirmButtonColor: '#059669',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET['deleted'])): ?>
|
||||
Swal.fire({ icon: 'success', title: 'Pedimento eliminado', text: 'El pedimento fue eliminado correctamente.', confirmButtonColor: '#198754' });
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Pedimento eliminado',
|
||||
text: 'El pedimento fue eliminado correctamente.',
|
||||
confirmButtonColor: '#059669',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
<?php endif; ?>
|
||||
|
||||
// --- Importación de pedimentos (múltiples) ---
|
||||
async function cargarConfigWinsaai() {
|
||||
try {
|
||||
const resp = await fetch(`${BASE_URL}/winsaai/get_config`);
|
||||
const data = await resp.json();
|
||||
hasWinsaaiConfig = !!(data && data.success && data.data);
|
||||
} catch (e) {
|
||||
hasWinsaaiConfig = false;
|
||||
}
|
||||
// Ajustar estilo/tooltip del botón según estado
|
||||
const btn = document.getElementById('btnImportarWinsaai');
|
||||
if (!btn) return;
|
||||
if (hasWinsaaiConfig) {
|
||||
btn.classList.remove('secondary');
|
||||
btn.style.background = 'var(--success)';
|
||||
btn.title = 'Configuración detectada: listo para importar (próximamente)';
|
||||
} else {
|
||||
btn.classList.add('secondary');
|
||||
btn.style.background = 'var(--secondary)';
|
||||
btn.title = 'Requiere configurar WINSAAI en Automatizaciones';
|
||||
}
|
||||
}
|
||||
|
||||
function importarDesdeWinsaai() {
|
||||
if (!hasWinsaaiConfig) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Configura WINSAAI primero',
|
||||
text: 'Para importar desde WINSAAI necesitas configurar la conexión en la sección Automatizaciones.',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Ir a Automatizaciones',
|
||||
cancelButtonText: 'Cancelar',
|
||||
confirmButtonColor: '#2563eb',
|
||||
cancelButtonColor: '#64748b',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
}).then(r => {
|
||||
if (r.isConfirmed) {
|
||||
window.location.href = `${BASE_URL}/automatizaciones`;
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Placeholder: funcionalidad próxima
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: 'Próximamente',
|
||||
text: 'La importación directa desde WINSAAI estará disponible pronto.',
|
||||
confirmButtonColor: '#2563eb',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
}
|
||||
|
||||
function abrirModalImportar() {
|
||||
const modal = new bootstrap.Modal(document.getElementById('modalImportarPedimentos'));
|
||||
modal.show();
|
||||
}
|
||||
|
||||
function configurarModalImportacion() {
|
||||
const archivoInput = document.getElementById('archivoImportar');
|
||||
const previewContainer = document.getElementById('previewContainer');
|
||||
const previewContent = document.getElementById('previewContent');
|
||||
const btnProcesar = document.getElementById('btnProcesarArchivo');
|
||||
|
||||
if (!archivoInput) return;
|
||||
|
||||
archivoInput.addEventListener('change', function(e) {
|
||||
const archivos = Array.from(e.target.files || []);
|
||||
if (archivos.length > 0) {
|
||||
const extensionesValidas = ['txt', 'dat'];
|
||||
for (const file of archivos) {
|
||||
const nombreArchivo = file.name.toLowerCase();
|
||||
const extension = nombreArchivo.split('.').pop();
|
||||
const esJuliana = /^\d{3}$/.test(extension);
|
||||
if (!extensionesValidas.includes(extension) && !esJuliana) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Archivo no válido',
|
||||
text: `"${file.name}" no es válido. Usa .txt, .dat o extensión juliana (.001-.366)`,
|
||||
confirmButtonColor: '#dc2626',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
archivoInput.value = '';
|
||||
previewContainer.style.display = 'none';
|
||||
btnProcesar.disabled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
const contenido = e.target.result;
|
||||
mostrarVistaPrevia(contenido, archivos.length);
|
||||
btnProcesar.disabled = false;
|
||||
};
|
||||
reader.readAsText(archivos[0]);
|
||||
} else {
|
||||
previewContainer.style.display = 'none';
|
||||
btnProcesar.disabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
btnProcesar.addEventListener('click', function() {
|
||||
procesarArchivoImportacion();
|
||||
});
|
||||
}
|
||||
|
||||
function mostrarVistaPrevia(contenido, totalArchivos = 1) {
|
||||
const previewContainer = document.getElementById('previewContainer');
|
||||
const previewContent = document.getElementById('previewContent');
|
||||
const lineas = contenido.split('\n').slice(0, 10);
|
||||
|
||||
const preview = lineas.map(linea => {
|
||||
let clase = '';
|
||||
if (linea.startsWith('501')) clase = 'style="color: #059669; font-weight: bold;"';
|
||||
else if (linea.startsWith('505')) clase = 'style="color: #d97706; font-weight: bold;"';
|
||||
else if (linea.startsWith('551')) clase = 'style="color: #2563eb; font-weight: bold;"';
|
||||
return `<div ${clase}>${linea}</div>`;
|
||||
}).join('');
|
||||
|
||||
let extra = '';
|
||||
if (contenido.split('\n').length > 10) {
|
||||
extra += '<div style="color: #6b7280; font-style: italic;">... y más líneas</div>';
|
||||
}
|
||||
if (totalArchivos > 1) {
|
||||
const restantes = totalArchivos - 1;
|
||||
extra += `<div class="mt-2" style="color: #374151; font-weight: 600;">+ ${restantes} archivo${restantes === 1 ? '' : 's'} más seleccionado${restantes === 1 ? '' : 's'}</div>`;
|
||||
}
|
||||
previewContent.innerHTML = preview + extra;
|
||||
previewContainer.style.display = 'block';
|
||||
}
|
||||
|
||||
function procesarArchivoImportacion() {
|
||||
const formData = new FormData(document.getElementById('formImportarPedimentos'));
|
||||
|
||||
const btnProcesar = document.getElementById('btnProcesarArchivo');
|
||||
const textoOriginal = btnProcesar.innerHTML;
|
||||
btnProcesar.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Procesando...';
|
||||
btnProcesar.disabled = true;
|
||||
|
||||
const url = `${BASE_URL}/public/importar_pedimentos.php`;
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(async response => {
|
||||
let data;
|
||||
try { data = await response.json(); } catch (_) { data = null; }
|
||||
if (!response.ok) {
|
||||
const msg = (data && data.message) ? data.message : `HTTP ${response.status}`;
|
||||
throw new Error(msg);
|
||||
}
|
||||
return data;
|
||||
})
|
||||
.then(data => {
|
||||
btnProcesar.innerHTML = textoOriginal;
|
||||
btnProcesar.disabled = false;
|
||||
|
||||
if (data.success) {
|
||||
const estadisticas = data.estadisticas || {};
|
||||
let mensaje = `Se procesaron ${estadisticas.pedimentos_procesados || 0} pedimentos exitosamente.\n\n`;
|
||||
mensaje += `• Pedimentos: ${estadisticas.pedimentos_procesados || 0}\n`;
|
||||
mensaje += `• Facturas: ${estadisticas.facturas_procesadas || 0}\n`;
|
||||
mensaje += `• Partidas: ${estadisticas.partidas_procesadas || 0}\n`;
|
||||
if ((estadisticas.duplicados || 0) > 0) mensaje += `• Duplicados omitidos: ${estadisticas.duplicados}\n`;
|
||||
if ((estadisticas.errores || 0) > 0) mensaje += `• Errores: ${estadisticas.errores}`;
|
||||
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Importación Completada',
|
||||
text: mensaje,
|
||||
confirmButtonColor: '#059669',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('modalImportarPedimentos'));
|
||||
modal.hide();
|
||||
document.getElementById('formImportarPedimentos').reset();
|
||||
document.getElementById('previewContainer').style.display = 'none';
|
||||
|
||||
// Recargar la tabla para ver nuevos pedimentos
|
||||
$('#tablaPedimentos').DataTable().ajax.reload(null, false);
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error en la Importación',
|
||||
text: data.message || 'Ocurrió un error al procesar los archivos',
|
||||
confirmButtonColor: '#dc2626',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
btnProcesar.innerHTML = textoOriginal;
|
||||
btnProcesar.disabled = false;
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error de Conexión',
|
||||
text: error.message || 'No se pudo conectar con el servidor.',
|
||||
confirmButtonColor: '#dc2626',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// --- Drawer de detalle de pedimento ---
|
||||
async function abrirDetalle(id) {
|
||||
const drawer = document.getElementById('drawerDetalle');
|
||||
const numeroEl = document.getElementById('drawerNumero');
|
||||
const subEl = document.getElementById('drawerSubtitulo');
|
||||
const tbodyF = document.getElementById('tbodyFacturas');
|
||||
const tbodyP = document.getElementById('tbodyPartidas');
|
||||
const headerPed = document.getElementById('headerPedimentoActual');
|
||||
if (!drawer) return;
|
||||
|
||||
// Limpia contenidos previos
|
||||
numeroEl.textContent = '';
|
||||
subEl.textContent = '';
|
||||
tbodyF.innerHTML = '<tr><td colspan="6" class="text-center text-muted">Cargando facturas...</td></tr>';
|
||||
tbodyP.innerHTML = '<tr><td colspan="6" class="text-center text-muted">Cargando partidas...</td></tr>';
|
||||
|
||||
// Abre el drawer inmediatamente para mejor UX
|
||||
drawer.classList.add('open');
|
||||
drawer.setAttribute('aria-hidden', 'false');
|
||||
|
||||
try {
|
||||
// Encabezado del pedimento
|
||||
const rPed = await fetch(`${BASE_URL}/catalogo_pedimentos/ajax_pedimento?id=${encodeURIComponent(id)}`);
|
||||
const dPed = await rPed.json();
|
||||
if (!dPed || !dPed.success || !dPed.data) throw new Error(dPed.message || 'No se encontró el pedimento');
|
||||
const ped = dPed.data;
|
||||
const numeroPed = ped.numero_pedimento || ped.numero || ped.id || id;
|
||||
numeroEl.textContent = numeroPed;
|
||||
const nombre = ped.nombre_importador || ped.importador || '';
|
||||
const rfc = ped.rfc_importador || ped.rfc || '';
|
||||
const fecha = (ped.fecha_creacion || ped.fecha || '').toString().substring(0, 10);
|
||||
subEl.textContent = `${nombre}${nombre && rfc ? ' • ' : ''}${rfc}${(nombre||rfc) && fecha ? ' • ' : ''}${fecha}`;
|
||||
|
||||
if (headerPed) {
|
||||
headerPed.textContent = `• Pedimento ${numeroPed}`;
|
||||
}
|
||||
|
||||
// Carga datos de detalle
|
||||
await Promise.all([
|
||||
cargarFacturas(id),
|
||||
cargarPartidas(id)
|
||||
]);
|
||||
} catch (err) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'No se pudo cargar el detalle',
|
||||
text: err.message || 'Error desconocido',
|
||||
confirmButtonColor: '#dc2626',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
cerrarDetalle();
|
||||
}
|
||||
}
|
||||
|
||||
function cerrarDetalle() {
|
||||
const drawer = document.getElementById('drawerDetalle');
|
||||
const headerPed = document.getElementById('headerPedimentoActual');
|
||||
if (!drawer) return;
|
||||
drawer.classList.remove('open');
|
||||
drawer.setAttribute('aria-hidden', 'true');
|
||||
if (headerPed) headerPed.textContent = '';
|
||||
}
|
||||
|
||||
async function cargarFacturas(pedimentoId) {
|
||||
const tbody = document.getElementById('tbodyFacturas');
|
||||
try {
|
||||
const r = await fetch(`${BASE_URL}/catalogo_pedimentos/ajax_facturas?pedimento_id=${encodeURIComponent(pedimentoId)}`);
|
||||
const d = await r.json();
|
||||
if (!d || !d.success) throw new Error(d.message || 'Error al cargar facturas');
|
||||
const arr = Array.isArray(d.data) ? d.data : [];
|
||||
if (arr.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-muted">Sin facturas</td></tr>';
|
||||
return;
|
||||
}
|
||||
const rows = arr.map((f, idx) => {
|
||||
const numero = f.numero_factura || f.factura || f.numero || '';
|
||||
const fecha = (f.fecha || f.fecha_factura || '').toString().substring(0,10);
|
||||
const proveedor = f.proveedor || f.nombre_proveedor || f.vendedor || '';
|
||||
const moneda = f.moneda || '';
|
||||
const totalRaw = f.total ?? f.importe_total ?? f.subtotal ?? f.valor_factura;
|
||||
const total = (typeof totalRaw === 'number') ? totalRaw.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) : (totalRaw || '');
|
||||
return `<tr>
|
||||
<td>${idx+1}</td>
|
||||
<td>${numero || '-'}</td>
|
||||
<td>${fecha || '-'}</td>
|
||||
<td>${proveedor || '-'}</td>
|
||||
<td>${moneda || '-'}</td>
|
||||
<td class="text-end">${total || '-'}</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
tbody.innerHTML = rows;
|
||||
} catch (e) {
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-danger">Error cargando facturas</td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
async function cargarPartidas(pedimentoId) {
|
||||
const tbody = document.getElementById('tbodyPartidas');
|
||||
try {
|
||||
const r = await fetch(`${BASE_URL}/catalogo_pedimentos/ajax_partidas?pedimento_id=${encodeURIComponent(pedimentoId)}`);
|
||||
const d = await r.json();
|
||||
if (!d || !d.success) throw new Error(d.message || 'Error al cargar partidas');
|
||||
const arr = Array.isArray(d.data) ? d.data : [];
|
||||
if (arr.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-muted">Sin partidas</td></tr>';
|
||||
return;
|
||||
}
|
||||
const rows = arr.map((p, idx) => {
|
||||
const fraccion = p.fraccion || p.fraccion_arancelaria || '';
|
||||
const descripcion = p.descripcion || p.descripcion_mercancia || '';
|
||||
const cantidad = p.cantidad ?? '';
|
||||
const um = p.um || p.unidad || '';
|
||||
const valorRaw = p.valor_aduana ?? p.valor_comercial ?? p.valor ?? '';
|
||||
const valor = (typeof valorRaw === 'number') ? valorRaw.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) : (valorRaw || '');
|
||||
return `<tr>
|
||||
<td>${idx+1}</td>
|
||||
<td>${fraccion || '-'}</td>
|
||||
<td>${descripcion || '-'}</td>
|
||||
<td class="text-end">${cantidad || '-'}</td>
|
||||
<td>${um || '-'}</td>
|
||||
<td class="text-end">${valor || '-'}</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
tbody.innerHTML = rows;
|
||||
} catch (e) {
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-danger">Error cargando partidas</td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
// Cerrar con Escape
|
||||
document.addEventListener('keyup', (ev) => {
|
||||
if (ev.key === 'Escape') {
|
||||
cerrarDetalle();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -9,92 +9,142 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.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://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||
<!-- Animate.css para animaciones adicionales -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<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; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
.card-hover:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
|
||||
.btn-pulse { animation: pulse 2s infinite; }
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
/* Responsive animations */
|
||||
@media (max-width: 768px) { .card-hover:hover { transform: translateY(-4px) scale(1.01); } }
|
||||
/* Efecto de glow para elementos activos */
|
||||
.btn.active { box-shadow: 0 0 20px rgba(var(--bs-primary-rgb), 0.5); }
|
||||
/* Animación para el título */
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.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%; }
|
||||
.table th, .table td { vertical-align: middle; }
|
||||
.folder-icon { color: #f0ad4e; font-size: 1.2rem; }
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f6f8fb; }
|
||||
.content { margin-top: 56px; padding: 32px 20px; background-color: #f6f8fb; }
|
||||
@media (min-width: 768px) { .content { margin-left: var(--sidebar-width, 280px); } }
|
||||
.title { font-weight: 700; }
|
||||
.kpi-card { border: 1px solid #e9ecef; border-radius: 12px; background: #fff; }
|
||||
.kpi-value { font-size: 1.25rem; font-weight: 700; }
|
||||
.search-wrap { gap: .75rem; }
|
||||
.card-exp { border: 1px solid #eef1f5; border-radius: 14px; transition: box-shadow .2s ease, transform .2s ease; }
|
||||
.card-exp:hover { box-shadow: 0 .75rem 1.5rem rgba(0,0,0,.08); transform: translateY(-2px); }
|
||||
.chip { display: inline-flex; align-items: center; gap: .35rem; padding: .15rem .5rem; border-radius: 999px; font-size: .78rem; border: 1px solid #e5e7eb; background: #f8fafc; color: #334155; }
|
||||
.chip i { font-size: .85rem; }
|
||||
.muted { color: #6b7280; }
|
||||
.ped-code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; letter-spacing: .5px; }
|
||||
.actions .btn { border-radius: 10px; }
|
||||
.empty-state { border: 2px dashed #cbd5e1; border-radius: 14px; padding: 32px; background: #fff; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">📁 Expedientes Electrónicos</h4>
|
||||
<div class="card p-3 shadow-sm card-hover position-relative h-auto fade-in-up">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-bordered">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th>Pedimento</th>
|
||||
<th>Fecha Factura</th>
|
||||
<th>Aduana</th>
|
||||
<th>Proveedor</th>
|
||||
<th>Archivos</th>
|
||||
<th>Tamaño Total</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($expedientes as $exp): ?>
|
||||
<tr>
|
||||
<td><i class="fas fa-folder folder-icon me-2"></i><?= htmlspecialchars($exp['numero_pedimento']) ?></td>
|
||||
<td><?= is_a($exp['fecha_factura'], 'DateTime') ? $exp['fecha_factura']->format('Y-m-d') : htmlspecialchars($exp['fecha_factura']) ?></td>
|
||||
<?php
|
||||
$totalPed = count($expedientes ?? []);
|
||||
$totalArch = 0; $totalSize = 0;
|
||||
foreach(($expedientes ?? []) as $e){ $totalArch += (int)($e['total_archivos'] ?? 0); $totalSize += (float)($e['total_tamano'] ?? 0); }
|
||||
?>
|
||||
|
||||
<td><?= htmlspecialchars($exp['aduana']) ?></td>
|
||||
<td><?= htmlspecialchars($exp['proveedor_clave']) ?></td>
|
||||
<td><?= $exp['total_archivos'] ?> archivos</td>
|
||||
<td><?= number_format($exp['total_tamano'], 2) ?> KB</td>
|
||||
<td>
|
||||
<a href="/IMPORTADORES/expediente/ver/<?= $exp['id_solicitud'] ?>" class="btn btn-outline-primary btn-sm mt-auto w-auto btn-animated">📂 Ver</a>
|
||||
<a href="/IMPORTADORES/expediente/subir/<?= $exp['id_solicitud'] ?>" class="btn btn-outline-success btn-sm mt-auto w-auto btn-animated">⬆ Subir</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="content">
|
||||
<div class="d-flex flex-wrap align-items-end justify-content-between mb-3">
|
||||
<div>
|
||||
<h3 class="title mb-1">📁 Expediente electrónico</h3>
|
||||
<div class="text-muted">Consulta y organiza los documentos por pedimento</div>
|
||||
</div>
|
||||
<div class="search-wrap d-flex align-items-center">
|
||||
<input id="searchExp" type="search" class="form-control" placeholder="Buscar pedimento, proveedor o aduana">
|
||||
<select id="filterArch" class="form-select">
|
||||
<option value="all">Todos</option>
|
||||
<option value="with">Con archivos</option>
|
||||
<option value="without">Sin archivos</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="kpi-card p-3">
|
||||
<div class="muted">Pedimentos</div>
|
||||
<div class="kpi-value"><?= (int)$totalPed ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="kpi-card p-3">
|
||||
<div class="muted">Archivos</div>
|
||||
<div class="kpi-value"><?= (int)$totalArch ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="kpi-card p-3 d-flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<div class="muted">Tamaño total</div>
|
||||
<div class="kpi-value"><?= number_format($totalSize, 2) ?> KB</div>
|
||||
</div>
|
||||
<div class="text-muted small">Incluye todos los archivos subidos a los pedimentos</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (empty($expedientes)): ?>
|
||||
<div class="empty-state text-center">
|
||||
<div class="mb-2">Aún no hay pedimentos listados.</div>
|
||||
<div class="text-muted">Cuando importes pedimentos, podrás cargar y consultar documentos desde aquí.</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="row" id="gridExp">
|
||||
<?php foreach ($expedientes as $exp):
|
||||
$ff = $exp['fecha_factura'] ?? null;
|
||||
$fecha = '-';
|
||||
if ($ff instanceof DateTime) { $fecha = $ff->format('Y-m-d'); }
|
||||
else if (is_array($ff) && isset($ff['date'])) { $fecha = substr($ff['date'],0,10); }
|
||||
elseif ($ff) { $fecha = htmlspecialchars((string)$ff); }
|
||||
$archCount = (int)($exp['total_archivos'] ?? 0);
|
||||
$hasFiles = $archCount > 0;
|
||||
?>
|
||||
<div class="col-12 col-md-6 col-xl-4 exp-card"
|
||||
data-key="<?= htmlspecialchars(($exp['pedimento_display'] ?? $exp['numero_pedimento'] ?? '').' '.($exp['proveedor'] ?? '').' '.($exp['aduana'] ?? '')) ?>"
|
||||
data-files="<?= $hasFiles ? 'with' : 'without' ?>">
|
||||
<div class="card-exp bg-white p-3 h-100">
|
||||
<div class="d-flex align-items-center justify-content-between mb-2">
|
||||
<div class="ped-code h5 mb-0"><?= htmlspecialchars($exp['pedimento_display'] ?? ($exp['numero_pedimento'] ?? '')) ?></div>
|
||||
<span class="chip <?= $hasFiles ? 'border-success text-success' : ''?>">
|
||||
<i class="fa-solid fa-paperclip"></i><?= $archCount ?> archivos
|
||||
</span>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap gap-2 mb-2">
|
||||
<span class="chip"><i class="fa-solid fa-user-tie"></i><?= htmlspecialchars($exp['proveedor'] ?? '-') ?></span>
|
||||
<span class="chip"><i class="fa-regular fa-calendar"></i><?= $fecha ?></span>
|
||||
<span class="chip"><i class="fa-solid fa-warehouse"></i><?= htmlspecialchars($exp['aduana'] ?? '-') ?></span>
|
||||
<span class="chip"><i class="fa-id-badge"></i><?= htmlspecialchars($exp['patente'] ?? '-') ?></span>
|
||||
</div>
|
||||
<div class="muted small mb-3">Tamaño: <?= number_format((float)($exp['total_tamano'] ?? 0), 2) ?> KB</div>
|
||||
<div class="actions d-flex gap-2">
|
||||
<a href="/IMPORTADORES/expediente/ver_pedimento/<?= (int)$exp['pedimento_id'] ?>" class="btn btn-outline-primary btn-sm flex-fill">
|
||||
<i class="fa-regular fa-folder-open me-1"></i> Ver expediente
|
||||
</a>
|
||||
<a href="/IMPORTADORES/expediente/subir_pedimento/<?= (int)$exp['pedimento_id'] ?>" class="btn btn-outline-success btn-sm">
|
||||
<i class="fa-solid fa-upload"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
const q = document.getElementById('searchExp');
|
||||
const f = document.getElementById('filterArch');
|
||||
const cards = Array.from(document.querySelectorAll('#gridExp .exp-card'));
|
||||
function apply(){
|
||||
const term = (q?.value || '').toLowerCase();
|
||||
const filt = f?.value || 'all';
|
||||
cards.forEach(c => {
|
||||
const key = (c.getAttribute('data-key')||'').toLowerCase();
|
||||
const has = c.getAttribute('data-files');
|
||||
const match = !term || key.includes(term);
|
||||
const byFiles = (filt==='all') || (filt==='with' && has==='with') || (filt==='without' && has==='without');
|
||||
c.style.display = (match && byFiles) ? '' : 'none';
|
||||
});
|
||||
}
|
||||
q?.addEventListener('input', apply);
|
||||
f?.addEventListener('change', apply);
|
||||
apply();
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -8,64 +8,67 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.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://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css para animaciones adicionales -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; background-color: #f4f6f9; }
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f6f8fb; }
|
||||
.content { margin-top: 56px; padding: 32px 20px; background-color: #f6f8fb; }
|
||||
@media (min-width: 768px) { .content { margin-left: var(--sidebar-width, 280px); } }
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
.card-hover:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
|
||||
.btn-pulse { animation: pulse 2s infinite; }
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
/* Responsive animations */
|
||||
@media (max-width: 768px) { .card-hover:hover { transform: translateY(-4px) scale(1.01); } }
|
||||
/* Efecto de glow para elementos activos */
|
||||
.btn.active { box-shadow: 0 0 20px rgba(var(--bs-primary-rgb), 0.5); }
|
||||
/* Animación para el título */
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.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%; }
|
||||
.table th, .table td { vertical-align: middle; }
|
||||
.folder-icon { color: #f0ad4e; font-size: 1.2rem; }
|
||||
.dropzone { border: 2px dashed #cbd5e1; border-radius: 12px; background: #fff; padding: 32px; text-align: center; cursor: pointer; transition: background .15s ease, border-color .15s ease; }
|
||||
.dropzone.dragover { background: #f1f5f9; border-color: #94a3b8; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">📤 Subir Archivos al Expediente</h4>
|
||||
<div class="card p-4 bg-white shadow-sm card-hover position-relative h-auto fade-in-up">
|
||||
<form action="/IMPORTADORES/expediente/subir_handler" method="POST" enctype="multipart/form-data">
|
||||
|
||||
<input type="hidden" name="id_solicitud" value="<?= $id_solicitud ?>">
|
||||
<h4 class="mb-4">📤 Subir archivos al expediente</h4>
|
||||
<div class="card p-4 bg-white shadow-sm">
|
||||
<form action="/IMPORTADORES/expediente/subir_handler" method="POST" enctype="multipart/form-data" id="formUpload">
|
||||
<?php if (isset($pedimento_id)): ?>
|
||||
<input type="hidden" name="pedimento_id" value="<?= (int)$pedimento_id ?>">
|
||||
<?php else: ?>
|
||||
<input type="hidden" name="id_solicitud" value="<?= $id_solicitud ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="archivo" class="form-label">Selecciona archivo(s)</label>
|
||||
<input class="form-control" type="file" name="archivos[]" id="archivo" multiple required>
|
||||
<div id="dz" class="dropzone">
|
||||
<i class="fa-solid fa-cloud-arrow-up fa-2xl mb-2"></i>
|
||||
<div class="mb-1">Arrastra y suelta los archivos aquí</div>
|
||||
<div class="text-muted small">o haz clic para seleccionar</div>
|
||||
<input class="form-control d-none" type="file" name="archivos[]" id="archivo" multiple required>
|
||||
</div>
|
||||
<div id="fileList" class="mt-3 small text-muted"></div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success mt-auto w-auto btn-animated">Subir</button>
|
||||
<a href="/IMPORTADORES/expediente/index" class="btn btn-secondary ms-2 mt-auto w-auto btn-animated">Cancelar</a>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-success">Subir</button>
|
||||
<a href="/IMPORTADORES/expediente/index" class="btn btn-secondary">Cancelar</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
const dz = document.getElementById('dz');
|
||||
const input = document.getElementById('archivo');
|
||||
const list = document.getElementById('fileList');
|
||||
function openPicker(){ input.click(); }
|
||||
function updateList(){
|
||||
const files = Array.from(input.files || []);
|
||||
if (!files.length) { list.textContent = ''; return; }
|
||||
list.innerHTML = files.map(f => `• ${f.name} (${(f.size/1024).toFixed(1)} KB)`).join('<br>');
|
||||
}
|
||||
dz.addEventListener('click', openPicker);
|
||||
dz.addEventListener('dragover', (e)=>{ e.preventDefault(); dz.classList.add('dragover'); });
|
||||
dz.addEventListener('dragleave', ()=> dz.classList.remove('dragover'));
|
||||
dz.addEventListener('drop', (e)=>{
|
||||
e.preventDefault(); dz.classList.remove('dragover');
|
||||
if (e.dataTransfer?.files?.length){ input.files = e.dataTransfer.files; updateList(); }
|
||||
});
|
||||
input.addEventListener('change', updateList);
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -7,104 +7,123 @@
|
||||
<title>📂 Ver Expediente</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css para animaciones adicionales -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; background-color: #f4f6f9; }
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
.file-icon { margin-right: 8px; }
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
.card-hover:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
|
||||
.btn-pulse { animation: pulse 2s infinite; }
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
/* Responsive animations */
|
||||
@media (max-width: 768px) { .card-hover:hover { transform: translateY(-4px) scale(1.01); } }
|
||||
/* Efecto de glow para elementos activos */
|
||||
.btn.active { box-shadow: 0 0 20px rgba(var(--bs-primary-rgb), 0.5); }
|
||||
/* Animación para el título */
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.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%; }
|
||||
.table th, .table td { vertical-align: middle; }
|
||||
.folder-icon { color: #f0ad4e; font-size: 1.2rem; }
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f6f8fb; }
|
||||
.content { margin-top: 56px; padding: 32px 20px; background-color: #f6f8fb; }
|
||||
@media (min-width: 768px) { .content { margin-left: var(--sidebar-width, 280px); } }
|
||||
.ped-code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; letter-spacing: .5px; }
|
||||
.chip { display: inline-flex; align-items: center; gap: .35rem; padding: .15rem .5rem; border-radius: 999px; font-size: .78rem; border: 1px solid #e5e7eb; background: #f8fafc; color: #334155; }
|
||||
.kpi-card { border: 1px solid #e9ecef; border-radius: 12px; background: #fff; }
|
||||
.file-card { border: 1px solid #eef1f5; border-radius: 12px; background:#fff; transition: box-shadow .2s ease, transform .2s ease; }
|
||||
.file-card:hover { box-shadow: 0 .75rem 1.5rem rgba(0,0,0,.08); transform: translateY(-2px); }
|
||||
.file-icon { width: 36px; height: 36px; display: inline-flex; align-items: center; justify-content: center; border-radius: 8px; background: #f1f5f9; }
|
||||
.toolbar .btn { border-radius: 10px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php
|
||||
$archCount = count($archivos ?? []);
|
||||
$totalSize = 0; foreach(($archivos ?? []) as $a){ $totalSize += (float)($a['tamano_archivo'] ?? 0); }
|
||||
$displayCode = $pedimento['pedimento_display'] ?? ($pedimento_id ?? '');
|
||||
?>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">📂 Archivos del Expediente</h4>
|
||||
<div class="card p-4 bg-white shadow-sm card-hover position-relative h-auto fade-in-up">
|
||||
<?php if (empty($archivos)): ?>
|
||||
|
||||
<p>No hay archivos subidos para esta solicitud.</p>
|
||||
<a href="/IMPORTADORES/expediente/subir/<?= $id_solicitud ?>" class="btn btn-success mt-auto w-auto btn-animated">➕ Subir archivos</a>
|
||||
|
||||
<?php else: ?>
|
||||
<div class="mb-3">
|
||||
<a href="/IMPORTADORES/expediente/descargar_zip/<?= $id_solicitud ?>" class="btn btn-outline-dark mt-auto w-auto btn-animated">📦 Descargar todo en ZIP</a>
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between mb-3">
|
||||
<div>
|
||||
<div class="text-muted small">Pedimento</div>
|
||||
<h3 class="ped-code mb-1"><?= htmlspecialchars($displayCode) ?></h3>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<span class="chip"><i class="fa-solid fa-warehouse"></i><?= htmlspecialchars($pedimento['aduana'] ?? '-') ?></span>
|
||||
<span class="chip"><i class="fa-id-badge"></i><?= htmlspecialchars($pedimento['patente'] ?? '-') ?></span>
|
||||
<span class="chip"><i class="fa-regular fa-calendar"></i><?= htmlspecialchars($pedimento['anio'] ?? '-') ?></span>
|
||||
<?php if (!empty($pedimento['rfc_importador'])): ?>
|
||||
<span class="chip"><i class="fa-solid fa-building"></i><?= htmlspecialchars($pedimento['rfc_importador']) ?></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<table class="table table-hover table-bordered">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Archivo </th>
|
||||
<th>Tipo</th>
|
||||
<th>Tamaño (KB)</th>
|
||||
<th>Subido por</th>
|
||||
<th>Fecha</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($archivos as $file): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<?php
|
||||
$icon = 'fa-file';
|
||||
$ext = strtolower(pathinfo($file['nombre_archivo'], PATHINFO_EXTENSION));
|
||||
if (in_array($ext, ['pdf'])) $icon = 'fa-file-pdf text-danger';
|
||||
elseif (in_array($ext, ['jpg', 'jpeg', 'png'])) $icon = 'fa-file-image text-info';
|
||||
elseif (in_array($ext, ['doc', 'docx'])) $icon = 'fa-file-word text-primary';
|
||||
elseif (in_array($ext, ['xls', 'xlsx'])) $icon = 'fa-file-excel text-success';
|
||||
elseif (in_array($ext, ['zip', 'rar'])) $icon = 'fa-file-archive text-warning';
|
||||
?>
|
||||
<i class="fas <?= $icon ?> file-icon"></i>
|
||||
<?= htmlspecialchars($file['nombre_archivo']) ?>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($file['tipo_archivo']) ?></td>
|
||||
<td><?= number_format($file['tamano_archivo'], 2) ?></td>
|
||||
<td><?= htmlspecialchars($file['creado_por']) ?></td>
|
||||
<td><?= $file['creado_en']->format('Y-m-d H:i') ?></td>
|
||||
<td class="d-flex gap-2">
|
||||
<a href="/IMPORTADORES/expediente/ver_archivo/<?= $file['id'] ?>" class="btn btn-sm btn-outline-secondary" target="_blank">👁 Ver</a>
|
||||
<a href="/IMPORTADORES/<?= $file['ruta_archivo'] ?>" download class="btn btn-sm btn-outline-primary">⬇ Descargar</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
<a href="/IMPORTADORES/expediente/index" class="btn btn-secondary mt-3">← Volver</a>
|
||||
</div>
|
||||
<div class="toolbar d-flex flex-wrap gap-2 align-items-center">
|
||||
<input id="searchFiles" type="search" class="form-control" placeholder="Buscar por nombre o tipo">
|
||||
<?php if (isset($pedimento_id)): ?>
|
||||
<a href="/IMPORTADORES/expediente/subir_pedimento/<?= (int)$pedimento_id ?>" class="btn btn-success"><i class="fa-solid fa-upload me-1"></i> Subir</a>
|
||||
<a href="/IMPORTADORES/expediente/descargar_zip_pedimento/<?= (int)$pedimento_id ?>" class="btn btn-outline-dark"><i class="fa-solid fa-box-archive me-1"></i> ZIP</a>
|
||||
<?php else: ?>
|
||||
<a href="/IMPORTADORES/expediente/subir/<?= $id_solicitud ?>" class="btn btn-success"><i class="fa-solid fa-upload me-1"></i> Subir</a>
|
||||
<a href="/IMPORTADORES/expediente/descargar_zip/<?= $id_solicitud ?>" class="btn btn-outline-dark"><i class="fa-solid fa-box-archive me-1"></i> ZIP</a>
|
||||
<?php endif; ?>
|
||||
<a href="/IMPORTADORES/expediente/index" class="btn btn-secondary">← Volver</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="kpi-card p-3">
|
||||
<div class="text-muted">Archivos</div>
|
||||
<div class="h5 mb-0"><?= (int)$archCount ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="kpi-card p-3">
|
||||
<div class="text-muted">Tamaño total</div>
|
||||
<div class="h5 mb-0"><?= number_format($totalSize, 2) ?> KB</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (empty($archivos)): ?>
|
||||
<div class="p-4 bg-white border rounded-3 text-center">
|
||||
No hay archivos subidos para este expediente.
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="row" id="gridFiles">
|
||||
<?php foreach ($archivos as $file):
|
||||
$icon = 'fa-file';
|
||||
$ext = strtolower(pathinfo($file['nombre_archivo'], PATHINFO_EXTENSION));
|
||||
if (in_array($ext, ['pdf'])) $icon = 'fa-file-pdf text-danger';
|
||||
elseif (in_array($ext, ['jpg', 'jpeg', 'png'])) $icon = 'fa-file-image text-info';
|
||||
elseif (in_array($ext, ['doc', 'docx'])) $icon = 'fa-file-word text-primary';
|
||||
elseif (in_array($ext, ['xls', 'xlsx'])) $icon = 'fa-file-excel text-success';
|
||||
elseif (in_array($ext, ['zip', 'rar'])) $icon = 'fa-file-archive text-warning';
|
||||
$fecha = $file['creado_en'] instanceof DateTime ? $file['creado_en']->format('Y-m-d H:i') : '';
|
||||
?>
|
||||
<div class="col-12 col-md-6 col-xl-4 file-item" data-key="<?= htmlspecialchars(($file['nombre_archivo'] ?? '').' '.($file['tipo_archivo'] ?? '')) ?>">
|
||||
<div class="file-card p-3 h-100">
|
||||
<div class="d-flex align-items-start gap-3 mb-2">
|
||||
<div class="file-icon"><i class="fas <?= $icon ?>"></i></div>
|
||||
<div class="flex-grow-1">
|
||||
<div class="fw-semibold"><?= htmlspecialchars($file['nombre_archivo']) ?></div>
|
||||
<div class="text-muted small">Tipo: <?= htmlspecialchars($file['tipo_archivo']) ?> · <?= number_format($file['tamano_archivo'], 2) ?> KB</div>
|
||||
<div class="text-muted small">Subido por <?= htmlspecialchars($file['creado_por']) ?> · <?= $fecha ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="/IMPORTADORES/expediente/ver_archivo/<?= $file['id'] ?>" class="btn btn-sm btn-outline-secondary" target="_blank"><i class="fa-regular fa-eye"></i></a>
|
||||
<a href="/IMPORTADORES/<?= $file['ruta_archivo'] ?>" download class="btn btn-sm btn-outline-primary"><i class="fa-solid fa-download"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
const q = document.getElementById('searchFiles');
|
||||
const items = Array.from(document.querySelectorAll('#gridFiles .file-item'));
|
||||
function apply(){
|
||||
const term = (q?.value || '').toLowerCase();
|
||||
items.forEach(it => {
|
||||
const key = (it.getAttribute('data-key')||'').toLowerCase();
|
||||
it.style.display = !term || key.includes(term) ? '' : 'none';
|
||||
});
|
||||
}
|
||||
q?.addEventListener('input', apply);
|
||||
apply();
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -355,10 +355,10 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-6">
|
||||
<div class="hero-content">
|
||||
<h1>Gestión Moderna de <span style="color: #10b981;">Importaciones</span></h1>
|
||||
<h1>Manifestación de Valor <span style="color: #10b981;">Electrónica</span></h1>
|
||||
<p class="lead">
|
||||
Simplifica tus procesos aduaneros con nuestra plataforma integral.
|
||||
Desde pedimentos hasta seguimiento, todo en un solo lugar.
|
||||
Simplifica tus trámites de MVE con nuestra plataforma especializada.
|
||||
Desde la solicitud hasta la respuesta del SAT, gestiona todo digitalmente.
|
||||
</p>
|
||||
<div class="d-flex flex-column flex-md-row gap-3">
|
||||
<a href="/IMPORTADORES/registro" class="btn-modern btn-primary-modern">
|
||||
@@ -392,7 +392,7 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-item">
|
||||
<span class="stat-number">25K+</span>
|
||||
<div class="stat-label">Trámites</div>
|
||||
<div class="stat-label">MVE Procesadas</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
@@ -414,20 +414,20 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<!-- FEATURES -->
|
||||
<section class="section-modern" id="features">
|
||||
<div class="container">
|
||||
<h2 class="section-title">Todo lo que necesitas</h2>
|
||||
<h2 class="section-title">MVE Digital Completa</h2>
|
||||
<p class="section-subtitle">
|
||||
Herramientas profesionales diseñadas para optimizar cada etapa del proceso de importación
|
||||
Herramientas especializadas para gestionar eficientemente tu Manifestación de Valor Electrónica
|
||||
</p>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-file-invoice"></i>
|
||||
<i class="fas fa-file-contract"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Gestión de Pedimentos</h5>
|
||||
<h5 class="feature-title">Solicitud MVE Automática</h5>
|
||||
<p class="feature-description">
|
||||
Crea, gestiona y da seguimiento a tus pedimentos de importación con validación automática de datos.
|
||||
Genera automáticamente tu Manifestación de Valor Electrónica con base en los datos del pedimento y facturas.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -435,11 +435,11 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-shield-check"></i>
|
||||
<i class="fas fa-clock"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Cumplimiento Automatizado</h5>
|
||||
<h5 class="feature-title">Seguimiento en Tiempo Real</h5>
|
||||
<p class="feature-description">
|
||||
Mantente al día con regulaciones aduaneras y recibe alertas sobre cambios normativos relevantes.
|
||||
Monitorea el estatus de tus MVE desde el envío hasta la respuesta del SAT con notificaciones automáticas.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -447,11 +447,11 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-analytics"></i>
|
||||
<i class="fas fa-file-signature"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Reportes Inteligentes</h5>
|
||||
<h5 class="feature-title">Firma Digital Integrada</h5>
|
||||
<p class="feature-description">
|
||||
Analiza tus operaciones con dashboards interactivos y reportes personalizables en tiempo real.
|
||||
Firma electrónicamente tus MVE con validación biométrica y cumplimiento normativo SAT.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -459,11 +459,11 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-users"></i>
|
||||
<i class="fas fa-database"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Colaboración</h5>
|
||||
<h5 class="feature-title">Historial Completo</h5>
|
||||
<p class="feature-description">
|
||||
Conecta con agentes aduanales, transportistas y proveedores en un ecosistema colaborativo.
|
||||
Mantén un registro detallado de todas tus MVE con expediente digital y documentación respaldatoria.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -471,11 +471,11 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-mobile-alt"></i>
|
||||
<i class="fas fa-calculator"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Acceso Universal</h5>
|
||||
<h5 class="feature-title">Validación Automática</h5>
|
||||
<p class="feature-description">
|
||||
Accede desde cualquier dispositivo con nuestra aplicación web responsiva y segura.
|
||||
Valida automáticamente precios, cantidades y valores con base de datos actualizada del SAT.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -483,11 +483,11 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-lock"></i>
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Seguridad Total</h5>
|
||||
<h5 class="feature-title">Cumplimiento Normativo</h5>
|
||||
<p class="feature-description">
|
||||
Protección enterprise con encriptación avanzada, backups automáticos y certificaciones de seguridad.
|
||||
Garantiza el cumplimiento total con las disposiciones del SAT para MVE según la normatividad vigente.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -498,9 +498,9 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<!-- CTA -->
|
||||
<section class="cta-section">
|
||||
<div class="container">
|
||||
<h2 class="section-title" style="margin-bottom: 1rem;">¿Listo para optimizar tus importaciones?</h2>
|
||||
<h2 class="section-title" style="margin-bottom: 1rem;">¿Listo para digitalizar tus MVE?</h2>
|
||||
<p class="section-subtitle" style="margin-bottom: 3rem;">
|
||||
Únete a más de 1,200 importadores que ya confían en nuestra plataforma
|
||||
Únete a más de 1,200 importadores que ya gestionan sus Manifestaciones de Valor Electrónica digitalmente
|
||||
</p>
|
||||
<a href="/IMPORTADORES/registro" class="btn-modern btn-primary-modern">
|
||||
<i class="fas fa-arrow-right"></i>
|
||||
|
||||
@@ -6,209 +6,357 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>Dashboard | Importador</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">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Font Awesome -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css para animaciones adicionales -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<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; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
.btn-indigo { background-color: #6610f2; color: white; }
|
||||
.btn-indigo:hover { background-color: #520dc2; color: white; }
|
||||
.text-indigo { color: #520dc2; }
|
||||
.btn-orange { background-color: orangeRed; color: white; }
|
||||
.btn-orange:hover { background-color: #ff3600; color: white; }
|
||||
.text-orange { color: orangeRed; }
|
||||
.btn-teal { background-color: #4dd4ac ; color: white; }
|
||||
.btn-teal:hover { background-color: #20c997; color: black; }
|
||||
.text-teal { color: #4dd4ac; }
|
||||
.btn-lime { background-color: #84cc16; color: white; }
|
||||
.text-lime { color: #84cc16; }
|
||||
.btn-lime:hover { background-color: #65a30d; color: black; }
|
||||
.btn-cyan { background-color: #06b6d4; color: white; }
|
||||
.text-cyan { color: #06b6d4; }
|
||||
.btn-cyan:hover { background-color: #0891b2; color: white; }
|
||||
.btn-violet { background-color: #8b5cf6; color: white; }
|
||||
.text-violet { color: #8b5cf6; }
|
||||
.btn-violet:hover { background-color: #7c3aed; color: white; }
|
||||
.btn-lila { background-color: #c4b5fd; /* violeta claro */ color: white; border: none; }
|
||||
.btn-lila:hover { background-color: #a78bfa; /* tono ligeramente más fuerte */ color: white; }
|
||||
.text-lila { color: #c4b5fd; }
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
:root {
|
||||
--primary: #2563eb;
|
||||
--primary-dark: #1d4ed8;
|
||||
--secondary: #64748b;
|
||||
--success: #059669;
|
||||
--warning: #d97706;
|
||||
--danger: #dc2626;
|
||||
--info: #0891b2;
|
||||
--light: #f8fafc;
|
||||
--dark: #0f172a;
|
||||
--border-color: #e2e8f0;
|
||||
--text-muted: #64748b;
|
||||
--bg-body: #f8fafc;
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
.card-hover:hover { transform: translateY(-8px) saqcle(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
|
||||
.btn-pulse { animation: pulse 2s infinite; }
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background-color: var(--bg-body);
|
||||
color: var(--dark);
|
||||
line-height: 1.6;
|
||||
}
|
||||
.loading-spinner { display: none; }
|
||||
/* Botón con efecto de loading */
|
||||
.btn-loading { position: relative; color: transparent !important; pointer-events: none; }
|
||||
.btn-loading::after { content: ''; position: absolute; width: 20px; height: 20px; top: 50%; left: 50%; margin-left: -10px;
|
||||
margin-top: -10px; border: 2px solid #fff; border-radius: 50%; border-top-color: transparent; animation: spin 1s linear infinite; }
|
||||
.btn-loading .loading-spinner { display: inline-block; }
|
||||
.btn-loading .btn-text { display: none; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
/* Responsive animations */
|
||||
@media (max-width: 768px) { .card-hover:hover { transform: translateY(-4px) scale(1.01); } }
|
||||
/* Efecto de glow para elementos activos */
|
||||
.btn.active { box-shadow: 0 0 20px rgba(var(--bs-primary-rgb), 0.5); }
|
||||
/* Animación para el título */
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.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%; }
|
||||
/* Animación para alertas */
|
||||
.alert-animated { animation: slideInDown 0.5s ease-out; }
|
||||
@keyframes slideInDown {
|
||||
from { transform: translateY(-100%); opacity: 0; }
|
||||
to { transform: translateY(0); opacity: 1; }
|
||||
|
||||
.main-content {
|
||||
margin-left: 280px;
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
/* Efecto de parpadeo para elementos obligatorios */
|
||||
.border-warning-animated { animation: borderGlow 2s ease-in-out infinite alternate; }
|
||||
@keyframes borderGlow {
|
||||
from { border-color: #ffc107; box-shadow: 0 0 5px rgba(255, 193, 7, 0.5); }
|
||||
to { border-color: #ffcd39; box-shadow: 0 0 20px rgba(255, 193, 7, 0.8); }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
color: var(--dark);
|
||||
font-weight: 600;
|
||||
font-size: 1.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
color: var(--text-muted);
|
||||
font-weight: 400;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.dashboard-card {
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
height: 100%;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dashboard-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.25rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.card-icon.primary { background: var(--primary); }
|
||||
.card-icon.success { background: var(--success); }
|
||||
.card-icon.warning { background: var(--warning); }
|
||||
.card-icon.info { background: var(--info); }
|
||||
.card-icon.danger { background: var(--danger); }
|
||||
|
||||
.card-title {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: var(--dark);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.card-description {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 1.5rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.card-action {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.btn-card {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.625rem 1.25rem;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn-card:hover {
|
||||
background: var(--primary-dark);
|
||||
color: white;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-card.success {
|
||||
background: var(--success);
|
||||
}
|
||||
|
||||
.btn-card.success:hover {
|
||||
background: #047857;
|
||||
}
|
||||
|
||||
.btn-card.warning {
|
||||
background: var(--warning);
|
||||
}
|
||||
|
||||
.btn-card.warning:hover {
|
||||
background: #b45309;
|
||||
}
|
||||
|
||||
.btn-card.info {
|
||||
background: var(--info);
|
||||
}
|
||||
|
||||
.btn-card.info:hover {
|
||||
background: #0e7490;
|
||||
}
|
||||
|
||||
.btn-card.danger {
|
||||
background: var(--danger);
|
||||
}
|
||||
|
||||
.btn-card.danger:hover {
|
||||
background: #b91c1c;
|
||||
}
|
||||
|
||||
.badge-required {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
background: var(--warning);
|
||||
color: white;
|
||||
font-size: 0.75rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.alert-professional {
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-left: 4px solid var(--info);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.alert-professional.warning {
|
||||
border-left-color: var(--warning);
|
||||
}
|
||||
|
||||
.alert-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 1rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.alert-icon.info {
|
||||
background: rgba(8, 145, 178, 0.1);
|
||||
color: var(--info);
|
||||
}
|
||||
|
||||
.alert-icon.warning {
|
||||
background: rgba(217, 119, 6, 0.1);
|
||||
color: var(--warning);
|
||||
}
|
||||
|
||||
.alert-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
color: var(--dark);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.alert-text {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.btn-loading {
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.btn-loading::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-left: -8px;
|
||||
margin-top: -8px;
|
||||
border: 2px solid transparent;
|
||||
border-top: 2px solid currentColor;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">📦 Panel del Importador</h4>
|
||||
<div class="main-content">
|
||||
<!-- Page Header -->
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Dashboard Importador</h1>
|
||||
<p class="page-subtitle">Gestiona tus catálogos y accede a las funcionalidades del sistema</p>
|
||||
</div>
|
||||
|
||||
<?php if ($catalogosData['catalogos_activos']): ?>
|
||||
<div class="row g-4">
|
||||
<?php
|
||||
$delay = 0;
|
||||
foreach ($catalogosData['catalogos'] as $catalogo):
|
||||
$delay += 0.1; // Incrementar el retraso para cada catálogo
|
||||
?>
|
||||
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
|
||||
<div class="card shadow-sm p-3 card-hover <?= $catalogo['obligatorio'] ? 'border-warning border-warning-animated' : '' ?>">
|
||||
<div class="card-body p-0 d-flex flex-column">
|
||||
<h5 class="text-<?= $catalogo['color'] ?> mb-3">
|
||||
<i class="<?= obtenerIconoCatalogo($catalogo['nombre']) ?> me-2"></i><?= $catalogo['nombre'] ?>
|
||||
</h5>
|
||||
<p class="text-muted small flex-grow-1 mb-4"><?= $catalogo['descripcion'] ?></p>
|
||||
<div class="mt-auto">
|
||||
<button
|
||||
class="btn btn-<?= $catalogo['color'] ?> btn-sm mt-auto w-100 btn-animated <?= $_SERVER['REQUEST_URI'] === $catalogo['ruta'] ? 'active' : '' ?>"
|
||||
onclick="navigateWithAnimation(this, '<?= $catalogo['ruta'] ?>')"
|
||||
data-url="<?= $catalogo['ruta'] ?>">
|
||||
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
<span class="btn-text"><?= $catalogo['accion'] ?></span>
|
||||
</button>
|
||||
</div>
|
||||
<?php if ($catalogo['obligatorio']): ?>
|
||||
<small class="text-warning mt-2 animate__animated animate__pulse animate__infinite">
|
||||
<i class="fas fa-exclamation-triangle me-1"></i>
|
||||
Obligatorio
|
||||
</small>
|
||||
<?php endif; ?>
|
||||
<!-- Catálogos Grid -->
|
||||
<div class="row g-4 mb-4">
|
||||
<?php foreach ($catalogosData['catalogos'] as $catalogo): ?>
|
||||
<div class="col-lg-4 col-md-6 fade-in">
|
||||
<div class="dashboard-card" onclick="navigateWithAnimation(this, '<?= $catalogo['ruta'] ?>')">
|
||||
<?php if ($catalogo['obligatorio']): ?>
|
||||
<div class="badge-required">Obligatorio</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card-icon <?= getCardColorClass($catalogo['color']) ?>">
|
||||
<i class="<?= obtenerIconoCatalogo($catalogo['nombre']) ?>"></i>
|
||||
</div>
|
||||
|
||||
<h3 class="card-title"><?= $catalogo['nombre'] ?></h3>
|
||||
<p class="card-description"><?= $catalogo['descripcion'] ?></p>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="btn-card <?= $catalogo['color'] ?>">
|
||||
<?= $catalogo['accion'] ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<?php if (count($catalogosData['catalogos']) === 2): // Solo obligatorios ?>
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="alert alert-info alert-animated">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="me-3">
|
||||
<i class="fas fa-lightbulb fa-2x text-info animate__animated animate__pulse animate__infinite"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="alert-heading">💡 Consejo</h6>
|
||||
<p class="mb-2">Parece que no tienes catálogos activados. Puedes activar los que necesites desde tus preferencias.</p>
|
||||
<button class="btn btn-primary btn-sm btn-pulse" onclick="navigateWithAnimation(this, '/IMPORTADORES/preferencias/catalogos')">
|
||||
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
<span class="btn-text">Configurar Catálogos</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php if (count($catalogosData['catalogos']) === 2): ?>
|
||||
<!-- Info Alert -->
|
||||
<div class="alert-professional">
|
||||
<div class="d-flex align-items-start">
|
||||
<div class="alert-icon info">
|
||||
<i class="fas fa-lightbulb"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="alert-title">Consejo del Sistema</h4>
|
||||
<p class="alert-text">Tienes disponibles solo los catálogos obligatorios. Puedes activar catálogos adicionales desde tu panel de preferencias para acceder a más funcionalidades.</p>
|
||||
<button class="btn-card info" onclick="navigateWithAnimation(this, '/IMPORTADORES/preferencias/catalogos')">
|
||||
Configurar Catálogos
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php else: ?>
|
||||
<!-- Si los catálogos están completamente desactivados -->
|
||||
<div class="row g-4">
|
||||
<div class="col-12">
|
||||
<div class="alert alert-warning alert-animated">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="me-3">
|
||||
<i class="fas fa-lock fa-2x text-warning animate__animated animate__swing animate__infinite"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h5 class="alert-heading">🔒 Catálogos Desactivados</h5>
|
||||
<p class="mb-3">Los catálogos están desactivados en tus preferencias. Solo tienes acceso a las funciones básicas del sistema.</p>
|
||||
<button class="btn btn-primary btn-sm btn-pulse" onclick="navigateWithAnimation(this, '/IMPORTADORES/preferencias/catalogos')">
|
||||
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
<span class="btn-text">Activar Catálogos</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Warning Alert -->
|
||||
<div class="alert-professional warning mb-4">
|
||||
<div class="d-flex align-items-start">
|
||||
<div class="alert-icon warning">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="alert-title">Catálogos Desactivados</h4>
|
||||
<p class="alert-text">Los catálogos están desactivados en tu configuración. Solo tienes acceso a las funciones básicas del sistema.</p>
|
||||
<button class="btn-card warning" onclick="navigateWithAnimation(this, '/IMPORTADORES/preferencias/catalogos')">
|
||||
Activar Catálogos
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mostrar solo catálogos obligatorios -->
|
||||
<?php
|
||||
$delay = 0;
|
||||
foreach ($catalogosData['catalogos'] as $catalogo):
|
||||
$delay += 0.1;
|
||||
?>
|
||||
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
|
||||
<div class="card shadow-sm p-3 card-hover border-warning border-warning-animated h-100">
|
||||
<div class="card-body p-0 d-flex flex-column">
|
||||
<h5 class="text-<?= $catalogo['color'] ?> mb-3">
|
||||
<i class="<?= obtenerIconoCatalogo($catalogo['nombre']) ?> me-2"></i>
|
||||
<?= $catalogo['nombre'] ?>
|
||||
</h5>
|
||||
<p class="text-muted small flex-grow-1 mb-4"><?= $catalogo['descripcion'] ?></p>
|
||||
<div class="mt-auto">
|
||||
<button
|
||||
class="btn btn-<?= $catalogo['color'] ?> btn-sm mt-auto w-100 btn-animated <?= $_SERVER['REQUEST_URI'] === $catalogo['ruta'] ? 'active' : '' ?>"
|
||||
onclick="navigateWithAnimation(this, '<?= $catalogo['ruta'] ?>')"
|
||||
data-url="<?= $catalogo['ruta'] ?>">
|
||||
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
<span class="btn-text"><?= $catalogo['accion'] ?></span>
|
||||
</button>
|
||||
</div>
|
||||
<small class="text-warning mt-2 d-flex align-items-center">
|
||||
<i class="fas fa-lock me-1 animate__animated animate__pulse animate__infinite"></i>
|
||||
Función básica
|
||||
</small>
|
||||
<!-- Basic Functions Grid -->
|
||||
<div class="row g-4">
|
||||
<?php foreach ($catalogosData['catalogos'] as $catalogo): ?>
|
||||
<div class="col-lg-4 col-md-6 fade-in">
|
||||
<div class="dashboard-card" onclick="navigateWithAnimation(this, '<?= $catalogo['ruta'] ?>')">
|
||||
<div class="badge-required">Básico</div>
|
||||
|
||||
<div class="card-icon <?= getCardColorClass($catalogo['color']) ?>">
|
||||
<i class="<?= obtenerIconoCatalogo($catalogo['nombre']) ?>"></i>
|
||||
</div>
|
||||
|
||||
<h3 class="card-title"><?= $catalogo['nombre'] ?></h3>
|
||||
<p class="card-description"><?= $catalogo['descripcion'] ?></p>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="btn-card <?= $catalogo['color'] ?>">
|
||||
<?= $catalogo['accion'] ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -217,79 +365,74 @@
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// Función para navegación con animación
|
||||
function navigateWithAnimation(button, url) {
|
||||
// Prevenir múltiples clics
|
||||
// Navigation function with professional loading animation
|
||||
function navigateWithAnimation(element, url) {
|
||||
// Find the button within the card
|
||||
const button = element.querySelector('.btn-card') || element;
|
||||
|
||||
// Prevent double clicks
|
||||
if (button.classList.contains('btn-loading')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Añadir estado de carga
|
||||
// Add loading state
|
||||
button.classList.add('btn-loading');
|
||||
button.disabled = true;
|
||||
|
||||
// Añadir efecto de salida a la tarjeta padre
|
||||
const card = button.closest('.card');
|
||||
if (card) {
|
||||
card.style.transform = 'scale(0.95)';
|
||||
card.style.opacity = '0.7';
|
||||
card.style.transition = 'all 0.3s ease';
|
||||
}
|
||||
// Add subtle card animation
|
||||
const card = element.closest('.dashboard-card') || element;
|
||||
card.style.transform = 'scale(0.98)';
|
||||
card.style.opacity = '0.8';
|
||||
|
||||
// Simular carga y navegar
|
||||
setTimeout(function() {
|
||||
// Navigate after short delay
|
||||
setTimeout(() => {
|
||||
window.location.href = url;
|
||||
}, 1000);
|
||||
}, 600);
|
||||
}
|
||||
|
||||
// Animación de entrada escalonada para las tarjetas
|
||||
// Initialize page animations
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const cards = document.querySelectorAll('.card-hover');
|
||||
|
||||
// Efecto de revelado progresivo
|
||||
cards.forEach((card, index) => {
|
||||
card.style.animationDelay = (index * 0.1) + 's';
|
||||
// Add staggered fade-in animation to cards
|
||||
const fadeElements = document.querySelectorAll('.fade-in');
|
||||
fadeElements.forEach((element, index) => {
|
||||
element.style.animationDelay = (index * 0.1) + 's';
|
||||
});
|
||||
|
||||
// Añadir efecto de hover mejorado
|
||||
// Add smooth hover effects to cards
|
||||
const cards = document.querySelectorAll('.dashboard-card');
|
||||
cards.forEach(card => {
|
||||
card.addEventListener('mouseenter', function() {
|
||||
this.style.transform = 'translateY(-5px) scale(1.02)';
|
||||
this.style.transform = 'translateY(-4px)';
|
||||
});
|
||||
|
||||
card.addEventListener('mouseleave', function() {
|
||||
this.style.transform = 'translateY(0) scale(1)';
|
||||
this.style.transform = 'translateY(0)';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Efecto de ondas en los botones
|
||||
document.querySelectorAll('.btn-animated').forEach(button => {
|
||||
button.addEventListener('click', function(e) {
|
||||
let ripple = document.createElement('span');
|
||||
ripple.classList.add('ripple');
|
||||
this.appendChild(ripple);
|
||||
|
||||
let x = e.clientX - e.target.offsetLeft;
|
||||
let y = e.clientY - e.target.offsetTop;
|
||||
|
||||
ripple.style.left = x + 'px';
|
||||
ripple.style.top = y + 'px';
|
||||
|
||||
setTimeout(() => {
|
||||
ripple.remove();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
// Animación para alertas que se pueden cerrar
|
||||
document.querySelectorAll('.alert').forEach(alert => {
|
||||
alert.addEventListener('closed.bs.alert', function() {
|
||||
this.style.animation = 'slideOutUp 0.5s ease-in';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php
|
||||
// Helper functions for the dashboard
|
||||
function getCardColorClass($color) {
|
||||
$colorMap = [
|
||||
'indigo' => 'primary',
|
||||
'teal' => 'success',
|
||||
'orange' => 'warning',
|
||||
'cyan' => 'info',
|
||||
'lime' => 'success',
|
||||
'violet' => 'primary',
|
||||
'lila' => 'primary'
|
||||
];
|
||||
return $colorMap[$color] ?? 'primary';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
2946
views/mve/lista.php
2946
views/mve/lista.php
File diff suppressed because it is too large
Load Diff
@@ -1,91 +1,618 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
/* Estilos inspirados en Tailwind */
|
||||
.sidebar-container { background-color: #f9fafb; border-right: 1px solid #e5e7eb; }
|
||||
.sidebar-dark { background-color: #1f2937; border-right: 1px solid #374151; }
|
||||
.sidebar-nav .nav-link { color: #374151; font-weight: 500; padding: 0.5rem 0.75rem; margin-bottom: 0.125rem;
|
||||
border-radius: 0.5rem; transition: all 0.15s ease-in-out; display: flex; align-items: center; }
|
||||
.sidebar-nav .nav-link:hover { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link.active { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link .icon { width: 1.25rem; height: 1.25rem; margin-right: 0.75rem; color: #6b7280; transition: color 0.15s ease-in-out; flex-shrink: 0; }
|
||||
.sidebar-nav .nav-link:hover .icon,
|
||||
.sidebar-nav .nav-link.active .icon { color: #374151; }
|
||||
.sidebar-nav .badge { background-color: #f3f4f6; color: #374151; font-size: 0.75rem; font-weight: 500; padding: 0.25rem 0.5rem; border-radius: 9999px; margin-left: auto; }
|
||||
.sidebar-nav .badge.badge-primary { background-color: #dbeafe; color: #1e40af; }
|
||||
.submenu .nav-link { padding-left: 2.75rem; color: #6b7280; font-weight: 400; }
|
||||
.submenu .nav-link:hover { background-color: #f3f4f6; color: #374151; }
|
||||
.submenu .nav-link.active { background-color: #f3f4f6; color: #374151; }
|
||||
.collapse-icon { width: 0.75rem; height: 0.75rem; margin-left: auto; transition: transform 0.15s ease-in-out; }
|
||||
.nav-link[aria-expanded="true"] .collapse-icon { transform: rotate(180deg); }
|
||||
/* Estilos para modo oscuro */
|
||||
.sidebar-dark .sidebar-nav .nav-link { color: #d1d5db; }
|
||||
.sidebar-dark .sidebar-nav .nav-link:hover { background-color: #374151; color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .nav-link.active { background-color: #374151; color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .nav-link .icon { color: #9ca3af; }
|
||||
.sidebar-dark .sidebar-nav .nav-link:hover .icon,
|
||||
.sidebar-dark .sidebar-nav .nav-link.active .icon { color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .badge { background-color: #374151; color: #d1d5db; }
|
||||
.sidebar-dark .sidebar-nav .badge.badge-primary { background-color: #1e3a8a; color: #93c5fd; }
|
||||
.sidebar-dark .submenu .nav-link { color: #9ca3af; }
|
||||
.sidebar-dark .submenu .nav-link:hover { background-color: #374151; color: #d1d5db; }
|
||||
.sidebar-dark .submenu .nav-link.active { background-color: #374151; color: #d1d5db; }
|
||||
/* Navbar */
|
||||
.navbar-brand { font-weight: 600; font-size: 1.125rem; }
|
||||
.menu-toggle { border: 1px solid #6b7280; border-radius: 0.5rem; padding: 0.5rem; transition: all 0.15s ease-in-out; }
|
||||
.menu-toggle:hover { background-color: #f3f4f6; }
|
||||
/* Offcanvas para móviles */
|
||||
.offcanvas { background-color: #ffffff; }
|
||||
.offcanvas-header { border-bottom: 1px solid #e5e7eb; padding: 1rem 1.5rem; }
|
||||
.offcanvas-title { font-weight: 600; font-size: 1.125rem; color: #111827; }
|
||||
.offcanvas-body { padding: 1rem 1.5rem; }
|
||||
/* Responsive adjustments */
|
||||
/* Variables CSS para un diseño profesional limpio */
|
||||
:root {
|
||||
--sidebar-bg: #ffffff;
|
||||
--sidebar-width: 280px;
|
||||
--navbar-height: 70px;
|
||||
--primary-color: #3b82f6;
|
||||
--primary-dark: #2563eb;
|
||||
--text-primary: #1f2937;
|
||||
--text-secondary: #6b7280;
|
||||
--text-muted: #9ca3af;
|
||||
--text-light: #ffffff;
|
||||
--bg-hover: #f8fafc;
|
||||
--bg-active: #f1f5f9;
|
||||
--bg-selected: #eff6ff;
|
||||
--border-color: #e5e7eb;
|
||||
--border-light: #f3f4f6;
|
||||
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
|
||||
--shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.08);
|
||||
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--navbar-bg: linear-gradient(135deg, var(--primary, #0f172a) 0%, var(--secondary, #334155) 100%);
|
||||
}
|
||||
|
||||
/* Reset y fuentes base */
|
||||
* {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
}
|
||||
|
||||
/* Asegurar espacio para navbar fijo */
|
||||
body {
|
||||
padding-top: var(--navbar-height) !important;
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
/* Prevenir que otros estilos interfieran con el navbar */
|
||||
html, body {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Reset específico para el navbar */
|
||||
.navbar-professional * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Asegurar que ningún elemento padre afecte el posicionamiento */
|
||||
.navbar-professional {
|
||||
transform: none !important;
|
||||
will-change: auto !important;
|
||||
}
|
||||
|
||||
/* Estilos del contenedor principal del sidebar */
|
||||
.sidebar-container {
|
||||
background: var(--sidebar-bg);
|
||||
border-right: 1px solid var(--border-color);
|
||||
box-shadow: var(--shadow-lg);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidebar-container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.01) 100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Estilos de navegación mejorados */
|
||||
.sidebar-nav {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-item {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link {
|
||||
color: var(--text-primary);
|
||||
font-weight: 500;
|
||||
font-size: 0.95rem;
|
||||
padding: 14px 20px;
|
||||
border-radius: 12px;
|
||||
transition: var(--transition);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
margin: 0 12px;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 4px;
|
||||
height: 0;
|
||||
background: var(--primary-color);
|
||||
border-radius: 2px;
|
||||
transition: height 0.3s ease;
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link:hover {
|
||||
background: var(--bg-hover);
|
||||
border-color: var(--border-light);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-sm);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link:hover::before {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link.active {
|
||||
background: var(--bg-selected);
|
||||
border-color: var(--primary-color);
|
||||
border-color: rgba(59, 130, 246, 0.2);
|
||||
box-shadow: var(--shadow-sm);
|
||||
font-weight: 600;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link.active::before {
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
/* Iconos mejorados */
|
||||
.sidebar-nav .nav-link .icon {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
margin-right: 16px;
|
||||
color: var(--text-secondary);
|
||||
transition: var(--transition);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link:hover .icon {
|
||||
color: var(--text-primary);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link.active .icon {
|
||||
color: var(--primary-color);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* Badges modernos */
|
||||
.sidebar-nav .badge {
|
||||
background: var(--border-light);
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
padding: 6px 10px;
|
||||
border-radius: 20px;
|
||||
margin-left: auto;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.sidebar-nav .badge.badge-primary {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
color: var(--primary-color);
|
||||
border-color: rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
/* Submenús mejorados */
|
||||
.submenu {
|
||||
padding-left: 0;
|
||||
margin-top: 8px;
|
||||
border-left: 2px solid var(--border-color);
|
||||
margin-left: 32px;
|
||||
}
|
||||
|
||||
.submenu .nav-link {
|
||||
padding: 10px 20px 10px 24px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 400;
|
||||
color: var(--text-secondary);
|
||||
margin: 0 12px 0 0;
|
||||
border-radius: 0 10px 10px 0;
|
||||
}
|
||||
|
||||
.submenu .nav-link::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.submenu .nav-link:hover {
|
||||
color: var(--text-primary);
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
|
||||
.submenu .nav-link.active {
|
||||
color: var(--primary-color);
|
||||
background: var(--bg-selected);
|
||||
}
|
||||
|
||||
/* Iconos de colapso mejorados */
|
||||
.collapse-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-left: auto;
|
||||
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.nav-link:hover .collapse-icon {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.nav-link[aria-expanded="true"] .collapse-icon {
|
||||
transform: rotate(180deg);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* Navbar profesional con posicionamiento reforzado */
|
||||
.navbar-professional {
|
||||
background: var(--navbar-bg) !important;
|
||||
height: var(--navbar-height) !important;
|
||||
box-shadow: var(--shadow-md) !important;
|
||||
border: none !important;
|
||||
backdrop-filter: blur(10px);
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
width: 100% !important;
|
||||
z-index: 9999 !important;
|
||||
overflow: hidden;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
/* Forzar que el navbar siempre esté en la parte superior */
|
||||
.navbar-professional.fixed-top {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-weight: 700;
|
||||
font-size: 1.4rem;
|
||||
color: var(--text-light) !important;
|
||||
letter-spacing: -0.5px;
|
||||
margin-bottom: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.navbar-brand .highlight {
|
||||
color: #ffd700;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
/* Contenedor del navbar reforzado */
|
||||
.navbar-professional .container-fluid {
|
||||
padding-left: 1rem !important;
|
||||
padding-right: 1rem !important;
|
||||
margin: 0 !important;
|
||||
width: 100% !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
/* Botón de menú mejorado */
|
||||
.menu-toggle {
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 10px;
|
||||
padding: 10px 12px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: var(--text-light);
|
||||
font-size: 1.2rem;
|
||||
transition: var(--transition);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.menu-toggle:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* Botones de acción mejorados */
|
||||
.btn-professional {
|
||||
padding: 10px 20px;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
transition: var(--transition);
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.btn-outline-light-professional {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
color: var(--text-light);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.btn-outline-light-professional:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
color: var(--text-light);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
/* Estilos para el contenido de bienvenida */
|
||||
.welcome-container {
|
||||
flex-shrink: 0;
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.welcome-text {
|
||||
font-size: 0.95rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 200px;
|
||||
color: white !important;
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
#navbar-content {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Offcanvas mejorado */
|
||||
.offcanvas-professional {
|
||||
background: var(--sidebar-bg);
|
||||
border: none;
|
||||
border-left: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.offcanvas-header {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 1.5rem;
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
|
||||
.offcanvas-title {
|
||||
font-weight: 700;
|
||||
font-size: 1.3rem;
|
||||
color: var(--text-primary);
|
||||
letter-spacing: -0.3px;
|
||||
}
|
||||
|
||||
.btn-close-white-professional {
|
||||
background: var(--border-light);
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
transition: var(--transition);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.btn-close-white-professional:hover {
|
||||
background: var(--border-color);
|
||||
transform: scale(1.1);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.offcanvas-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
/* Efectos de scroll personalizado */
|
||||
.sidebar-scroll::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.sidebar-scroll::-webkit-scrollbar-track {
|
||||
background: var(--border-light);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.sidebar-scroll::-webkit-scrollbar-thumb {
|
||||
background: var(--border-color);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.sidebar-scroll::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--text-muted);
|
||||
}
|
||||
|
||||
/* Separadores visuales */
|
||||
.nav-separator {
|
||||
height: 1px;
|
||||
background: var(--border-color);
|
||||
margin: 16px 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-separator::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 40px;
|
||||
height: 1px;
|
||||
background: var(--text-muted);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
/* Animaciones mejoradas */
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
animation: slideIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
.nav-item:nth-child(1) { animation-delay: 0.05s; }
|
||||
.nav-item:nth-child(2) { animation-delay: 0.1s; }
|
||||
.nav-item:nth-child(3) { animation-delay: 0.15s; }
|
||||
.nav-item:nth-child(4) { animation-delay: 0.2s; }
|
||||
.nav-item:nth-child(5) { animation-delay: 0.25s; }
|
||||
.nav-item:nth-child(6) { animation-delay: 0.3s; }
|
||||
.nav-item:nth-child(7) { animation-delay: 0.35s; }
|
||||
.nav-item:nth-child(8) { animation-delay: 0.4s; }
|
||||
.nav-item:nth-child(9) { animation-delay: 0.45s; }
|
||||
.nav-item:nth-child(10) { animation-delay: 0.5s; }
|
||||
|
||||
/* Responsive mejorado */
|
||||
@media (max-width: 767.98px) {
|
||||
.sidebar-nav .nav-link { color: #374151; }
|
||||
.sidebar-nav .nav-link:hover { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link.active { background-color: #f3f4f6; color: #111827; }
|
||||
:root {
|
||||
--navbar-height: 60px;
|
||||
}
|
||||
|
||||
.navbar-professional {
|
||||
height: 60px;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.navbar-professional .container-fluid {
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-size: 1.0rem;
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.navbar-brand span:first-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.menu-toggle {
|
||||
padding: 6px 8px;
|
||||
margin-right: 0.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.welcome-text {
|
||||
font-size: 0.85rem;
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
.welcome-container {
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link {
|
||||
margin: 0 8px;
|
||||
padding: 12px 16px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.offcanvas-professional {
|
||||
width: 280px !important;
|
||||
}
|
||||
|
||||
#navbar-content {
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
/* Para pantallas muy pequeñas */
|
||||
@media (max-width: 575.98px) {
|
||||
.navbar-brand {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.welcome-text {
|
||||
font-size: 0.8rem;
|
||||
max-width: 120px;
|
||||
}
|
||||
|
||||
.navbar-professional .container-fluid {
|
||||
padding-left: 0.25rem;
|
||||
padding-right: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Estados especiales */
|
||||
.nav-link.loading {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-link.loading .icon {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- NAVBAR -->
|
||||
<nav class="navbar navbar-dark bg-dark fixed-top">
|
||||
<div class="container-fluid">
|
||||
<!-- Botón de menú para móviles -->
|
||||
<button class="menu-toggle d-md-none me-3" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu">
|
||||
☰
|
||||
</button>
|
||||
<span class="navbar-brand">SIIH | Administrador</span>
|
||||
<div class="d-flex ms-auto text-white">
|
||||
<div id="navbar-content">
|
||||
<!-- NAVBAR PROFESIONAL -->
|
||||
<nav class="navbar navbar-professional fixed-top navbar-dark">
|
||||
<div class="container-fluid px-4 h-100">
|
||||
<div class="d-flex align-items-center justify-content-between w-100 h-100">
|
||||
<!-- Lado izquierdo: Menú + Brand -->
|
||||
<div class="d-flex align-items-center h-100">
|
||||
<!-- Botón de menú para móviles -->
|
||||
<button class="menu-toggle d-md-none me-3" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Brand mejorado -->
|
||||
<div class="navbar-brand d-flex align-items-center mb-0">
|
||||
<svg class="me-2" width="32" height="32" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
|
||||
</svg>
|
||||
<span>SIIH <span class="highlight">|</span> Administrador</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lado derecho: Contenido del navbar -->
|
||||
<div class="d-flex align-items-center h-100">
|
||||
<?php
|
||||
// Verifica si estás en el dashboard principal del administrador
|
||||
$esDashboard = str_contains($_SERVER['REQUEST_URI'], '/administrador/dashboard');
|
||||
|
||||
if ($esDashboard): ?>
|
||||
<span id="welcome-text">Bienvenido, <?= htmlspecialchars($_SESSION['usuario_nombre']) ?></span>
|
||||
<div class="welcome-container">
|
||||
<svg class="me-2 d-none d-sm-block" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="12" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
<span id="welcome-text" class="welcome-text">Bienvenido, <?= htmlspecialchars($_SESSION['usuario_nombre']) ?></span>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<a href="/IMPORTADORES/sistemas/logout" class="btn btn-outline-light btn-sm ms-2 W-auto btn-animated" id="logout-btn">Cerrar Sesión</a>
|
||||
<a href="/IMPORTADORES/sistemas/logout"
|
||||
class="btn btn-outline-light-professional btn-professional d-flex align-items-center"
|
||||
id="logout-btn">
|
||||
<svg class="me-2" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
|
||||
<polyline points="16,17 21,12 16,7"></polyline>
|
||||
<line x1="21" y1="12" x2="9" y2="12"></line>
|
||||
</svg>
|
||||
Cerrar Sesión
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- SIDEBAR FIJA (escritorio) -->
|
||||
<div class="d-none d-md-block sidebar-container sidebar-dark position-fixed h-100" style="width: 250px; top: 0; padding-top: 4rem; z-index: 1000;">
|
||||
<!-- SIDEBAR PROFESIONAL (escritorio) -->
|
||||
<div class="d-none d-md-block sidebar-container position-fixed h-100 sidebar-scroll" style="width: var(--sidebar-width); top: 0; padding-top: var(--navbar-height); z-index: 1000;">
|
||||
<div class="px-3 py-4 h-100 overflow-y-auto">
|
||||
<nav class="sidebar-nav">
|
||||
<ul class="nav flex-column">
|
||||
<!-- INICIO -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/administrador/dashboard" class="nav-link active">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 22 21">
|
||||
<path d="M16.975 11H10V4.025a1 1 0 0 0-1.066-.998 8.5 8.5 0 1 0 9.039 9.039.999.999 0 0 0-1-1.066h.002Z"/>
|
||||
<path d="M12.5 0c-.157 0-.311.01-.565.027A1 1 0 0 0 11 1.02V10h8.975a1 1 0 0 0 1-.935c.013-.188.028-.374.028-.565A8.51 8.51 0 0 0 12.5 0Z"/>
|
||||
<svg class="icon" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||||
<path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/>
|
||||
<polyline points="9,22 9,12 15,12 15,22"/>
|
||||
</svg>
|
||||
<span>Inicio</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<div class="nav-separator"></div>
|
||||
<!-- AGENCIAS -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuAgencias" role="button" aria-expanded="false">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
386
views/partials/sidebar_agencia_old.php
Normal file
386
views/partials/sidebar_agencia_old.php
Normal file
@@ -0,0 +1,386 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
/* Estilos inspirados en Tailwind */
|
||||
.sidebar-container { background-color: #f9fafb; border-right: 1px solid #e5e7eb; }
|
||||
.sidebar-dark { background-color: #1f2937; border-right: 1px solid #374151; }
|
||||
.sidebar-nav .nav-link { color: #374151; font-weight: 500; padding: 0.5rem 0.75rem; margin-bottom: 0.125rem;
|
||||
border-radius: 0.5rem; transition: all 0.15s ease-in-out; display: flex; align-items: center; }
|
||||
.sidebar-nav .nav-link:hover { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link.active { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link .icon { width: 1.25rem; height: 1.25rem; margin-right: 0.75rem; color: #6b7280; transition: color 0.15s ease-in-out; flex-shrink: 0; }
|
||||
.sidebar-nav .nav-link:hover .icon,
|
||||
.sidebar-nav .nav-link.active .icon { color: #374151; }
|
||||
.sidebar-nav .badge { background-color: #f3f4f6; color: #374151; font-size: 0.75rem; font-weight: 500; padding: 0.25rem 0.5rem; border-radius: 9999px; margin-left: auto; }
|
||||
.sidebar-nav .badge.badge-primary { background-color: #dbeafe; color: #1e40af; }
|
||||
.submenu .nav-link { padding-left: 2.75rem; color: #6b7280; font-weight: 400; }
|
||||
.submenu .nav-link:hover { background-color: #f3f4f6; color: #374151; }
|
||||
.submenu .nav-link.active { background-color: #f3f4f6; color: #374151; }
|
||||
.collapse-icon { width: 0.75rem; height: 0.75rem; margin-left: auto; transition: transform 0.15s ease-in-out; }
|
||||
.nav-link[aria-expanded="true"] .collapse-icon { transform: rotate(180deg); }
|
||||
/* Estilos para modo oscuro */
|
||||
.sidebar-dark .sidebar-nav .nav-link { color: #d1d5db; }
|
||||
.sidebar-dark .sidebar-nav .nav-link:hover { background-color: #374151; color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .nav-link.active { background-color: #374151; color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .nav-link .icon { color: #9ca3af; }
|
||||
.sidebar-dark .sidebar-nav .nav-link:hover .icon,
|
||||
.sidebar-dark .sidebar-nav .nav-link.active .icon { color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .badge { background-color: #374151; color: #d1d5db; }
|
||||
.sidebar-dark .sidebar-nav .badge.badge-primary { background-color: #1e3a8a; color: #93c5fd; }
|
||||
.sidebar-dark .submenu .nav-link { color: #9ca3af; }
|
||||
.sidebar-dark .submenu .nav-link:hover { background-color: #374151; color: #d1d5db; }
|
||||
.sidebar-dark .submenu .nav-link.active { background-color: #374151; color: #d1d5db; }
|
||||
/* Navbar */
|
||||
.navbar-brand { font-weight: 600; font-size: 1.125rem; }
|
||||
.menu-toggle { border: 1px solid #6b7280; border-radius: 0.5rem; padding: 0.5rem; transition: all 0.15s ease-in-out; }
|
||||
.menu-toggle:hover { background-color: #f3f4f6; }
|
||||
/* Offcanvas para móviles */
|
||||
.offcanvas { background-color: #ffffff; }
|
||||
.offcanvas-header { border-bottom: 1px solid #e5e7eb; padding: 1rem 1.5rem; }
|
||||
.offcanvas-title { font-weight: 600; font-size: 1.125rem; color: #111827; }
|
||||
.offcanvas-body { padding: 1rem 1.5rem; }
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 767.98px) {
|
||||
.sidebar-nav .nav-link { color: #374151; }
|
||||
.sidebar-nav .nav-link:hover { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link.active { background-color: #f3f4f6; color: #111827; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 🔷 NAVBAR -->
|
||||
<nav class="navbar navbar-dark bg-dark fixed-top">
|
||||
<div class="container-fluid">
|
||||
<!-- Botón de menú para móviles -->
|
||||
<button class="menu-toggle d-md-none me-3" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu">
|
||||
☰
|
||||
</button>
|
||||
<span class="navbar-brand">SIIH | Agencia Aduanal</span>
|
||||
<div class="d-flex ms-auto text-white">
|
||||
<div class="navbar-content">
|
||||
<?php
|
||||
// Verifica si estás en el dashboard principal del importador
|
||||
$esDashboard = str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/agencias/dashboard');
|
||||
|
||||
if ($esDashboard): ?>
|
||||
<span id="welcome-text">Bienvenido, <?= htmlspecialchars($_SESSION['usuario_nombre']) ?></span>
|
||||
<?php else: ?>
|
||||
<a href="/IMPORTADORES/sistemas/logout" class="btn btn-outline-light btn-sm ms-2 W-auto btn-animated">Cerrar Sesión</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- SIDEBAR FIJA (escritorio) -->
|
||||
<div class="d-none d-md-block sidebar-container sidebar-dark position-fixed h-100" style="width: 250px; top: 0; padding-top: 4rem; z-index: 1000;">
|
||||
<div class="px-3 py-4 h-100 overflow-y-auto">
|
||||
<nav class="sidebar-nav">
|
||||
<ul class="nav flex-column">
|
||||
<!-- INICIO -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/agencias/dashboard" class="nav-link active">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 22 21">
|
||||
<path d="M16.975 11H10V4.025a1 1 0 0 0-1.066-.998 8.5 8.5 0 1 0 9.039 9.039.999.999 0 0 0-1-1.066h.002Z"/>
|
||||
<path d="M12.5 0c-.157 0-.311.01-.565.027A1 1 0 0 0 11 1.02V10h8.975a1 1 0 0 0 1-.935c.013-.188.028-.374.028-.565A8.51 8.51 0 0 0 12.5 0Z"/>
|
||||
</svg>
|
||||
<span>Inicio</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- SOLICITUDES VINCULACIÓN -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/vinculaciones/solicitudesVinculacion" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5z"/>
|
||||
<path d="M7.414 15.414a2 2 0 01-2.828-2.828l3-3a2 2 0 012.828 0 1 1 0 001.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5z"/>
|
||||
</svg>
|
||||
<span>Vinculación</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- USUARIOS VINCULADOS -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/vinculaciones/vinculacionesAgencia" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M14 2a3.963 3.963 0 0 0-1.4.267 6.439 6.439 0 0 1-1.331 6.638A4 4 0 1 0 14 2Zm1 9h-1.264A6.957 6.957 0 0 1 15 15v2a2.97 2.97 0 0 1-.184 1H19a1 1 0 0 0 1-1v-1a5.006 5.006 0 0 0-5-5ZM6.5 9a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9ZM8 10H5a5.006 5.006 0 0 0-5 5v2a1 1 0 0 0 1 1h11a1 1 0 0 0 1-1v-2a5.006 5.006 0 0 0-5-5Z"/>
|
||||
</svg>
|
||||
<span>Usuarios Vinculados</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Patentes -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuPatentesDesktop" role="button" aria-expanded="false" aria-controls="submenuPatentesDesktop">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Patentes</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="collapse" id="submenuPatentesDesktop">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/patente/lista" class="nav-link">
|
||||
Ver Patentes
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/patente/alta" class="nav-link">
|
||||
Nueva Patente
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- LOCACIONES -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuLocacionesDesktop" role="button" aria-expanded="false" aria-controls="submenuLocacionesDesktop">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10 2C6.686 2 4 4.686 4 8c0 5.25 6 12 6 12s6-6.75 6-12c0-3.314-2.686-6-6-6zm0 8c-1.105 0-2-.895-2-2s.895-2 2-2 2 .895 2 2-.895 2-2 2z"/>
|
||||
</svg>
|
||||
<span>Locaciones</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="collapse" id="submenuLocacionesDesktop">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/locaciones/lista" class="nav-link">
|
||||
Ver Locaciones
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/locaciones/alta" class="nav-link">
|
||||
Agregar Locaciones
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- ALTA DE AGENTES -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/agencias/alta" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Alta de agentes</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- CONFIGURACIÓN -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/configuracion" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M11.983 1.034a1 1 0 00-1.966 0l-.26 1.857a6.996 6.996 0 00-1.223.51l-1.7-1.04a1 1 0 00-1.366.366l-1 1.732a1 1 0 00.366 1.366l1.7 1.04a6.984 6.984 0 000 1.02l-1.7 1.04a1 1 0 00-.366 1.366l1 1.732a1 1 0 001.366.366l1.7-1.04c.392.212.803.386 1.223.51l.26 1.857a1 1 0 001.966 0l.26-1.857c.42-.124.831-.298 1.223-.51l1.7 1.04a1 1 0 001.366-.366l1-1.732a1 1 0 00-.366-1.366l-1.7-1.04a6.984 6.984 0 000-1.02l1.7-1.04a1 1 0 00.366-1.366l-1-1.732a1 1 0 00-1.366-.366l-1.7 1.04a6.996 6.996 0 00-1.223-.51l-.26-1.857zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Configuración</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OFFCANVAS PARA MÓVILES -->
|
||||
<div class="offcanvas offcanvas-start d-md-none" tabindex="-1" id="sidebarMenu" style="top: 0; width: 250px;">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title">Menú</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="offcanvas"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<nav class="sidebar-nav">
|
||||
<ul class="nav flex-column">
|
||||
<!-- INICIO -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/agentes/dashboard" class="nav-link active">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 22 21">
|
||||
<path d="M16.975 11H10V4.025a1 1 0 0 0-1.066-.998 8.5 8.5 0 1 0 9.039 9.039.999.999 0 0 0-1-1.066h.002Z"/>
|
||||
<path d="M12.5 0c-.157 0-.311.01-.565.027A1 1 0 0 0 11 1.02V10h8.975a1 1 0 0 0 1-.935c.013-.188.028-.374.028-.565A8.51 8.51 0 0 0 12.5 0Z"/>
|
||||
</svg>
|
||||
<span>Inicio</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- SOLICITUDES VINCULACIÓN -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/vinculaciones/solicitudesVinculacion" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5z"/>
|
||||
<path d="M7.414 15.414a2 2 0 01-2.828-2.828l3-3a2 2 0 012.828 0 1 1 0 001.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5z"/>
|
||||
</svg>
|
||||
<span>Vinculación</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- USUARIOS VINCULADOS -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/vinculaciones/vinculacionesAgencia" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M14 2a3.963 3.963 0 0 0-1.4.267 6.439 6.439 0 0 1-1.331 6.638A4 4 0 1 0 14 2Zm1 9h-1.264A6.957 6.957 0 0 1 15 15v2a2.97 2.97 0 0 1-.184 1H19a1 1 0 0 0 1-1v-1a5.006 5.006 0 0 0-5-5ZM6.5 9a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9ZM8 10H5a5.006 5.006 0 0 0-5 5v2a1 1 0 0 0 1 1h11a1 1 0 0 0 1-1v-2a5.006 5.006 0 0 0-5-5Z"/>
|
||||
</svg>
|
||||
<span>Usuarios Vinculados</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Patentes -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuPatentesMobile" role="button" aria-expanded="false" aria-controls="submenuPatentesMobile">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Patentes</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="collapse" id="submenuPatentesMobile">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/patente/lista" class="nav-link">
|
||||
Ver Patentes
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/patente/alta" class="nav-link">
|
||||
Nueva Patente
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- LOCACIONES -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuLocacionesMobile" role="button" aria-expanded="false" aria-controls="submenuLocacionesMobile">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10 2C6.686 2 4 4.686 4 8c0 5.25 6 12 6 12s6-6.75 6-12c0-3.314-2.686-6-6-6zm0 8c-1.105 0-2-.895-2-2s.895-2 2-2 2 .895 2 2-.895 2-2 2z"/>
|
||||
</svg>
|
||||
<span>Locaciones</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="collapse" id="submenuLocacionesMobile">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/locaciones/lista" class="nav-link">
|
||||
Ver Locaciones
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/locaciones/alta" class="nav-link">
|
||||
Agregar Locaciones
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- ALTA DE AGENTES -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/agencias/alta" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Alta de agentes</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- CONFIGURACIÓN -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/configuracion" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M11.983 1.034a1 1 0 00-1.966 0l-.26 1.857a6.996 6.996 0 00-1.223.51l-1.7-1.04a1 1 0 00-1.366.366l-1 1.732a1 1 0 00.366 1.366l1.7 1.04a6.984 6.984 0 000 1.02l-1.7 1.04a1 1 0 00-.366 1.366l1 1.732a1 1 0 001.366.366l1.7-1.04c.392.212.803.386 1.223.51l.26 1.857a1 1 0 001.966 0l.26-1.857c.42-.124.831-.298 1.223-.51l1.7 1.04a1 1 0 001.366-.366l1-1.732a1 1 0 00-.366-1.366l-1.7-1.04a6.984 6.984 0 000-1.02l1.7-1.04a1 1 0 00.366-1.366l-1-1.732a1 1 0 00-1.366-.366l-1.7 1.04a6.996 6.996 0 00-1.223-.51l-.26-1.857zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Configuración</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// Simular lógica de dashboard
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const esDashboard = window.location.pathname.includes('/agencias/dashboard');
|
||||
const welcomeText = document.getElementById('welcome-text');
|
||||
const logoutBtn = document.getElementById('logout-btn');
|
||||
|
||||
if (!esDashboard) {
|
||||
if (welcomeText) {
|
||||
welcomeText.style.display = 'none';
|
||||
}
|
||||
if (logoutBtn) {
|
||||
logoutBtn.style.display = 'inline-block';
|
||||
}
|
||||
}
|
||||
|
||||
// Manejar estados activos basados en URL actual
|
||||
const currentPath = window.location.pathname;
|
||||
const navLinks = document.querySelectorAll('.nav-link');
|
||||
|
||||
navLinks.forEach(link => {
|
||||
const href = link.getAttribute('href');
|
||||
if (href && currentPath.includes(href)) {
|
||||
link.classList.add('active');
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-bs-toggle="collapse"]').forEach(trigger => {
|
||||
// Desactivar Bootstrap
|
||||
trigger.removeAttribute('data-bs-toggle');
|
||||
|
||||
// Inicializar estado correcto
|
||||
const target = document.querySelector(trigger.getAttribute('href'));
|
||||
if (target) {
|
||||
target.style.transition = 'height 0.3s ease, opacity 0.2s ease';
|
||||
target.style.overflow = 'hidden';
|
||||
|
||||
if (!target.classList.contains('show')) {
|
||||
target.style.height = '0';
|
||||
target.style.opacity = '0';
|
||||
}
|
||||
}
|
||||
|
||||
trigger.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const target = document.querySelector(this.getAttribute('href'));
|
||||
const icon = this.querySelector('.collapse-icon');
|
||||
|
||||
if (!target) return;
|
||||
|
||||
const isExpanding = this.getAttribute('aria-expanded') !== 'true';
|
||||
|
||||
if (isExpanding) {
|
||||
// Abrir
|
||||
target.classList.add('show');
|
||||
target.style.height = '0';
|
||||
target.style.opacity = '0';
|
||||
target.style.display = 'block';
|
||||
|
||||
// Forzar reflow
|
||||
void target.offsetHeight;
|
||||
|
||||
target.style.height = target.scrollHeight + 'px';
|
||||
target.style.opacity = '1';
|
||||
|
||||
this.setAttribute('aria-expanded', 'true');
|
||||
if (icon) icon.style.transform = 'rotate(180deg)';
|
||||
} else {
|
||||
// Cerrar
|
||||
target.style.height = target.scrollHeight + 'px';
|
||||
target.style.opacity = '1';
|
||||
|
||||
// Forzar reflow
|
||||
void target.offsetHeight;
|
||||
|
||||
target.style.height = '0';
|
||||
target.style.opacity = '0';
|
||||
|
||||
setTimeout(() => {
|
||||
target.classList.remove('show');
|
||||
target.style.display = '';
|
||||
}, 300);
|
||||
|
||||
this.setAttribute('aria-expanded', 'false');
|
||||
if (icon) icon.style.transform = 'rotate(0deg)';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
File diff suppressed because it is too large
Load Diff
315
views/partials/sidebar_agente_old.php
Normal file
315
views/partials/sidebar_agente_old.php
Normal file
@@ -0,0 +1,315 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
/* Estilos inspirados en Tailwind */
|
||||
.sidebar-container { background-color: #f9fafb; border-right: 1px solid #e5e7eb; }
|
||||
.sidebar-dark { background-color: #1f2937; border-right: 1px solid #374151; }
|
||||
.sidebar-nav .nav-link { color: #374151; font-weight: 500; padding: 0.5rem 0.75rem; margin-bottom: 0.125rem;
|
||||
border-radius: 0.5rem; transition: all 0.15s ease-in-out; display: flex; align-items: center; }
|
||||
.sidebar-nav .nav-link:hover { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link.active { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link .icon { width: 1.25rem; height: 1.25rem; margin-right: 0.75rem; color: #6b7280; transition: color 0.15s ease-in-out; flex-shrink: 0; }
|
||||
.sidebar-nav .nav-link:hover .icon,
|
||||
.sidebar-nav .nav-link.active .icon { color: #374151; }
|
||||
.sidebar-nav .badge { background-color: #f3f4f6; color: #374151; font-size: 0.75rem; font-weight: 500; padding: 0.25rem 0.5rem; border-radius: 9999px; margin-left: auto; }
|
||||
.sidebar-nav .badge.badge-primary { background-color: #dbeafe; color: #1e40af; }
|
||||
.submenu .nav-link { padding-left: 2.75rem; color: #6b7280; font-weight: 400; }
|
||||
.submenu .nav-link:hover { background-color: #f3f4f6; color: #374151; }
|
||||
.submenu .nav-link.active { background-color: #f3f4f6; color: #374151; }
|
||||
.collapse-icon { width: 0.75rem; height: 0.75rem; margin-left: auto; transition: transform 0.15s ease-in-out; }
|
||||
.nav-link[aria-expanded="true"] .collapse-icon { transform: rotate(180deg); }
|
||||
/* Estilos para modo oscuro */
|
||||
.sidebar-dark .sidebar-nav .nav-link { color: #d1d5db; }
|
||||
.sidebar-dark .sidebar-nav .nav-link:hover { background-color: #374151; color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .nav-link.active { background-color: #374151; color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .nav-link .icon { color: #9ca3af; }
|
||||
.sidebar-dark .sidebar-nav .nav-link:hover .icon,
|
||||
.sidebar-dark .sidebar-nav .nav-link.active .icon { color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .badge { background-color: #374151; color: #d1d5db; }
|
||||
.sidebar-dark .sidebar-nav .badge.badge-primary { background-color: #1e3a8a; color: #93c5fd; }
|
||||
.sidebar-dark .submenu .nav-link { color: #9ca3af; }
|
||||
.sidebar-dark .submenu .nav-link:hover { background-color: #374151; color: #d1d5db; }
|
||||
.sidebar-dark .submenu .nav-link.active { background-color: #374151; color: #d1d5db; }
|
||||
/* Navbar */
|
||||
.navbar-brand { font-weight: 600; font-size: 1.125rem; }
|
||||
.menu-toggle { border: 1px solid #6b7280; border-radius: 0.5rem; padding: 0.5rem; transition: all 0.15s ease-in-out; }
|
||||
.menu-toggle:hover { background-color: #f3f4f6; }
|
||||
/* Offcanvas para móviles */
|
||||
.offcanvas { background-color: #ffffff; }
|
||||
.offcanvas-header { border-bottom: 1px solid #e5e7eb; padding: 1rem 1.5rem; }
|
||||
.offcanvas-title { font-weight: 600; font-size: 1.125rem; color: #111827; }
|
||||
.offcanvas-body { padding: 1rem 1.5rem; }
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 767.98px) {
|
||||
.sidebar-nav .nav-link { color: #374151; }
|
||||
.sidebar-nav .nav-link:hover { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link.active { background-color: #f3f4f6; color: #111827; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 🔷 NAVBAR -->
|
||||
<nav class="navbar navbar-dark bg-dark fixed-top">
|
||||
<div class="container-fluid">
|
||||
<!-- Botón de menú para móviles -->
|
||||
<button class="menu-toggle d-md-none me-3" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu">
|
||||
☰
|
||||
</button>
|
||||
<span class="navbar-brand">SIIH | Agente Aduanal</span>
|
||||
<div class="d-flex ms-auto text-white">
|
||||
<div class="navbar-content">
|
||||
<?php
|
||||
// Verifica si estás en el dashboard principal del importador
|
||||
$esDashboard = str_contains($_SERVER['REQUEST_URI'], '/agentes/dashboard');
|
||||
|
||||
if ($esDashboard): ?>
|
||||
<span id="welcome-text">Bienvenido, <?= htmlspecialchars($_SESSION['usuario_nombre']) ?></span>
|
||||
<?php else: ?>
|
||||
<a href="/IMPORTADORES/sistemas/logout" class="btn btn-outline-light btn-sm ms-2 W-auto btn-animated">Cerrar Sesión</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- SIDEBAR FIJA (escritorio) -->
|
||||
<div class="d-none d-md-block sidebar-container sidebar-dark position-fixed h-100" style="width: 250px; top: 0; padding-top: 4rem; z-index: 1000;">
|
||||
<div class="px-3 py-4 h-100 overflow-y-auto">
|
||||
<nav class="sidebar-nav">
|
||||
<ul class="nav flex-column">
|
||||
<!-- INICIO -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/agentes/dashboard" class="nav-link active">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 22 21">
|
||||
<path d="M16.975 11H10V4.025a1 1 0 0 0-1.066-.998 8.5 8.5 0 1 0 9.039 9.039.999.999 0 0 0-1-1.066h.002Z"/>
|
||||
<path d="M12.5 0c-.157 0-.311.01-.565.027A1 1 0 0 0 11 1.02V10h8.975a1 1 0 0 0 1-.935c.013-.188.028-.374.028-.565A8.51 8.51 0 0 0 12.5 0Z"/>
|
||||
</svg>
|
||||
<span>Inicio</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- IMPORTADORES VINCULADOS -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/agentes/vinculados" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5z"/>
|
||||
<path d="M7.414 15.414a2 2 0 01-2.828-2.828l3-3a2 2 0 012.828 0 1 1 0 001.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5z"/>
|
||||
</svg>
|
||||
<span>Vinculación</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Patentes -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuPatentesDesktop" role="button" aria-expanded="false" aria-controls="submenuPatentesDesktop">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Patentes</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="collapse" id="submenuPatentesDesktop">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/patente/lista" class="nav-link">
|
||||
Ver Patentes
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/patente/alta" class="nav-link">
|
||||
Nueva Patente
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- LOCACIONES -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuLocacionesDesktop" role="button" aria-expanded="false" aria-controls="submenuLocacionesDesktop">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10 2C6.686 2 4 4.686 4 8c0 5.25 6 12 6 12s6-6.75 6-12c0-3.314-2.686-6-6-6zm0 8c-1.105 0-2-.895-2-2s.895-2 2-2 2 .895 2 2-.895 2-2 2z"/>
|
||||
</svg>
|
||||
<span>Locaciones</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="collapse" id="submenuLocacionesDesktop">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/locaciones/lista" class="nav-link">
|
||||
Ver Locaciones
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/locaciones/alta" class="nav-link">
|
||||
Agregar Locaciones
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- CONFIGURACIÓN -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/configuracion" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M11.983 1.034a1 1 0 00-1.966 0l-.26 1.857a6.996 6.996 0 00-1.223.51l-1.7-1.04a1 1 0 00-1.366.366l-1 1.732a1 1 0 00.366 1.366l1.7 1.04a6.984 6.984 0 000 1.02l-1.7 1.04a1 1 0 00-.366 1.366l1 1.732a1 1 0 001.366.366l1.7-1.04c.392.212.803.386 1.223.51l.26 1.857a1 1 0 001.966 0l.26-1.857c.42-.124.831-.298 1.223-.51l1.7 1.04a1 1 0 001.366-.366l1-1.732a1 1 0 00-.366-1.366l-1.7-1.04a6.984 6.984 0 000-1.02l1.7-1.04a1 1 0 00.366-1.366l-1-1.732a1 1 0 00-1.366-.366l-1.7 1.04a6.996 6.996 0 00-1.223-.51l-.26-1.857zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Configuración</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OFFCANVAS PARA MÓVILES -->
|
||||
<div class="offcanvas offcanvas-start d-md-none" tabindex="-1" id="sidebarMenu" style="top: 0; width: 250px;">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title">Menú</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="offcanvas"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<nav class="sidebar-nav">
|
||||
<ul class="nav flex-column">
|
||||
<!-- INICIO -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/agentes/dashboard" class="nav-link active">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 22 21">
|
||||
<path d="M16.975 11H10V4.025a1 1 0 0 0-1.066-.998 8.5 8.5 0 1 0 9.039 9.039.999.999 0 0 0-1-1.066h.002Z"/>
|
||||
<path d="M12.5 0c-.157 0-.311.01-.565.027A1 1 0 0 0 11 1.02V10h8.975a1 1 0 0 0 1-.935c.013-.188.028-.374.028-.565A8.51 8.51 0 0 0 12.5 0Z"/>
|
||||
</svg>
|
||||
<span>Inicio</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- IMPORTADORES VINCULADOS -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/agentes/vinculados" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5z"/>
|
||||
<path d="M7.414 15.414a2 2 0 01-2.828-2.828l3-3a2 2 0 012.828 0 1 1 0 001.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5z"/>
|
||||
</svg>
|
||||
<span>Vinculación</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Patentes -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuPatentesMobile" role="button" aria-expanded="false" aria-controls="submenuPatentesMobile">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Patentes</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="collapse" id="submenuPatentesMobile">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/patente/lista" class="nav-link">
|
||||
Ver Patentes
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/patente/alta" class="nav-link">
|
||||
Nueva Patente
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- LOCACIONES -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuLocacionesMobile" role="button" aria-expanded="false" aria-controls="submenuLocacionesMobile">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10 2C6.686 2 4 4.686 4 8c0 5.25 6 12 6 12s6-6.75 6-12c0-3.314-2.686-6-6-6zm0 8c-1.105 0-2-.895-2-2s.895-2 2-2 2 .895 2 2-.895 2-2 2z"/>
|
||||
</svg>
|
||||
<span>Locaciones</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="collapse" id="submenuLocacionesMobile">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/locaciones/lista" class="nav-link">
|
||||
Ver Locaciones
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/locaciones/alta" class="nav-link">
|
||||
Agregar Locaciones
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- CONFIGURACIÓN -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/configuracion" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M11.983 1.034a1 1 0 00-1.966 0l-.26 1.857a6.996 6.996 0 00-1.223.51l-1.7-1.04a1 1 0 00-1.366.366l-1 1.732a1 1 0 00.366 1.366l1.7 1.04a6.984 6.984 0 000 1.02l-1.7 1.04a1 1 0 00-.366 1.366l1 1.732a1 1 0 001.366.366l1.7-1.04c.392.212.803.386 1.223.51l.26 1.857a1 1 0 001.966 0l.26-1.857c.42-.124.831-.298 1.223-.51l1.7 1.04a1 1 0 001.366-.366l1-1.732a1 1 0 00-.366-1.366l-1.7-1.04a6.984 6.984 0 000-1.02l1.7-1.04a1 1 0 00.366-1.366l-1-1.732a1 1 0 00-1.366-.366l-1.7 1.04a6.996 6.996 0 00-1.223-.51l-.26-1.857zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Configuración</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// Simular lógica de dashboard
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const esDashboard = window.location.pathname.includes('/agentes/dashboard');
|
||||
const welcomeText = document.getElementById('welcome-text');
|
||||
const logoutBtn = document.getElementById('logout-btn');
|
||||
|
||||
if (!esDashboard) {
|
||||
if (welcomeText) {
|
||||
welcomeText.style.display = 'none';
|
||||
}
|
||||
if (logoutBtn) {
|
||||
logoutBtn.style.display = 'inline-block';
|
||||
}
|
||||
}
|
||||
|
||||
// Manejar estados activos basados en URL actual
|
||||
const currentPath = window.location.pathname;
|
||||
const navLinks = document.querySelectorAll('.nav-link');
|
||||
|
||||
navLinks.forEach(link => {
|
||||
const href = link.getAttribute('href');
|
||||
if (href && currentPath.includes(href)) {
|
||||
link.classList.add('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Manejar collapse manualmente
|
||||
document.querySelectorAll('[data-bs-toggle="collapse"]').forEach(trigger => {
|
||||
trigger.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const targetId = this.getAttribute('href');
|
||||
const targetElement = document.querySelector(targetId);
|
||||
const isExpanded = this.getAttribute('aria-expanded') === 'true';
|
||||
|
||||
if (targetElement) {
|
||||
if (isExpanded) {
|
||||
// Cerrar
|
||||
targetElement.classList.remove('show');
|
||||
this.setAttribute('aria-expanded', 'false');
|
||||
} else {
|
||||
// Abrir
|
||||
targetElement.classList.add('show');
|
||||
this.setAttribute('aria-expanded', 'true');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Remover el atributo data-bs-toggle para evitar conflictos
|
||||
trigger.removeAttribute('data-bs-toggle');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
File diff suppressed because it is too large
Load Diff
594
views/partials/sidebar_configuracion_old.php
Normal file
594
views/partials/sidebar_configuracion_old.php
Normal file
@@ -0,0 +1,594 @@
|
||||
<!-- views/partials/sidebar_configuracion.php -->
|
||||
<?php
|
||||
// Verificar que el usuario esté autenticado
|
||||
if (!isset($_SESSION['usuario_id'])) {
|
||||
header('Location: /IMPORTADORES/login');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Obtener información del usuario
|
||||
$tipoUsuario = $_SESSION['tipo_usuario'] ?? '';
|
||||
$usuarioId = $_SESSION['usuario_id'] ?? null;
|
||||
|
||||
// Definir permisos por tipo de usuario
|
||||
$permisos = [
|
||||
'super_admin' => [
|
||||
'informacion_general' => true,
|
||||
'automatizaciones' => true,
|
||||
'preferencias' => true,
|
||||
'seguridad' => true,
|
||||
'bitacoras' => [
|
||||
'registro_accesos' => true,
|
||||
'registro_cambios' => true,
|
||||
'registro_usuarios' => true,
|
||||
'registro_agencias' => true,
|
||||
'mi_actividad' => true
|
||||
],
|
||||
],
|
||||
'admin_agencia' => [
|
||||
'informacion_general' => true,
|
||||
'automatizaciones' => true,
|
||||
'preferencias' => true,
|
||||
'seguridad' => true,
|
||||
'bitacoras' => [
|
||||
'acceso_usuarios_agencia' => true,
|
||||
'registro_vinculaciones' => true,
|
||||
'mi_actividad' => true
|
||||
],
|
||||
],
|
||||
'agente_aduanal' => [
|
||||
'informacion_general' => true,
|
||||
'automatizaciones' => true,
|
||||
'preferencias' => true,
|
||||
'seguridad' => true,
|
||||
'bitacoras' => [
|
||||
'acceso_usuarios_agencia' => true,
|
||||
'registro_vinculaciones' => true,
|
||||
'mi_actividad' => true
|
||||
],
|
||||
],
|
||||
'importador' => [
|
||||
'informacion_general' => true,
|
||||
'automatizaciones' => true,
|
||||
'preferencias' => true,
|
||||
'seguridad' => true,
|
||||
'bitacoras' => [
|
||||
'vinculaciones_usuario' => true,
|
||||
'mi_actividad' => true
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Obtener permisos del usuario actual
|
||||
$permisosUsuario = $permisos[$tipoUsuario] ?? $permisos['importador'];
|
||||
$permisosBitacoras = $permisosUsuario['bitacoras'] ?? [];
|
||||
|
||||
// Función para verificar si el usuario tiene acceso a bitácoras
|
||||
function tieneAccesoBitacoras($permisosBitacoras) {
|
||||
if (is_bool($permisosBitacoras)) {
|
||||
return $permisosBitacoras;
|
||||
}
|
||||
return !empty($permisosBitacoras) && array_filter($permisosBitacoras);
|
||||
}
|
||||
|
||||
// Contar bitácoras disponibles según permisos
|
||||
function contarBitacorasDisponibles($permisosBitacoras) {
|
||||
if (!is_array($permisosBitacoras)) {
|
||||
return is_bool($permisosBitacoras) && $permisosBitacoras ? 1 : 0;
|
||||
}
|
||||
return count(array_filter($permisosBitacoras));
|
||||
}
|
||||
|
||||
$cantidadBitacoras = contarBitacorasDisponibles($permisosBitacoras);
|
||||
|
||||
// Variables para detectar las vistas actuales
|
||||
$currentPath = $_SERVER['REQUEST_URI'];
|
||||
$esConfiguracion = $currentPath === '/IMPORTADORES/configuracion/index' || $currentPath === '/IMPORTADORES/configuracion';
|
||||
$esVistaAutomatizaciones = strpos($currentPath, '/IMPORTADORES/automatizaciones/') === 0;
|
||||
$esVistaPreferencias = strpos($currentPath, '/IMPORTADORES/preferencias/') === 0;
|
||||
$esVistaSeguridad = strpos($currentPath, '/IMPORTADORES/seguridad/') === 0;
|
||||
$esVistaBitacoras = strpos($currentPath, '/IMPORTADORES/bitacoras/') === 0 && $currentPath !== '/IMPORTADORES/configuracion';
|
||||
$esVistaReset = strpos($currentPath, '/IMPORTADORES/reset/') === 0;
|
||||
|
||||
// Función para obtener el texto descriptivo según el permiso
|
||||
function obtenerTextoPermiso($permiso, $tipoUsuario) {
|
||||
$textos = [
|
||||
'registro_accesos' => ($tipoUsuario === 'super_admin') ? 'Registro de accesos' : null,
|
||||
'registro_cambios' => ($tipoUsuario === 'super_admin') ? 'Cambios de usuarios' : null,
|
||||
'registro_usuarios' => ($tipoUsuario === 'super_admin') ? 'Registro de usuarios' : null,
|
||||
'registro_agencias' => ($tipoUsuario === 'super_admin') ? 'Registro de agencias' : null,
|
||||
'acceso_usuarios_agencia' => ($tipoUsuario === 'admin_agencia' || $tipoUsuario === 'agente_aduanal') ? 'Registro de accesos' : null,
|
||||
'registro_vinculaciones' => ($tipoUsuario === 'admin_agencia' || $tipoUsuario === 'agente_aduanal') ? 'Vinculaciones (Agencia)' : null,
|
||||
'mi_actividad' => in_array($tipoUsuario, ['super_admin', 'admin_agencia', 'agente_aduanal', 'importador']) ? 'Mi actividad' : null,
|
||||
'vinculaciones_usuario' => ($tipoUsuario === 'importador') ? 'Mis vinculaciones' : null,
|
||||
];
|
||||
|
||||
return $textos[$permiso] ?? ucfirst(str_replace('_', ' ', $permiso));
|
||||
}
|
||||
|
||||
// Función para obtener la URL según el permiso
|
||||
function obtenerUrlPermiso($permiso) {
|
||||
$urls = [
|
||||
'registro_accesos' => '/IMPORTADORES/bitacoras/sistema', // ✅
|
||||
'registro_cambios' => '/IMPORTADORES/bitacoras/cambios', // ✅
|
||||
'registro_usuarios' => '/IMPORTADORES/bitacoras/usuarios', //
|
||||
'registro_agencias' => '/IMPORTADORES/bitacoras/agencias', // ✅
|
||||
'acceso_usuarios_agencia' => '/IMPORTADORES/bitacoras/sistemaAgencia', // ✅
|
||||
'registro_vinculaciones' => '/IMPORTADORES/bitacoras/vinculaciones', // ✅
|
||||
'mi_actividad' => '/IMPORTADORES/bitacoras/miAcceso', // ✅
|
||||
'vinculaciones_usuario' => '/IMPORTADORES/bitacoras/vinculacionesUsuario' // ✅
|
||||
];
|
||||
|
||||
return $urls[$permiso] ?? '/IMPORTADORES/bitacoras/index';
|
||||
}
|
||||
?>
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
/* Estilos inspirados en Tailwind */
|
||||
.sidebar-container { background-color: #f9fafb; border-right: 1px solid #e5e7eb; }
|
||||
.sidebar-dark { background-color: #1f2937; border-right: 1px solid #374151; }
|
||||
.sidebar-nav .nav-link { color: #374151; font-weight: 500; padding: 0.5rem 0.75rem; margin-bottom: 0.125rem;
|
||||
border-radius: 0.5rem; transition: all 0.15s ease-in-out; display: flex; align-items: center; }
|
||||
.sidebar-nav .nav-link:hover { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link.active { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link .icon { width: 1.25rem; height: 1.25rem; margin-right: 0.75rem; color: #6b7280; transition: color 0.15s ease-in-out; flex-shrink: 0; }
|
||||
.sidebar-nav .nav-link:hover .icon,
|
||||
.sidebar-nav .nav-link.active .icon { color: #374151; }
|
||||
.sidebar-nav .badge { background-color: #f3f4f6; color: #374151; font-size: 0.75rem; font-weight: 500; padding: 0.25rem 0.5rem; border-radius: 9999px; margin-left: auto; }
|
||||
.sidebar-nav .badge.badge-primary { background-color: #dbeafe; color: #1e40af; }
|
||||
.submenu .nav-link { padding-left: 2.75rem; color: #6b7280; font-weight: 400; }
|
||||
.submenu .nav-link:hover { background-color: #f3f4f6; color: #374151; }
|
||||
.submenu .nav-link.active { background-color: #f3f4f6; color: #374151; }
|
||||
.collapse-icon { width: 0.75rem; height: 0.75rem; margin-left: auto; transition: transform 0.15s ease-in-out; }
|
||||
.nav-link[aria-expanded="true"] .collapse-icon { transform: rotate(180deg); }
|
||||
/* Estilos para modo oscuro */
|
||||
.sidebar-dark .sidebar-nav .nav-link { color: #d1d5db; }
|
||||
.sidebar-dark .sidebar-nav .nav-link:hover { background-color: #374151; color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .nav-link.active { background-color: #374151; color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .nav-link .icon { color: #9ca3af; }
|
||||
.sidebar-dark .sidebar-nav .nav-link:hover .icon,
|
||||
.sidebar-dark .sidebar-nav .nav-link.active .icon { color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .badge { background-color: #374151; color: #d1d5db; }
|
||||
.sidebar-dark .sidebar-nav .badge.badge-primary { background-color: #1e3a8a; color: #93c5fd; }
|
||||
.sidebar-dark .submenu .nav-link { color: #9ca3af; }
|
||||
.sidebar-dark .submenu .nav-link:hover { background-color: #374151; color: #d1d5db; }
|
||||
.sidebar-dark .submenu .nav-link.active { background-color: #374151; color: #d1d5db; }
|
||||
/* Navbar */
|
||||
.navbar-brand { font-weight: 600; font-size: 1.125rem; }
|
||||
.menu-toggle { border: 1px solid #6b7280; border-radius: 0.5rem; padding: 0.5rem; transition: all 0.15s ease-in-out; }
|
||||
.menu-toggle:hover { background-color: #f3f4f6; }
|
||||
/* Offcanvas para móviles */
|
||||
.offcanvas { background-color: #ffffff; }
|
||||
.offcanvas-header { border-bottom: 1px solid #e5e7eb; padding: 1rem 1.5rem; }
|
||||
.offcanvas-title { font-weight: 600; font-size: 1.125rem; color: #111827; }
|
||||
.offcanvas-body { padding: 1rem 1.5rem; }
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 767.98px) {
|
||||
.sidebar-nav .nav-link { color: #374151; }
|
||||
.sidebar-nav .nav-link:hover { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link.active { background-color: #f3f4f6; color: #111827; }
|
||||
.permission-scope { background-color: rgba(108, 117, 125, 0.2); }
|
||||
.submenu-item:hover { background-color: rgba(233, 236, 239, 0.8) !important; }
|
||||
}
|
||||
.user-badge { font-size: 0.75rem; padding: 2px 6px; border-radius: 3px; }
|
||||
.permission-indicator { font-size: 0.7rem; color: #28a745; margin-left: 5px; }
|
||||
.permission-scope { font-size: 0.65rem; color: #6c757d; margin-left: 5px; padding: 1px 4px; background-color: rgba(108, 117, 125, 0.1); border-radius: 2px; }
|
||||
.submenu-item { font-size: 0.9rem; padding-left: 1rem !important; }
|
||||
.submenu-item:hover { background-color: rgba(73, 80, 87, 0.7) !important; }
|
||||
/* Transiciones para collapse manual */
|
||||
.collapse {
|
||||
overflow: hidden;
|
||||
transition: height 0.3s ease;
|
||||
}
|
||||
|
||||
/* Estilo para íconos */
|
||||
.collapse-icon {
|
||||
transition: transform 0.3s ease;
|
||||
margin-left: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- NAVBAR -->
|
||||
<nav class="navbar navbar-dark bg-dark fixed-top">
|
||||
<div class="container-fluid">
|
||||
<!-- Botón de menú para móviles -->
|
||||
<button class="menu-toggle d-md-none me-3" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu">
|
||||
☰
|
||||
</button>
|
||||
<span class="navbar-brand">
|
||||
SIIH | Configuración
|
||||
<span class="user-badge bg-info text-dark ms-2"><?= strtoupper(str_replace('_', ' ', $tipoUsuario)) ?></span>
|
||||
</span>
|
||||
<div class="d-flex ms-auto">
|
||||
<?php
|
||||
// Definir la URL de regreso según el tipo de usuario
|
||||
$urlRegreso = match($tipoUsuario) {
|
||||
'super_admin' => '/IMPORTADORES/administrador/dashboard',
|
||||
'admin_agencia' => '/IMPORTADORES/agencias/dashboard',
|
||||
'agente_aduanal' => '/IMPORTADORES/agentes/dashboard',
|
||||
'importador' => '/IMPORTADORES/importadores/dashboard',
|
||||
default => '/IMPORTADORES/importadores/dashboard'
|
||||
};
|
||||
?>
|
||||
<a href="<?= $urlRegreso ?>" class="btn btn-outline-light btn-sm ms-2 W-auto btn-animated" style="margin: 0 0 0 25px;">
|
||||
← Panel Principal
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- SIDEBAR FIJA (escritorio) -->
|
||||
<div class="d-none d-md-block sidebar-container sidebar-dark position-fixed h-100" style="width: 250px; top: 0; padding-top: 4rem; z-index: 1000;">
|
||||
<div class="px-3 py-4 h-100 overflow-y-auto">
|
||||
<nav class="sidebar-nav">
|
||||
<ul class="nav flex-column">
|
||||
<!-- INFORMACIÓN GENERAL -->
|
||||
<li class="nav-item">
|
||||
<?php if ($permisosUsuario['informacion_general']): ?>
|
||||
<a href="/IMPORTADORES/configuracion/index" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M18 10c0 4.418-3.582 8-8 8s-8-3.582-8-8 3.582-8 8-8 8 3.582 8 8zm-8-4a1 1 0 100 2 1 1 0 000-2zm-1 4a1 1 0 000 2h1v3a1 1 0 102 0v-4a1 1 0 00-1-1h-2z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<span>Información general</span>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<!-- AUTOMATIZACIONES -->
|
||||
<li class="nav-item">
|
||||
<?php if ($permisosUsuario['automatizaciones']): ?>
|
||||
<a href="/IMPORTADORES/automatizaciones/index" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M13 7H7v6h6V7z" />
|
||||
<path fill-rule="evenodd" d="M5 3a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2V9.586a1 1 0 00-.293-.707l-4.586-4.586A1 1 0 0012.414 4H5zm7 1.414L16.586 10H13a1 1 0 01-1-1V4.414z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Automatizaciones</span>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<!-- PREFERENCIAS -->
|
||||
<li class="nav-item">
|
||||
<?php if ($permisosUsuario['preferencias']): ?>
|
||||
<?php $enDashboardPreferencias = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/preferencias/index'; ?>
|
||||
<a class="nav-link <?= $esVistaPreferencias ? 'active' : '' ?>"
|
||||
href="<?= $enDashboardPreferencias ? '#submenuPreferenciasDesktop' : '/IMPORTADORES/preferencias/index' ?>"
|
||||
<?= $enDashboardPreferencias ? 'data-bs-toggle="collapse"' : '' ?>
|
||||
role="button" aria-expanded="false" aria-controls="submenuPreferenciasDesktop"
|
||||
onclick="<?= $enDashboardPreferencias ? 'return true;' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/preferencias/index\';' ?>">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M11.3 1.046a1 1 0 00-2.6 0L7.528 3H4a1 1 0 000 2h1v10H4a1 1 0 000 2h3.528l1.172 1.954a1 1 0 001.6 0L12.472 17H16a1 1 0 000-2h-1V5h1a1 1 0 100-2h-3.528L11.3 1.046zM9 6a1 1 0 011 1v6a1 1 0 11-2 0V7a1 1 0 011-1z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Preferencias</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="collapse" id="submenuPreferenciasDesktop">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/preferencias/notificaciones" class="nav-link">
|
||||
Gestión de Notificaciones
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/preferencias/catalogos" class="nav-link">
|
||||
Visualización de Catálogos
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<!-- SEGURIDAD -->
|
||||
<li class="nav-item">
|
||||
<?php if ($permisosUsuario['seguridad']): ?>
|
||||
<?php $enDashboardSeguridad = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/seguridad/index'; ?>
|
||||
<a class="nav-link <?= $esVistaSeguridad ? 'active' : '' ?>"
|
||||
href="<?= $enDashboardSeguridad ? '#submenuSeguridadDesktop' : '/IMPORTADORES/seguridad/index' ?>"
|
||||
<?= $enDashboardSeguridad ? 'data-bs-toggle="collapse"' : '' ?>
|
||||
role="button" aria-expanded="false" aria-controls="submenuSeguridadDesktop"
|
||||
onclick="<?= $enDashboardSeguridad ? 'return true;' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/seguridad/index\';' ?>">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm2 6V6a2 2 0 10-4 0v2h4z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Seguridad</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="collapse" id="submenuSeguridadDesktop">
|
||||
<nav class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/seguridad/opciones" class="nav-link">
|
||||
Opciones de Seguridad
|
||||
</a>
|
||||
</li>
|
||||
</nav>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<!-- BITÁCORAS CON PERMISOS ESPECÍFICOS -->
|
||||
<li class="nav-item">
|
||||
<?php if (tieneAccesoBitacoras($permisosBitacoras)): ?>
|
||||
<?php $enDashboardBitacoras = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/index'; ?>
|
||||
<a class="nav-link <?= $esVistaBitacoras ? 'active' : '' ?>"
|
||||
href="<?= $enDashboardBitacoras ? '#submenuBitacorasDesktop' : '/IMPORTADORES/bitacoras/index' ?>"
|
||||
<?= $enDashboardBitacoras ? 'data-bs-toggle="collapse"' : '' ?>
|
||||
role="button" aria-expanded="<?= $enDashboardBitacoras ? 'false' : 'false' ?>" aria-controls="submenuBitacorasDesktop"
|
||||
onclick="<?= $enDashboardBitacoras ? 'return true;' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/bitacoras/index\';' ?>">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a1 1 0 001-1V4a1 1 0 00-1-1H4zm0 2h11v10H4V5z"/>
|
||||
<path d="M6 7h5v1H6V7zm0 3h8v1H6v-1z"/>
|
||||
</svg>
|
||||
<span>Bitácoras</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="collapse" id="submenuBitacorasDesktop">
|
||||
<nav class="nav flex-column submenu">
|
||||
<?php foreach ($permisosBitacoras as $permiso => $tienePermiso): ?>
|
||||
<li class="nav-item">
|
||||
<?php if ($tienePermiso): ?>
|
||||
<?php
|
||||
$url = obtenerUrlPermiso($permiso);
|
||||
$texto = obtenerTextoPermiso($permiso, $tipoUsuario);
|
||||
$esActivo = $_SERVER['REQUEST_URI'] === $url;
|
||||
|
||||
// Scope según dashboard
|
||||
$scope = '';
|
||||
if ($tipoUsuario === 'super_admin') {
|
||||
$scope = 'Global';
|
||||
} elseif ($tipoUsuario === 'admin_agencia' || $tipoUsuario === 'agente_aduanal') {
|
||||
if (in_array($permiso, ['acceso_usuarios_agencia', 'registro_vinculaciones'])) {
|
||||
$scope = 'Agencia';
|
||||
} elseif (in_array($permiso, ['mis_vinculaciones', 'mi_actividad'])) {
|
||||
$scope = 'Personal';
|
||||
}
|
||||
} elseif ($tipoUsuario === 'importador') {
|
||||
$scope = 'Personal';
|
||||
}
|
||||
?>
|
||||
<a href="<?= $url ?>"
|
||||
class="nav-link submenu-item px-3 py-2 d-flex justify-content-between align-items-center <?= $esActivo ? 'active' : '' ?>">
|
||||
<span>
|
||||
<?= htmlspecialchars($texto) ?>
|
||||
<?php if ($scope): ?>
|
||||
<span class="permission-scope"><?= $scope ?></span>
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</nav>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OFFCANVAS PARA MÓVILES -->
|
||||
<div class="offcanvas offcanvas-start d-md-none" tabindex="-1" id="sidebarMenu" style="top: 0; width: 250px;">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title">Menú</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="offcanvas"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<nav class="sidebar-nav">
|
||||
<ul class="nav flex-column">
|
||||
<!-- INFORMACIÓN GENERAL -->
|
||||
<li class="nav-item">
|
||||
<?php if ($permisosUsuario['informacion_general']): ?>
|
||||
<a href="/IMPORTADORES/configuracion/index" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M18 10c0 4.418-3.582 8-8 8s-8-3.582-8-8 3.582-8 8-8 8 3.582 8 8zm-8-4a1 1 0 100 2 1 1 0 000-2zm-1 4a1 1 0 000 2h1v3a1 1 0 102 0v-4a1 1 0 00-1-1h-2z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<span>Información general</span>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<!-- AUTOMATIZACIONES -->
|
||||
<li class="nav-item">
|
||||
<?php if ($permisosUsuario['automatizaciones']): ?>
|
||||
<a href="/IMPORTADORES/automatizaciones/index" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M13 7H7v6h6V7z" />
|
||||
<path fill-rule="evenodd" d="M5 3a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2V9.586a1 1 0 00-.293-.707l-4.586-4.586A1 1 0 0012.414 4H5zm7 1.414L16.586 10H13a1 1 0 01-1-1V4.414z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Automatizaciones</span>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<!-- PREFERENCIAS -->
|
||||
<li class="nav-item">
|
||||
<?php if ($permisosUsuario['preferencias']): ?>
|
||||
<?php $enDashboardPreferencias = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/preferencias/index'; ?>
|
||||
<a class="nav-link <?= $esVistaPreferencias ? 'active' : '' ?>"
|
||||
href="<?= $enDashboardPreferencias ? '#submenuPreferenciasMobile' : '/IMPORTADORES/preferencias/index' ?>"
|
||||
<?= $enDashboardPreferencias ? 'data-bs-toggle="collapse"' : '' ?>
|
||||
role="button" aria-expanded="false" aria-controls="submenuPreferenciasMobile"
|
||||
onclick="<?= $enDashboardPreferencias ? 'return true;' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/preferencias/index\';' ?>">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M11.3 1.046a1 1 0 00-2.6 0L7.528 3H4a1 1 0 000 2h1v10H4a1 1 0 000 2h3.528l1.172 1.954a1 1 0 001.6 0L12.472 17H16a1 1 0 000-2h-1V5h1a1 1 0 100-2h-3.528L11.3 1.046zM9 6a1 1 0 011 1v6a1 1 0 11-2 0V7a1 1 0 011-1z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Preferencias</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="collapse" id="submenuPreferenciasMobile">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/preferencias/notificaciones" class="nav-link">
|
||||
Gestión de Notificaciones
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/preferencias/catalogos" class="nav-link">
|
||||
Visualización de Catálogos
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<!-- SEGURIDAD -->
|
||||
<li class="nav-item">
|
||||
<?php if ($permisosUsuario['seguridad']): ?>
|
||||
<?php $enDashboardSeguridad = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/seguridad/index'; ?>
|
||||
<a class="nav-link <?= $esVistaSeguridad ? 'active' : '' ?>"
|
||||
href="<?= $enDashboardSeguridad ? '#submenuSeguridadMobile' : '/IMPORTADORES/seguridad/index' ?>"
|
||||
<?= $enDashboardSeguridad ? 'data-bs-toggle="collapse"' : '' ?>
|
||||
role="button" aria-expanded="false" aria-controls="submenuSeguridadMobile"
|
||||
onclick="<?= $enDashboardSeguridad ? 'return true;' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/seguridad/index\';' ?>">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm2 6V6a2 2 0 10-4 0v2h4z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Seguridad</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="collapse" id="submenuSeguridadMobile">
|
||||
<nav class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/seguridad/opciones" class="nav-link">
|
||||
Opciones de Seguridad
|
||||
</a>
|
||||
</li>
|
||||
</nav>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<!-- BITÁCORAS CON PERMISOS ESPECÍFICOS -->
|
||||
<li class="nav-item">
|
||||
<?php if (tieneAccesoBitacoras($permisosBitacoras)): ?>
|
||||
<?php $enDashboardBitacoras = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/index'; ?>
|
||||
<a class="nav-link <?= $esVistaBitacoras ? 'active' : '' ?>"
|
||||
href="<?= $enDashboardBitacoras ? '#submenuBitacorasMobile' : '/IMPORTADORES/bitacoras/index' ?>"
|
||||
<?= $enDashboardBitacoras ? 'data-bs-toggle="collapse"' : '' ?>
|
||||
role="button" aria-expanded="<?= $enDashboardBitacoras ? 'false' : 'false' ?>" aria-controls="submenuBitacorasMobile"
|
||||
onclick="<?= $enDashboardBitacoras ? 'return true;' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/bitacoras/index\';' ?>">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a1 1 0 001-1V4a1 1 0 00-1-1H4zm0 2h11v10H4V5z"/>
|
||||
<path d="M6 7h5v1H6V7zm0 3h8v1H6v-1z"/>
|
||||
</svg>
|
||||
<span>Bitácoras</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="collapse" id="submenuBitacorasMobile">
|
||||
<nav class="nav flex-column submenu">
|
||||
<?php foreach ($permisosBitacoras as $permiso => $tienePermiso): ?>
|
||||
<li class="nav-item">
|
||||
<?php if ($tienePermiso): ?>
|
||||
<?php
|
||||
$url = obtenerUrlPermiso($permiso);
|
||||
$texto = obtenerTextoPermiso($permiso, $tipoUsuario);
|
||||
$esActivo = $_SERVER['REQUEST_URI'] === $url;
|
||||
|
||||
// Scope según dashboard
|
||||
$scope = '';
|
||||
if ($tipoUsuario === 'super_admin') {
|
||||
$scope = 'Global';
|
||||
} elseif ($tipoUsuario === 'admin_agencia' || $tipoUsuario === 'agente_aduanal') {
|
||||
if (in_array($permiso, ['acceso_usuarios_agencia', 'registro_vinculaciones'])) {
|
||||
$scope = 'Agencia';
|
||||
} elseif (in_array($permiso, ['mis_vinculaciones', 'mi_actividad'])) {
|
||||
$scope = 'Personal';
|
||||
}
|
||||
} elseif ($tipoUsuario === 'importador') {
|
||||
$scope = 'Personal';
|
||||
}
|
||||
?>
|
||||
<a href="<?= $url ?>"
|
||||
class="nav-link submenu-item <?= $esActivo ? 'active' : '' ?>">
|
||||
<span>
|
||||
<?= htmlspecialchars($texto) ?>
|
||||
<?php if ($scope): ?>
|
||||
<span class="permission-scope"><?= $scope ?></span>
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</nav>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const currentPath = window.location.pathname.replace(/\/$/, '');
|
||||
|
||||
// Inicializar todos los collapses
|
||||
document.querySelectorAll('[data-bs-toggle="collapse"]').forEach(trigger => {
|
||||
const targetId = trigger.getAttribute('data-bs-target') || trigger.getAttribute('href');
|
||||
const target = document.querySelector(targetId);
|
||||
const icon = trigger.querySelector('.collapse-icon');
|
||||
|
||||
if (!target) return;
|
||||
|
||||
// 1. Inicialización automática basada en la ruta
|
||||
const shouldExpand = Array.from(target.querySelectorAll('a')).some(link => {
|
||||
const href = link.getAttribute('href')?.replace(/\/$/, '');
|
||||
return href && currentPath.startsWith(href);
|
||||
});
|
||||
|
||||
// 2. Configurar el estado inicial
|
||||
if (shouldExpand) {
|
||||
target.classList.add('show');
|
||||
trigger.setAttribute('aria-expanded', 'true');
|
||||
if (icon) icon.style.transform = 'rotate(180deg)';
|
||||
}
|
||||
|
||||
// 3. Manejar clicks manuales
|
||||
trigger.addEventListener('click', function(e) {
|
||||
// Solo prevenir el comportamiento por defecto si es necesario
|
||||
if (this.getAttribute('href') === '#' || this.getAttribute('data-bs-target') === '#') {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
const isExpanded = this.getAttribute('aria-expanded') === 'true';
|
||||
|
||||
// Rotar el ícono inmediatamente
|
||||
if (icon) {
|
||||
icon.style.transform = isExpanded ? 'rotate(0deg)' : 'rotate(180deg)';
|
||||
}
|
||||
|
||||
// Actualizar atributo ARIA
|
||||
this.setAttribute('aria-expanded', !isExpanded);
|
||||
});
|
||||
|
||||
// 4. Manejar eventos de Bootstrap para sincronización
|
||||
target.addEventListener('show.bs.collapse', () => {
|
||||
trigger.setAttribute('aria-expanded', 'true');
|
||||
if (icon) icon.style.transform = 'rotate(180deg)';
|
||||
});
|
||||
|
||||
target.addEventListener('hide.bs.collapse', () => {
|
||||
trigger.setAttribute('aria-expanded', 'false');
|
||||
if (icon) icon.style.transform = 'rotate(0deg)';
|
||||
});
|
||||
});
|
||||
|
||||
// Marcar enlaces activos
|
||||
document.querySelectorAll('.nav-link').forEach(link => {
|
||||
const href = link.getAttribute('href')?.replace(/\/$/, '');
|
||||
if (href && (currentPath === href || currentPath.startsWith(href + '/'))) {
|
||||
link.classList.add('active');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,16 +19,10 @@
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<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; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
/* Contenido principal: sin solaparse con sidebar/navbar */
|
||||
.content { margin-top: 0; padding: 24px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
|
||||
@media (min-width: 768px) { .content { margin-left: var(--sidebar-width, 280px); } }
|
||||
@media (max-width: 767.98px) { .content { margin-left: 0; } }
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
@@ -57,7 +51,7 @@
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
.hide { display: none !important; }
|
||||
.alert { top: 75px; left: 275px; right: 50px; position: fixed; z-index: 1050; width: calc(100% - 285px); }
|
||||
.alert { top: calc(var(--navbar-height, 70px) + 5px); left: calc(var(--sidebar-width, 280px) + 15px); right: 15px; position: fixed; z-index: 1050; width: calc(100% - var(--sidebar-width, 280px) - 30px); }
|
||||
/* ========== ANIMACIONES PARA INPUTS TEXTO ========== */
|
||||
.form-control:not(.no-animation) { transition: all 0.3s ease; border: 2px solid #dee2e6; position: relative; }
|
||||
.form-control:not(.no-animation).shake { animation: shake 0.5s ease-in-out; }
|
||||
@@ -241,7 +235,7 @@
|
||||
/* Indicador de progreso */
|
||||
.progress-indicator {
|
||||
position: fixed;
|
||||
top: 60px;
|
||||
top: calc(var(--navbar-height, 70px) + 10px);
|
||||
right: 20px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
@@ -293,7 +287,7 @@
|
||||
/* Auto-save indicator */
|
||||
.auto-save-indicator {
|
||||
position: fixed;
|
||||
top: 60px;
|
||||
top: calc(var(--navbar-height, 70px) + 10px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: #28a745;
|
||||
@@ -308,7 +302,7 @@
|
||||
|
||||
.auto-save-indicator.show {
|
||||
opacity: 1;
|
||||
top: 80px;
|
||||
top: calc(var(--navbar-height, 70px) + 30px);
|
||||
}
|
||||
|
||||
/* Templates rápidos */
|
||||
@@ -926,7 +920,7 @@
|
||||
<!-- Panel de referencia (columna derecha) - MEJORADO -->
|
||||
<div class="col-lg-3">
|
||||
<!-- Card de Pedimentos -->
|
||||
<div class="card bg-light shadow-sm fade-in-up mb-3" style="animation-delay: 0.2s; position: sticky; top: 20px;">
|
||||
<div class="card bg-light shadow-sm fade-in-up mb-3" style="animation-delay: 0.2s; position: sticky; top: calc(var(--navbar-height, 70px) + 20px);">
|
||||
<div class="card-header bg-primary text-white py-2">
|
||||
<h6 class="mb-0"><i class="fas fa-list-ul me-1"></i> Pedimentos Disponibles</h6>
|
||||
</div>
|
||||
@@ -1161,7 +1155,6 @@
|
||||
|
||||
<!-- Choices.js JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.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/select2@4.1.0/dist/js/select2.min.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
-- Tabla para configuración de WINSAAI vinculada a usuarios (SQL Server)
|
||||
CREATE TABLE winsaai_config (
|
||||
id INT IDENTITY(1,1) PRIMARY KEY,
|
||||
id_usuario INT NOT NULL, -- ID del usuario propietario de la configuración
|
||||
host NVARCHAR(255) NOT NULL, -- Dirección IP o DNS del servidor WINSAAI
|
||||
port INT NOT NULL DEFAULT 80, -- Puerto de conexión
|
||||
protocol NVARCHAR(10) NOT NULL DEFAULT 'https' CHECK (protocol IN ('http', 'https')), -- Protocolo de conexión
|
||||
usuario NVARCHAR(100) NOT NULL, -- Usuario de autenticación
|
||||
password NVARCHAR(500) NOT NULL, -- Contraseña encriptada
|
||||
sync_pedimentos BIT DEFAULT 1, -- Sincronizar pedimentos
|
||||
sync_coves BIT DEFAULT 1, -- Sincronizar COVES
|
||||
status NVARCHAR(20) NOT NULL DEFAULT 'inactivo' CHECK (status IN ('activo', 'inactivo', 'error')), -- Estado de la conexión
|
||||
last_sync DATETIME2 NULL, -- Última sincronización exitosa
|
||||
created_at DATETIME2 NOT NULL DEFAULT GETDATE(),
|
||||
updated_at DATETIME2 NOT NULL DEFAULT GETDATE(),
|
||||
|
||||
-- Constraints
|
||||
CONSTRAINT UQ_winsaai_config_usuario UNIQUE (id_usuario) -- Un usuario solo puede tener una configuración
|
||||
);
|
||||
|
||||
-- Indices para mejorar rendimiento
|
||||
CREATE INDEX IX_winsaai_config_status ON winsaai_config (status);
|
||||
CREATE INDEX IX_winsaai_config_usuario ON winsaai_config (id_usuario);
|
||||
|
||||
-- Trigger para actualizar updated_at automáticamente
|
||||
CREATE TRIGGER TR_winsaai_config_updated_at
|
||||
ON winsaai_config
|
||||
AFTER UPDATE
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
UPDATE winsaai_config
|
||||
SET updated_at = GETDATE()
|
||||
WHERE id IN (SELECT id FROM inserted);
|
||||
END;
|
||||
@@ -1,19 +0,0 @@
|
||||
-- Tabla para configuración de WINSAAI vinculada a usuarios
|
||||
CREATE TABLE winsaai_config (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
id_usuario INT NOT NULL COMMENT 'ID del usuario propietario de la configuración',
|
||||
host VARCHAR(255) NOT NULL COMMENT 'Dirección IP o DNS del servidor WINSAAI',
|
||||
port INT NOT NULL DEFAULT 80 COMMENT 'Puerto de conexión',
|
||||
protocol ENUM('http', 'https') NOT NULL DEFAULT 'https' COMMENT 'Protocolo de conexión',
|
||||
usuario VARCHAR(100) NOT NULL COMMENT 'Usuario de autenticación',
|
||||
password VARCHAR(255) NOT NULL COMMENT 'Contraseña encriptada',
|
||||
sync_pedimentos BOOLEAN DEFAULT TRUE COMMENT 'Sincronizar pedimentos',
|
||||
sync_coves BOOLEAN DEFAULT TRUE COMMENT 'Sincronizar COVES',
|
||||
status ENUM('activo', 'inactivo', 'error') DEFAULT 'inactivo' COMMENT 'Estado de la conexión',
|
||||
last_sync TIMESTAMP NULL COMMENT 'Última sincronización exitosa',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY unique_user_config (id_usuario) COMMENT 'Un usuario solo puede tener una configuración',
|
||||
INDEX idx_status (status),
|
||||
INDEX idx_usuario (id_usuario)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
Reference in New Issue
Block a user