Compare commits

10 Commits

Author SHA1 Message Date
d0af739ce2 Preparando proyecto para migración a repositorio MVE 2025-10-23 08:48:00 -06:00
b1b9e4a77c grales 2025-10-21 11:41:30 -06:00
ed2ef4e01b files 2025-09-25 20:27:18 -06:00
b18c05d52a Suggestions 2025-07-22 13:41:13 -06:00
8bb0f84fd1 Cambios 2025-07-22 10:44:40 -06:00
597ab73d4a Sinónimos - Solicitudes de importación 2025-07-21 09:44:26 -06:00
c376ddbcdf Icons - sidabars 2025-07-18 13:12:26 -06:00
90e9eea12b Settings 2025-07-18 12:28:22 -06:00
37dcfffd7b Sidebars - animaciones 2025-07-18 12:21:26 -06:00
1900091b99 Sidebars 2025-07-17 13:31:55 -06:00
175 changed files with 32050 additions and 4791 deletions

4
.env
View File

@@ -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
View 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

View File

@@ -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]);

View 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');
}
}
?>

View File

@@ -237,6 +237,154 @@ function aprobar_usuario()
exit;
}
function denegar_usuario()
{
$conn = getConnection();
$id = $_GET['id'] ?? null;
$motivo = trim($_POST['motivo'] ?? '');
// Definir motivo por defecto si no se ingresó uno
if ($motivo === '') {
$motivo = 'La solicitud ha sido rechazada por no cumplir con los requisitos establecidos.';
}
// Validar parámetros
if (!$id || !is_numeric($id)) {
header("Location: /IMPORTADORES/administrador/aprobarUsuarios?error=invalid_id");
exit;
}
if (!$motivo || trim($motivo) === '') {
header("Location: /IMPORTADORES/administrador/aprobarUsuarios?error=motivo_required");
exit;
}
// Verificar sesión del usuario administrador
if (!isset($_SESSION['usuario_id']) || empty($_SESSION['usuario_id'])) {
header("Location: /IMPORTADORES/administrador/aprobarUsuarios?error=session_error");
exit;
}
try {
// Obtener la solicitud
$sql = "SELECT * FROM solicitudes_importadores WHERE request_id = ?";
$stmt = sqlsrv_query($conn, $sql, [$id]);
if (!$stmt) {
throw new Exception("Error en consulta de solicitud: " . print_r(sqlsrv_errors(), true));
}
$solicitud = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
if (!$solicitud) {
throw new Exception("Solicitud no encontrada");
}
// Validar que no haya sido procesada ya
if ($solicitud['request_status'] === 'denied') {
header("Location: /IMPORTADORES/administrador/aprobarUsuarios?error=already_denied");
exit;
}
if ($solicitud['request_status'] === 'approved') {
header("Location: /IMPORTADORES/administrador/aprobarUsuarios?error=already_approved");
exit;
}
// Preparar datos para el correo
$nombre = !empty($solicitud['company_name']) ? decrypt($solicitud['company_name']) : 'Usuario';
$email = $solicitud['email'] ?? null;
if (empty($email)) {
throw new Exception("Email no disponible en la solicitud");
}
// Actualizar solicitud como denegada
$sqlUpdate = "UPDATE solicitudes_importadores
SET request_status = 'denied',
approval_date = GETDATE(),
approved_by = ?
WHERE request_id = ?
";
$stmtUpdate = sqlsrv_query($conn, $sqlUpdate, [$_SESSION['usuario_id'], $id]);
if (!$stmtUpdate) {
throw new Exception("Error al actualizar solicitud: " . print_r(sqlsrv_errors(), true));
}
// Enviar correo de notificación de denegación
$mail = new PHPMailer(true);
try {
$mail->isSMTP();
$mail->Host = 'secure.emailsrvr.com';
$mail->SMTPAuth = true;
$mail->Username = 'noreply@aduanasoft.com.mx';
$mail->Password = $_ENV['SMTP_PASS'] ?? '';
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
$mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | Sistema Integral para Importadores de Hidrocarburos');
$mail->addAddress($email);
$mail->CharSet = 'UTF-8';
$mail->isHTML(true);
$mail->Subject = 'Solicitud de registro denegada - SIIH';
$mail->Body = "
<div style='font-family: Segoe UI, sans-serif; background-color: #f4f6f9; padding: 40px;'>
<div style='max-width: 600px; margin: auto; background: #fff; border: 1px solid #ddd; border-radius: 10px; overflow: hidden;'>
<div style='background: linear-gradient(to right, #dc3545, #c82333); padding: 20px; text-align: center;'>
<h2 style='color: white;'>Solicitud Denegada</h2>
</div>
<div style='padding: 30px; color: #333; font-size: 16px;'>
<p>Estimado(a) <strong>$nombre</strong>,</p>
<p>Lamentamos informarte que tu solicitud de registro como importador en el sistema SIIH ha sido denegada.</p>
<div style='background: #f8f9fa; padding: 15px; border-left: 4px solid #dc3545; margin: 20px 0;'>
<p style='margin: 0; font-weight: bold; color: #dc3545;'>Motivo de la denegación:</p>
<p style='margin: 10px 0 0 0;'>$motivo</p>
</div>
<p>Si consideras que esta decisión es incorrecta o deseas obtener más información, puedes contactarnos para revisar tu caso.</p>
<p>Agradecemos tu interés en formar parte del Sistema Integral para Importadores de Hidrocarburos.</p>
</div>
<div style='background: #e9ecef; text-align: center; padding: 15px; font-size: 13px; color: #666;'>
&copy; " . date('Y') . " SIIH · Sistema Integral para Importadores de Hidrocarburos
</div>
</div>
</div>";
$mail->send();
// Éxito completo (solicitud denegada y correo enviado)
header("Location: /IMPORTADORES/administrador/aprobarUsuarios?success=denied");
} catch (Exception $mailException) {
// Error en el correo, pero la solicitud fue denegada exitosamente
error_log("Error al enviar correo de denegación: " . $mailException->getMessage());
// Redirigir con mensaje de éxito parcial
header("Location: /IMPORTADORES/administrador/aprobarUsuarios?success=denied&warning=email_failed");
}
} catch (Exception $e) {
// Log del error para debugging
error_log("Error al denegar usuario: " . $e->getMessage());
// Redirigir con error específico basado en el tipo de error
if (strpos($e->getMessage(), 'Solicitud no encontrada') !== false) {
header("Location: /IMPORTADORES/administrador/aprobarUsuarios?error=invalid_request");
} elseif (strpos($e->getMessage(), 'Email no disponible') !== false) {
header("Location: /IMPORTADORES/administrador/aprobarUsuarios?error=email_unavailable");
} else {
header("Location: /IMPORTADORES/administrador/aprobarUsuarios?error=process_failed");
}
}
exit;
}
function altaUsuarios()
{
if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'super_admin') {
@@ -270,6 +418,7 @@ function guardar_usuario()
exit;
}
$conn = getConnection();
// 1. Capturar y validar datos
@@ -649,6 +798,123 @@ function aprobar_agencia()
exit;
}
function denegar_agencia()
{
$conn = getConnection();
$id = $_GET['id'] ?? null;
$motivo = trim($_POST['motivo'] ?? '');
$usuario_id = $_SESSION['usuario_id'];
// Definir motivo por defecto si no se ingresó uno
if ($motivo === '') {
$motivo = 'La solicitud ha sido rechazada por no cumplir con los requisitos establecidos.';
}
// Validar parámetros
if (!$id || !is_numeric($id)) {
header("Location: /IMPORTADORES/administrador/aprobarAgencias?error=invalid_id");
exit;
}
if (!$motivo || trim($motivo) === '') {
header("Location: /IMPORTADORES/administrador/aprobarAgencias?error=motivo_required");
exit;
}
// Obtener la solicitud
$sql = "SELECT * FROM solicitudes_agencias WHERE request_id = ?";
$stmt = sqlsrv_query($conn, $sql, [$id]);
$solicitud = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
if (!$solicitud) {
header("Location: /IMPORTADORES/administrador/aprobarAgencias?error=invalid_request");
exit;
}
// Validar que no haya sido procesada ya
if ($solicitud['request_status'] === 'denied') {
die("⚠️ Esta solicitud ya fue denegada.");
}
if ($solicitud['request_status'] === 'approved') {
die("⚠️ Esta solicitud ya fue aprobada y no se puede denegar.");
}
// Preparar datos para el correo
$nombre = decrypt($solicitud['agencia_name']);
$admin_name = decrypt($solicitud['admin_name']);
$admin_email = $solicitud['admin_email'];
// Actualizar solicitud como denegada
$sqlUpdate = "UPDATE solicitudes_agencias
SET request_status = 'denied',
approval_date = GETDATE(),
approved_by = ?
WHERE request_id = ?
";
$stmtUpdate = sqlsrv_query($conn, $sqlUpdate, [$usuario_id, $id]);
if (!$stmtUpdate) {
header("Location: /IMPORTADORES/administrador/aprobarAgencias?error=update_request_failed");
exit;
}
// Enviar correo de notificación de denegación
$mail = new PHPMailer(true);
try {
$mail->isSMTP();
$mail->Host = 'secure.emailsrvr.com';
$mail->SMTPAuth = true;
$mail->Username = 'noreply@aduanasoft.com.mx';
$mail->Password = $_ENV['SMTP_PASS'];
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
$mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | Sistema Integral para Importadores de Hidrocarburos');
$mail->addAddress($admin_email);
$mail->CharSet = 'UTF-8';
$mail->isHTML(true);
$mail->Subject = 'Solicitud de agencia denegada - SIIH';
$mail->Body = "
<div style='font-family: Segoe UI, sans-serif; background-color: #f4f6f9; padding: 40px;'>
<div style='max-width: 600px; margin: auto; background: #fff; border: 1px solid #ddd; border-radius: 10px; overflow: hidden;'>
<div style='background: linear-gradient(to right, #dc3545, #c82333); padding: 20px; text-align: center;'>
<h2 style='color: white;'>Solicitud Denegada</h2>
</div>
<div style='padding: 30px; color: #333; font-size: 16px;'>
<p>Estimado(a) <strong>$admin_name</strong>,</p>
<p>Lamentamos informarte que tu solicitud para registrar la agencia <strong>$nombre</strong> en el sistema SIIH ha sido denegada.</p>
<div style='background: #f8f9fa; padding: 15px; border-left: 4px solid #dc3545; margin: 20px 0;'>
<p style='margin: 0; font-weight: bold; color: #dc3545;'>Motivo de la denegación:</p>
<p style='margin: 10px 0 0 0;'>$motivo</p>
</div>
<p>Si consideras que esta decisión es incorrecta o deseas obtener más información, puedes contactarnos para revisar tu caso.</p>
<p>Agradecemos tu interés en formar parte del Sistema Integral para Importadores de Hidrocarburos.</p>
</div>
<div style='background: #e9ecef; text-align: center; padding: 15px; font-size: 13px; color: #666;'>
&copy; " . date('Y') . " SIIH · Sistema Integral para Importadores de Hidrocarburos
</div>
</div>
</div>";
$mail->send();
} catch (Exception $e) {
error_log("Error al enviar correo de denegación: {$mail->ErrorInfo}");
}
// Registrar en bitácora (opcional - crear función específica para denegaciones)
registrar_bitacora_agencia($id, $nombre, 'DENEGACION', $usuario_id);
header("Location: /IMPORTADORES/administrador/aprobarAgencias?success=denied");
exit;
}
function altaAgencias()
{
if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'super_admin') {
@@ -1125,27 +1391,4 @@ function suspenderAgencia()
header("Location: /IMPORTADORES/administrador/agenciasActivas?success=status_$accion");
exit;
}
function configuracion()
{
$conn = getConnection();
$id_usuario = $_SESSION['usuario_id'] ?? null;
if (!$id_usuario) {
die("ID de usuario no disponible en la sesión.");
}
$sql = "SELECT * FROM informacion_general WHERE id_usuario = ?";
$params = [$id_usuario];
$stmt = sqlsrv_query($conn, $sql, $params);
if ($stmt === false) {
die(print_r(sqlsrv_errors(), true));
}
$datos = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
include __DIR__ . '/../../views/configuracion/dashboard_configuracion.php';
}

View File

@@ -18,6 +18,8 @@ function dashboard()
exit;
}
$idUsuario = $_SESSION['usuario_id'] ?? null;
include __DIR__ . '/../../views/agencias/dashboard_agencias.php';
}

View File

@@ -18,7 +18,7 @@ function dashboard()
exit;
}
$nombreAgente = $_SESSION['usuario_nombre'];
$idUsuario = $_SESSION['usuario_id'] ?? null;
include __DIR__ . '/../../views/agentes/dashboard_agentes.php';
}

View File

@@ -0,0 +1,687 @@
<?php
require_once __DIR__ . '/../helpers/session.php';
require_once __DIR__ . '/../../config/database.php';
require_once __DIR__ . '/../helpers/crypto.php';
function index()
{
if (!($_SESSION['usuario_id'] ?? false)) {
header('Location: /IMPORTADORES/login');
exit;
}
include __DIR__ . '/../../views/catalogo_pedimentos/index.php';
}
function lista()
{
if (!($_SESSION['usuario_id'] ?? false)) {
header('Location: /IMPORTADORES/login');
exit;
}
include __DIR__ . '/../../views/catalogo_pedimentos/lista.php';
}
function crear()
{
if (!($_SESSION['usuario_id'] ?? false)) {
header('Location: /IMPORTADORES/login');
exit;
}
$conn = getConnection();
$id_usuario = $_SESSION['usuario_id'];
// Obtener información del importador
$sqlImportador = "SELECT rfc, nombre, correo FROM informacion_general WHERE id_usuario = ?";
$stmtImportador = sqlsrv_query($conn, $sqlImportador, [$id_usuario]);
if ($stmtImportador === false) {
die("❌ Error al consultar información del importador: " . print_r(sqlsrv_errors(), true));
}
$importador = sqlsrv_fetch_array($stmtImportador, SQLSRV_FETCH_ASSOC);
// Obtener claves de pedimentos activas del usuario
$sqlClaves = "SELECT codigo, descripcion FROM claves_pedimentos_usuario
WHERE id_usuario = ? AND activo = 1 AND tipo_operacion = 'importacion'
ORDER BY codigo ASC";
$stmtClaves = sqlsrv_query($conn, $sqlClaves, [$id_usuario]);
$claves_pedimentos = [];
if ($stmtClaves !== false) {
while ($row = sqlsrv_fetch_array($stmtClaves, SQLSRV_FETCH_ASSOC)) {
$claves_pedimentos[] = $row;
}
}
include __DIR__ . '/../../views/catalogo_pedimentos/crear.php';
}
function guardar()
{
if (!($_SESSION['usuario_id'] ?? false)) {
die("⚠️ No autorizado.");
}
$conn = getConnection();
$id_usuario = $_SESSION['usuario_id'];
// Obtener información del importador automáticamente
$sqlImportador = "SELECT rfc, nombre FROM informacion_general WHERE id_usuario = ?";
$stmtImportador = sqlsrv_query($conn, $sqlImportador, [$id_usuario]);
if ($stmtImportador === false) {
die("❌ Error al consultar información del importador: " . print_r(sqlsrv_errors(), true));
}
$importador = sqlsrv_fetch_array($stmtImportador, SQLSRV_FETCH_ASSOC);
if (!$importador) {
die("❌ Error: No se encontró información del importador. Configure su información general primero.");
}
// Obtener datos del formulario
$pedimento = trim($_POST['pedimento'] ?? '');
$clave_ped = trim($_POST['clave_ped'] ?? '');
$tipo_operacion = $_POST['tipo_operacion'] ?? 1; // Por defecto importación
$tipo_pedimento = $_POST['tipo_pedimento'] ?? 1; // Por defecto normal
$regimen = trim($_POST['regimen'] ?? '');
$destino = trim($_POST['destino'] ?? '');
// Fechas
$fecha_pedimento = $_POST['fecha_pedimento'] ?? null;
$fecha_inicio = $_POST['fecha_inicio'] ?? null;
$fecha_final = $_POST['fecha_final'] ?? null;
// Información adicional
$archivo_final_previo = trim($_POST['archivo_final_previo'] ?? '');
$acuse_cons = trim($_POST['acuse_cons'] ?? '');
$tipo = trim($_POST['tipo'] ?? '');
$status = isset($_POST['status']) ? (int)$_POST['status'] : 1;
// Validaciones
if (empty($pedimento)) {
die("❌ El número de pedimento es obligatorio.");
}
if (empty($clave_ped)) {
die("❌ La clave de pedimento es obligatoria.");
}
// Convertir fechas a formato YYYYMMDD si están presentes
$fecha_pedimento_int = null;
$fecha_inicio_int = null;
$fecha_final_int = null;
if ($fecha_pedimento) {
$fecha_pedimento_int = (int)str_replace('-', '', $fecha_pedimento);
}
if ($fecha_inicio) {
$fecha_inicio_int = (int)str_replace('-', '', $fecha_inicio);
}
if ($fecha_final) {
$fecha_final_int = (int)str_replace('-', '', $fecha_final);
}
$sql = "INSERT INTO PREVIOS_COMPARTIDOS_WS
(Pedimento, ClienteRFC, ClienteNombre, ClavePed, TipoOperacion, TipoPedimento,
Regimen, Destino, FechaPedimento, FechaInicio, FechaFinal,
ArchivoFinalPrevio, AcuseCons, Tipo, Status, Timestamp, id_usuario)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, GETDATE(), ?)";
$params = [
$pedimento,
$importador['rfc'],
$importador['nombre'],
$clave_ped,
$tipo_operacion,
$tipo_pedimento,
$regimen,
$destino,
$fecha_pedimento_int,
$fecha_inicio_int,
$fecha_final_int,
$archivo_final_previo,
$acuse_cons,
$tipo,
$status,
$id_usuario
];
$stmt = sqlsrv_query($conn, $sql, $params);
if ($stmt === false) {
die("❌ Error al guardar: " . print_r(sqlsrv_errors(), true));
}
header('Location: /IMPORTADORES/catalogo_pedimentos/lista?created=ok');
exit;
}
function editar()
{
if (!($_SESSION['usuario_id'] ?? false)) {
header('Location: /IMPORTADORES/login');
exit;
}
$id = $_GET['id'] ?? null;
if (!$id || !is_numeric($id)) {
die("❌ ID inválido.");
}
$conn = getConnection();
$id_usuario = $_SESSION['usuario_id'];
// Obtener información del importador
$sqlImportador = "SELECT rfc, nombre FROM informacion_general WHERE id_usuario = ?";
$stmtImportador = sqlsrv_query($conn, $sqlImportador, [$id_usuario]);
if ($stmtImportador === false) {
die("❌ Error al consultar información del importador: " . print_r(sqlsrv_errors(), true));
}
$importador = sqlsrv_fetch_array($stmtImportador, SQLSRV_FETCH_ASSOC);
// Obtener claves de pedimentos activas del usuario
$sqlClaves = "SELECT codigo, descripcion FROM claves_pedimentos_usuario
WHERE id_usuario = ? AND activo = 1 AND tipo_operacion = 'importacion'
ORDER BY codigo ASC";
$stmtClaves = sqlsrv_query($conn, $sqlClaves, [$id_usuario]);
$claves_pedimentos = [];
if ($stmtClaves !== false) {
while ($row = sqlsrv_fetch_array($stmtClaves, SQLSRV_FETCH_ASSOC)) {
$claves_pedimentos[] = $row;
}
}
// Obtener datos del pedimento
$sql = "SELECT * FROM PREVIOS_COMPARTIDOS_WS WHERE IdPrevio = ?";
$stmt = sqlsrv_query($conn, $sql, [$id]);
if ($stmt === false) {
die("❌ Error en consulta: " . print_r(sqlsrv_errors(), true));
}
$previo = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
if (!$previo) {
die("❌ Pedimento no encontrado.");
}
include __DIR__ . '/../../views/catalogo_pedimentos/editar.php';
}
function actualizar()
{
if (!($_SESSION['usuario_id'] ?? false)) {
die("⚠️ No autorizado.");
}
$conn = getConnection();
$id_usuario = $_SESSION['usuario_id'];
// Obtener información del importador automáticamente
$sqlImportador = "SELECT rfc, nombre FROM informacion_general WHERE id_usuario = ?";
$stmtImportador = sqlsrv_query($conn, $sqlImportador, [$id_usuario]);
if ($stmtImportador === false) {
die("❌ Error al consultar información del importador: " . print_r(sqlsrv_errors(), true));
}
$importador = sqlsrv_fetch_array($stmtImportador, SQLSRV_FETCH_ASSOC);
if (!$importador) {
die("❌ Error: No se encontró información del importador.");
}
$id_previo = $_POST['id_previo'] ?? null;
$pedimento = trim($_POST['pedimento'] ?? '');
$clave_ped = trim($_POST['clave_ped'] ?? '');
$tipo_operacion = $_POST['tipo_operacion'] ?? 1;
$tipo_pedimento = $_POST['tipo_pedimento'] ?? 1;
$regimen = trim($_POST['regimen'] ?? '');
$destino = trim($_POST['destino'] ?? '');
// Fechas
$fecha_pedimento = $_POST['fecha_pedimento'] ?? null;
$fecha_inicio = $_POST['fecha_inicio'] ?? null;
$fecha_final = $_POST['fecha_final'] ?? null;
// Información adicional
$archivo_final_previo = trim($_POST['archivo_final_previo'] ?? '');
$acuse_cons = trim($_POST['acuse_cons'] ?? '');
$tipo = trim($_POST['tipo'] ?? '');
$status = isset($_POST['status']) ? (int)$_POST['status'] : 1;
if (!$id_previo || !is_numeric($id_previo)) {
die("❌ ID inválido.");
}
// Validaciones
if (empty($pedimento)) {
die("❌ El número de pedimento es obligatorio.");
}
if (empty($clave_ped)) {
die("❌ La clave de pedimento es obligatoria.");
}
// Convertir fechas a formato YYYYMMDD si están presentes
$fecha_pedimento_int = null;
$fecha_inicio_int = null;
$fecha_final_int = null;
if ($fecha_pedimento) {
$fecha_pedimento_int = (int)str_replace('-', '', $fecha_pedimento);
}
if ($fecha_inicio) {
$fecha_inicio_int = (int)str_replace('-', '', $fecha_inicio);
}
if ($fecha_final) {
$fecha_final_int = (int)str_replace('-', '', $fecha_final);
}
$sql = "UPDATE PREVIOS_COMPARTIDOS_WS SET
Pedimento = ?, ClienteRFC = ?, ClienteNombre = ?, ClavePed = ?,
TipoOperacion = ?, TipoPedimento = ?, Regimen = ?, Destino = ?,
FechaPedimento = ?, FechaInicio = ?, FechaFinal = ?,
ArchivoFinalPrevio = ?, AcuseCons = ?, Tipo = ?, Status = ?
WHERE IdPrevio = ?";
$params = [
$pedimento,
$importador['rfc'],
$importador['nombre'],
$clave_ped,
$tipo_operacion,
$tipo_pedimento,
$regimen,
$destino,
$fecha_pedimento_int,
$fecha_inicio_int,
$fecha_final_int,
$archivo_final_previo,
$acuse_cons,
$tipo,
$status,
$id_previo
];
$stmt = sqlsrv_query($conn, $sql, $params);
if ($stmt === false) {
die("❌ Error al actualizar: " . print_r(sqlsrv_errors(), true));
}
header('Location: /IMPORTADORES/catalogo_pedimentos/lista?updated=ok');
exit;
}
function eliminar()
{
if (!($_SESSION['usuario_id'] ?? false)) {
header('Location: /IMPORTADORES/login');
exit;
}
$id = $_GET['id'] ?? null;
if (!$id || !is_numeric($id)) {
die("❌ ID inválido.");
}
$conn = getConnection();
$sql = "DELETE FROM PREVIOS_COMPARTIDOS_WS WHERE IdPrevio = ?";
$stmt = sqlsrv_query($conn, $sql, [$id]);
if ($stmt === false) {
die("❌ Error al eliminar: " . print_r(sqlsrv_errors(), true));
}
header('Location: /IMPORTADORES/catalogo_pedimentos/lista?deleted=ok');
exit;
}
function ajax_lista()
{
if (!($_SESSION['usuario_id'] ?? false)) {
http_response_code(403);
echo json_encode([]);
exit;
}
$id_usuario = $_SESSION['usuario_id'];
$conn = getConnection();
// 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 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,
"recordsFiltered" => 0,
"data" => [],
"error" => "Error al contar registros totales"
]);
exit;
}
$rowT = sqlsrv_fetch_array($stmtTotal, SQLSRV_FETCH_ASSOC);
$recordsTotal = (int)($rowT['total'] ?? 0);
// Filtro y búsqueda
$where = "p.usuario_id = ?";
$params = [$id_usuario];
if ($search !== '') {
$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, $like, $like]);
}
// 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,
"recordsTotal" => $recordsTotal,
"recordsFiltered" => 0,
"data" => [],
"error" => "Error al contar registros filtrados"
]);
exit;
}
$rowF = sqlsrv_fetch_array($stmtF, SQLSRV_FETCH_ASSOC);
$recordsFiltered = (int)($rowF['total'] ?? 0);
// 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 p.fecha_creacion DESC
OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
$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)) {
$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['id'],
$r['numero_pedimento'],
$r['rfc_importador'],
$r['nombre_importador'] ?? '',
$fecha,
$estado
];
}
}
$response = [
"draw" => $draw,
"recordsTotal" => $recordsTotal,
"recordsFiltered" => $recordsFiltered,
"data" => $data
];
header('Content-Type: application/json; charset=UTF-8');
echo json_encode($response, JSON_UNESCAPED_UNICODE);
exit;
}
function buscar_pedimentos()
{
if (!($_SESSION['usuario_id'] ?? false)) {
http_response_code(403);
echo json_encode([]);
exit;
}
$id_usuario = $_SESSION['usuario_id'];
$query = trim($_GET['q'] ?? '');
$limit = intval($_GET['limit'] ?? 10); // ✅ NUEVO: Parámetro limit con valor por defecto
// ✅ MODIFICADO: Para el panel de referencia, si no hay query, traer los más recientes
if (empty($query)) {
// Si no hay query, obtener los pedimentos más recientes para el panel de referencia
$whereCondition = "ClienteRFC = ? AND Status = 1";
$searchParams = [];
} else {
// Si hay query, mantener la lógica original de búsqueda
if (strlen($query) < 3) {
echo json_encode([]);
exit;
}
$whereCondition = "ClienteRFC = ? AND (Pedimento LIKE ? OR ClienteNombre LIKE ? OR ClavePed LIKE ?) AND Status = 1";
$like = "%{$query}%";
$searchParams = [$like, $like, $like];
}
$conn = getConnection();
// Obtener RFC del usuario 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([]);
exit;
}
$importador = sqlsrv_fetch_array($stmtImportador, SQLSRV_FETCH_ASSOC);
if (!$importador) {
echo json_encode([]);
exit;
}
// ✅ MODIFICADO: Query dinámico con límite configurable
$sql = "SELECT TOP {$limit} IdPrevio, Pedimento, ClienteRFC, ClienteNombre, ClavePed, Timestamp
FROM PREVIOS_COMPARTIDOS_WS
WHERE {$whereCondition}
ORDER BY Timestamp DESC";
$params = array_merge([$importador['rfc']], $searchParams);
$stmt = sqlsrv_query($conn, $sql, $params);
$pedimentos = [];
if ($stmt !== false) {
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$fecha_formateada = '';
if ($row['Timestamp'] instanceof DateTime) {
$fecha_formateada = $row['Timestamp']->format('d/m/Y');
}
$pedimentos[] = [
'IdPrevio' => $row['IdPrevio'],
'Pedimento' => $row['Pedimento'],
'ClienteRFC' => $row['ClienteRFC'],
'ClienteNombre' => $row['ClienteNombre'],
'ClavePed' => $row['ClavePed'],
'fecha_formateada' => $fecha_formateada
];
}
}
header('Content-Type: application/json; charset=UTF-8');
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;
}

View File

@@ -0,0 +1,553 @@
<?php
require_once __DIR__ . '/../helpers/session.php';
require_once __DIR__ . '/../../config/database.php';
require_once __DIR__ . '/../helpers/crypto.php';
function index()
{
if (!($_SESSION['usuario_id'] ?? false)) {
header('Location: /IMPORTADORES/login');
exit;
}
include __DIR__ . '/../../views/claves_pedimentos/index.php';
}
function lista()
{
if (!($_SESSION['usuario_id'] ?? false)) {
header('Location: /IMPORTADORES/login');
exit;
}
include __DIR__ . '/../../views/claves_pedimentos/lista.php';
}
function crear()
{
if (!($_SESSION['usuario_id'] ?? false)) {
header('Location: /IMPORTADORES/login');
exit;
}
include __DIR__ . '/../../views/claves_pedimentos/crear.php';
}
function guardar()
{
if (!($_SESSION['usuario_id'] ?? false)) {
die("⚠️ No autorizado.");
}
$conn = getConnection();
$id_usuario = $_SESSION['usuario_id'];
$codigo = strtoupper(trim($_POST['codigo'] ?? ''));
$descripcion = trim($_POST['descripcion'] ?? '');
$tipo_operacion = $_POST['tipo_operacion'] ?? 'importacion';
$activo = isset($_POST['activo']) ? 1 : 0;
// Validaciones
if (empty($codigo) || empty($descripcion)) {
die("❌ El código y descripción son obligatorios.");
}
if (strlen($codigo) > 10) {
die("❌ El código no puede tener más de 10 caracteres.");
}
if (!preg_match('/^[A-Z0-9]+$/', $codigo)) {
die("❌ El código solo puede contener letras y números.");
}
// Verificar que no exista el código para este usuario
$sqlCheck = "SELECT COUNT(*) as count FROM claves_pedimentos_usuario WHERE id_usuario = ? AND codigo = ?";
$stmtCheck = sqlsrv_query($conn, $sqlCheck, [$id_usuario, $codigo]);
if ($stmtCheck === false) {
die("❌ Error al verificar código existente: " . print_r(sqlsrv_errors(), true));
}
$result = sqlsrv_fetch_array($stmtCheck, SQLSRV_FETCH_ASSOC);
if ($result['count'] > 0) {
die("❌ Ya existe una clave con el código '{$codigo}' para este usuario.");
}
$sql = "INSERT INTO claves_pedimentos_usuario
(id_usuario, codigo, descripcion, tipo_operacion, activo, fecha_creacion, fecha_modificacion)
VALUES (?, ?, ?, ?, ?, GETDATE(), GETDATE())";
$params = [$id_usuario, $codigo, $descripcion, $tipo_operacion, $activo];
$stmt = sqlsrv_query($conn, $sql, $params);
if ($stmt === false) {
die("❌ Error al guardar: " . print_r(sqlsrv_errors(), true));
}
header('Location: /IMPORTADORES/claves_pedimentos/lista?created=ok');
exit;
}
function editar()
{
if (!($_SESSION['usuario_id'] ?? false)) {
header('Location: /IMPORTADORES/login');
exit;
}
$id = $_GET['id'] ?? null;
$id_usuario = $_SESSION['usuario_id'];
if (!$id || !is_numeric($id)) {
die("❌ ID inválido.");
}
$conn = getConnection();
$sql = "SELECT * FROM claves_pedimentos_usuario WHERE id_clave_pedimento = ? AND id_usuario = ?";
$stmt = sqlsrv_query($conn, $sql, [$id, $id_usuario]);
if ($stmt === false) {
die("❌ Error en consulta: " . print_r(sqlsrv_errors(), true));
}
$clave = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
if (!$clave) {
die("❌ Clave de pedimento no encontrada.");
}
include __DIR__ . '/../../views/claves_pedimentos/editar.php';
}
function actualizar()
{
if (!($_SESSION['usuario_id'] ?? false)) {
die("⚠️ No autorizado.");
}
$conn = getConnection();
$id_usuario = $_SESSION['usuario_id'];
$id_clave_pedimento = $_POST['id_clave_pedimento'] ?? null;
$codigo = strtoupper(trim($_POST['codigo'] ?? ''));
$descripcion = trim($_POST['descripcion'] ?? '');
$tipo_operacion = $_POST['tipo_operacion'] ?? 'importacion';
$activo = isset($_POST['activo']) ? 1 : 0;
if (!$id_clave_pedimento || !is_numeric($id_clave_pedimento)) {
die("❌ ID inválido.");
}
// Validaciones
if (empty($codigo) || empty($descripcion)) {
die("❌ El código y descripción son obligatorios.");
}
if (strlen($codigo) > 10) {
die("❌ El código no puede tener más de 10 caracteres.");
}
if (!preg_match('/^[A-Z0-9]+$/', $codigo)) {
die("❌ El código solo puede contener letras y números.");
}
// Verificar que no exista otro código igual (excepto el actual)
$sqlCheck = "SELECT COUNT(*) as count FROM claves_pedimentos_usuario
WHERE id_usuario = ? AND codigo = ? AND id_clave_pedimento != ?";
$stmtCheck = sqlsrv_query($conn, $sqlCheck, [$id_usuario, $codigo, $id_clave_pedimento]);
if ($stmtCheck === false) {
die("❌ Error al verificar código existente: " . print_r(sqlsrv_errors(), true));
}
$result = sqlsrv_fetch_array($stmtCheck, SQLSRV_FETCH_ASSOC);
if ($result['count'] > 0) {
die("❌ Ya existe otra clave con el código '{$codigo}' para este usuario.");
}
$sql = "UPDATE claves_pedimentos_usuario SET
codigo = ?, descripcion = ?, tipo_operacion = ?, activo = ?, fecha_modificacion = GETDATE()
WHERE id_clave_pedimento = ? AND id_usuario = ?";
$params = [$codigo, $descripcion, $tipo_operacion, $activo, $id_clave_pedimento, $id_usuario];
$stmt = sqlsrv_query($conn, $sql, $params);
if ($stmt === false) {
die("❌ Error al actualizar: " . print_r(sqlsrv_errors(), true));
}
header('Location: /IMPORTADORES/claves_pedimentos/lista?updated=ok');
exit;
}
function eliminar()
{
if (!($_SESSION['usuario_id'] ?? false)) {
header('Location: /IMPORTADORES/login');
exit;
}
$id = $_GET['id'] ?? null;
$id_usuario = $_SESSION['usuario_id'];
if (!$id || !is_numeric($id)) {
die("❌ ID inválido.");
}
$conn = getConnection();
$sql = "DELETE FROM claves_pedimentos_usuario WHERE id_clave_pedimento = ? AND id_usuario = ?";
$stmt = sqlsrv_query($conn, $sql, [$id, $id_usuario]);
if ($stmt === false) {
die("❌ Error al eliminar: " . print_r(sqlsrv_errors(), true));
}
header('Location: /IMPORTADORES/claves_pedimentos/lista?deleted=ok');
exit;
}
function ajax_lista()
{
if (!($_SESSION['usuario_id'] ?? false)) {
http_response_code(403);
echo json_encode([]);
exit;
}
$id_usuario = $_SESSION['usuario_id'];
$conn = getConnection();
// 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 claves_pedimentos_usuario WHERE id_usuario = ?";
$stmt = sqlsrv_query($conn, $sqlTotal, [$id_usuario]);
if ($stmt === false) {
echo json_encode([
"draw" => $draw,
"recordsTotal" => 0,
"recordsFiltered" => 0,
"data" => [],
"error" => "Error al contar registros totales"
]);
exit;
}
$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
$recordsTotal = (int)($row['total'] ?? 0);
// Construir condiciones de filtro
$where = "id_usuario = ?";
$params = [$id_usuario];
if ($search !== '') {
$where .= " AND (codigo LIKE ? OR descripcion LIKE ? OR tipo_operacion LIKE ?)";
$like = "%{$search}%";
$params = array_merge($params, [$like, $like, $like]);
}
// Total registros filtrados
$sqlFiltered = "SELECT COUNT(*) AS total FROM claves_pedimentos_usuario WHERE $where";
$stmtF = sqlsrv_query($conn, $sqlFiltered, $params);
if ($stmtF === false) {
echo json_encode([
"draw" => $draw,
"recordsTotal" => $recordsTotal,
"recordsFiltered" => 0,
"data" => [],
"error" => "Error al contar registros filtrados"
]);
exit;
}
$rowF = sqlsrv_fetch_array($stmtF, SQLSRV_FETCH_ASSOC);
$recordsFiltered = (int)($rowF['total'] ?? 0);
// Datos de la página
$sqlData = "SELECT id_clave_pedimento, codigo, descripcion, tipo_operacion,
CASE WHEN activo = 1 THEN 'Activo' ELSE 'Inactivo' END as estado,
FORMAT(fecha_creacion, 'dd/MM/yyyy HH:mm') as fecha_creacion
FROM claves_pedimentos_usuario
WHERE $where
ORDER BY codigo ASC
OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
$params[] = $start;
$params[] = $length;
$stmtD = sqlsrv_query($conn, $sqlData, $params);
$data = [];
if ($stmtD !== false) {
while ($r = sqlsrv_fetch_array($stmtD, SQLSRV_FETCH_ASSOC)) {
$data[] = [
$r['id_clave_pedimento'],
$r['codigo'],
$r['descripcion'],
ucfirst($r['tipo_operacion']),
$r['estado'],
$r['fecha_creacion']
];
}
}
$response = [
"draw" => $draw,
"recordsTotal" => $recordsTotal,
"recordsFiltered" => $recordsFiltered,
"data" => $data
];
header('Content-Type: application/json; charset=UTF-8');
echo json_encode($response, JSON_UNESCAPED_UNICODE);
exit;
}
function inicializar_claves_usuario()
{
if (!($_SESSION['usuario_id'] ?? false)) {
header('Location: /IMPORTADORES/login');
exit;
}
$conn = getConnection();
$id_usuario = $_SESSION['usuario_id'];
// Verificar si ya tiene claves configuradas
$sqlCheck = "SELECT COUNT(*) as count FROM claves_pedimentos_usuario WHERE id_usuario = ?";
$stmtCheck = sqlsrv_query($conn, $sqlCheck, [$id_usuario]);
if ($stmtCheck === false) {
die("❌ Error al verificar claves existentes: " . print_r(sqlsrv_errors(), true));
}
$result = sqlsrv_fetch_array($stmtCheck, SQLSRV_FETCH_ASSOC);
if ($result['count'] > 0) {
header('Location: /IMPORTADORES/claves_pedimentos/lista?info=already_initialized');
exit;
}
// Claves de pedimentos por defecto para importación
$claves_default = [
['A1', 'Importación definitiva de mercancías', 'importacion'],
['A3', 'Importación definitiva de vehículos usados', 'importacion'],
['A4', 'Importación definitiva de vehículos nuevos', 'importacion'],
['B1', 'Importación temporal para elaborar, transformar o reparar', 'importacion'],
['C1', 'Importación definitiva de mercancías donadas', 'importacion'],
['G1', 'Importación de mercancías con Programa IMMEX', 'importacion'],
['I1', 'Importación definitiva exenta', 'importacion'],
['J1', 'Importación temporal para reexportación en el mismo estado', 'importacion'],
['L1', 'Importación definitiva con franquicia arancelaria con TLC', 'importacion'],
['M1', 'Importación de menajes de casa', 'importacion'],
['N1', 'Importación de equipaje', 'importacion'],
['P1', 'Importación temporal de remolques y semirremolques', 'importacion'],
['R1', 'Importación temporal de contenedores', 'importacion'],
['S1', 'Importación temporal de vehículos', 'importacion'],
['T1', 'Importación temporal de enseres de tripulantes', 'importacion'],
['V1', 'Importación temporal de mercancías para exposición', 'importacion']
];
$sql = "INSERT INTO claves_pedimentos_usuario
(id_usuario, codigo, descripcion, tipo_operacion, activo, fecha_creacion, fecha_modificacion)
VALUES (?, ?, ?, ?, 1, GETDATE(), GETDATE())";
$insertadas = 0;
foreach ($claves_default as $clave) {
$params = [$id_usuario, $clave[0], $clave[1], $clave[2]];
$stmt = sqlsrv_query($conn, $sql, $params);
if ($stmt !== false) {
$insertadas++;
}
}
if ($insertadas > 0) {
header('Location: /IMPORTADORES/claves_pedimentos/lista?created=initialized');
} else {
die("❌ Error al inicializar las claves de pedimentos.");
}
exit;
}
function importar_csv()
{
if (!($_SESSION['usuario_id'] ?? false)) {
header('Location: /IMPORTADORES/login');
exit;
}
include __DIR__ . '/../../views/claves_pedimentos/importar_csv.php';
}
function procesar_csv()
{
if (!($_SESSION['usuario_id'] ?? false)) {
die("⚠️ No autorizado.");
}
$conn = getConnection();
$id_usuario = $_SESSION['usuario_id'];
// Verificar que se subió un archivo
if (!isset($_FILES['csv_file']) || $_FILES['csv_file']['error'] !== UPLOAD_ERR_OK) {
die("❌ Error: No se pudo cargar el archivo CSV.");
}
$archivo_csv = $_FILES['csv_file']['tmp_name'];
$nombre_archivo = $_FILES['csv_file']['name'];
// Validar extensión
if (!str_ends_with(strtolower($nombre_archivo), '.csv')) {
die("❌ Error: El archivo debe tener extensión .csv");
}
// Opciones de importación
$omitir_duplicados = isset($_POST['omitir_duplicados']);
$activar_todas = isset($_POST['activar_todas']);
try {
// Leer archivo CSV
$archivo = fopen($archivo_csv, 'r');
if (!$archivo) {
die("❌ Error: No se pudo abrir el archivo CSV.");
}
// Leer encabezados
$encabezados = fgetcsv($archivo, 1000, ',');
if (!$encabezados) {
fclose($archivo);
die("❌ Error: El archivo CSV está vacío o no tiene el formato correcto.");
}
// Validar encabezados requeridos
$encabezados_requeridos = ['codigo', 'descripcion', 'tipo_operacion', 'activo'];
$encabezados_faltantes = array_diff($encabezados_requeridos, $encabezados);
if (!empty($encabezados_faltantes)) {
fclose($archivo);
die("❌ Error: Faltan las siguientes columnas: " . implode(', ', $encabezados_faltantes));
}
$insertadas = 0;
$omitidas = 0;
$errores = [];
$fila_numero = 1;
// SQL para verificar códigos existentes
$sqlCheck = "SELECT COUNT(*) as count FROM claves_pedimentos_usuario WHERE id_usuario = ? AND codigo = ?";
// SQL para insertar
$sqlInsert = "INSERT INTO claves_pedimentos_usuario
(id_usuario, codigo, descripcion, tipo_operacion, activo, fecha_creacion, fecha_modificacion)
VALUES (?, ?, ?, ?, ?, GETDATE(), GETDATE())";
// Procesar cada fila
while (($fila = fgetcsv($archivo, 1000, ',')) !== FALSE) {
$fila_numero++;
if (count($fila) < count($encabezados_requeridos)) {
$errores[] = "Fila $fila_numero: Datos insuficientes";
continue;
}
// Crear array asociativo
$datos = array_combine($encabezados, $fila);
// Validar datos
$codigo = strtoupper(trim($datos['codigo']));
$descripcion = trim($datos['descripcion']);
$tipo_operacion = trim($datos['tipo_operacion']);
$activo = $activar_todas ? 1 : (int)($datos['activo'] ?? 1);
// Validaciones
if (empty($codigo)) {
$errores[] = "Fila $fila_numero: Código vacío";
continue;
}
if (empty($descripcion)) {
$errores[] = "Fila $fila_numero: Descripción vacía";
continue;
}
if (!in_array($tipo_operacion, ['importacion', 'exportacion'])) {
$errores[] = "Fila $fila_numero: Tipo de operación inválido ($tipo_operacion)";
continue;
}
if (strlen($codigo) > 10) {
$errores[] = "Fila $fila_numero: Código muy largo (máximo 10 caracteres)";
continue;
}
if (!preg_match('/^[A-Z0-9]+$/', $codigo)) {
$errores[] = "Fila $fila_numero: Código inválido (solo letras y números)";
continue;
}
// Verificar si ya existe
if ($omitir_duplicados) {
$stmtCheck = sqlsrv_query($conn, $sqlCheck, [$id_usuario, $codigo]);
if ($stmtCheck === false) {
$errores[] = "Fila $fila_numero: Error al verificar código existente";
continue;
}
$result = sqlsrv_fetch_array($stmtCheck, SQLSRV_FETCH_ASSOC);
if ($result['count'] > 0) {
$omitidas++;
continue;
}
}
// Insertar registro
$params = [$id_usuario, $codigo, $descripcion, $tipo_operacion, $activo];
$stmt = sqlsrv_query($conn, $sqlInsert, $params);
if ($stmt === false) {
$sql_errors = sqlsrv_errors();
$errores[] = "Fila $fila_numero: Error al insertar - " . $sql_errors[0]['message'];
} else {
$insertadas++;
}
}
fclose($archivo);
// Preparar mensaje de resultado
$mensaje = "✅ Proceso completado:";
$mensaje .= "<br>• Registros insertados: $insertadas";
if ($omitidas > 0) {
$mensaje .= "<br>• Registros omitidos (duplicados): $omitidas";
}
if (!empty($errores)) {
$mensaje .= "<br>• Errores encontrados: " . count($errores);
$mensaje .= "<br><br>Detalle de errores:<br>" . implode("<br>", array_slice($errores, 0, 10));
if (count($errores) > 10) {
$mensaje .= "<br>... y " . (count($errores) - 10) . " errores más.";
}
}
// Redirigir con resultado
$encoded_message = urlencode($mensaje);
header("Location: /IMPORTADORES/claves_pedimentos/lista?imported=ok&message=" . $encoded_message);
exit;
} catch (Exception $e) {
die("❌ Error inesperado: " . $e->getMessage());
}
}

View File

@@ -124,18 +124,17 @@ function guardar()
}
// Captura los datos del formulario
$campos = [
'clave', 'tipo_identificador', 'curp', 'calle', 'num_exterior', 'num_interior',
'ciudad', 'colonia', 'pais', 'codigo_postal', 'municipio', 'estado', 'telefono', 'fax', 'observaciones'
];
$campos = [
'clave', 'tipo_identificador', 'curp', 'calle', 'num_exterior', 'num_interior',
'ciudad', 'colonia', 'pais', 'codigo_postal', 'municipio', 'estado', 'telefono', 'fax', 'observaciones'
];
$sql_parts = [];
$params = [];
foreach ($campos as $campo) {
if (isset($_POST[$campo]) && $_POST[$campo] !== '') {
$sql_parts[] = "$campo = ?";
$params[] = $_POST[$campo];
$params[] = $_POST[$campo];
}
}
@@ -218,7 +217,7 @@ function guardar()
// Confirmar transacción
sqlsrv_commit($conn);
header("Location: /IMPORTADORES/configuracion/index");
header("Location: /IMPORTADORES/configuracion/index?updated=ok");
exit;
} catch (Exception $e) {

170
app/controllers/cove.php Normal file
View 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;
}

View 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>";
?>

View File

@@ -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);
@@ -224,4 +361,50 @@ function descargar_zip($id_solicitud)
readfile($zip_file);
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;
}

View File

@@ -2,6 +2,12 @@
require_once __DIR__ . '/../helpers/session.php';
require_once __DIR__ . '/../../config/database.php';
require_once __DIR__ . '/../helpers/crypto.php';
require_once __DIR__ . '/../../vendor/autoload.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
loadEnv();
// Función para obtener catálogos visibles del usuario
function obtenerCatalogosVisibles($idUsuario)
@@ -136,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
@@ -228,6 +236,21 @@ function vincular()
}
}
// Obtener información del importador y la agencia para el correo
$infoSql = "SELECT
u.nombre AS nombre_importador, u.email AS email_importador,
a.nombre_agencia, a.email AS email_agencia,
admin.email AS admin_email, admin.nombre AS admin_nombre
FROM usuarios_sistema u
CROSS JOIN agencias_aduanales a
LEFT JOIN usuarios_sistema admin
ON a.id_administrador = admin.id_usuario
WHERE u.id_usuario = ?
AND a.id_agencia = ?
";
$infoStmt = sqlsrv_query($conn, $infoSql, [$id_importador, $id_agencia]);
$info = sqlsrv_fetch_array($infoStmt, SQLSRV_FETCH_ASSOC);
// Insertar la nueva solicitud
$sql = "INSERT INTO solicitudes_vinculacion
(id_importador, id_agencia, mensaje, estado, fecha_solicitud)
@@ -237,6 +260,48 @@ function vincular()
$stmt = sqlsrv_prepare($conn, $sql, $params);
if ($stmt && sqlsrv_execute($stmt)) {
// Enviar notificación al administrador de la agencia
if (!empty($info['admin_email'])) {
$mail = new PHPMailer(true);
try {
$mail->isSMTP();
$mail->Host = 'secure.emailsrvr.com';
$mail->SMTPAuth = true;
$mail->Username = 'noreply@aduanasoft.com.mx';
$mail->Password = $_ENV['SMTP_PASS'];
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
$mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | Sistema Integral para Importadores de Hidrocarburos');
$mail->addAddress($info['admin_email']);
$mail->CharSet = 'UTF-8';
$mail->isHTML(true);
$mail->Subject = 'Nueva solicitud de vinculación en SIIH';
$mail->Body = "
<div style='font-family: Segoe UI, sans-serif; background-color: #f4f6f9; padding: 40px;'>
<div style='max-width: 600px; margin: auto; background: #fff; border: 1px solid #ddd; border-radius: 10px; overflow: hidden;'>
<div style='background: linear-gradient(to right, #003366, #0055A5); padding: 20px; text-align: center;'>
<h2 style='color: white;'>¡Nueva solicitud de vinculación!</h2>
</div>
<div style='padding: 30px; color: #333; font-size: 16px;'>
<p>El importador <strong>{$info['nombre_importador']}</strong> ha solicitado vincularse con su agencia <strong>{$info['nombre_agencia']}</strong>.</p>
<p><strong>Correo del importador:</strong> {$info['email_importador']}</p>
<p>Por favor, revise la solicitud en su panel de administración para aprobarla o rechazarla.</p>
</div>
<div style='background: #e9ecef; text-align: center; padding: 15px; font-size: 13px; color: #666;'>
&copy; " . date('Y') . " SIIH · Sistema Integral para Importadores de Hidrocarburos
</div>
</div>
</div>
";
$mail->send();
} catch (Exception $e) {
error_log("Error al enviar correo: " . $mail->ErrorInfo);
}
}
header('Location: /IMPORTADORES/vinculaciones/nuevaVinculacion?success=sended_request');
exit;
} else {
@@ -257,6 +322,18 @@ function cancelarVinculacion()
$id_importador = $_SESSION['usuario_id'];
$id_agencia = (int) $_GET['id'];
// Obtener información para el correo
$infoSql = "SELECT
u.nombre AS nombre_importador, u.email AS email_importador,
a.nombre_agencia
FROM solicitudes_vinculacion sv
JOIN usuarios_sistema u ON sv.id_importador = u.id_usuario
JOIN agencias_aduanales a ON sv.id_agencia = a.id_agencia
WHERE sv.id_importador = ? AND sv.id_agencia = ? AND sv.estado = 'PENDIENTE'";
$infoStmt = sqlsrv_query($conn, $infoSql, [$id_importador, $id_agencia]);
$info = sqlsrv_fetch_array($infoStmt, SQLSRV_FETCH_ASSOC);
$sql = "UPDATE solicitudes_vinculacion
SET estado = 'CANCELADA', fecha_respuesta = GETDATE()
WHERE id_importador = ?
@@ -267,6 +344,48 @@ function cancelarVinculacion()
$stmt = sqlsrv_prepare($conn, $sql, $params);
if ($stmt && sqlsrv_execute($stmt)) {
// Enviar notificación al importador
if (!empty($info['email_importador'])) {
$mail = new PHPMailer(true);
try {
$mail->isSMTP();
$mail->Host = 'secure.emailsrvr.com';
$mail->SMTPAuth = true;
$mail->Username = 'noreply@aduanasoft.com.mx';
$mail->Password = $_ENV['SMTP_PASS'];
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
$mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | Sistema Integral para Importadores de Hidrocarburos');
$mail->addAddress($info['email_importador']);
$mail->CharSet = 'UTF-8';
$mail->isHTML(true);
$mail->Subject = 'Cancelación de solicitud de vinculación en SIIH';
$mail->Body = "
<div style='font-family: Segoe UI, sans-serif; background-color: #f4f6f9; padding: 40px;'>
<div style='max-width: 600px; margin: auto; background: #fff; border: 1px solid #ddd; border-radius: 10px; overflow: hidden;'>
<div style='background: linear-gradient(to right, #003366, #0055A5); padding: 20px; text-align: center;'>
<h2 style='color: white;'>Solicitud cancelada</h2>
</div>
<div style='padding: 30px; color: #333; font-size: 16px;'>
<p>Estimado/a <strong>{$info['nombre_importador']}</strong>,</p>
<p>Has cancelado tu solicitud de vinculación con la agencia <strong>{$info['nombre_agencia']}</strong>.</p>
<p>Si esto fue un error, puedes volver a solicitar la vinculación desde tu panel de importador.</p>
</div>
<div style='background: #e9ecef; text-align: center; padding: 15px; font-size: 13px; color: #666;'>
&copy; " . date('Y') . " SIIH · Sistema Integral para Importadores de Hidrocarburos
</div>
</div>
</div>
";
$mail->send();
} catch (Exception $e) {
error_log("Error al enviar correo: " . $mail->ErrorInfo);
}
}
header('Location: /IMPORTADORES/vinculaciones/nuevaVinculacion?success=cancelled');
exit;
}
@@ -289,17 +408,23 @@ function desvincularUsuario()
$conn = getConnection();
try {
$stmtActualizarAgencia = null;
// Iniciar transacción
sqlsrv_begin_transaction($conn);
// 1. Verificar que la relación existe y pertenece a la agencia del admin
$sqlVerificar = "SELECT
ia.*, u.id_usuario, u.nombre as importador_nombre, aa.nombre_agencia
ia.*, u.id_usuario, u.nombre as importador_nombre, u.email as importador_email,
aa.nombre_agencia, aa.email as agencia_email,
admin.email as admin_email, admin.nombre as admin_nombre
FROM importador_agencia ia
INNER JOIN usuarios_sistema u
ON ia.id_importador = u.id_usuario
INNER JOIN agencias_aduanales aa
ON ia.id_agencia = aa.id_agencia
LEFT JOIN usuarios_sistema admin
ON aa.id_administrador = admin.id_usuario
WHERE ia.id_relacion = ?
AND ia.id_importador = ?
";
@@ -356,10 +481,103 @@ function desvincularUsuario()
}
}
// Enviar notificaciones por correo
if (!empty($relacion['importador_email'])) {
// Notificación al importador
$mailImportador = new PHPMailer(true);
try {
$mailImportador->isSMTP();
$mailImportador->Host = 'secure.emailsrvr.com';
$mailImportador->SMTPAuth = true;
$mailImportador->Username = 'noreply@aduanasoft.com.mx';
$mailImportador->Password = $_ENV['SMTP_PASS'];
$mailImportador->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mailImportador->Port = 587;
$mailImportador->setFrom('noreply@aduanasoft.com.mx', 'SIIH | Sistema Integral para Importadores de Hidrocarburos');
$mailImportador->addAddress($relacion['importador_email']);
$mailImportador->CharSet = 'UTF-8';
$mailImportador->isHTML(true);
$mailImportador->Subject = 'Confirmación de desvinculación en SIIH';
$mailImportador->Body = "
<div style='font-family: Segoe UI, sans-serif; background-color: #f4f6f9; padding: 40px;'>
<div style='max-width: 600px; margin: auto; background: #fff; border: 1px solid #ddd; border-radius: 10px; overflow: hidden;'>
<div style='background: linear-gradient(to right, #003366, #0055A5); padding: 20px; text-align: center;'>
<h2 style='color: white;'>Desvinculación completada</h2>
</div>
<div style='padding: 30px; color: #333; font-size: 16px;'>
<p>Estimado/a <strong>{$relacion['importador_nombre']}</strong>,</p>
<p>Has sido desvinculado de la agencia <strong>{$relacion['nombre_agencia']}</strong>.</p>
<p><strong>Fecha de desvinculación:</strong> " . date('d/m/Y H:i:s') . "</p>
<p>Si esto es un error, por favor contacta al administrador de la agencia.</p>
</div>
<div style='background: #e9ecef; text-align: center; padding: 15px; font-size: 13px; color: #666;'>
&copy; " . date('Y') . " SIIH · Sistema Integral para Importadores de Hidrocarburos
</div>
</div>
</div>
";
$mailImportador->send();
} catch (Exception $e) {
error_log("Error al enviar correo a importador: " . $mailImportador->ErrorInfo);
}
}
if (!empty($relacion['admin_email'])) {
// Notificación al administrador de la agencia
$mailAdmin = new PHPMailer(true);
try {
$mailAdmin->isSMTP();
$mailAdmin->Host = 'secure.emailsrvr.com';
$mailAdmin->SMTPAuth = true;
$mailAdmin->Username = 'noreply@aduanasoft.com.mx';
$mailAdmin->Password = $_ENV['SMTP_PASS'];
$mailAdmin->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mailAdmin->Port = 587;
$mailAdmin->setFrom('noreply@aduanasoft.com.mx', 'SIIH | Sistema Integral para Importadores de Hidrocarburos');
$mailAdmin->addAddress($relacion['admin_email']);
$mailAdmin->CharSet = 'UTF-8';
$mailAdmin->isHTML(true);
$mailAdmin->Subject = 'Notificación de desvinculación en SIIH';
$mailAdmin->Body = "
<div style='font-family: Segoe UI, sans-serif; background-color: #f4f6f9; padding: 40px;'>
<div style='max-width: 600px; margin: auto; background: #fff; border: 1px solid #ddd; border-radius: 10px; overflow: hidden;'>
<div style='background: linear-gradient(to right, #003366, #0055A5); padding: 20px; text-align: center;'>
<h2 style='color: white;'>Notificación de desvinculación</h2>
</div>
<div style='padding: 30px; color: #333; font-size: 16px;'>
<p>Estimado/a <strong>{$relacion['admin_nombre']}</strong>,</p>
<p>El importador <strong>{$relacion['importador_nombre']}</strong> se ha desvinculado de su agencia <strong>{$relacion['nombre_agencia']}</strong>.</p>
<p><strong>Correo del importador:</strong> {$relacion['importador_email']}</p>
<p><strong>Fecha de desvinculación:</strong> " . date('d/m/Y H:i:s') . "</p>
</div>
<div style='background: #e9ecef; text-align: center; padding: 15px; font-size: 13px; color: #666;'>
&copy; " . date('Y') . " SIIH · Sistema Integral para Importadores de Hidrocarburos
</div>
</div>
</div>
";
$mailAdmin->send();
} catch (Exception $e) {
error_log("Error al enviar correo a administrador: " . $mailAdmin->ErrorInfo);
}
}
// Limpiar statements
sqlsrv_free_stmt($stmtVerificar);
sqlsrv_free_stmt($stmtDesactivar);
sqlsrv_free_stmt($stmtActualizarAgencia);
if (isset($stmtVerificar) && is_resource($stmtVerificar)) {
sqlsrv_free_stmt($stmtVerificar);
}
if (isset($stmtDesactivar) && is_resource($stmtDesactivar)) {
sqlsrv_free_stmt($stmtDesactivar);
}
if (isset($stmtActualizarAgencia) && is_resource($stmtActualizarAgencia)) {
sqlsrv_free_stmt($stmtActualizarAgencia);
}
// Confirmar transacción
sqlsrv_commit($conn);

View 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()
]);
}
?>

View File

@@ -1112,4 +1112,38 @@ function cambiarPassword()
<script>
setTimeout(() => { window.location.href = '/IMPORTADORES/login'; }, 4000);
</script>";
}
}
// MÉTODOS ADICIONALES CON NOMBRES DE RUTA COMPATIBLES
function enviar_codigo()
{
// Redirigir al método camelCase existente
return enviarCodigo();
}
function verificar_codigo_recuperacion()
{
// Redirigir al método camelCase existente
return verificarCodigo();
}
function reenviar_codigo()
{
// Redirigir al método camelCase existente
return reenviarCodigo();
}
function actualizar_password()
{
// Redirigir al método camelCase existente
return cambiarPassword();
}
function verificar_codigo()
{
// Mostrar vista de verificar código
return verificarCodigoVista();
}
?>

677
app/controllers/mve.php Normal file
View File

@@ -0,0 +1,677 @@
<?php
require_once __DIR__ . '/../../config/database.php';
require_once __DIR__ . '/../helpers/session.php';
// 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';
}
// 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;
}
$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 <= 0 || $id_pedimento <= 0) {
echo json_encode(['success' => false, 'message' => 'Parámetros inválidos']);
return;
}
$conn = getConnection();
// 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;
}
// ¿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 = ?,
art65_fecha_contribuciones = ?, art65_importe_contribuciones = ?,
art65_fecha_pagos_vendedor = ?, art65_importe_pagos_vendedor = ?,
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 = ?,
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 = ?
WHERE id = ?";
$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,
art65_fecha_posteriores, art65_importe_posteriores,
art65_fecha_contribuciones, art65_importe_contribuciones,
art65_fecha_pagos_vendedor, art65_importe_pagos_vendedor,
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,
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 (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
// 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 = [
$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,
]
];
echo json_encode(['success' => true, 'datos' => $datosEstructurados]);
} else {
echo json_encode(['success' => true, 'datos' => 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']);
}
}
// 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;
}
// 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]);
$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;
}
// 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]);
}

View File

@@ -106,7 +106,7 @@ function lista()
}
// === Consulta de productos frecuentes ===
$sql = "SELECT
$sql = "SELECT
pf.id_producto_frecuente,
pf.sinonimo,
pf.fraccion,
@@ -120,16 +120,16 @@ function lista()
pf.estado_mercancia,
pf.preferencia,
pf.frecuencia_uso
FROM dbo.productos_frecuentes pf
LEFT JOIN dbo.unidades_medida_apendice7 u
FROM dbo.productos_frecuentes pf
LEFT JOIN dbo.unidades_medida_apendice7 u
ON pf.umc_id = u.id
LEFT JOIN dbo.paises por
LEFT JOIN dbo.paises por
ON pf.pais_origen_destino = por.nombre
LEFT JOIN dbo.paises pc
LEFT JOIN dbo.paises pc
ON pf.pais_comprador_vendedor = pc.nombre
WHERE pf.id_importador = ?
WHERE pf.id_importador = ?
AND pf.status = 1
ORDER BY pf.fecha_alta DESC
ORDER BY pf.frecuencia_uso DESC
";
$stmt = sqlsrv_query($conn, $sql, [$_SESSION['usuario_id']]);
@@ -142,9 +142,9 @@ function lista()
$productos = [];
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$clave = $row['proveedor_guardado'];
$clave = $row['proveedor_guardado'];
$row['nombre_proveedor'] = $proveedoresApi[$clave] ?? 'N/D';
$productos[] = $row;
$productos[] = $row;
}
// === Cargar la vista con productos y nombres de proveedor ===
@@ -369,18 +369,20 @@ function guardar()
}
// Convertir valores vacíos a NULL para campos opcionales
if ($numero_parte === '') $numero_parte = null;
if ($descripcion === '') $descripcion = null;
if ($uso_mercancia === '') $uso_mercancia = null;
if ($estado_mercancia === '') $estado_mercancia = null;
if ($vinculacion === '') $vinculacion = null;
if ($observaciones === '') $observaciones = null;
if ($preferencia === '') $preferencia = null;
if ($criterio_preferencia === '') $criterio_preferencia = null;
if ($uso_producto === '') $uso_producto = null;
if ($descripcion_producto === '') $descripcion_producto = null;
if ($tipo_mercancia === '') $tipo_mercancia = null;
if ($proveedor === '') $proveedor = null;
if ($numero_parte === '') $numero_parte = null;
if ($descripcion === '') $descripcion = null;
if ($uso_mercancia === '') $uso_mercancia = null;
if ($estado_mercancia === '') $estado_mercancia = null;
if ($vinculacion === '') $vinculacion = null;
if ($observaciones === '') $observaciones = null;
if ($preferencia === '') $preferencia = null;
if ($criterio_preferencia === '') $criterio_preferencia = null;
if ($uso_producto === '') $uso_producto = null;
if ($descripcion_producto === '') $descripcion_producto = null;
if ($tipo_mercancia === '') $tipo_mercancia = null;
if ($proveedor === '') $proveedor = null;
if ($certificado_origen === '') $certificado_origen = null;
if ($documento_en_original === '') $documento_en_original = null;
// INSERT
$sql = "INSERT INTO dbo.productos_frecuentes

View File

@@ -131,6 +131,10 @@ function obtenerConfiguracion($conn)
// Función auxiliar para enviar email
function enviarEmailConfirmacion($destinatario, $datosEmpresa, $esAgencia = false)
{
if ($datosEmpresa['status'] !== 'pending') {
return false;
}
$mail = new PHPMailer(true);
try {

View File

@@ -342,4 +342,281 @@ function obtenerCorreos($conn, $id_usuario)
}
return $correos;
}
function ventanillaUnica()
{
$conn = getConnection();
$id_usuario = $_SESSION['usuario_id'] ?? null;
// Asegúrate de que el usuario está autenticado
if (!$id_usuario || ($_SESSION['pendiente_confirmacion'] ?? false)) {
die("Usuario no autenticado.");
}
// Obtener configuración actual de ventanilla única
$configuracion_vu = obtenerConfiguracionVU($conn, $id_usuario);
include __DIR__ . '/../../views/seguridad/ventanilla_unica.php';
}
function guardarConfiguracionVU()
{
// Solo ejecutar si es una petición POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
exit;
}
$conn = getConnection();
$id_usuario = $_SESSION['usuario_id'] ?? null;
if (!$id_usuario || ($_SESSION['pendiente_confirmacion'] ?? false)) {
die("No autorizado - Sesión inválida");
}
// Obtener datos del formulario (sin ruta_ejecutable)
$clave_fiel = trim($_POST['clave_fiel'] ?? '');
$rfc_usuario_vu = trim($_POST['rfc_usuario_vu'] ?? '');
$clave_webservice = trim($_POST['clave_webservice'] ?? '');
// Validar campos obligatorios (sin ruta_ejecutable)
if (empty($clave_fiel) || empty($rfc_usuario_vu)) {
$_SESSION['config_error'] = 'Los campos Clave FIEL y RFC Usuario VU son obligatorios.';
header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
exit;
}
// Obtener configuración actual para conservar archivos existentes
$configuracion_actual = obtenerConfiguracionVU($conn, $id_usuario);
$ruta_archivo_key = $configuracion_actual['ruta_archivo_key'];
$ruta_archivo_cer = $configuracion_actual['ruta_archivo_cer'];
// Directorio para archivos de certificados
$upload_dir = __DIR__ . '/../../storage/certificados/';
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0755, true);
}
// Procesar archivo KEY
if (!empty($_FILES['archivo_key']['tmp_name']) && $_FILES['archivo_key']['error'] === UPLOAD_ERR_OK) {
$key_extension = pathinfo($_FILES['archivo_key']['name'], PATHINFO_EXTENSION);
if (strtolower($key_extension) !== 'key') {
$_SESSION['config_error'] = 'El archivo KEY debe tener extensión .key';
header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
exit;
}
$key_filename = 'key_' . $id_usuario . '_' . time() . '.key';
$key_destination = $upload_dir . $key_filename;
if (move_uploaded_file($_FILES['archivo_key']['tmp_name'], $key_destination)) {
// Eliminar archivo anterior si existe
if ($ruta_archivo_key && file_exists($ruta_archivo_key)) {
unlink($ruta_archivo_key);
}
$ruta_archivo_key = $key_destination;
} else {
$_SESSION['config_error'] = 'Error al subir el archivo KEY.';
header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
exit;
}
}
// Procesar archivo CER
if (!empty($_FILES['archivo_cer']['tmp_name']) && $_FILES['archivo_cer']['error'] === UPLOAD_ERR_OK) {
$cer_extension = pathinfo($_FILES['archivo_cer']['name'], PATHINFO_EXTENSION);
if (strtolower($cer_extension) !== 'cer') {
$_SESSION['config_error'] = 'El archivo CER debe tener extensión .cer';
header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
exit;
}
$cer_filename = 'cer_' . $id_usuario . '_' . time() . '.cer';
$cer_destination = $upload_dir . $cer_filename;
if (move_uploaded_file($_FILES['archivo_cer']['tmp_name'], $cer_destination)) {
// Eliminar archivo anterior si existe
if ($ruta_archivo_cer && file_exists($ruta_archivo_cer)) {
unlink($ruta_archivo_cer);
}
$ruta_archivo_cer = $cer_destination;
} else {
$_SESSION['config_error'] = 'Error al subir el archivo CER.';
header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
exit;
}
}
// Validar que se hayan subido los certificados (obligatorios para nueva configuración)
if (empty($ruta_archivo_key) || empty($ruta_archivo_cer)) {
$_SESSION['config_error'] = 'Los archivos CER y KEY son obligatorios.';
header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
exit;
}
// Encriptar las contraseñas sensibles
$clave_fiel_encrypted = encrypt($clave_fiel);
$clave_webservice_encrypted = !empty($clave_webservice) ? encrypt($clave_webservice) : '';
// Verificar si ya existe configuración - CORREGIR ERROR SQL
$sql_check = "SELECT COUNT(*) as total FROM configuracion_ventanilla_unica WHERE id_usuario = ?";
$stmt_check = sqlsrv_query($conn, $sql_check, [$id_usuario]);
if ($stmt_check === false) {
$_SESSION['config_error'] = 'Error en la consulta de configuración: ' . print_r(sqlsrv_errors(), true);
header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
exit;
}
$row = sqlsrv_fetch_array($stmt_check, SQLSRV_FETCH_ASSOC);
if ($row['total'] > 0) {
// Actualizar configuración existente (sin ruta_ejecutable)
$sql = "UPDATE configuracion_ventanilla_unica SET
ruta_archivo_key = ?,
ruta_archivo_cer = ?,
clave_fiel = ?,
rfc_usuario_vu = ?,
clave_webservice = ?,
fecha_actualizacion = GETDATE()
WHERE id_usuario = ?";
$params = [
$ruta_archivo_key,
$ruta_archivo_cer,
$clave_fiel_encrypted,
$rfc_usuario_vu,
$clave_webservice_encrypted,
$id_usuario
];
} else {
// Insertar nueva configuración (sin ruta_ejecutable)
$sql = "INSERT INTO configuracion_ventanilla_unica
(id_usuario, ruta_archivo_key, ruta_archivo_cer,
clave_fiel, rfc_usuario_vu, clave_webservice, fecha_creacion)
VALUES (?, ?, ?, ?, ?, ?, GETDATE())";
$params = [
$id_usuario,
$ruta_archivo_key,
$ruta_archivo_cer,
$clave_fiel_encrypted,
$rfc_usuario_vu,
$clave_webservice_encrypted
];
}
$stmt = sqlsrv_prepare($conn, $sql, $params);
if (!$stmt) {
$_SESSION['config_error'] = "Error en la preparación: " . print_r(sqlsrv_errors(), true);
header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
exit;
}
$result = sqlsrv_execute($stmt);
if ($result === false) {
$_SESSION['config_error'] = "Error al guardar configuración: " . print_r(sqlsrv_errors(), true);
header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
exit;
}
sqlsrv_free_stmt($stmt);
sqlsrv_close($conn);
$_SESSION['config_success'] = 'Configuración de Ventanilla Única guardada correctamente.';
header('Location: /IMPORTADORES/seguridad/ventanillaUnica');
exit;
}
function probarConexionVU()
{
header('Content-Type: application/json');
$id_usuario = $_SESSION['usuario_id'] ?? null;
if (!$id_usuario || ($_SESSION['pendiente_confirmacion'] ?? false)) {
echo json_encode(['success' => false, 'message' => 'Usuario no autenticado']);
exit;
}
$conn = getConnection();
$configuracion = obtenerConfiguracionVU($conn, $id_usuario);
// Verificar que todos los campos requeridos estén configurados (sin ruta_ejecutable)
if (empty($configuracion['rfc_usuario_vu']) || empty($configuracion['clave_fiel'])) {
echo json_encode([
'success' => false,
'message' => 'Configuración incompleta. Verifica que RFC Usuario VU y Clave FIEL estén configurados.'
]);
exit;
}
// Verificar archivos de certificados
if (!empty($configuracion['ruta_archivo_cer']) && !file_exists($configuracion['ruta_archivo_cer'])) {
echo json_encode([
'success' => false,
'message' => 'El archivo CER no existe en la ruta especificada.'
]);
exit;
}
if (!empty($configuracion['ruta_archivo_key']) && !file_exists($configuracion['ruta_archivo_key'])) {
echo json_encode([
'success' => false,
'message' => 'El archivo KEY no existe en la ruta especificada.'
]);
exit;
}
// Verificar que existan ambos archivos de certificados
if (empty($configuracion['ruta_archivo_cer']) || empty($configuracion['ruta_archivo_key'])) {
echo json_encode([
'success' => false,
'message' => 'Los archivos CER y KEY son obligatorios para la configuración.'
]);
exit;
}
// Si llegamos aquí, la configuración es válida
echo json_encode([
'success' => true,
'message' => 'Configuración válida. Los archivos de certificados existen y todos los campos obligatorios están completos.'
]);
exit;
}
function obtenerConfiguracionVU($conn, $id_usuario)
{
$configuracion = [
'ruta_archivo_key' => '',
'ruta_archivo_cer' => '',
'clave_fiel' => '',
'rfc_usuario_vu' => '',
'clave_webservice' => '',
'fecha_creacion' => null,
'fecha_actualizacion' => null
];
$sql = "SELECT * FROM configuracion_ventanilla_unica WHERE id_usuario = ?";
$stmt = sqlsrv_prepare($conn, $sql, [$id_usuario]);
if ($stmt && sqlsrv_execute($stmt)) {
if ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$configuracion['ruta_archivo_key'] = $row['ruta_archivo_key'] ?? '';
$configuracion['ruta_archivo_cer'] = $row['ruta_archivo_cer'] ?? '';
$configuracion['rfc_usuario_vu'] = $row['rfc_usuario_vu'] ?? '';
// Desencriptar contraseñas
$configuracion['clave_fiel'] = !empty($row['clave_fiel']) ? decrypt($row['clave_fiel']) : '';
$configuracion['clave_webservice'] = !empty($row['clave_webservice']) ? decrypt($row['clave_webservice']) : '';
$configuracion['fecha_creacion'] = $row['fecha_creacion'];
$configuracion['fecha_actualizacion'] = $row['fecha_actualizacion'];
}
}
return $configuracion;
}

View File

@@ -239,6 +239,150 @@ function obtenerChoferesPorTransportista()
}
}
function buscar_productos()
{
header('Content-Type: application/json');
// 1. Validar usuario autenticado
if (!($_SESSION['usuario_id'] ?? false)) {
http_response_code(401);
echo json_encode(['error' => 'No autorizado']);
exit;
}
$id_importador = $_SESSION['usuario_id'];
$query = trim($_GET['q'] ?? '');
// 2. Validar longitud mínima
if (strlen($query) < 2) {
echo json_encode([]);
exit;
}
try {
$conn = getConnection(); // Obtener conexión
// 3. Búsqueda mejorada con ponderación
$searchTerm = "%$query%";
$sql = "SELECT TOP 10
id_producto_frecuente,
sinonimo,
descripcion,
preferencia,
fraccion,
nico,
numero_parte,
CAST(umc_id AS VARCHAR) AS umc_id,
-- Campos para cálculo de relevancia
CASE
WHEN sinonimo LIKE ? THEN 100
WHEN descripcion LIKE ? THEN 50
ELSE 0
END AS relevancia
FROM dbo.productos_frecuentes
WHERE id_importador = ?
AND status = 1
AND (sinonimo LIKE ? OR descripcion LIKE ? OR numero_parte LIKE ?)
ORDER BY relevancia DESC, frecuencia_uso DESC, sinonimo";
$params = [
"$query%", // Para búsqueda al inicio del sinonimo
"$query%", // Para búsqueda al inicio de descripción
$id_importador,
$searchTerm,
$searchTerm,
$searchTerm
];
$stmt = sqlsrv_query($conn, $sql, $params);
if ($stmt === false) {
error_log("Error en búsqueda de productos: " . print_r(sqlsrv_errors(), true));
echo json_encode([]);
exit;
}
$productos = [];
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$productos[] = [
'id' => $row['id_producto_frecuente'],
'sinonimo' => $row['sinonimo'],
'descripcion' => $row['descripcion'] ?? '',
'preferencia' => $row['preferencia'] ?? '',
'fraccion' => $row['fraccion'] ?? '',
'nico' => $row['nico'] ?? '',
'numero_parte' => $row['numero_parte'] ?? '',
'umc_id' => $row['umc_id'] ?? null
];
}
sqlsrv_free_stmt($stmt);
echo json_encode($productos);
} catch (Exception $e) {
error_log("Excepción en buscar_productos: " . $e->getMessage());
http_response_code(500);
echo json_encode(['error' => 'Error interno']);
}
exit;
}
function incrementar_frecuencia()
{
header('Content-Type: application/json');
// 1. Validar usuario
if (!($_SESSION['usuario_id'] ?? false)) {
http_response_code(401);
echo json_encode(['success' => false, 'error' => 'No autorizado']);
exit;
}
$id_importador = $_SESSION['usuario_id'];
$producto_id = (int)($_POST['producto_id'] ?? 0);
if ($producto_id <= 0) {
echo json_encode(['success' => false, 'error' => 'ID inválido']);
exit;
}
try {
$conn = getConnection();
// 2. Verificar que el producto pertenece al usuario
$sqlValidate = "SELECT 1 FROM dbo.productos_frecuentes
WHERE id_producto_frecuente = ? AND id_importador = ?";
$stmtValidate = sqlsrv_query($conn, $sqlValidate, [$producto_id, $id_importador]);
if (!$stmtValidate || !sqlsrv_fetch_array($stmtValidate)) {
http_response_code(403);
echo json_encode(['success' => false, 'error' => 'Producto no válido']);
exit;
}
// 3. Actualizar frecuencia
$sqlUpdate = "UPDATE dbo.productos_frecuentes
SET frecuencia_uso = frecuencia_uso + 1
WHERE id_producto_frecuente = ?";
$stmtUpdate = sqlsrv_query($conn, $sqlUpdate, [$producto_id]);
if ($stmtUpdate === false) {
error_log("Error actualizando frecuencia: " . print_r(sqlsrv_errors(), true));
echo json_encode(['success' => false, 'error' => 'Error en actualización']);
exit;
}
echo json_encode(['success' => true]);
} catch (Exception $e) {
error_log("Excepción en incrementar_frecuencia: " . $e->getMessage());
http_response_code(500);
echo json_encode(['success' => false, 'error' => 'Error interno']);
}
exit;
}
/** Procesa la creación de una nueva factura y sus partidas **/
function guardar()
{
@@ -266,6 +410,7 @@ function guardar()
$aduana_seccion = $_POST['anexo22_apendice'] ?? null;
$num_factura = trim($_POST['numero_factura'] ?? '');
$fecha = $_POST['fecha_factura'] ?? null;
$pedimento = trim($_POST['pedimento'] ?? ''); // ✅ NUEVO CAMPO
$incoterm = $_POST['incoterm'] ?? null;
$pais_proveedor = $_POST['pais_proveedor'] ?? null;
$tipo_moneda = $_POST['tipo_moneda'] ?? null;
@@ -278,9 +423,7 @@ function guardar()
$patente_id = $_POST['patente'] ?? null;
if ($patente_id) {
$stmtValidatePatente = sqlsrv_query($conn,
"SELECT id_agente FROM dbo.agentes_aduanales WHERE id_agente = ? AND id_agencia = ? AND activo = 1",
[$patente_id, $id_agencia]);
$stmtValidatePatente = sqlsrv_query($conn, "SELECT id_agente FROM dbo.agentes_aduanales WHERE id_agente = ? AND id_agencia = ? AND activo = 1", [$patente_id, $id_agencia]);
if (!$stmtValidatePatente || !sqlsrv_fetch_array($stmtValidatePatente, SQLSRV_FETCH_ASSOC)) {
die("❌ La patente seleccionada no es válida para su agencia.");
@@ -320,15 +463,16 @@ function guardar()
$fotoUrl,
$status,
$proveedor_clave,
$patente_id ? (int)$patente_id : null
$patente_id ? (int)$patente_id : null,
$pedimento ?: null // ✅ CORREGIDO: Campo pedimento_vinculado al final
];
$sql = "INSERT INTO dbo.solicitud_importacion_factura
(id_importador, id_agencia, aduana, anexo22_apendice, numero_factura,
fecha_factura, numero_pedimento, incoterm, pais_proveedor, tipo_moneda,
fecha_factura, incoterm, pais_proveedor, tipo_moneda,
valor_factura, vinculacion, transportista_id, chofer_id,
foto_solicitud_url, status, proveedor_clave, patente_id)
foto_solicitud_url, status, proveedor_clave, patente_id, pedimento_vinculado)
OUTPUT INSERTED.id_solicitud
VALUES(?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
";
$stmt = sqlsrv_query($conn, $sql, $params, ['Scrollable' => SQLSRV_CURSOR_KEYSET]);
@@ -418,9 +562,9 @@ function guardar()
if(!empty($_POST['partidas'])&&is_array($_POST['partidas'])){
$sqlP = "INSERT INTO dbo.solicitud_importacion_partidas
(id_solicitud, descripcion, cantidad_comercial, cantidad_tarifa, valor_factura, peso_bruto, unidad_comercial_id, tasa_preferencial)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
(id_solicitud, descripcion, cantidad_comercial, cantidad_tarifa, valor_factura, peso_bruto, unidad_comercial_id, tasa_preferencial)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
";
$partidas_insertadas = 0; // ← contador
foreach ($_POST['partidas'] as $i => $p) {
@@ -493,46 +637,47 @@ function enviarNotificacionNuevaSolicitud($email, $nombreUsuario, $datosSolicitu
</div>' : '';
$mail->Body = "
<div style='font-family: Segoe UI, sans-serif; background-color: #f4f6f9; padding: 30px;'>
<div style='max-width: 600px; margin: auto; background: #fff; border: 1px solid #ccc; border-radius: 10px;'>
<div style='background: linear-gradient(to right, #003366, #0055A5); padding: 20px; text-align: center;'>
<h2 style='color: white; margin: 0;'>📥 Nueva Solicitud Registrada</h2>
</div>
<div style='padding: 20px;'>
$tipoNotificacion
<p>Hola <strong>" . htmlspecialchars($nombreUsuario) . "</strong>,</p>
<p>Tu solicitud de importación ha sido registrada correctamente con los siguientes datos:</p>
<div style='background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 15px 0;'>
<table style='width: 100%; border-collapse: collapse;'>
<tr>
<td style='padding: 5px 0; font-weight: bold;'>ID Solicitud:</td>
<td style='padding: 5px 0;'>$idSolicitud</td>
</tr>
<tr>
<td style='padding: 5px 0; font-weight: bold;'>Número de Factura:</td>
<td style='padding: 5px 0;'>$numeroFactura</td>
</tr>
<tr>
<td style='padding: 5px 0; font-weight: bold;'>Fecha:</td>
<td style='padding: 5px 0;'>$fechaFactura</td>
</tr>
<tr>
<td style='padding: 5px 0; font-weight: bold;'>Valor:</td>
<td style='padding: 5px 0;'>$valorFactura $tipoMoneda</td>
</tr>
</table>
<div style='font-family: Segoe UI, sans-serif; background-color: #f4f6f9; padding: 30px;'>
<div style='max-width: 600px; margin: auto; background: #fff; border: 1px solid #ccc; border-radius: 10px;'>
<div style='background: linear-gradient(to right, #003366, #0055A5); padding: 20px; text-align: center;'>
h2 style='color: white; margin: 0;'>📥 Nueva Solicitud Registrada</h2>
</div>
<div style='padding: 20px;'>
$tipoNotificacion
<p>Hola <strong>" . htmlspecialchars($nombreUsuario) . "</strong>,</p>
<p>Tu solicitud de importación ha sido registrada correctamente con los siguientes datos:</p>
<div style='background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 15px 0;'>
<table style='width: 100%; border-collapse: collapse;'>
<tr>
<td style='padding: 5px 0; font-weight: bold;'>ID Solicitud:</td>
<td style='padding: 5px 0;'>$idSolicitud</td>
</tr>
<tr>
<td style='padding: 5px 0; font-weight: bold;'>Número de Factura:</td>
<td style='padding: 5px 0;'>$numeroFactura</td>
</tr>
<tr>
<td style='padding: 5px 0; font-weight: bold;'>Fecha:</td>
<td style='padding: 5px 0;'>$fechaFactura</td>
</tr>
<tr>
<td style='padding: 5px 0; font-weight: bold;'>Valor:</td>
<td style='padding: 5px 0;'>$valorFactura $tipoMoneda</td>
</tr>
</table>
</div>
<p>Puedes consultar el estado de tu solicitud accediendo a tu panel de control.</p>
<br>
<p style='color: #888; font-size: 14px;'>Si no realizaste esta acción, contacta al administrador del sistema.</p>
</div>
<div style='background: #e9ecef; text-align: center; padding: 10px; font-size: 13px; color: #666;'>
&copy; " . date('Y') . " SIIH · Desarrollado por AduanaSoft
</div>
</div>
<p>Puedes consultar el estado de tu solicitud accediendo a tu panel de control.</p>
<br>
<p style='color: #888; font-size: 14px;'>Si no realizaste esta acción, contacta al administrador del sistema.</p>
</div>
<div style='background: #e9ecef; text-align: center; padding: 10px; font-size: 13px; color: #666;'>
&copy; " . date('Y') . " SIIH · Desarrollado por AduanaSoft
</div>
</div>
</div>";
";
$envioExitoso = $mail->send();
@@ -733,22 +878,22 @@ function actualizar()
$id_agencia
];
$sqlU = "UPDATE dbo.solicitud_importacion_factura SET
aduana = ?,
anexo22_apendice = ?,
numero_factura = ?,
fecha_factura = ?,
incoterm = ?,
pais_proveedor = ?,
tipo_moneda = ?,
valor_factura = ?,
vinculacion = ?,
transportista_id = ?,
chofer_id = ?,
foto_solicitud_url = ?,
status = ?,
proveedor_clave = ?,
patente_id = ?,
updated_at = GETDATE()
aduana = ?,
anexo22_apendice = ?,
numero_factura = ?,
fecha_factura = ?,
incoterm = ?,
pais_proveedor = ?,
tipo_moneda = ?,
valor_factura = ?,
vinculacion = ?,
transportista_id = ?,
chofer_id = ?,
foto_solicitud_url = ?,
status = ?,
proveedor_clave = ?,
patente_id = ?,
updated_at = GETDATE()
WHERE id_solicitud = ?
AND id_importador = ?
AND id_agencia = ?
@@ -759,20 +904,17 @@ function actualizar()
die("❌ Error ejecutando UPDATE: " . print_r(sqlsrv_errors(), true));
}
// 7) Borrar partidas anteriores
$del = sqlsrv_query($conn, "DELETE FROM dbo.solicitud_importacion_partidas WHERE id_solicitud = ?", [ $id_solicitud ]);
if ($del === false) {
die("❌ Error borrando partidas previas: " . print_r(sqlsrv_errors(), true));
}
// 8) Reinsertar partidas desde el formulario
// 7) Manejo de partidas - Versión mejorada
if (!empty($_POST['partidas']) && is_array($_POST['partidas'])) {
$sqlP = "INSERT INTO dbo.solicitud_importacion_partidas
(id_solicitud, descripcion, cantidad_comercial, cantidad_tarifa,
valor_factura, peso_bruto, unidad_comercial_id, tasa_preferencial)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
";
// Obtener partidas existentes de la base de datos
$partidasExistentes = [];
$stmtPartidas = sqlsrv_query($conn, "SELECT id_partida FROM dbo.solicitud_importacion_partidas WHERE id_solicitud = ?", [$id_solicitud]);
while ($row = sqlsrv_fetch_array($stmtPartidas, SQLSRV_FETCH_ASSOC)) {
$partidasExistentes[] = $row['id_partida'];
}
// Procesar cada partida del formulario
foreach ($_POST['partidas'] as $i => $p) {
$desc = trim($p['descripcion'] ?? '');
$cantCom = floatval($p['cantidad_comercial'] ?? 0);
@@ -781,29 +923,63 @@ function actualizar()
$peso = floatval($p['peso_bruto'] ?? 0);
$umId = intval($p['unidad_comercial_id'] ?? 0) ?: null;
$tasaPref = trim($p['tasa_preferencial'] ?? '');
// Sólo inserta si descripción y cantidad comercial válidos
// Solo procesar si tiene descripción y cantidad válida
if ($desc !== '' && $cantCom > 0) {
$paramsP = [
$id_solicitud,
$desc,
$cantCom,
$cantTar,
$valPart,
$peso,
$umId,
$tasaPref
];
$stmtP = sqlsrv_query($conn, $sqlP, $paramsP);
if ($stmtP === false) {
die("❌ Error insertando partida #$i: " . print_r(sqlsrv_errors(), true));
// Verificar si es una partida existente (tiene id_partida numérico > 0)
if (!empty($p['id_partida']) && intval($p['id_partida']) > 0) {
// ACTUALIZAR partida existente
$sql = "UPDATE dbo.solicitud_importacion_partidas SET
descripcion = ?,
cantidad_comercial = ?,
cantidad_tarifa = ?,
valor_factura = ?,
peso_bruto = ?,
unidad_comercial_id = ?,
tasa_preferencial = ?
WHERE id_partida = ?
AND id_solicitud = ?
";
$params = [ $desc, $cantCom, $cantTar, $valPart, $peso, $umId, $tasaPref, intval($p['id_partida']), $id_solicitud ];
// Eliminar de la lista de existentes
if (($key = array_search($p['id_partida'], $partidasExistentes)) !== false) {
unset($partidasExistentes[$key]);
}
} else {
// INSERTAR nueva partida (asegurarse que no tenga id_partida o sea 0)
$sql = "INSERT INTO dbo.solicitud_importacion_partidas
(id_solicitud, descripcion, cantidad_comercial, cantidad_tarifa, valor_factura,
peso_bruto, unidad_comercial_id, tasa_preferencial)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
";
$params = [ $id_solicitud, $desc, $cantCom, $cantTar, $valPart, $peso, $umId, $tasaPref ];
error_log("Insertando nueva partida: " . print_r($params, true));
}
$stmt = sqlsrv_query($conn, $sql, $params);
if ($stmt === false) {
error_log("Error en consulta SQL: " . print_r(sqlsrv_errors(), true));
die("❌ Error procesando partida #$i: " . print_r(sqlsrv_errors(), true));
}
}
}
// Eliminar partidas que ya no están en el formulario
if (!empty($partidasExistentes)) {
$ids = implode(',', $partidasExistentes);
$sql = "DELETE FROM dbo.solicitud_importacion_partidas
WHERE id_partida IN ($ids)
AND id_solicitud = ?
";
$stmt = sqlsrv_query($conn, $sql, [$id_solicitud]);
if ($stmt === false) {
die("❌ Error eliminando partidas obsoletas: " . print_r(sqlsrv_errors(), true));
}
}
}
// 9) Redirigir
// 8) Redirigir
header('Location: /IMPORTADORES/solicitud_importacion/lista?updated=ok');
exit;
}
@@ -1056,11 +1232,11 @@ function ajax_lista()
$dataList[] = [
$clave,
htmlspecialchars($p['Nombre'] ?? '', ENT_QUOTES),
htmlspecialchars($p['RFC'] ?? '', ENT_QUOTES),
htmlspecialchars($p['Ciudad'] ?? '', ENT_QUOTES),
htmlspecialchars($p['Telefono']?? '', ENT_QUOTES),
htmlspecialchars($direccion ?? '', ENT_QUOTES),
htmlspecialchars($p['Nombre'] ?? '', ENT_QUOTES),
htmlspecialchars($p['RFC'] ?? '', ENT_QUOTES),
htmlspecialchars($p['Ciudad'] ?? '', ENT_QUOTES),
htmlspecialchars($p['Telefono'] ?? '', ENT_QUOTES),
htmlspecialchars($direccion ?? '', ENT_QUOTES),
// Acciones
"<a href=\"/IMPORTADORES/proveedores/editar?clave=" . rawurlencode($clave) . "\" class=\"btn btn-sm btn-primary\">✏️</a>
<button class=\"btn btn-sm btn-danger\" onclick=\"confirmDelete('{$clave}')\">🗑️</button>"
@@ -1117,7 +1293,7 @@ function update_status()
ON s.id_importador = u.id_usuario
WHERE u.id_usuario = ?
AND s.id_solicitud = ?
";
";
$stmtNotif = sqlsrv_prepare($conn, $sqlNotif, [$_SESSION['usuario_id'], $id]);
if ($stmtNotif && sqlsrv_execute($stmtNotif)) {
@@ -1300,27 +1476,27 @@ function update_status()
// 4.5) Construir el arreglo PHP con la misma estructura JSON que envías
$payload = [
"id_solicitud" => intval($solicitud['id_solicitud']),
"id_importador" => intval($solicitud['id_importador']),
"aduana" => strval($solicitud['aduana']),
"patente" => strval($solicitud['patente']),
"anexo22_apendice" => strval($solicitud['anexo22_apendice']),
"numero_factura" => strval($solicitud['numero_factura']),
"fecha_factura" => $fechaFactura,
"numero_pedimento" => "", // La API lo rellenará
"incoterm" => strval($solicitud['incoterm']),
"pais_proveedor" => strval($solicitud['pais_proveedor']),
"tipo_moneda" => strval($solicitud['tipo_moneda']),
"valor_factura" => floatval($solicitud['valor_factura']),
"vinculacion" => intval($solicitud['vinculacion']),
"transportista_id" => intval($solicitud['transportista_id']),
"created_at" => $createdAt,
"updated_at" => $updatedAt,
"transporte_id" => null,
"chofer_id" => intval($solicitud['chofer_id']),
"foto_solicitud_url"=> strval($solicitud['foto_solicitud_url']),
"proveedor_clave" => strval($solicitud['proveedor_clave']),
"partidas" => $partidas
"id_solicitud" => intval($solicitud['id_solicitud']),
"id_importador" => intval($solicitud['id_importador']),
"aduana" => strval($solicitud['aduana']),
"patente" => strval($solicitud['patente']),
"anexo22_apendice" => strval($solicitud['anexo22_apendice']),
"numero_factura" => strval($solicitud['numero_factura']),
"fecha_factura" => $fechaFactura,
"numero_pedimento" => "", // La API lo rellenará
"incoterm" => strval($solicitud['incoterm']),
"pais_proveedor" => strval($solicitud['pais_proveedor']),
"tipo_moneda" => strval($solicitud['tipo_moneda']),
"valor_factura" => floatval($solicitud['valor_factura']),
"vinculacion" => intval($solicitud['vinculacion']),
"transportista_id" => intval($solicitud['transportista_id']),
"created_at" => $createdAt,
"updated_at" => $updatedAt,
"transporte_id" => null,
"chofer_id" => intval($solicitud['chofer_id']),
"foto_solicitud_url" => strval($solicitud['foto_solicitud_url']),
"proveedor_clave" => strval($solicitud['proveedor_clave']),
"partidas" => $partidas
];
$jsonPayload = json_encode($payload);
@@ -1688,7 +1864,8 @@ function pdf() {
}
// NUEVA FUNCIÓN: Obtener información del proveedor por clave
function obtenerProveedorPorClave($clave) {
function obtenerProveedorPorClave($clave)
{
// Obtener token de la API
$token = getApiToken();
if (!$token) {
@@ -1736,7 +1913,8 @@ function obtenerProveedorPorClave($clave) {
return null;
}
function generarHTMLPDF($solicitud, $partidas, $configuracion, $proveedor_info = null) {
function generarHTMLPDF($solicitud, $partidas, $configuracion, $proveedor_info = null)
{
// Formatear fecha
$fecha_expedicion = $solicitud['fecha_factura']->format('d/m/Y');
$fecha_vencimiento = $solicitud['fecha_factura']->modify('+30 days')->format('d/m/Y');
@@ -1792,7 +1970,7 @@ function generarHTMLPDF($solicitud, $partidas, $configuracion, $proveedor_info =
$unidades = ['', 'uno', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve'];
$decenas = ['', '', 'veinte', 'treinta', 'cuarenta', 'cincuenta', 'sesenta', 'setenta', 'ochenta', 'noventa'];
$especiales = ['diez', 'once', 'doce', 'trece', 'catorce', 'quince', 'dieciséis', 'diecisiete', 'dieciocho', 'diecinueve'];
$centenas = ['', 'ciento', 'doscientos', 'trescientos', 'cuatrocientos', 'quinientos', 'seiscientos', 'setecientos', 'ochocientos', 'novecientos'];
$centenas = ['', 'ciento', 'doscientos', 'trescientos', 'cuatrocientos', 'quinientos', 'seiscientos', 'setecientos', 'ochocientos'];
if ($numero == 0) return 'cero';
if ($numero == 100) return 'cien';
@@ -1877,162 +2055,248 @@ function generarHTMLPDF($solicitud, $partidas, $configuracion, $proveedor_info =
}
$html = '
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Solicitud de Importación</title>
<style>
body { font-family: Arial, sans-serif; font-size: 9px; margin: 0; padding: 15px; line-height: 1.2; }
/** Encabezado **/
.header { padding-bottom: 50px; }
.logo-section { width: 20%; text-align: left; }
.logo { max-width: 125px; height: auto; vertical-align: top; }
.company-info { width: 60%; text-align: center; vertical-align: top; font-size: 12px; }
.company-name { font-weight: bold; font-size: 25px; margin-bottom: 3px; }
.invoice-info { width: 20%; text-align: right; vertical-align: top; font-size: 12px; }
/** Sección de Información **/
.info-section { border: 1px solid black; border-collapse: collapse; width: 100%; table-layout: fixed; }
.clave-section { border: 0.5px solid black; border-collapse: collapse; width: 100%; table-layout: fixed; padding-bottom: 15px; }
/** Información del Proveedor **/
.proveedor-info { width: 100%; }
.provedor-info table { width: 100%; border-collapse: collapse; table-layout: fixed; }
.p-field { width: 100px; background-color: #d0d0d0; font-weight: bold; text-align: center; font-size: 12px; }
.field { border-bottom: 0.5px solid #000; padding: 5px; font-size: 12px; }
/** Fechas **/
.dates-info { width: 25%; border: 1px solid #000; }
.dates-info table { width: 100%; border-collapse: collapse; table-layout: fixed; }
.d-field { background-color: #d0d0d0; font-weight: bold; text-align: center; font-size: 12px; }
.date { font-weight: bold; text-align: center; font-size: 12px; padding: 7.5px; }
/** Partidas **/
.products-table { border-collapse: collapse; border: 0.5px solid #000; }
.products-table td { border: 0.5px solid #000; padding: 10px; text-align: center; font-size: 10px; }
.products-table th { border: 0.5px solid #000; padding: 5px; background-color: #d0d0d0; font-weight: bold; text-align: center; }
.text-center { text-align: center; }
.text-right { text-align: right; }
.font-bold { font-weight: bold; }
/** Total **/
.totals-section { float: right; width: 250px; }
.total-row { display: flex; justify-content: space-between; margin-top: 25px; font-size: 12px; }
/** Nota inferior **/
.footer-info { }
.footer-note { font-size: 10px; background-color: #d0d0d0; padding: 5px; border: 0.5px solid #000; }
</style>
</head>
<body>
<!-- ENCABEZADO -->
<table class="header" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td class="logo-section">
<img src="' . htmlspecialchars($configuracion['logo_url'] ?? 'assets/img/logo_siih.png') . '" alt="Logo" class="logo"><br>
</td>
<td class="company-info">
<div class="company-name">' . htmlspecialchars($solicitud['importador_nombre']) . '</div>
<div>' . htmlspecialchars($direccion_completa ?: 'Dirección no disponible') . '</div>
<div>RFC: ' . htmlspecialchars($solicitud['importador_rfc'] ?? 'No disponible') . '</div>
<div>Tel: ' . htmlspecialchars($solicitud['telefono'] ?? 'No disponible') . '</div>
<div>Email: ' . htmlspecialchars($solicitud['correo'] ?? 'No disponible') . '</div>
</td>
<td class="invoice-info">
<div><strong>' . htmlspecialchars($configuracion['nombre_plataforma'] ?? 'Sistema Integral para Importadores de Hidrocarburos') . '</strong></div><br>
<div class="invoice-title">Solicitud de Importación</div>
<div><strong>No. ' . htmlspecialchars($solicitud['id_solicitud']) . '</strong></div>
</td>
</tr>
</table>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Solicitud de Importación</title>
<style>
body { font-family: Arial, sans-serif; font-size: 9px; margin: 0; padding: 15px; line-height: 1.2; }
/** Encabezado **/
.header { padding-bottom: 50px; }
.logo-section { width: 20%; text-align: left; }
.logo { max-width: 125px; height: auto; vertical-align: top; }
.company-info { width: 60%; text-align: center; vertical-align: top; font-size: 12px; }
.company-name { font-weight: bold; font-size: 25px; margin-bottom: 3px; }
.invoice-info { width: 20%; text-align: right; vertical-align: top; font-size: 12px; }
/** Sección de Información **/
.info-section { border: 1px solid black; border-collapse: collapse; width: 100%; table-layout: fixed; }
.clave-section { border: 0.5px solid black; border-collapse: collapse; width: 100%; table-layout: fixed; padding-bottom: 15px; }
/** Información del Proveedor **/
.proveedor-info { width: 100%; }
.provedor-info table { width: 100%; border-collapse: collapse; table-layout: fixed; }
.p-field { width: 100px; background-color: #d0d0d0; font-weight: bold; text-align: center; font-size: 12px; }
.field { border-bottom: 0.5px solid #000; padding: 5px; font-size: 12px; }
/** Fechas **/
.dates-info { width: 25%; border: 1px solid #000; }
.dates-info table { width: 100%; border-collapse: collapse; table-layout: fixed; }
.d-field { background-color: #d0d0d0; font-weight: bold; text-align: center; font-size: 12px; }
.date { font-weight: bold; text-align: center; font-size: 12px; padding: 7.5px; }
/** Partidas **/
.products-table { border-collapse: collapse; border: 0.5px solid #000; }
.products-table td { border: 0.5px solid #000; padding: 10px; text-align: center; font-size: 10px; }
.products-table th { border: 0.5px solid #000; padding: 5px; background-color: #d0d0d0; font-weight: bold; text-align: center; }
.text-center { text-align: center; }
.text-right { text-align: right; }
.font-bold { font-weight: bold; }
/** Total **/
.totals-section { float: right; width: 250px; }
.total-row { display: flex; justify-content: space-between; margin-top: 25px; font-size: 12px; }
/** Nota inferior **/
.footer-info { }
.footer-note { font-size: 10px; background-color: #d0d0d0; padding: 5px; border: 0.5px solid #000; }
</style>
</head>
<body>
<!-- ENCABEZADO -->
<table class="header" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td class="logo-section">
<img src="' . htmlspecialchars($configuracion['logo_url'] ?? 'assets/img/logo_siih.png') . '" alt="Logo" class="logo"><br>
</td>
<td class="company-info">
<div class="company-name">' . htmlspecialchars($solicitud['importador_nombre']) . '</div>
<div>' . htmlspecialchars($direccion_completa ?: 'Dirección no disponible') . '</div>
<div>RFC: ' . htmlspecialchars($solicitud['importador_rfc'] ?? 'No disponible') . '</div>
<div>Tel: ' . htmlspecialchars($solicitud['telefono'] ?? 'No disponible') . '</div>
<div>Email: ' . htmlspecialchars($solicitud['correo'] ?? 'No disponible') . '</div>
</td>
<td class="invoice-info">
<div><strong>' . htmlspecialchars($configuracion['nombre_plataforma'] ?? 'Sistema Integral para Importadores de Hidrocarburos') . '</strong></div><br>
<div class="invoice-title">Solicitud de Importación</div>
<div><strong>No. ' . htmlspecialchars($solicitud['id_solicitud']) . '</strong></div>
</td>
</tr>
</table>
<!-- SECCIÓN DE INFORMACIÓN DEL PROVEEDOR Y FECHAS -->
<table class="info-section" cellspacing="0" cellpadding="0">
<tr>
<!-- PROVEEDOR -->
<td>
<table class="proveedor-info" cellspacing="0" cellpadding="0">
<!-- SECCIÓN DE INFORMACIÓN DEL PROVEEDOR Y FECHAS -->
<table class="info-section" cellspacing="0" cellpadding="0">
<tr>
<!-- PROVEEDOR -->
<td>
<table class="proveedor-info" cellspacing="0" cellpadding="0">
<tr>
<td class="p-field">RAZÓN SOCIAL:</td>
<td class="field">' . htmlspecialchars($proveedor_nombre) . '</td>
</tr>
</table>
<table class="proveedor-info" cellspacing="0" cellpadding="0">
<tr>
<td class="p-field" style="height: 45px;">DIRECCIÓN:</td>
<td class="field">' . htmlspecialchars($proveedor_direccion) . '</td>
</tr>
</table>
<table class="proveedor-info" cellspacing="0" cellpadding="0">
<tr>
<td class="p-field">RFC:</td>
<td class="field" style="border-bottom: none;">' . htmlspecialchars($proveedor_rfc) . '</td>
</tr>
</table>
</td>
<!-- FECHAS -->
<td class="dates-info">
<table cellspacing="0" cellpadding="2">
<tr><td class="d-field" style="border-left: 0.5px solid black;">FECHA DE EXPEDICIÓN</td></tr>
<tr><td class="date" style="border-bottom: 0.5px solid black;">' . $fecha_expedicion . '</td></tr>
<tr><td class="d-field" style="border-left: 0.5px solid black;">FECHA DE VENCIMIENTO</td></tr>
<tr><td class="date">' . $fecha_vencimiento . '</td></tr>
</table>
</td>
</tr>
</table>
<table class="clave-section" cellspacing="0" cellpadding="0">
<tr>
<td class="p-field">CLAVE:</td>
<td class="field" style="border-bottom: none;">' . htmlspecialchars($solicitud['proveedor_clave'] ?? 'No disponible') . '</td>
<td class="p-field">TELÉFONO:</td>
<td class="field" style="border-bottom: none;">' . htmlspecialchars($proveedor_telefono) . '</td>
</tr>
</table>
<!-- TABLA DE PRODUCTOS/PARTIDAS -->
<table class="products-table" cellspacing="0" cellpadding="0" width="100%">
<thead>
<tr>
<td class="p-field">RAZÓN SOCIAL:</td>
<td class="field">' . htmlspecialchars($proveedor_nombre) . '</td>
<th>Producto</th>
<th>Unidad de Medida</th>
<th>Precio Unitario</th>
<th>Cantidad</th>
<th>Total</th>
</tr>
</table>
<table class="proveedor-info" cellspacing="0" cellpadding="0">
<tr>
<td class="p-field" style="height: 45px;">DIRECCIÓN:</td>
<td class="field">' . htmlspecialchars($proveedor_direccion) . '</td>
</tr>
</table>
<table class="proveedor-info" cellspacing="0" cellpadding="0">
<tr>
<td class="p-field">RFC:</td>
<td class="field" style="border-bottom: none;">' . htmlspecialchars($proveedor_rfc) . '</td>
</tr>
</table>
</td>
<!-- FECHAS -->
<td class="dates-info">
<table cellspacing="0" cellpadding="2">
<tr><td class="d-field" style="border-left: 0.5px solid black;">FECHA DE EXPEDICIÓN</td></tr>
<tr><td class="date" style="border-bottom: 0.5px solid black;">' . $fecha_expedicion . '</td></tr>
<tr><td class="d-field" style="border-left: 0.5px solid black;">FECHA DE VENCIMIENTO</td></tr>
<tr><td class="date">' . $fecha_vencimiento . '</td></tr>
</table>
</td>
</tr>
</table>
<table class="clave-section" cellspacing="0" cellpadding="0">
<tr>
<td class="p-field">CLAVE:</td>
<td class="field" style="border-bottom: none;">' . htmlspecialchars($solicitud['proveedor_clave'] ?? 'No disponible') . '</td>
<td class="p-field">TELÉFONO:</td>
<td class="field" style="border-bottom: none;">' . htmlspecialchars($proveedor_telefono) . '</td>
</tr>
</table>
</thead>
<tbody>';
<!-- TABLA DE PRODUCTOS/PARTIDAS -->
<table class="products-table" cellspacing="0" cellpadding="0" width="100%">
<thead>
<tr>
<th>Producto</th>
<th>Unidad de Medida</th>
<th>Precio Unitario</th>
<th>Cantidad</th>
<th>Total</th>
</tr>
</thead>
<tbody>';
// Agregar partidas
foreach ($partidas as $partida) {
$precio_unitario = (float)($partida['precio_unitario'] ?? 0);
$cantidad = (float)($partida['cantidad_comercial'] ?? 0);
$valor_partida = (float)($partida['valor_factura'] ?? 0);
$html .= '
<tr>
<td>' . htmlspecialchars($partida['descripcion']) . '</td>
<td>' . htmlspecialchars($partida['unidad_descripcion'] ?? 'Unidad de servicio (E48)') . '</td>
<td>' . $moneda_codigo . ' ' . number_format($precio_unitario > 0 ? $precio_unitario : $valor_partida, 2) . '</td>
<td>' . number_format($cantidad > 0 ? $cantidad : 1, 0) . '</td>
<td>' . $moneda_codigo . ' ' . number_format($valor_partida, 2) . '</td>
</tr>';
}
// Agregar partidas
foreach ($partidas as $partida) {
$precio_unitario = (float)($partida['precio_unitario'] ?? 0);
$cantidad = (float)($partida['cantidad_comercial'] ?? 0);
$valor_partida = (float)($partida['valor_factura'] ?? 0);
$html .= '
<tr>
<td>' . htmlspecialchars($partida['descripcion']) . '</td>
<td>' . htmlspecialchars($partida['unidad_descripcion'] ?? 'Unidad de servicio (E48)') . '</td>
<td>' . $moneda_codigo . ' ' . number_format($precio_unitario > 0 ? $precio_unitario : $valor_partida, 2) . '</td>
<td>' . number_format($cantidad > 0 ? $cantidad : 1, 0) . '</td>
<td>' . $moneda_codigo . ' ' . number_format($valor_partida, 2) . '</td>
</tr>';
}
$html .= '
</tbody>
</table>
$html .= '
</tbody>
</table>
<!-- NOTA INFERIOR -->
<div class="footer-info">
<div class="footer-note">' . $total_texto . '</div>
</div>
<!-- NOTA INFERIOR -->
<div class="footer-info">
<div class="footer-note">' . $total_texto . '</div>
</div>
<!-- TOTALES -->
<div class="totals-section">
<div class="total-row text-right">
<span><strong>Total:</strong></span>
<span><strong>' . $moneda_codigo . ' ' . number_format($total, 2) . '</strong></span>
</div>
</div>
<!-- TOTALES -->
<div class="totals-section">
<div class="total-row text-right">
<span><strong>Total:</strong></span>
<span><strong>' . $moneda_codigo . ' ' . number_format($total, 2) . '</strong></span>
</div>
</div>
</body>
</html>';
</body>
</html>
';
return $html;
}
// NUEVO ENDPOINT: Pedimentos de catálogo activos del importador para panel de referencia
function ajax_pedimentos_catalogo()
{
header('Content-Type: application/json; charset=utf-8');
if (!($_SESSION['usuario_id'] ?? false)) {
http_response_code(401);
echo json_encode(['data' => []]);
exit;
}
$id_usuario = $_SESSION['usuario_id'];
$limit = isset($_GET['limit']) ? intval($_GET['limit']) : 10;
$conn = getConnection();
// Obtener RFC del importador
$sqlImportador = "SELECT rfc FROM informacion_general WHERE id_usuario = ?";
$stmtImportador = sqlsrv_query($conn, $sqlImportador, [$id_usuario]);
if ($stmtImportador === false) {
echo json_encode(['data' => []]);
exit;
}
$importador = sqlsrv_fetch_array($stmtImportador, SQLSRV_FETCH_ASSOC);
if (!$importador) {
echo json_encode(['data' => []]);
exit;
}
// Obtener pedimentos activos del catálogo
$sql = "SELECT TOP {$limit} IdPrevio, Pedimento, ClienteNombre, ClavePed, Timestamp
FROM PREVIOS_COMPARTIDOS_WS
WHERE ClienteRFC = ? AND Status = 1
ORDER BY Timestamp DESC";
$stmt = sqlsrv_query($conn, $sql, [$importador['rfc']]);
$pedimentos = [];
if ($stmt !== false) {
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$fecha = '';
if ($row['Timestamp'] instanceof DateTime) {
$fecha = $row['Timestamp']->format('d/m/Y');
}
$pedimentos[] = [
'numero' => $row['Pedimento'],
'cliente' => $row['ClienteNombre'],
'clave' => $row['ClavePed'],
'fecha' => $fecha
];
}
}
echo json_encode(['data' => $pedimentos]);
exit;
}
function ajax_facturas_por_pedimento() {
header('Content-Type: application/json');
$id_previo = isset($_GET['id_pedimento']) ? trim($_GET['id_pedimento']) : '';
if (!$id_previo) {
echo json_encode(['success' => false, 'error' => 'ID de pedimento no válido']);
return;
}
$conn = getConnection();
// 1. Buscar el número de pedimento real en PREVIOS_COMPARTIDOS_WS
$stmt = sqlsrv_query($conn, "SELECT Pedimento FROM PREVIOS_COMPARTIDOS_WS WHERE IdPrevio = ?", [$id_previo]);
if ($stmt === false || !($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC))) {
echo json_encode(['success' => false, 'error' => 'No se encontró el pedimento en PREVIOS_COMPARTIDOS_WS']);
return;
}
$numero_pedimento = $row['Pedimento'];
// 2. Buscar las facturas en solicitud_importacion_factura usando el número de pedimento
// ✅ CORREGIDO: Incluir id_solicitud como id_factura
$stmt2 = sqlsrv_query($conn, "SELECT id_solicitud, numero_factura, fecha_factura, valor_factura FROM solicitud_importacion_factura WHERE numero_pedimento = ?", [$numero_pedimento]);
if ($stmt2 === false) {
echo json_encode(['success' => false, 'error' => 'Error en la consulta de facturas']);
return;
}
$facturas = [];
while ($row2 = sqlsrv_fetch_array($stmt2, SQLSRV_FETCH_ASSOC)) {
$facturas[] = [
'id_factura' => $row2['id_solicitud'], // ✅ AGREGADO: Usar id_solicitud como id_factura
'numero_factura' => $row2['numero_factura'],
'fecha' => ($row2['fecha_factura'] instanceof DateTime) ? $row2['fecha_factura']->format('Y-m-d') : $row2['fecha_factura'],
'monto' => $row2['valor_factura']
];
}
echo json_encode(['success' => true, 'facturas' => $facturas]);
}

View File

@@ -0,0 +1,913 @@
<?php
require_once __DIR__ . '/../helpers/session.php';
require_once __DIR__ . '/../../config/database.php';
function index()
{
if (!($_SESSION['usuario_id'] ?? false)) {
header('Location: /IMPORTADORES/login');
exit;
}
include __DIR__ . '/../../views/templates_rapidos/index.php';
}
function lista()
{
if (!($_SESSION['usuario_id'] ?? false)) {
header('Location: /IMPORTADORES/login');
exit;
}
include __DIR__ . '/../../views/templates_rapidos/lista.php';
}
function crear()
{
if (!($_SESSION['usuario_id'] ?? false)) {
header('Location: /IMPORTADORES/login');
exit;
}
$conn = getConnection();
// Obtener datos para los selects
$aduanas = obtenerAduanas($conn);
$patentes = obtenerPatentes($conn);
$incoterms = obtenerIncoterms($conn);
$paises = obtenerPaises($conn);
$transportistas = obtenerTransportistas($conn);
$choferes = obtenerChoferes($conn);
$unidades_medida = obtenerUnidadesMedida($conn);
include __DIR__ . '/../../views/templates_rapidos/crear.php';
}
function guardar()
{
if (!($_SESSION['usuario_id'] ?? false)) {
die("⚠️ No autorizado.");
}
$conn = getConnection();
$id_usuario = $_SESSION['usuario_id'];
$id_agencia = $_SESSION['id_agencia_en_uso'] ?? null;
// Obtener datos del formulario
$nombre = trim($_POST['nombre'] ?? '');
$descripcion = trim($_POST['descripcion'] ?? '');
$icono = trim($_POST['icono'] ?? '🏢');
// Configuración del template
$config = [
'tipo_moneda' => $_POST['tipo_moneda'] ?? '',
'incoterm' => $_POST['incoterm'] ?? '',
'vinculacion' => $_POST['vinculacion'] ?? '',
'pais_proveedor' => $_POST['pais_proveedor'] ?? '',
'pais_proveedor_texto' => $_POST['pais_proveedor_texto'] ?? '',
'anexo22_apendice' => $_POST['anexo22_apendice'] ?? '',
'patente' => $_POST['patente'] ?? '',
'transportista_id' => $_POST['transportista_id'] ?? '',
'chofer_id' => $_POST['chofer_id'] ?? '',
'tasa_preferencial' => $_POST['tasa_preferencial'] ?? '',
'unidad_comercial_id' => $_POST['unidad_comercial_id'] ?? ''
];
$config_json = json_encode($config, JSON_UNESCAPED_UNICODE);
// Validaciones
if (empty($nombre)) {
die("❌ El nombre del template es obligatorio.");
}
if (strlen($nombre) > 100) {
die("❌ El nombre del template es muy largo (máximo 100 caracteres).");
}
$sql = "INSERT INTO dbo.templates_rapidos
(nombre, descripcion, icono, config_json, tipo_moneda, incoterm,
vinculacion, pais_proveedor, tasa_preferencial, id_agencia, id_usuario_creador)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
$params = [
$nombre,
$descripcion,
$icono,
$config_json,
$config['tipo_moneda'],
$config['incoterm'],
$config['vinculacion'] ?: null,
$config['pais_proveedor'],
$config['tasa_preferencial'],
$id_agencia,
$id_usuario
];
$stmt = sqlsrv_query($conn, $sql, $params);
if ($stmt === false) {
die("❌ Error al guardar template: " . print_r(sqlsrv_errors(), true));
}
header('Location: /IMPORTADORES/templates_rapidos/lista?created=ok');
exit;
}
function editar()
{
if (!($_SESSION['usuario_id'] ?? false)) {
header('Location: /IMPORTADORES/login');
exit;
}
$id = $_GET['id'] ?? null;
if (!$id || !is_numeric($id)) {
die("❌ ID inválido.");
}
$conn = getConnection();
$id_usuario = $_SESSION['usuario_id'];
$id_agencia = $_SESSION['id_agencia_en_uso'] ?? null;
// Obtener el template
$sql = "SELECT * FROM dbo.templates_rapidos
WHERE id = ? AND (id_usuario_creador = ? OR id_agencia = ? OR id_agencia IS NULL)";
$stmt = sqlsrv_query($conn, $sql, [$id, $id_usuario, $id_agencia]);
if ($stmt === false) {
die("❌ Error en consulta: " . print_r(sqlsrv_errors(), true));
}
$template = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
if (!$template) {
die("❌ Template no encontrado o sin permisos.");
}
// Decodificar configuración JSON
$template['config'] = json_decode($template['config_json'], true) ?: [];
// Obtener datos para los selects
$conn = getConnection();
$aduanas = obtenerAduanas($conn);
$patentes = obtenerPatentes($conn);
$incoterms = obtenerIncoterms($conn);
$paises = obtenerPaises($conn);
$transportistas = obtenerTransportistas($conn);
$choferes = obtenerChoferes($conn);
$unidades_medida = obtenerUnidadesMedida($conn);
include __DIR__ . '/../../views/templates_rapidos/editar.php';
}
function actualizar()
{
if (!($_SESSION['usuario_id'] ?? false)) {
die("⚠️ No autorizado.");
}
$conn = getConnection();
$id_usuario = $_SESSION['usuario_id'];
$id_agencia = $_SESSION['id_agencia_en_uso'] ?? null;
$id = $_POST['id'] ?? null;
$nombre = trim($_POST['nombre'] ?? '');
$descripcion = trim($_POST['descripcion'] ?? '');
$icono = trim($_POST['icono'] ?? '🏢');
if (!$id || !is_numeric($id)) {
die("❌ ID inválido.");
}
// Configuración del template
$config = [
'tipo_moneda' => $_POST['tipo_moneda'] ?? '',
'incoterm' => $_POST['incoterm'] ?? '',
'vinculacion' => $_POST['vinculacion'] ?? '',
'pais_proveedor' => $_POST['pais_proveedor'] ?? '',
'pais_proveedor_texto' => $_POST['pais_proveedor_texto'] ?? '',
'anexo22_apendice' => $_POST['anexo22_apendice'] ?? '',
'patente' => $_POST['patente'] ?? '',
'transportista_id' => $_POST['transportista_id'] ?? '',
'chofer_id' => $_POST['chofer_id'] ?? '',
'tasa_preferencial' => $_POST['tasa_preferencial'] ?? '',
'unidad_comercial_id' => $_POST['unidad_comercial_id'] ?? ''
];
$config_json = json_encode($config, JSON_UNESCAPED_UNICODE);
// Validaciones
if (empty($nombre)) {
die("❌ El nombre del template es obligatorio.");
}
// Verificar permisos
$sqlCheck = "SELECT id FROM dbo.templates_rapidos
WHERE id = ? AND (id_usuario_creador = ? OR id_agencia = ?)";
$stmtCheck = sqlsrv_query($conn, $sqlCheck, [$id, $id_usuario, $id_agencia]);
$exists = sqlsrv_fetch_array($stmtCheck, SQLSRV_FETCH_ASSOC);
if (!$exists) {
die("❌ Template no encontrado o sin permisos para editarlo.");
}
$sql = "UPDATE dbo.templates_rapidos SET
nombre = ?, descripcion = ?, icono = ?, config_json = ?,
tipo_moneda = ?, incoterm = ?, vinculacion = ?,
pais_proveedor = ?, tasa_preferencial = ?, fecha_modificacion = GETDATE()
WHERE id = ?";
$params = [
$nombre,
$descripcion,
$icono,
$config_json,
$config['tipo_moneda'],
$config['incoterm'],
$config['vinculacion'] ?: null,
$config['pais_proveedor'],
$config['tasa_preferencial'],
$id
];
$stmt = sqlsrv_query($conn, $sql, $params);
if ($stmt === false) {
die("❌ Error al actualizar template: " . print_r(sqlsrv_errors(), true));
}
header('Location: /IMPORTADORES/templates_rapidos/lista?updated=ok');
exit;
}
function eliminar()
{
if (!($_SESSION['usuario_id'] ?? false)) {
header('Location: /IMPORTADORES/login');
exit;
}
$id = $_GET['id'] ?? null;
if (!$id || !is_numeric($id)) {
die("❌ ID inválido.");
}
$conn = getConnection();
$id_usuario = $_SESSION['usuario_id'];
$id_agencia = $_SESSION['id_agencia_en_uso'] ?? null;
// Verificar permisos
$sqlCheck = "SELECT id FROM dbo.templates_rapidos
WHERE id = ? AND (id_usuario_creador = ? OR id_agencia = ?)";
$stmtCheck = sqlsrv_query($conn, $sqlCheck, [$id, $id_usuario, $id_agencia]);
$exists = sqlsrv_fetch_array($stmtCheck, SQLSRV_FETCH_ASSOC);
if (!$exists) {
die("❌ Template no encontrado o sin permisos para eliminarlo.");
}
$sql = "UPDATE dbo.templates_rapidos SET activo = 0 WHERE id = ?";
$stmt = sqlsrv_query($conn, $sql, [$id]);
if ($stmt === false) {
die("❌ Error al eliminar template: " . print_r(sqlsrv_errors(), true));
}
header('Location: /IMPORTADORES/templates_rapidos/lista?deleted=ok');
exit;
}
function ajax_lista()
{
if (!($_SESSION['usuario_id'] ?? false)) {
http_response_code(403);
echo json_encode([]);
exit;
}
$id_usuario = $_SESSION['usuario_id'];
$id_agencia = $_SESSION['id_agencia_en_uso'] ?? null;
$conn = getConnection();
// Parámetros de DataTables
$draw = intval($_GET['draw'] ?? 0);
$start = intval($_GET['start'] ?? 0);
$length = intval($_GET['length'] ?? 10);
$search = $_GET['search']['value'] ?? '';
// Construir condiciones de filtro
$where = "activo = 1 AND (id_agencia IS NULL OR id_agencia = ? OR id_usuario_creador = ?)";
$params = [$id_agencia, $id_usuario];
if ($search !== '') {
$where .= " AND (nombre LIKE ? OR descripcion LIKE ?)";
$like = "%{$search}%";
$params = array_merge($params, [$like, $like]);
}
// Total registros filtrados
$sqlFiltered = "SELECT COUNT(*) AS total FROM dbo.templates_rapidos WHERE $where";
$stmtF = sqlsrv_query($conn, $sqlFiltered, $params);
$rowF = sqlsrv_fetch_array($stmtF, SQLSRV_FETCH_ASSOC);
$recordsFiltered = (int)($rowF['total'] ?? 0);
// Total registros sin filtro
$sqlTotal = "SELECT COUNT(*) AS total FROM dbo.templates_rapidos WHERE activo = 1 AND (id_agencia IS NULL OR id_agencia = ? OR id_usuario_creador = ?)";
$stmtT = sqlsrv_query($conn, $sqlTotal, [$id_agencia, $id_usuario]);
$rowT = sqlsrv_fetch_array($stmtT, SQLSRV_FETCH_ASSOC);
$recordsTotal = (int)($rowT['total'] ?? 0);
// Consulta principal con paginación
$sql = "SELECT t.id, t.nombre, t.descripcion, t.icono, t.tipo_moneda, t.incoterm,
t.vinculacion, t.pais_proveedor, t.tasa_preferencial, t.fecha_creacion,
u.nombre_usuario AS usuario_creador,
CASE WHEN t.id_agencia IS NULL THEN 'Sistema' ELSE a.nombre END AS ambito
FROM dbo.templates_rapidos t
LEFT JOIN dbo.usuarios u ON t.id_usuario_creador = u.id
LEFT JOIN dbo.agencias a ON t.id_agencia = a.id
WHERE $where
ORDER BY t.fecha_creacion DESC
OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
$paramsData = array_merge($params, [$start, $length]);
$stmt = sqlsrv_query($conn, $sql, $paramsData);
$data = [];
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$fecha = $row['fecha_creacion'] instanceof DateTime
? $row['fecha_creacion']->format('d/m/Y H:i')
: 'N/A';
$data[] = [
'id' => $row['id'],
'icono' => htmlspecialchars($row['icono'] ?? '🏢'),
'nombre' => htmlspecialchars($row['nombre']),
'descripcion' => htmlspecialchars($row['descripcion'] ?? ''),
'tipo_moneda' => htmlspecialchars($row['tipo_moneda'] ?? ''),
'incoterm' => htmlspecialchars($row['incoterm'] ?? ''),
'pais_proveedor' => htmlspecialchars($row['pais_proveedor'] ?? ''),
'ambito' => htmlspecialchars($row['ambito'] ?? ''),
'usuario_creador' => htmlspecialchars($row['usuario_creador'] ?? ''),
'fecha_creacion' => $fecha
];
}
echo json_encode([
'draw' => $draw,
'recordsTotal' => $recordsTotal,
'recordsFiltered' => $recordsFiltered,
'data' => $data
]);
}
function ajax_obtener_templates_debug()
{
header('Content-Type: application/json; charset=UTF-8');
if (!($_SESSION['usuario_id'] ?? false)) {
echo json_encode(['error' => 'Usuario no autenticado']);
exit;
}
$id_usuario = $_SESSION['usuario_id'];
$id_agencia = $_SESSION['id_agencia_en_uso'] ?? null;
try {
$conn = getConnection();
// ✅ CONSULTA SIN FILTROS para debug
$sql = "SELECT id, nombre, descripcion, icono, config_json,
ISNULL(veces_usado, 0) as veces_usado,
id_usuario_creador, id_agencia, activo,
fecha_creacion
FROM dbo.templates_rapidos
ORDER BY fecha_creacion DESC";
$stmt = sqlsrv_query($conn, $sql);
if ($stmt === false) {
$errors = sqlsrv_errors();
echo json_encode([
'error' => 'Error en consulta SQL',
'message' => $errors[0]['message'] ?? 'Error desconocido',
'sql_errors' => $errors
]);
exit;
}
$templates = [];
$debug_info = [
'usuario_actual' => $id_usuario,
'agencia_actual' => $id_agencia,
'todos_los_templates' => []
];
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
// Info completa para debug
$template_debug = [
'id' => $row['id'],
'nombre' => $row['nombre'],
'id_usuario_creador' => $row['id_usuario_creador'],
'id_agencia' => $row['id_agencia'],
'activo' => $row['activo'],
'fecha_creacion' => $row['fecha_creacion'] instanceof DateTime
? $row['fecha_creacion']->format('Y-m-d H:i:s')
: $row['fecha_creacion'],
'es_del_usuario_actual' => ($row['id_usuario_creador'] == $id_usuario),
'es_de_la_agencia' => ($row['id_agencia'] == $id_agencia),
'deberia_mostrarse' => (
$row['activo'] == 1 && (
$row['id_agencia'] === null ||
$row['id_agencia'] == $id_agencia ||
$row['id_usuario_creador'] == $id_usuario
)
)
];
$debug_info['todos_los_templates'][] = $template_debug;
// Solo agregar a templates para mostrar si cumple condiciones
if ($template_debug['deberia_mostrarse']) {
$config = [];
if (!empty($row['config_json'])) {
$decoded = json_decode($row['config_json'], true);
$config = is_array($decoded) ? $decoded : [];
}
$templates[] = [
'id' => (int)$row['id'],
'nombre' => $row['nombre'],
'descripcion' => $row['descripcion'] ?? '',
'icono' => $row['icono'] ?? '📋',
'config' => $config,
'veces_usado' => (int)$row['veces_usado'],
'id_usuario_creador' => $row['id_usuario_creador'],
'es_mio' => ($row['id_usuario_creador'] == $id_usuario)
];
}
}
echo json_encode([
'success' => true,
'count' => count($templates),
'templates' => $templates,
'debug_info' => $debug_info
], JSON_UNESCAPED_UNICODE);
} catch (Exception $e) {
echo json_encode([
'error' => 'Error interno del servidor',
'message' => $e->getMessage()
]);
}
exit;
}
function ajax_obtener_templates()
{
// ✅ VERSIÓN CORREGIDA: Función simplificada sin filtros complejos
header('Content-Type: application/json; charset=UTF-8');
if (!($_SESSION['usuario_id'] ?? false)) {
http_response_code(403);
echo json_encode(['error' => 'Usuario no autenticado', 'templates' => []]);
exit;
}
$id_usuario = $_SESSION['usuario_id'];
$id_agencia = $_SESSION['id_agencia_en_uso'] ?? null;
try {
$conn = getConnection();
// ✅ CONSULTA SIMPLIFICADA: Mostrar todos los templates activos del usuario o agencia
$sql = "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";
$params = [$id_usuario, $id_agencia, $id_usuario];
$stmt = sqlsrv_query($conn, $sql, $params);
if ($stmt === false) {
$errors = sqlsrv_errors();
echo json_encode([
'error' => 'Error en consulta SQL',
'message' => $errors[0]['message'] ?? 'Error desconocido',
'templates' => []
]);
exit;
}
$templates = [];
$templates_personales = [];
$templates_otros = [];
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
// Decodificar JSON con manejo de errores
$config = [];
if (!empty($row['config_json'])) {
$decoded = json_decode($row['config_json'], true);
$config = is_array($decoded) ? $decoded : [];
}
$template = [
'id' => (int)$row['id'],
'nombre' => $row['nombre'],
'descripcion' => $row['descripcion'] ?? '',
'icono' => $row['icono'] ?? '📋',
'config' => $config,
'veces_usado' => (int)$row['veces_usado'],
'id_usuario_creador' => $row['id_usuario_creador'],
'id_agencia' => $row['id_agencia'],
'es_mio' => ($row['id_usuario_creador'] == $id_usuario),
'ambito' => ($row['id_usuario_creador'] == $id_usuario) ? 'personal' :
(($row['id_agencia'] == $id_agencia) ? 'agencia' : 'sistema')
];
// Separar templates personales de otros
if ($template['es_mio']) {
$templates_personales[] = $template;
} else {
$templates_otros[] = $template;
}
$templates[] = $template;
}
// Respuesta con información detallada
echo json_encode([
'success' => true,
'count' => count($templates),
'count_personales' => count($templates_personales),
'count_otros' => count($templates_otros),
'usuario_id' => $id_usuario,
'agencia_id' => $id_agencia,
'templates' => $templates,
'templates_personales' => $templates_personales,
'templates_otros' => $templates_otros
], JSON_UNESCAPED_UNICODE);
} catch (Exception $e) {
echo json_encode([
'error' => 'Error interno del servidor',
'message' => $e->getMessage(),
'templates' => []
]);
}
exit;
}
function ajax_lista_por_seccion()
{
if (!($_SESSION['usuario_id'] ?? false)) {
die(json_encode(['error' => 'No autorizado']));
}
$conn = getConnection();
$id_usuario = $_SESSION['usuario_id'];
$id_agencia = $_SESSION['id_agencia_en_uso'] ?? null;
$seccion = $_GET['seccion'] ?? 'personal';
$data = [];
try {
switch($seccion) {
case 'personal':
// Solo templates creados por el usuario actual
$sql = "SELECT id, nombre, descripcion, icono, config_json,
veces_usado, fecha_creacion, id_usuario_creador
FROM dbo.templates_rapidos
WHERE id_usuario_creador = ? AND estado = 1
ORDER BY veces_usado DESC, fecha_creacion DESC";
$params = [$id_usuario];
break;
case 'agencia':
// Templates de la agencia (excluyendo los personales ya mostrados)
$sql = "SELECT id, nombre, descripcion, icono, config_json,
veces_usado, fecha_creacion, id_usuario_creador
FROM dbo.templates_rapidos
WHERE id_agencia = ? AND id_usuario_creador != ? AND estado = 1
ORDER BY veces_usado DESC, fecha_creacion DESC";
$params = [$id_agencia, $id_usuario];
break;
case 'global':
// Templates globales del sistema (solo si se solicitan explícitamente)
$sql = "SELECT id, nombre, descripcion, icono, config_json,
veces_usado, fecha_creacion, id_usuario_creador
FROM dbo.templates_rapidos
WHERE id_agencia IS NULL AND id_usuario_creador IS NULL AND estado = 1
ORDER BY veces_usado DESC, fecha_creacion DESC";
$params = [];
break;
default:
throw new Exception('Sección inválida');
}
$stmt = sqlsrv_query($conn, $sql, $params);
if ($stmt === false) {
throw new Exception('Error en consulta: ' . print_r(sqlsrv_errors(), true));
}
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
// Formatear fecha
$fecha = $row['fecha_creacion'] instanceof DateTime
? $row['fecha_creacion']->format('d/m/Y')
: date('d/m/Y', strtotime($row['fecha_creacion']));
// Determinar acciones según el tipo de template
$acciones = '';
$esPropio = ($row['id_usuario_creador'] == $id_usuario);
$esAgencia = ($seccion === 'agencia');
$esGlobal = ($seccion === 'global');
if ($esPropio) {
$acciones = '
<div class="btn-group" role="group">
<a href="/IMPORTADORES/templates_rapidos/editar?id=' . $row['id'] . '"
class="btn btn-sm btn-outline-primary" title="Editar">
<i class="fas fa-edit"></i>
</a>
<button onclick="duplicarTemplate(' . $row['id'] . ')"
class="btn btn-sm btn-outline-info" title="Duplicar">
<i class="fas fa-copy"></i>
</button>
<button onclick="eliminarTemplate(' . $row['id'] . ')"
class="btn btn-sm btn-outline-danger" title="Eliminar">
<i class="fas fa-trash"></i>
</button>
</div>';
} elseif ($esAgencia) {
$acciones = '
<div class="btn-group" role="group">
<button onclick="mostrarVistaPrevia(' . $row['id'] . ')"
class="btn btn-sm btn-outline-info" title="Ver">
<i class="fas fa-eye"></i>
</button>
<button onclick="duplicarTemplate(' . $row['id'] . ')"
class="btn btn-sm btn-outline-success" title="Duplicar">
<i class="fas fa-copy"></i>
</button>
</div>';
} else { // Global
$acciones = '
<button onclick="mostrarVistaPrevia(' . $row['id'] . ')"
class="btn btn-sm btn-outline-info" title="Ver detalles">
<i class="fas fa-eye"></i>
</button>';
}
// Construir nombre con icono
$nombreConIcono = '<span class="d-flex align-items-center">
<span class="me-2" style="font-size: 18px;">' . htmlspecialchars($row['icono']) . '</span>
<div>
<strong>' . htmlspecialchars($row['nombre']) . '</strong>';
// Agregar badge según el tipo
if ($esPropio) {
$nombreConIcono .= ' <span class="badge bg-primary scope-badge ms-2">Mío</span>';
} elseif ($esAgencia) {
$nombreConIcono .= ' <span class="badge bg-info scope-badge ms-2">Agencia</span>';
} else {
$nombreConIcono .= ' <span class="badge bg-secondary scope-badge ms-2">Global</span>';
}
$nombreConIcono .= '</div></span>';
$data[] = [
$row['id'],
$nombreConIcono,
htmlspecialchars($row['descripcion'] ?? 'Sin descripción'),
'<span class="badge bg-success">' . ($row['veces_usado'] ?? 0) . '</span>',
$fecha,
$acciones
];
}
echo json_encode([
'success' => true,
'data' => $data,
'count' => count($data)
]);
} catch (Exception $e) {
echo json_encode([
'success' => false,
'error' => $e->getMessage(),
'data' => []
]);
}
}
function duplicar()
{
if (!($_SESSION['usuario_id'] ?? false)) {
die("⚠️ No autorizado.");
}
$conn = getConnection();
$id_usuario = $_SESSION['usuario_id'];
$id_agencia = $_SESSION['id_agencia_en_uso'] ?? null;
$template_id = $_POST['template_id'] ?? null;
if (!$template_id || !is_numeric($template_id)) {
die("❌ ID de template inválido.");
}
// Obtener template original
$sql = "SELECT * FROM dbo.templates_rapidos WHERE id = ?";
$stmt = sqlsrv_query($conn, $sql, [$template_id]);
if ($stmt === false) {
die("❌ Error al buscar template: " . print_r(sqlsrv_errors(), true));
}
$original = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
if (!$original) {
die("❌ Template no encontrado.");
}
// Crear duplicado con nuevo nombre
$nuevo_nombre = $original['nombre'] . ' (Copia)';
$sql_insert = "INSERT INTO dbo.templates_rapidos
(nombre, descripcion, icono, config_json, tipo_moneda, incoterm,
vinculacion, pais_proveedor, tasa_preferencial, id_agencia, id_usuario_creador)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
$params_insert = [
$nuevo_nombre,
$original['descripcion'],
$original['icono'],
$original['config_json'],
$original['tipo_moneda'],
$original['incoterm'],
$original['vinculacion'],
$original['pais_proveedor'],
$original['tasa_preferencial'],
$id_agencia,
$id_usuario
];
$stmt_insert = sqlsrv_query($conn, $sql_insert, $params_insert);
if ($stmt_insert === false) {
die("❌ Error al duplicar: " . print_r(sqlsrv_errors(), true));
}
echo "✅ Template duplicado correctamente como '{$nuevo_nombre}'.";
}
function ajax_usar_template()
{
if (!($_SESSION['usuario_id'] ?? false)) {
http_response_code(403);
echo json_encode(['success' => false, 'message' => 'No autorizado']);
exit;
}
$id_template = intval($_POST['id_template'] ?? 0);
$id_usuario = $_SESSION['usuario_id'];
if ($id_template <= 0) {
echo json_encode(['success' => false, 'message' => 'ID de template inválido']);
exit;
}
$conn = getConnection();
// Incrementar contador de uso
$sql = "UPDATE dbo.templates_rapidos
SET veces_usado = veces_usado + 1, ultima_vez_usado = GETDATE()
WHERE id = ? AND activo = 1";
$stmt = sqlsrv_query($conn, $sql, [$id_template]);
if ($stmt === false) {
echo json_encode(['success' => false, 'message' => 'Error al actualizar estadísticas']);
exit;
}
echo json_encode(['success' => true]);
exit;
}
// Funciones auxiliares
function obtenerAduanas($conn) {
$sql = "SELECT DISTINCT RIGHT(REPLICATE('0', 3) + CAST(aduana_seccion AS VARCHAR), 3) AS aduana_seccion, nombre FROM dbo.aduanas ORDER BY aduana_seccion";
$stmt = sqlsrv_query($conn, $sql);
$result = [];
if ($stmt !== false) {
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$result[] = $row;
}
}
return $result;
}
function obtenerPatentes($conn) {
$id_agencia = $_SESSION['id_agencia_en_uso'] ?? null;
if (!$id_agencia) {
return [];
}
$sql = "SELECT id_agente, patente, agente_aduanal FROM dbo.agentes_aduanales WHERE id_agencia = ? AND activo = 1 ORDER BY patente";
$stmt = sqlsrv_query($conn, $sql, [$id_agencia]);
$result = [];
if ($stmt !== false) {
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$result[] = $row;
}
}
return $result;
}
function obtenerIncoterms($conn) {
$sql = "SELECT INCOTERM, DESCESPANOL FROM dbo.gIncoterms ORDER BY INCOTERM";
$stmt = sqlsrv_query($conn, $sql);
$result = [];
if ($stmt !== false) {
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$result[] = $row;
}
}
return $result;
}
function obtenerPaises($conn) {
$sql = "SELECT id_pais, nombre FROM dbo.paises ORDER BY nombre";
$stmt = sqlsrv_query($conn, $sql);
$result = [];
if ($stmt !== false) {
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$result[] = $row;
}
}
return $result;
}
function obtenerTransportistas($conn) {
$id_usuario = $_SESSION['usuario_id'] ?? null;
if (!$id_usuario) {
return [];
}
$sql = "SELECT id_transportista, clave_identificador, nombre FROM dbo.transportistas WHERE id_usuario = ? AND activo = 1 ORDER BY nombre";
$stmt = sqlsrv_query($conn, $sql, [$id_usuario]);
$result = [];
if ($stmt !== false) {
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$result[] = $row;
}
}
return $result;
}
function obtenerChoferes($conn) {
$id_usuario = $_SESSION['usuario_id'] ?? null;
if (!$id_usuario) {
return [];
}
$sql = "SELECT c.id_chofer, c.nombre+' '+c.apellido AS nombre, c.transportista_id
FROM dbo.choferes c
JOIN dbo.transportistas t ON c.transportista_id = t.id_transportista
WHERE t.id_usuario = ? AND c.status = 1
ORDER BY c.nombre";
$stmt = sqlsrv_query($conn, $sql, [$id_usuario]);
$result = [];
if ($stmt !== false) {
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$result[] = $row;
}
}
return $result;
}
function obtenerUnidadesMedida($conn) {
$sql = "SELECT id, descripcion FROM dbo.unidades_medida_apendice7 ORDER BY id";
$stmt = sqlsrv_query($conn, $sql);
$result = [];
if ($stmt !== false) {
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$result[] = $row;
}
}
return $result;
}

View 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'
]);
?>

237
app/controllers/winsaai.php Normal file
View File

@@ -0,0 +1,237 @@
<?php
require_once __DIR__ . '/../helpers/session.php';
require_once __DIR__ . '/../../config/database.php';
require_once __DIR__ . '/../helpers/crypto.php';
require_once __DIR__ . '/../helpers/env.php';
loadEnv();
function save_config() {
$conn = getConnection();
$userId = $_SESSION['usuario_id'] ?? null;
if (!$userId) {
http_response_code(401);
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => 'Sesión expirada']);
exit;
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => 'Método no permitido']);
exit;
}
$input = json_decode(file_get_contents('php://input'), true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => 'JSON inválido']);
exit;
}
$host = trim($input['host'] ?? '');
$port = intval($input['port'] ?? 80);
$protocol = $input['protocol'] ?? 'https';
$usuario = trim($input['usuario'] ?? '');
$password = $input['password'] ?? '';
$sync_pedimentos = $input['sync_pedimentos'] ?? true;
$sync_coves = $input['sync_coves'] ?? true;
// Validaciones
if (empty($host) || empty($usuario) || empty($password)) {
http_response_code(400);
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => 'Todos los campos son obligatorios']);
exit;
}
// Encriptar contraseña
$encryptedPassword = encrypt($password);
// Verificar si existe configuración
$sqlCheck = "SELECT id FROM winsaai_config WHERE id_usuario = ?";
$stmtCheck = sqlsrv_query($conn, $sqlCheck, [$userId]);
if ($stmtCheck === false) {
http_response_code(500);
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => 'Error en base de datos']);
exit;
}
$existingConfig = sqlsrv_fetch_array($stmtCheck, SQLSRV_FETCH_ASSOC);
sqlsrv_free_stmt($stmtCheck);
if ($existingConfig) {
// Actualizar
$sql = "UPDATE winsaai_config
SET host = ?, port = ?, protocol = ?, usuario = ?, password = ?,
sync_pedimentos = ?, sync_coves = ?, updated_at = GETDATE()
WHERE id_usuario = ?";
$params = [$host, $port, $protocol, $usuario, $encryptedPassword,
$sync_pedimentos ? 1 : 0, $sync_coves ? 1 : 0, $userId];
} else {
// Insertar
$sql = "INSERT INTO winsaai_config (id_usuario, host, port, protocol, usuario, password, sync_pedimentos, sync_coves)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
$params = [$userId, $host, $port, $protocol, $usuario, $encryptedPassword,
$sync_pedimentos ? 1 : 0, $sync_coves ? 1 : 0];
}
$stmt = sqlsrv_query($conn, $sql, $params);
if ($stmt === false) {
http_response_code(500);
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => 'Error al guardar configuración']);
exit;
}
sqlsrv_free_stmt($stmt);
header('Content-Type: application/json');
echo json_encode(['success' => true, 'message' => 'Configuración guardada correctamente']);
}
function get_config() {
$conn = getConnection();
$userId = $_SESSION['usuario_id'] ?? null;
if (!$userId) {
http_response_code(401);
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => 'Sesión expirada']);
exit;
}
$sql = "SELECT * FROM winsaai_config WHERE id_usuario = ?";
$stmt = sqlsrv_query($conn, $sql, [$userId]);
if ($stmt === false) {
http_response_code(500);
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => 'Error en base de datos']);
exit;
}
$config = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
sqlsrv_free_stmt($stmt);
if ($config) {
// No enviar contraseña por seguridad
unset($config['password']);
// Convertir BIT a boolean
$config['sync_pedimentos'] = (bool)$config['sync_pedimentos'];
$config['sync_coves'] = (bool)$config['sync_coves'];
header('Content-Type: application/json');
echo json_encode(['success' => true, 'data' => $config]);
} else {
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => 'No hay configuración']);
}
}
function test_connection() {
$conn = getConnection();
$userId = $_SESSION['usuario_id'] ?? null;
if (!$userId) {
http_response_code(401);
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => 'Sesión expirada']);
exit;
}
$input = json_decode(file_get_contents('php://input'), true);
$host = trim($input['host'] ?? '');
$port = intval($input['port'] ?? 80);
$protocol = $input['protocol'] ?? 'https';
$usuario = trim($input['usuario'] ?? '');
$password = $input['password'] ?? '';
if (empty($host) || empty($usuario) || empty($password)) {
http_response_code(400);
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => 'Faltan datos para probar conexión']);
exit;
}
$url = "{$protocol}://{$host}:{$port}/api/test";
// Simular prueba de conexión (aquí pondrías la lógica real)
header('Content-Type: application/json');
echo json_encode(['success' => true, 'message' => "Conexión exitosa con {$protocol}://{$host}:{$port}"]);
}
function sync_data() {
$conn = getConnection();
$userId = $_SESSION['usuario_id'] ?? null;
if (!$userId) {
http_response_code(401);
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => 'Sesión expirada']);
exit;
}
// Obtener configuración del usuario
$sql = "SELECT * FROM winsaai_config WHERE id_usuario = ?";
$stmt = sqlsrv_query($conn, $sql, [$userId]);
if ($stmt === false) {
http_response_code(500);
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => 'Error en base de datos']);
exit;
}
$config = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
sqlsrv_free_stmt($stmt);
if (!$config) {
http_response_code(400);
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => 'No hay configuración de WINSAAI']);
exit;
}
$input = json_decode(file_get_contents('php://input'), true);
$syncType = $input['sync_type'] ?? 'both';
// Simular sincronización
$results = [];
if ($syncType === 'pedimentos' || $syncType === 'both') {
$results['pedimentos'] = ['total' => 10, 'processed' => 10];
}
if ($syncType === 'coves' || $syncType === 'both') {
$results['coves'] = ['total' => 5, 'processed' => 5];
}
// Actualizar última sincronización
$sqlUpdate = "UPDATE winsaai_config SET last_sync = GETDATE() WHERE id_usuario = ?";
sqlsrv_query($conn, $sqlUpdate, [$userId]);
header('Content-Type: application/json');
echo json_encode(['success' => true, 'message' => 'Sincronización completada', 'data' => $results]);
}
function index() {
header('Content-Type: application/json');
echo json_encode([
'success' => true,
'message' => 'API WINSAAI disponible',
'endpoints' => [
'save_config' => '/IMPORTADORES/winsaai/save_config',
'test_connection' => '/IMPORTADORES/winsaai/test_connection',
'sync_data' => '/IMPORTADORES/winsaai/sync_data',
'get_config' => '/IMPORTADORES/winsaai/get_config'
]
]);
}
?>

View File

@@ -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
);

View File

@@ -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)
);

View File

@@ -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;

View File

@@ -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

View File

@@ -1,37 +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)
);

View File

@@ -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
View 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
View 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
View 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>";
?>

View 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) ? "" : "NO"));
logDebug("Permisos escritura temp: " . (is_writable($temp_dir) ? "" : "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);
}
?>

View File

@@ -0,0 +1,6 @@
codigo,descripcion,tipo_operacion,activo
A1,Importación definitiva de mercancías,importacion,1
A3,Importación definitiva de vehículos usados,importacion,1
B1,Importación temporal para elaborar transformar o reparar,importacion,1
C1,Importación definitiva de mercancías donadas,importacion,1
G1,Importación de mercancías con Programa IMMEX,importacion,1
1 codigo descripcion tipo_operacion activo
2 A1 Importación definitiva de mercancías importacion 1
3 A3 Importación definitiva de vehículos usados importacion 1
4 B1 Importación temporal para elaborar transformar o reparar importacion 1
5 C1 Importación definitiva de mercancías donadas importacion 1
6 G1 Importación de mercancías con Programa IMMEX importacion 1

View 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);
}
?>

View 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()
]);
}
?>

View 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;
}
}
?>

View 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')
]
]);
}
?>

View 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
View 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
View 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')
]);
?>

View 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()
]);
}
?>

View 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()
]);
}
?>

View File

@@ -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');

View File

@@ -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;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -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)
);

View File

@@ -0,0 +1 @@
40012L5000100032402OCINP10709999 5000100IMS030409FZ0 0426642147138023062025 9:32:47457APKZ87845GY74546120364879805503

View File

@@ -0,0 +1,7 @@
Agencia Aduanal
admiagenci.aduanal@gmail.com
13a91ace36
Agente Aduanal
jorgevergaramendiola@gmail.com
123456789

View File

@@ -0,0 +1,4 @@
500|1|9999|5000100|070||
601|9999|5000100|070|IN|1||IMS030409FZ0|CALL740419HTSNZN00|7||
800|5000100|1|CYd5l4voZrTBXJNXOdRjj5sZeDiKWngrY/2fB0aT43TetLDEW2xl06JT5PJN3qpIe+398SeWSiL/KipHXkkzxrW4EJYQQyxbgSBmYDa5Uwp/cP27wttIKoZ28meZMsObSbld2JK0z52zPcTjIYjWD6ZlHgdYtaiEfcAcjUQ07zd/RFQwYERKqkrqYVosOxr8JJZzj8Y4k3QvnIHnDNZIhK6RBFnDBXV3UfJEx1fBKKF4UH6kQLi820SbeF5yhzg6aMbHcFL5t18zhVnP74Gf642Vr/qm7gWeddWbeZwTsu+xc6z93ILKuyFSS95XWbh4l7kCMO1S7SyWOdeFsScEmg==|00001000000516139173|
801|M9999003.170|1|3|010|

View File

@@ -0,0 +1 @@
F5000100RXMK2128

View File

@@ -0,0 +1,83 @@
500|1|9999|5000100|070||
501|9999|5000100|070|1|IN|070||IMS030409FZ0|CALL740419HTSNZN00|19.08980|0|0|0|0||16699|7|7|7|7|INTERNATIONAL MANUFACTURING SOLUTIONS|SANTOS DUMONT||6630|32695|CD. JUAREZ, CHIH|CH|MEX||0|0|0|0|0|
506|5000100|1|19062025|
506|5000100|2|23062025|
507|5000100|PC||||
509|5000100|15|290.00000|2|
509|5000100|23|16.00000|1|
510|5000100|1|15|0|
510|5000100|15|0|290|
510|5000100|23|0|46|
551|5000100|85444299|1|01|CABLE ELECTRICO CON CONECTOR|19.09000|11454|11454|600.00|600.000|6|57.00000|1||0|6||||CHN|USA|||||
556|5000100|85444299|1|3|16.0000000000|1|
556|5000100|85444299|1|6|5.0000000000|1|
557|5000100|85444299|1|3|0|1929|
557|5000100|85444299|1|6|0|573|
551|5000100|85444299|2|01|CABLE ELECTRICO CON CONECTOR|19.09000|7636|7636|400.00|400.000|6|43.00000|1||0|6||||CHN|USA|||||
556|5000100|85444299|2|3|16.0000000000|1|
556|5000100|85444299|2|6|5.0000000000|1|
557|5000100|85444299|2|3|0|1288|
557|5000100|85444299|2|6|0|382|
551|5000100|85444299|3|01|CABLE ELECTRICO CON CONECTOR|19.09000|3818|3818|200.00|200.000|6|2.00000|1||0|6||||CHN|USA|||||
556|5000100|85444299|3|3|16.0000000000|1|
556|5000100|85444299|3|6|5.0000000000|1|
557|5000100|85444299|3|3|0|646|
557|5000100|85444299|3|6|0|191|
551|5000100|85437099|4|01|CONTROL REMOTO (INCLUYE BATERIAS)|19.09014|11225|11225|588.00|588.000|6|588.00000|6||0|6||||CHN|USA|||||
556|5000100|85437099|4|3|16.0000000000|1|
557|5000100|85437099|4|3|0|1801|
551|5000100|85371099|5|01|CAJA DE CONTROL|293.69444|148022|148022|7754.00|504.000|6|121.00000|1||0|6||||CHN|USA|||||
556|5000100|85371099|5|3|16.0000000000|1|
557|5000100|85371099|5|3|0|23688|
551|5000100|85371099|6|01|CAJA DE CONTROL|293.61905|18498|18498|969.00|63.000|6|15.00000|1||0|6||||CHN|USA|||||
556|5000100|85371099|6|3|16.0000000000|1|
557|5000100|85371099|6|3|0|2964|
551|5000100|85013199|7|00|MOTOR ELECTRICO LINEAL|19.08730|2405|2405|126.00|126.000|6|126.00000|6||0|6||||CHN|USA|||||
556|5000100|85013199|7|3|16.0000000000|1|
556|5000100|85013199|7|6|5.0000000000|1|
557|5000100|85013199|7|3|0|409|
557|5000100|85013199|7|6|0|120|
551|5000100|85013199|8|00|MOTOR ELECTRICO LINEAL|19.09091|7560|7560|396.00|396.000|6|396.00000|6||0|6||||CHN|USA|||||
556|5000100|85013199|8|3|16.0000000000|1|
556|5000100|85013199|8|6|5.0000000000|1|
557|5000100|85013199|8|3|0|1275|
557|5000100|85013199|8|6|0|378|
551|5000100|85013199|9|00|MOTOR ELECTRICO LINEAL|19.08750|3054|3054|160.00|160.000|6|160.00000|6||0|6||||CHN|USA|||||
556|5000100|85013199|9|3|16.0000000000|1|
556|5000100|85013199|9|6|5.0000000000|1|
557|5000100|85013199|9|3|0|518|
557|5000100|85013199|9|6|0|153|
551|5000100|85013199|10|00|MOTOR ELECTRICO LINEAL|237.84511|87527|87527|4585.00|368.000|6|368.00000|6||0|6||||CHN|USA|||||
556|5000100|85013199|10|3|16.0000000000|1|
556|5000100|85013199|10|6|5.0000000000|1|
557|5000100|85013199|10|3|0|14709|
557|5000100|85013199|10|6|0|4376|
551|5000100|85444299|11|01|CABLE ELECTRICO CON CONECTOR|19.08980|9354|9354|490.00|490.000|6|108.00000|1||0|6||||CHN|USA|||||
556|5000100|85444299|11|3|16.0000000000|1|
556|5000100|85444299|11|6|5.0000000000|1|
557|5000100|85444299|11|3|0|1576|
557|5000100|85444299|11|6|0|468|
551|5000100|85444299|12|01|CABLE ELECTRICO CON CONECTOR|19.08571|1336|1336|70.00|70.000|6|15.00000|1||0|6||||CHN|USA|||||
556|5000100|85444299|12|3|16.0000000000|1|
556|5000100|85444299|12|6|5.0000000000|1|
557|5000100|85444299|12|3|0|229|
557|5000100|85444299|12|6|0|67|
551|5000100|85044099|13|00|FUENTE DE CONVERSION DE VOLTAJE|71.81349|18097|18097|948.00|252.000|6|252.00000|6||0|6||||CHN|USA|||||
556|5000100|85044099|13|3|16.0000000000|1|
556|5000100|85044099|13|6|10.0000000000|1|
557|5000100|85044099|13|3|0|3190|
557|5000100|85044099|13|6|0|1810|
551|5000100|85044099|14|00|FUENTE DE CONVERSION DE VOLTAJE|19.08929|6414|6414|336.00|336.000|6|336.00000|6||0|6||||CHN|USA|||||
556|5000100|85044099|14|3|16.0000000000|1|
556|5000100|85044099|14|6|10.0000000000|1|
557|5000100|85044099|14|3|0|1134|
557|5000100|85044099|14|6|0|641|
551|5000100|94039101|15|00|BASE DE ACERO PARA CAMA|79.02326|40776|40776|2136.00|516.000|6|10103.00000|1||0|6||||CHN|USA|||||
553|5000100|94039101|15|T5|||2136.00|10103.00000|
553|5000100|94039101|15|T9|||2136.00|10103.00000|
554|5000100|94039101|15|GA||||
556|5000100|94039101|15|3|16.0000000000|1|
557|5000100|94039101|15|3|0|6529|
557|5000100|94039101|15|3|15|0|
800|5000100|1|QZe3dT6rwoxwP+fyTNK5zf+UeS1PWLXwJk9WZ7TGSo6C0xGy3ER6MF7SP6M6An4x7520QUyw++oanF3EFZa4zS+G+kaoQq2UNMj17tFP2kAgdibUWEEg4HlRcb0U5jZ2bCtMuH+4YX7WU2cfH2wI3Q+DEBIR+jV0u23o6U3+jNqUnVEcuhPg0wEZNtliPdlLEQxdNchyhPr4XsPQtocUE1Ji8IMb6Z7Y1WwVT9jQAGsCOSpKz3597iSs6M3Flh1JfhDVizWJsJ93CRM51PdMFOEETKOHSsOhhHD7rcjwigZiFoEtuSQRKIAzGaXLeh7oJ6iAQSGdbmsmTPvI79FPuw==|00001000000516139173|
801|M9999006.174|1|82|010|

View File

@@ -0,0 +1,3 @@
F5000100XYJU1KF2
L5000100032402OCINP142664214336
C00010023

View File

@@ -0,0 +1 @@
40012L5000100032402OCINP10709999 5000100IMS030409FZ0 04266421471380

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View 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|

View 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|

View 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|

View 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|

View 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|

View 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|

View 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
}

View File

@@ -68,7 +68,7 @@
<div class="content">
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">✅ Agencias Activas</h4>
<!-- TABLA DE AGENCIAS -->
<div class="card p-3 shadow-sm card-hover position-relative h-auto">
<div class="card p-3 shadow-sm card-hover position-relative h-auto fade-in-up">
<div class="table-responsive">
<table class="table table-striped table-hover align-middle" id="tabla-agencias-activas">
<thead>
@@ -98,7 +98,7 @@
<td><?= htmlspecialchars(decrypt($ag['nombre_admin'] ?? '')) ?></td>
<td>
<?php if (isset($ag['activo']) && $ag['activo'] == 1): ?>
<a href="/IMPORTADORES/administrador/suspenderAgencia?id=<?= $ag['id_agencia'] ?>&success=1" class="btn btn-sm btn-danger mt-auto w-auto btn-animated">Suspender</a>
<a href="#" onclick="confirmSuspencion(<?= (int) ($ag['id_agencia'] ?? 0) ?>)" class="btn btn-sm btn-danger mt-auto w-auto btn-animated">Suspender</a>
<?php endif; ?>
</td>
</tr>
@@ -119,6 +119,21 @@
}
});
});
function confirmSuspencion(id) {
Swal.fire({
title: '¿Estás seguro?',
text: 'Solo se suspenderá el perfil del usuario.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Sí, suspender',
cancelButtonText: 'Cancelar'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = `/IMPORTADORES/administrador/suspenderAgencia?id=${id}`;
}
});
}
</script>
<?php if (isset($_GET['success'])): ?>

View File

@@ -129,7 +129,7 @@
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">🏪 Alta de Agencias Aduanales</h4>
<!-- FORMULARIO -->
<div class="card p-4 mb-5 shadow-sm card-hover position-relative h-auto">
<div class="card p-4 mb-5 shadow-sm card-hover position-relative h-auto fade-in-up">
<form action="/IMPORTADORES/administrador/guardar_agencia" method="POST" id="formAltaAgencias">
<h5 class="mb-3"> Nueva Agencia Aduanal</h5>
<div class="row g-3">

View File

@@ -163,7 +163,7 @@
<div class="content">
<h3 class="mb-4 animate__animated animate__fadeInDown title_glow">👥 Alta de Usuarios</h3>
<!-- FORMULARIO -->
<div class="card p-4 mb-4 shadow-sm bg-white card-hover position-relative h-auto">
<div class="card p-4 mb-4 shadow-sm bg-white card-hover position-relative h-auto fade-in-up">
<form action="/IMPORTADORES/administrador/guardar_usuario" method="POST" id="formAltaUsuarios">
<h5 class="mb-3"> Nuevo Usuario</h5>
<div class="row g-3">
@@ -228,7 +228,7 @@
<td><?= isset($u['creado_en']) && $u['creado_en'] instanceof DateTime ? $u['creado_en']->format('Y-m-d H:i') : '' ?></td>
<td>
<?php if (isset($u['activo']) && $u['activo'] == 1): ?>
<a href="/IMPORTADORES/administrador/suspender?id=<?= $u['id_usuario'] ?>&success=1" class="btn btn-sm btn-danger mt-auto w-auto btn-animated">Suspender</a>
<a href="#" onclick="confirmSuspencion(<?= (int) ($u['id_usuario'] ?? 0) ?>)" class="btn btn-sm btn-danger mt-auto w-auto btn-animated">Suspender</a>
<?php endif; ?>
</td>
</tr>
@@ -252,7 +252,8 @@
// Script para manejar las animaciones de validación
document.addEventListener('DOMContentLoaded', function() {
const inputs = document.querySelectorAll('input.form-control, select.form_select');
const inputs = document.querySelectorAll('input.form-control, select.form_select');
const selects = document.querySelectorAll('select.form_select');
inputs.forEach(input => {
// Validación en tiempo real
@@ -301,14 +302,41 @@
e.preventDefault();
let isValid = true;
const inputs = document.querySelectorAll('input.form-control, select.form-select');
// Validar todos los campos
inputs.forEach(input => {
if (!input.checkValidity()) {
input.classList.add('shake');
isValid = false;
setTimeout(() => input.classList.remove('shake'), 500);
}
});
// Si todo es válido, enviar el formulario
if (isValid) {
this.submit(); // Esta línea faltaba en tu código original
} else {
// Mostrar mensaje de error si lo deseas
Swal.fire({ title: 'Campos incompletos', text: 'Por favor complete todos los campos requeridos', icon: 'error' });
}
});
});
function confirmSuspencion(id) {
Swal.fire({
title: '¿Estás seguro?',
text: 'Esta acción no se puede deshacer.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Sí, suspender',
cancelButtonText: 'Cancelar'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = `/IMPORTADORES/administrador/suspender?id=${id}`;
}
});
}
</script>
<?php if (isset($_GET['success'])): ?>

View File

@@ -70,7 +70,7 @@
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">✅ Aprobación de Agencias Aduanales</h4>
<!-- TABLA DE AGENCIAS -->
<div class="card p-3 shadow-sm card-hover position-relative h-auto">
<div class="card p-3 shadow-sm card-hover position-relative h-auto fade-in-up">
<h5 class="mb-3">🏪 Agencias Solicitantes</h5>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle" id="tabla-agencias-pendientes">
@@ -106,7 +106,8 @@
<?php endif; ?>
</td>
<td>
<a href="/IMPORTADORES/administrador/aprobar_agencia?id=<?= $s['request_id'] ?>" class="btn btn-sm btn-success mt-auto w-auto btn-animated">Aprobar</a>
<a href="#" onclick="confirmAprobacion(<?= $s['request_id'] ?>)" class="btn btn-sm btn-success mt-auto w-auto btn-animated">Aprobar</a>
<a href="#" onclick="denegateRequest(<?= $s['request_id'] ?>)" class="btn btn-sm btn-danger mt-auto w-auto btn-animated">Denegar</a>
</td>
</tr>
<?php endforeach; ?>
@@ -117,6 +118,14 @@
</div>
</div>
<?php if (isset($_GET['success'])): ?>
<script>
<?php if ($_GET['success'] === 'denied'): ?>
Swal.fire({ icon: 'success', title: 'Registro de Agencia denegado', text: 'La solicitud fue denegada.', confirmButtonColor: '#198754' });
<?php endif; ?>
</script>
<?php endif; ?>
<script>
$(document).ready(function () {
$('#tabla-agencias-pendientes').DataTable({
@@ -126,6 +135,36 @@
}
});
});
function confirmAprobacion(id) {
Swal.fire({
title: '¿Estás seguro?',
text: 'Se creará el perfil y se notificará al usuario.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Sí, aprobar',
cancelButtonText: 'Cancelar'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = `/IMPORTADORES/administrador/aprobar_agencia?id=${id}`;
}
});
}
function denegateRequest(id) {
Swal.fire({
title: '¿Estás seguro?',
text: 'Se denegara la solicitud de registro de la agencia.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Sí, denegar',
cancelButtonText: 'Cancelar'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = `/IMPORTADORES/administrador/denegar_agencia?id=${id}`;
}
});
}
</script>
<?php if (isset($_GET['error'])): ?>

View File

@@ -69,7 +69,7 @@
<div class="content">
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">✅ Aprobación de Usuarios</h4>
<!-- TABLA DE USUARIOS -->
<div class="card p-3 shadow-sm card-hover position-relative h-auto">
<div class="card p-3 shadow-sm card-hover position-relative h-auto fade-in-up">
<h5 class="mb-3">👥 Usuarios Solicitantes</h5>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle" id="tabla-usuarios-pendientes">
@@ -103,7 +103,8 @@
<?php endif; ?>
</td>
<td>
<a href="/IMPORTADORES/administrador/aprobar_usuario?id=<?= $s['request_id'] ?>" class="btn btn-sm btn-success mt-auto w-auto btn-animated">Aprobar</a>
<a href="#" onclick="confirmAprobacion(<?= $s['request_id'] ?>)" class="btn btn-sm btn-success mt-auto w-auto btn-animated">Aprobar</a>
<a href="#" onclick="denegateRequest(<?= $s['request_id'] ?>)" class="btn btn-sm btn-danger mt-auto w-auto btn-animated">Denegar</a>
</td>
</tr>
<?php endforeach; ?>
@@ -123,14 +124,46 @@
}
});
});
function confirmAprobacion(id) {
Swal.fire({
title: '¿Estás seguro?',
text: 'Se creará el perfil y se notificará al usuario.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Sí, aprobar',
cancelButtonText: 'Cancelar'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = `/IMPORTADORES/administrador/aprobar_usuario?id=${id}`;
}
});
}
function denegateRequest(id) {
Swal.fire({
title: '¿Estás seguro?',
text: 'Se denegara la solicitud de registro del usuario.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Sí, denegar',
cancelButtonText: 'Cancelar'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = `/IMPORTADORES/administrador/denegar_usuario?id=${id}`;
}
});
}
</script>
<?php if (isset($_GET['success'])): ?>
<script>
<?php if ($_GET['success'] === 'approved'): ?>
Swal.fire({ icon: 'success', title: 'Usuario aprobado',
text: 'El usuario ha sido aprobado correctamente y se ha enviado un correo con las credenciales.',
confirmButtonColor: '#198754' });
Swal.fire({ icon: 'success', title: 'Usuario aprobado', text: 'El usuario ha sido aprobado correctamente y se ha enviado un correo con las credenciales.', confirmButtonColor: '#198754' });
<?php endif; ?>
<?php if ($_GET['success'] === 'denied'): ?>
Swal.fire({ icon: 'success', title: 'Registro de Usuario denegado', text: 'La solicitud fue denegada.', confirmButtonColor: '#198754' });
<?php endif; ?>
</script>
<?php endif; ?>

View File

@@ -38,6 +38,9 @@
.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 */
@@ -49,22 +52,39 @@
}
.card { border-radius: 12px; }
/* Animaciones personalizadas */
.card-hover { transition: all 0.3s ease; }
.card-hover:hover { transform: translateY(-5px); box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; }
.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); }
}
.fade-in-up { animation: fadeInUp 0.6s ease-out; }
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
.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 {
@@ -76,7 +96,7 @@
@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); }
}
}
</style>
</head>
<body>
@@ -87,70 +107,110 @@
<!-- <?php // if ($catalogosData['catalogos_activos']): ?> -->
<div class="row g-4">
<!-- <?php
// $delay = 0;
<?php
$delay = 0;
// foreach ($catalogosData['catalogos'] as $catalogo):
// $delay += 0.1; // Incrementar el retraso para cada catálogo
?> -->
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-teal">Aprobar agencias</h5>
<p>Aprueba las agencias que solicitaron un registro.</p>
<a href="/IMPORTADORES/administrador/aprobarAgencias"
class="btn btn-teal btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/sistemas/aprobarAgencias' ? 'active' : '' ?>">
Ver agencias solicitantes</a>
$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 position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-teal mb-3">Aprobar agencias</h5>
<p class="text-muted small flex-grow-1 mb-4">Aprueba las agencias que solicitaron un registro.</p>
<div class="mt-auto">
<button class="btn btn-teal btn-sm mt-2 mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/administrador/aprobarAgencias')"
data-url="/IMPORTADORES/administrador/aprobarAgencias">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Ver agencias solicitantes</span>
</button>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-lime">Alta de agencias</h5>
<p>Da de alta manualmente una agencia.</p>
<a href="/IMPORTADORES/administrador/altaAgencias"
class="btn btn-lime btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/sistemas/altaAgencias' ? 'active' : '' ?>">
Nueva agencia
</a>
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
<div class="card shadow-sm p-3 card-hover position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-lime mb-3">Alta de agencias</h5>
<p class="text-muted small flex-grow-1 mb-4">Da de alta manualmente una agencia.</p>
<div class="mt-auto">
<button class="btn btn-lime btn-sm mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/administrador/altaAgencias')"
data-url="/IMPORTADORES/administrador/altaAgencias">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Nueva agencia</span>
</button>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-success">Aprobar de usuarios</h5>
<p>Aprueba los usuarios que solicitaron un registro.</p>
<a href="/IMPORTADORES/administrador/aprobarUsuarios"
class="btn btn-success btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/sistemas/aprobarUsuarios' ? 'active' : '' ?>">
Ver usuarios solicitantes
</a>
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
<div class="card shadow-sm p-3 card-hover position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-success mb-3">Aprobar usuarios</h5>
<p class="text-muted small flex-grow-1 mb-4">Aprueba los usuarios que solicitaron un registro.</p>
<div class="mt-auto">
<button class="btn btn-success btn-sm mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/administrador/aprobarUsuarios')"
data-url="/IMPORTADORES/administrador/aprobarUsuarios">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Ver usuarios solicitantes</span>
</button>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-info">Alta de usuarios</h5>
<p>Da de alta manualmente a un usuario.</p>
<a href="/IMPORTADORES/administrador/altaUsuarios"
class="btn btn-info btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/sistemas/altaUsuarios' ? 'active' : '' ?>">
Nuevo usuario
</a>
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
<div class="card shadow-sm p-3 card-hover position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-info mb-3">Alta de usuarios</h5>
<p class="text-muted small flex-grow-1 mb-4">Da de alta manualmente a un usuario.</p>
<div class="mt-auto">
<button class="btn btn-info btn-sm mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/administrador/altaUsuarios')"
data-url="/IMPORTADORES/administrador/altaUsuarios">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Nuevo usuario</span>
</button>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-warning">Configuración</h5>
<p>Administra los datos de tu agencia.</p>
<a href="/IMPORTADORES/administrador/configuracion"
class="btn btn-warning btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/sistemas/configuracion' ? 'active' : '' ?>">
Configurar
</a>
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
<div class="card shadow-sm p-3 card-hover position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-warning mb-3">Configuración</h5>
<p class="text-muted small flex-grow-1 mb-4">Administra los datos de tu empresa y preferencias.</p>
<div class="mt-auto">
<button class="btn btn-warning btn-sm mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/configuracion')"
data-url="/IMPORTADORES/configuracion">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Ir a configuración</span>
</button>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-danger">Cerrar sesión</h5>
<p>Salir del sistema de forma segura.</p>
<a href="/IMPORTADORES/sistemas/logout" class="btn btn-danger btn-sm mt-2">Logout</a>
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
<div class="card shadow-sm p-3 card-hover position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-danger mb-3">Cerrar sesión</h5>
<p class="text-muted small flex-grow-1 mb-4">Salir del sistema de forma segura.</p>
<div class="mt-auto">
<button class="btn btn-danger btn-sm mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/sistemas/logout')"
data-url="/IMPORTADORES/sistemas/logout">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Cerrar sesión</span>
</button>
</div>
</div>
</div>
</div>
<!-- <?php // endforeach; ?> -->

View File

@@ -70,7 +70,7 @@
<!-- 📄 CONTENIDO -->
<div class="content">
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">🚷 Usuarios Inactivos</h4>
<div class="card p-3 shadow-sm card-hover position-relative h-auto">
<div class="card p-3 shadow-sm card-hover position-relative h-auto fade-in-up">
<div class="table-responsive">
<table class="table table-striped" id="tabla-inactivos">
<thead class="table-dark">
@@ -95,8 +95,8 @@
<td><?= isset($u['creado_en']) && $u['creado_en'] instanceof DateTime ? $u['creado_en']->format('Y-m-d H:i') : '' ?></td>
<td>
<?php if (isset($u['activo']) && $u['activo'] == 0): ?>
<a href="/IMPORTADORES/administrador/activar?id=<?= $u['id_usuario'] ?>&success=1" class="btn btn-sm btn-success mt-auto w-auto btn-animated">Activar</a>
<a href="/IMPORTADORES/administrador/reset_password?id=<?= $u['id_usuario'] ?>" class="btn btn-sm btn-secondary mt-auto w-auto btn-animated">Nueva Contraseña</a>
<a href="#" onclick="confirmActivacion(<?= (int) ($u['id_usuario'] ?? 0) ?>)" class="btn btn-sm btn-success mt-auto w-auto btn-animated">Activar</a>
<a href="#" onclick="changePassword(<?= (int) ($u['id_usuario'] ?? 0) ?>)" class="btn btn-sm btn-secondary mt-auto w-auto btn-animated">Restablecer</a>
<?php endif; ?>
</td>
</tr>
@@ -117,6 +117,36 @@
}
});
});
function confirmActivacion(id) {
Swal.fire({
title: '¿Estás seguro?',
text: 'Se reactivara el perfil del usuario.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Sí, reactivar',
cancelButtonText: 'Cancelar'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = `/IMPORTADORES/administrador/activar?id=${id}`;
}
});
}
function changePassword(id) {
Swal.fire({
title: '¿Estás seguro?',
text: 'Se restablecera la contraseña del usuario.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Sí, restablecer',
cancelButtonText: 'Cancelar'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = `/IMPORTADORES/administrador/reset_password?id=${id}`;
}
});
}
</script>
<?php if (isset($_GET['success'])): ?>

View File

@@ -162,7 +162,7 @@
<div class="content px-4">
<h3 class="mb-4 animate__animated animate__fadeInDown title_glow">👨‍✈️ Administración de Agentes</h3>
<!-- FORMULARIO -->
<div class="card p-4 mb-4 shadow-sm bg-white card-hover position-relative h-auto">
<div class="card p-4 mb-4 shadow-sm bg-white card-hover position-relative h-auto fade-in-up">
<form action="/IMPORTADORES/agencias/guardarAgente" method="POST" id="formAltaAgentes">
<h5 class="mb-3"> Nuevo Agente</h5>
<div class="row g-3">
@@ -200,8 +200,8 @@
</form>
</div>
<h4 class="mb-4">✅ Agentes Activos</h4>
<div class="card p-3 shadow-sm">
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">✅ Agentes Activos</h4>
<div class="card p-3 shadow-sm fade-in-up">
<div class="table-responsive">
<table class="table table-striped table-hover align-middle" id="tabla-agentes-activos">
<thead class="table-dark">
@@ -227,7 +227,7 @@
<td>
<?php if (in_array($a['tipo_usuario_sistema'], ['agente_aduanal'])): ?>
<a href="/IMPORTADORES/vinculaciones/desvincularAgencia?id=<?= $a['id_relacion'] ?>&tipo=<?= $a['tipo_vinculacion'] ?>"
class="btn btn-sm btn-danger"
class="btn btn-sm btn-danger mt-auto w-auto btn-animated"
onclick="return confirm('¿Está seguro de que desea desvincular este usuario?')">
Desvincular
</a>

View File

@@ -38,6 +38,9 @@
.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 */
@@ -49,22 +52,39 @@
}
.card { border-radius: 12px; }
/* Animaciones personalizadas */
.card-hover { transition: all 0.3s ease; }
.card-hover:hover { transform: translateY(-5px); box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; }
.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); }
}
.fade-in-up { animation: fadeInUp 0.6s ease-out; }
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
.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 {
@@ -76,7 +96,7 @@
@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); }
}
}
</style>
</head>
<body>
@@ -87,82 +107,127 @@
<!-- <?php // if ($catalogosData['catalogos_activos']): ?> -->
<div class="row g-4">
<!-- <?php
// $delay = 0;
<?php
$delay = 0;
// foreach ($catalogosData['catalogos'] as $catalogo):
// $delay += 0.1; // Incrementar el retraso para cada catálogo
?> -->
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-teal">Solicitudes de vinculación</h5>
<p>Aprueba las solicitudes de vinculación de los importadores.</p>
<a href="/IMPORTADORES/vinculaciones/solicitudesVinculacion"
class="btn btn-teal btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/vinculaciones/solicitudesVinculacion' ? 'active' : '' ?>">
Ver solicitudes
</a>
$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 position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-teal mb-3">Solicitudes de vinculación</h5>
<p class="text-muted small flex-grow-1 mb-4">Aprueba las solicitudes de vinculación de los importadores.</p>
<div class="mt-auto">
<button class="btn btn-teal btn-sm mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/vinculaciones/solicitudesVinculacion')"
data-url="/IMPORTADORES/vinculaciones/solicitudesVinculacion">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Ver solicitudes</span>
</button>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-lime">Usuarios vinculados</h5>
<p>Consulta los que ya fueron autorizados.</p>
<a href="/IMPORTADORES/vinculaciones/vinculacionesAgencia"
class="btn btn-lime btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/vinculaciones/vinculacionesAgencia' ? 'active' : '' ?>">
Ver usuarios
</a>
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
<div class="card shadow-sm p-3 card-hover position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-lime mb-3">Usuarios vinculados</h5>
<p class="text-muted small flex-grow-1 mb-4">Consulta los que ya fueron autorizados.</p>
<div class="mt-auto">
<button class="btn btn-lime btn-sm mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/vinculaciones/vinculacionesAgencia')"
data-url="/IMPORTADORES/vinculaciones/vinculacionesAgencia">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Ver usuarios</span>
</button>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-success">Alta de agentes</h5>
<p>Da de alta a tus agentes aduanales.</p>
<a href="/IMPORTADORES/agencias/alta"
class="btn btn-success btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agencias/alta' ? 'active' : '' ?>">
Nuevo agente
</a>
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
<div class="card shadow-sm p-3 card-hover position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-success mb-3">Patentes</h5>
<p class="text-muted small flex-grow-1 mb-4">Gestiona las patentes de la agencia.</p>
<div class="mt-auto">
<button class="btn btn-success btn-sm mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/patente/dashboard')"
data-url="/IMPORTADORES/patente/dashboard">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Ver agentes</span>
</button>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-info">Patentes</h5>
<p>Gestiona las patentes de la agencia.</p>
<a href="/IMPORTADORES/patente/dashboard"
class="btn btn-info btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/patente/dashboard' ? 'active' : '' ?>">
Ver agentes
</a>
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
<div class="card shadow-sm p-3 card-hover position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-info mb-3">Locaciones</h5>
<p class="text-muted small flex-grow-1 mb-4">Gestiona la locaciones validas para nuevos registros.</p>
<div class="mt-auto">
<button class="btn btn-info btn-sm mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/locaciones/lista')"
data-url="/IMPORTADORES/locaciones/lista">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Gestionar locaciones</span>
</button>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-cyan">Locaciones</h5>
<p>Gestiona la locaciones validas para nuevos registros.</p>
<a href="/IMPORTADORES/locaciones/lista"
class="btn btn-cyan btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/locaciones/lista' ? 'active' : '' ?>">
Gestionar locaciones
</a>
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
<div class="card shadow-sm p-3 card-hover position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-cyan mb-3">Alta de agentes</h5>
<p class="text-muted small flex-grow-1 mb-4">Da de alta a tus agentes aduanales.</p>
<div class="mt-auto">
<button class="btn btn-cyan btn-sm mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/agencias/alta')"
data-url="/IMPORTADORES/agencias/alta">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Nuevo agente</span>
</button>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-warning">Configuración</h5>
<p>Administra tus datos y preferencias.</p>
<a href="/IMPORTADORES/configuracion"
class="btn btn-warning btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agencias/configuracion' ? 'active' : '' ?>">
Configurar
</a>
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
<div class="card shadow-sm p-3 card-hover position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-warning mb-3">Configuración</h5>
<p class="text-muted small flex-grow-1 mb-4">Administra los datos de tu empresa y preferencias.</p>
<div class="mt-auto">
<button class="btn btn-warning btn-sm mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/configuracion')"
data-url="/IMPORTADORES/configuracion">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Ir a configuración</span>
</button>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-danger">Cerrar sesión</h5>
<p>Salir del sistema de forma segura.</p>
<a href="/IMPORTADORES/sistemas/logout" class="btn btn-danger btn-sm mt-2">Logout</a>
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
<div class="card shadow-sm p-3 card-hover position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-danger mb-3">Cerrar sesión</h5>
<p class="text-muted small flex-grow-1 mb-4">Salir del sistema de forma segura.</p>
<div class="mt-auto">
<button class="btn btn-danger btn-sm mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/sistemas/logout')"
data-url="/IMPORTADORES/sistemas/logout">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Cerrar sesión</span>
</button>
</div>
</div>
</div>
</div>
<!-- <?php // endforeach; ?> -->

View File

@@ -41,6 +41,9 @@ include __DIR__ . '/../partials/sidebar_agente.php';
.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 */
@@ -52,22 +55,39 @@ include __DIR__ . '/../partials/sidebar_agente.php';
}
.card { border-radius: 12px; }
/* Animaciones personalizadas */
.card-hover { transition: all 0.3s ease; }
.card-hover:hover { transform: translateY(-5px); box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; }
.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); }
}
.fade-in-up { animation: fadeInUp 0.6s ease-out; }
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
.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 {
@@ -90,60 +110,93 @@ include __DIR__ . '/../partials/sidebar_agente.php';
<!-- <?php // if ($catalogosData ['catalogos_activos']): ?> -->
<div class="row g-4">
<!-- <?php
// $delay = 0;
<?php
$delay = 0;
// foreach ($catalogosData['catalogos'] as $catalogo):
// $delay += 0.1; // Incrementar el retraso para cada catálogo
?> -->
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-teal">Importadores vinculados</h5>
<p>Consulta los que ya fueron autorizados.</p>
<a href="/IMPORTADORES/agentes/vinculados"
class="btn btn-teal btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/vinculados' ? 'active' : '' ?>">
Ver importadores
</a>
$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 position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-teal mb-3">Importadores vinculados</h5>
<p class="text-muted small flex-grow-1 mb-4">Consulta los que ya fueron autorizados.</p>
<div class="mt-auto">
<button class="btn btn-teal btn-sm mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/agentes/vinculados')"
data-url="/IMPORTADORES/agentes/vinculados">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Ver importadores</span>
</button>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-lime">Patentes</h5>
<p>Gestiona las patentes de la agencia.</p>
<a href="/IMPORTADORES/patente/dashboard"
class="btn btn-lime btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES//patente/dashboard' ? 'active' : '' ?>">
Ver solicitudes
</a>
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
<div class="card shadow-sm p-3 card-hover position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-lime mb-3">Patentes</h5>
<p class="text-muted small flex-grow-1 mb-4">Gestiona las patentes de la agencia.</p>
<div class="mt-auto">
<button class="btn btn-lime btn-sm mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/patente/dashboard')"
data-url="/IMPORTADORES/patente/dashboard">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Ver patentes</span>
</button>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-success">Locaciones</h5>
<p>Gestiona la locaciones validas para nuevos registros.</p>
<a href="/IMPORTADORES/locaciones/lista"
class="btn btn-success btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/locaciones/lista' ? 'active' : '' ?>">
Gestionar locaciones
</a>
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
<div class="card shadow-sm p-3 card-hover position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-success mb-3">Locaciones</h5>
<p class="text-muted small flex-grow-1 mb-4">Gestiona la locaciones validas para nuevos registros.</p>
<div class="mt-auto">
<button class="btn btn-success btn-sm mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/locaciones/lista')"
data-url="/IMPORTADORES/locaciones/lista">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Gestionar locaciones</span>
</button>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-warning">Configuración</h5>
<p>Administra tus datos y preferencias.</p>
<a href="/IMPORTADORES/configuracion"
class="btn btn-warning btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/configuracion' ? 'active' : '' ?>">
Configurar
</a>
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
<div class="card shadow-sm p-3 card-hover position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-warning mb-3">Configuración</h5>
<p class="text-muted small flex-grow-1 mb-4">Administra los datos de tu empresa y preferencias.</p>
<div class="mt-auto">
<button class="btn btn-warning btn-sm mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/configuracion')"
data-url="/IMPORTADORES/configuracion">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Ir a configuración</span>
</button>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-danger">Cerrar sesión</h5>
<p>Salir del sistema de forma segura.</p>
<a href="/IMPORTADORES/sistemas/logout" class="btn btn-danger btn-sm mt-2">Logout</a>
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
<div class="card shadow-sm p-3 card-hover position-relative h-100">
<div class="card-body p-0 d-flex flex-column">
<h5 class="text-danger mb-3">Cerrar sesión</h5>
<p class="text-muted small flex-grow-1 mb-4">Salir del sistema de forma segura.</p>
<div class="mt-auto">
<button class="btn btn-danger btn-sm mt-auto w-100 btn-animated"
onclick="navigateWithAnimation(this, '/IMPORTADORES/sistemas/logout')"
data-url="/IMPORTADORES/sistemas/logout">
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
<span class="btn-text">Cerrar sesión</span>
</button>
</div>
</div>
</div>
</div>
<!-- <?php // endforeach; ?> -->

View File

@@ -61,8 +61,718 @@
<body>
<div class="content">
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">⚙️ Automatizaciones</h4>
<h4 class="mb-4 animate__animated animate__fadeInDown title-glow">⚙️ Automatizaciones</h4>
<!-- Card de conexión WINSAAI -->
<div class="row mb-4 fade-in-up">
<div class="col-12">
<div class="card shadow-sm card-hover">
<div class="card-header bg-primary text-white d-flex align-items-center justify-content-between">
<div class="d-flex align-items-center">
<i class="fas fa-plug me-2"></i>
<h5 class="mb-0">Conexión con WINSAAI</h5>
</div>
<div id="connection_status_header">
<span class="badge bg-secondary">Verificando...</span>
</div>
</div>
<div class="card-body">
<!-- Estado cuando NO hay configuración -->
<div id="no_config_section">
<p class="text-muted mb-3">
<i class="fas fa-info-circle me-2"></i>
Configura la conexión con el sistema WINSAAI para importar automáticamente pedimentos y COVES.
</p>
<!-- Botón para conectar -->
<button class="btn btn-success btn-animated btn-pulse" type="button"
onclick="showConfigForm()">
<i class="fas fa-wifi me-2"></i>
Conectar con WINSAAI
</button>
</div>
<!-- Estado cuando SÍ hay configuración -->
<div id="existing_config_section" style="display: none;">
<div class="alert alert-success border-0" role="alert">
<div class="d-flex align-items-center mb-3">
<i class="fas fa-check-circle me-2 text-success fs-5"></i>
<h6 class="mb-0 fw-bold">¡Estás configurado para conectarte a WINSAAI!</h6>
</div>
<!-- Información de la configuración actual -->
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="d-flex align-items-center">
<i class="fas fa-server me-2 text-primary"></i>
<div>
<small class="text-muted d-block">Servidor</small>
<span id="current_server" class="fw-semibold">-</span>
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center">
<i class="fas fa-user me-2 text-info"></i>
<div>
<small class="text-muted d-block">Usuario</small>
<span id="current_user" class="fw-semibold">-</span>
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center">
<i class="fas fa-shield-alt me-2 text-warning"></i>
<div>
<small class="text-muted d-block">Protocolo</small>
<span id="current_protocol" class="fw-semibold">-</span>
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center">
<i class="fas fa-clock me-2 text-secondary"></i>
<div>
<small class="text-muted d-block">Última sincronización</small>
<span id="last_sync_display" class="fw-semibold">Nunca</span>
</div>
</div>
</div>
</div>
<!-- Opciones de sincronización activas -->
<div class="mb-3">
<small class="text-muted d-block mb-2">Datos configurados para sincronizar:</small>
<div class="d-flex gap-2">
<span id="sync_pedimentos_badge" class="badge bg-primary" style="display: none;">
<i class="fas fa-file-alt me-1"></i> Pedimentos
</span>
<span id="sync_coves_badge" class="badge bg-info" style="display: none;">
<i class="fas fa-ship me-1"></i> COVES
</span>
</div>
</div>
<!-- Botones de acción -->
<div class="d-flex gap-2 flex-wrap">
<button type="button" class="btn btn-outline-primary btn-animated btn-sm"
onclick="showConfigForm(true)">
<i class="fas fa-edit me-2"></i>
Editar Conexión
</button>
<button type="button" class="btn btn-outline-warning btn-animated btn-sm"
onclick="toggleConfigStatus()">
<i class="fas fa-pause me-2"></i>
<span id="toggle_status_text">Desactivar</span>
</button>
<button type="button" class="btn btn-outline-secondary btn-animated btn-sm"
onclick="testExistingConnection()">
<i class="fas fa-vial me-2"></i>
Probar Conexión
</button>
<button type="button" class="btn btn-outline-success btn-animated btn-sm"
onclick="syncData()">
<i class="fas fa-sync-alt me-2"></i>
Sincronizar Ahora
</button>
<button type="button" class="btn btn-outline-danger btn-animated btn-sm"
onclick="deleteConfig()">
<i class="fas fa-trash me-2"></i>
Eliminar
</button>
</div>
</div>
</div>
<!-- Formulario de configuración (inicialmente oculto) -->
<div class="collapse mt-4" id="winsaaiConfig">
<div class="card border-secondary">
<div class="card-header bg-light d-flex justify-content-between align-items-center">
<h6 class="mb-0">
<i class="fas fa-cog me-2"></i>
<span id="form_title">Configuración de Conexión API</span>
</h6>
<button type="button" class="btn btn-sm btn-outline-secondary"
onclick="hideConfigForm()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="card-body">
<form id="winsaaiForm">
<div class="row">
<!-- Dirección IP/DNS -->
<div class="col-md-6 mb-3">
<label for="winsaai_host" class="form-label">
<i class="fas fa-server me-1"></i>
Dirección IP / DNS
</label>
<input type="text" class="form-control" id="winsaai_host"
placeholder="192.168.1.100 o api.winsaai.com" required>
<div class="form-text">
Ingresa la dirección IP o nombre de dominio del servidor WINSAAI
</div>
</div>
<!-- Puerto -->
<div class="col-md-6 mb-3">
<label for="winsaai_port" class="form-label">
<i class="fas fa-network-wired me-1"></i>
Puerto
</label>
<input type="number" class="form-control" id="winsaai_port"
placeholder="8080" min="1" max="65535" required>
<div class="form-text">
Puerto del servicio API (ejemplo: 8080, 80, 443)
</div>
</div>
</div>
<div class="row">
<!-- Usuario -->
<div class="col-md-6 mb-3">
<label for="winsaai_usuario" class="form-label">
<i class="fas fa-user me-1"></i>
Usuario
</label>
<input type="text" class="form-control" id="winsaai_usuario"
placeholder="Tu usuario de WINSAAI" required>
<div class="form-text">
Nombre de usuario proporcionado por WINSAAI
</div>
</div>
<!-- Contraseña -->
<div class="col-md-6 mb-3">
<label for="winsaai_password" class="form-label">
<i class="fas fa-lock me-1"></i>
Contraseña
</label>
<input type="password" class="form-control" id="winsaai_password"
placeholder="Tu contraseña" required>
<div class="form-text">
Contraseña de acceso al sistema WINSAAI
</div>
</div>
</div>
<div class="row">
<!-- Protocolo -->
<div class="col-md-6 mb-3">
<label for="winsaai_protocol" class="form-label">
<i class="fas fa-shield-alt me-1"></i>
Protocolo
</label>
<select class="form-select" id="winsaai_protocol" required>
<option value="">Seleccionar protocolo</option>
<option value="http">HTTP</option>
<option value="https">HTTPS (Recomendado)</option>
</select>
</div>
<!-- Estado Actual -->
<div class="col-md-6 mb-3">
<label class="form-label">
<i class="fas fa-info-circle me-1"></i>
Estado Actual
</label>
<div id="connection_status_display" class="form-control-plaintext">
<span class="badge bg-secondary">No configurado</span>
</div>
</div>
</div>
<!-- Opciones de sincronización -->
<div class="row">
<div class="col-12 mb-3">
<label class="form-label">
<i class="fas fa-sync me-1"></i>
Datos a sincronizar
</label>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="sync_pedimentos" checked>
<label class="form-check-label" for="sync_pedimentos">
<i class="fas fa-file-alt me-1 text-primary"></i>
Pedimentos
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="sync_coves" checked>
<label class="form-check-label" for="sync_coves">
<i class="fas fa-ship me-1 text-info"></i>
COVES
</label>
</div>
</div>
</div>
<!-- Botones de acción -->
<div class="d-flex gap-2 flex-wrap">
<button type="button" class="btn btn-outline-secondary btn-animated"
onclick="testConnection()">
<i class="fas fa-vial me-2"></i>
Probar Conexión
</button>
<button type="submit" class="btn btn-primary btn-animated">
<i class="fas fa-save me-2"></i>
Guardar Configuración
</button>
<button type="button" class="btn btn-warning btn-animated"
onclick="syncData()">
<i class="fas fa-sync-alt me-2"></i>
Sincronizar Ahora
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Estado de conexión -->
<div id="connectionStatus" class="mt-3" style="display: none;">
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
<span id="statusMessage">Verificando conexión...</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Scripts para funcionalidad WINSAAI -->
<script>
let currentConfig = null;
// Cargar configuración al inicio
document.addEventListener('DOMContentLoaded', function() {
loadExistingConfig();
});
// Cargar configuración existente
async function loadExistingConfig() {
try {
const response = await fetch('/IMPORTADORES/winsaai/get_config');
const result = await response.json();
if (result.success && result.data) {
currentConfig = result.data;
showExistingConfig(result.data);
} else {
showNoConfig();
}
} catch (error) {
console.error('Error cargando configuración:', error);
showNoConfig();
}
}
// Mostrar estado cuando NO hay configuración
function showNoConfig() {
document.getElementById('no_config_section').style.display = 'block';
document.getElementById('existing_config_section').style.display = 'none';
document.getElementById('connection_status_header').innerHTML = '<span class="badge bg-secondary">Sin configurar</span>';
}
// Mostrar estado cuando SÍ hay configuración
function showExistingConfig(config) {
// Ocultar sección de "no config" y mostrar sección existente
document.getElementById('no_config_section').style.display = 'none';
document.getElementById('existing_config_section').style.display = 'block';
// Actualizar información mostrada
document.getElementById('current_server').textContent = `${config.protocol}://${config.host}:${config.port}`;
document.getElementById('current_user').textContent = config.usuario;
document.getElementById('current_protocol').textContent = config.protocol.toUpperCase();
// Mostrar última sincronización
if (config.last_sync) {
const lastSyncDate = new Date(config.last_sync);
document.getElementById('last_sync_display').textContent = lastSyncDate.toLocaleString();
} else {
document.getElementById('last_sync_display').textContent = 'Nunca';
}
// Mostrar badges de sincronización
const pedimentosBadge = document.getElementById('sync_pedimentos_badge');
const covesBadge = document.getElementById('sync_coves_badge');
if (config.sync_pedimentos) {
pedimentosBadge.style.display = 'inline-block';
} else {
pedimentosBadge.style.display = 'none';
}
if (config.sync_coves) {
covesBadge.style.display = 'inline-block';
} else {
covesBadge.style.display = 'none';
}
// Actualizar header según estado
updateHeaderStatus(config.status);
// Actualizar botón de toggle según estado
updateToggleButton(config.status);
// Llenar formulario con datos existentes para edición
document.getElementById('winsaai_host').value = config.host || '';
document.getElementById('winsaai_port').value = config.port || '';
document.getElementById('winsaai_protocol').value = config.protocol || '';
document.getElementById('winsaai_usuario').value = config.usuario || '';
document.getElementById('sync_pedimentos').checked = config.sync_pedimentos == 1;
document.getElementById('sync_coves').checked = config.sync_coves == 1;
}
// Actualizar estado en el header
function updateHeaderStatus(status) {
const headerStatus = document.getElementById('connection_status_header');
let badgeHtml = '';
switch(status) {
case 'activo':
badgeHtml = '<span class="badge bg-success"><i class="fas fa-check-circle me-1"></i>Conectado</span>';
break;
case 'inactivo':
badgeHtml = '<span class="badge bg-warning"><i class="fas fa-pause me-1"></i>Inactivo</span>';
break;
case 'error':
badgeHtml = '<span class="badge bg-danger"><i class="fas fa-exclamation-triangle me-1"></i>Error</span>';
break;
default:
badgeHtml = '<span class="badge bg-secondary">Sin configurar</span>';
}
headerStatus.innerHTML = badgeHtml;
}
// Actualizar botón de toggle según estado
function updateToggleButton(status) {
const toggleBtn = document.querySelector('button[onclick="toggleConfigStatus()"]');
const toggleText = document.getElementById('toggle_status_text');
const icon = toggleBtn.querySelector('i');
if (status === 'activo') {
toggleText.textContent = 'Desactivar';
icon.className = 'fas fa-pause me-2';
toggleBtn.className = 'btn btn-outline-warning btn-animated btn-sm';
} else {
toggleText.textContent = 'Activar';
icon.className = 'fas fa-play me-2';
toggleBtn.className = 'btn btn-outline-success btn-animated btn-sm';
}
}
// Mostrar formulario de configuración
function showConfigForm(isEdit = false) {
const formTitle = document.getElementById('form_title');
const configCollapse = new bootstrap.Collapse(document.getElementById('winsaaiConfig'), { show: true });
if (isEdit) {
formTitle.textContent = 'Editar Configuración de WINSAAI';
} else {
formTitle.textContent = 'Nueva Configuración de WINSAAI';
// Limpiar formulario para nueva configuración
document.getElementById('winsaaiForm').reset();
}
}
// Ocultar formulario de configuración
function hideConfigForm() {
const configCollapse = new bootstrap.Collapse(document.getElementById('winsaaiConfig'), { hide: true });
}
// Probar conexión con configuración existente
async function testExistingConnection() {
if (!currentConfig) {
showStatus('error', 'No hay configuración para probar');
return;
}
showStatus('info', 'Probando conexión con configuración actual...');
try {
const response = await fetch('/IMPORTADORES/winsaai/test_connection', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
host: currentConfig.host,
port: currentConfig.port,
protocol: currentConfig.protocol,
usuario: currentConfig.usuario,
password: 'existing_config' // Indicador para usar configuración guardada
})
});
const result = await response.json();
if (result.success) {
showStatus('success', result.message);
updateHeaderStatus('activo');
} else {
showStatus('error', result.message);
updateHeaderStatus('error');
}
} catch (error) {
showStatus('error', 'Error de red: ' + error.message);
updateHeaderStatus('error');
}
}
// Activar/Desactivar configuración
async function toggleConfigStatus() {
if (!currentConfig) {
showStatus('error', 'No hay configuración para modificar');
return;
}
const currentStatus = currentConfig.status;
const newStatus = currentStatus === 'activo' ? 'inactivo' : 'activo';
const action = newStatus === 'activo' ? 'activar' : 'desactivar';
if (confirm(`¿Estás seguro de que deseas ${action} la configuración de WINSAAI?`)) {
showStatus('info', `${action === 'activar' ? 'Activando' : 'Desactivando'} configuración...`);
try {
// Aquí harías la llamada al servidor para cambiar el estado
// Por ahora simularemos el cambio
currentConfig.status = newStatus;
updateHeaderStatus(newStatus);
updateToggleButton(newStatus);
showStatus('success', `Configuración ${action === 'activar' ? 'activada' : 'desactivada'} correctamente`);
} catch (error) {
showStatus('error', `Error al ${action} configuración: ` + error.message);
}
}
}
// Eliminar configuración
async function deleteConfig() {
if (!currentConfig) {
showStatus('error', 'No hay configuración para eliminar');
return;
}
if (confirm('¿Estás seguro de que deseas eliminar completamente la configuración de WINSAAI? Esta acción no se puede deshacer.')) {
showStatus('info', 'Eliminando configuración...');
try {
// Aquí harías la llamada al servidor para eliminar
// Por ahora simularemos la eliminación
currentConfig = null;
showNoConfig();
hideConfigForm();
showStatus('success', 'Configuración eliminada correctamente');
} catch (error) {
showStatus('error', 'Error al eliminar configuración: ' + error.message);
}
}
}
// Función para probar la conexión (desde formulario)
async function testConnection() {
const host = document.getElementById('winsaai_host').value;
const port = document.getElementById('winsaai_port').value;
const protocol = document.getElementById('winsaai_protocol').value;
const usuario = document.getElementById('winsaai_usuario').value;
const password = document.getElementById('winsaai_password').value;
if (!host || !port || !protocol || !usuario || !password) {
showStatus('error', 'Por favor completa todos los campos obligatorios');
return;
}
showStatus('info', 'Probando conexión con WINSAAI...');
try {
const response = await fetch('/IMPORTADORES/winsaai/test_connection', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
host: host,
port: parseInt(port),
protocol: protocol,
usuario: usuario,
password: password
})
});
const result = await response.json();
if (result.success) {
showStatus('success', result.message);
} else {
showStatus('error', result.message);
}
} catch (error) {
showStatus('error', 'Error de red: ' + error.message);
}
}
// Función para sincronizar datos
async function syncData() {
let pedimentos, coves;
if (currentConfig) {
// Usar configuración existente
pedimentos = currentConfig.sync_pedimentos;
coves = currentConfig.sync_coves;
} else {
// Usar valores del formulario
pedimentos = document.getElementById('sync_pedimentos').checked;
coves = document.getElementById('sync_coves').checked;
}
if (!pedimentos && !coves) {
showStatus('warning', 'No hay datos configurados para sincronizar');
return;
}
showStatus('info', 'Iniciando sincronización de datos...');
let syncType = 'both';
if (pedimentos && !coves) syncType = 'pedimentos';
else if (!pedimentos && coves) syncType = 'coves';
try {
const response = await fetch('/IMPORTADORES/winsaai/sync_data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
sync_type: syncType
})
});
const result = await response.json();
if (result.success) {
let message = 'Sincronización completada: ';
if (result.data.pedimentos) {
message += `${result.data.pedimentos.processed} pedimentos `;
}
if (result.data.coves) {
message += `${result.data.coves.processed} COVES `;
}
showStatus('success', message);
// Actualizar última sincronización si hay configuración
if (currentConfig) {
currentConfig.last_sync = new Date().toISOString();
document.getElementById('last_sync_display').textContent = new Date().toLocaleString();
}
} else {
showStatus('error', result.message);
}
} catch (error) {
showStatus('error', 'Error de red: ' + error.message);
}
}
// Función para mostrar estado
function showStatus(type, message) {
const statusDiv = document.getElementById('connectionStatus');
const statusMessage = document.getElementById('statusMessage');
const alertDiv = statusDiv.querySelector('.alert');
// Remover clases anteriores
alertDiv.className = 'alert';
// Agregar nueva clase según el tipo
switch(type) {
case 'success':
alertDiv.classList.add('alert-success');
statusMessage.innerHTML = `<i class="fas fa-check-circle me-2"></i>${message}`;
break;
case 'error':
alertDiv.classList.add('alert-danger');
statusMessage.innerHTML = `<i class="fas fa-exclamation-circle me-2"></i>${message}`;
break;
case 'warning':
alertDiv.classList.add('alert-warning');
statusMessage.innerHTML = `<i class="fas fa-exclamation-triangle me-2"></i>${message}`;
break;
default:
alertDiv.classList.add('alert-info');
statusMessage.innerHTML = `<i class="fas fa-info-circle me-2"></i>${message}`;
}
statusDiv.style.display = 'block';
// Auto-ocultar después de 5 segundos para mensajes de éxito
if (type === 'success') {
setTimeout(() => {
statusDiv.style.display = 'none';
}, 5000);
}
}
// Manejar envío del formulario
document.getElementById('winsaaiForm').addEventListener('submit', async function(e) {
e.preventDefault();
const config = {
host: document.getElementById('winsaai_host').value,
port: parseInt(document.getElementById('winsaai_port').value),
protocol: document.getElementById('winsaai_protocol').value,
usuario: document.getElementById('winsaai_usuario').value,
password: document.getElementById('winsaai_password').value,
sync_pedimentos: document.getElementById('sync_pedimentos').checked,
sync_coves: document.getElementById('sync_coves').checked
};
// Validar campos obligatorios
if (!config.host || !config.port || !config.protocol || !config.usuario || !config.password) {
showStatus('error', 'Por favor completa todos los campos obligatorios');
return;
}
showStatus('info', 'Guardando configuración...');
try {
const response = await fetch('/IMPORTADORES/winsaai/save_config', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(config)
});
const result = await response.json();
if (result.success) {
showStatus('success', result.message);
// Recargar configuración para mostrar el nuevo estado
await loadExistingConfig();
hideConfigForm();
} else {
showStatus('error', result.message);
}
} catch (error) {
console.error('Error completo:', error);
showStatus('error', 'Error de red: ' + error.message);
}
});
// Animación suave para el collapse
document.getElementById('winsaaiConfig').addEventListener('show.bs.collapse', function() {
this.style.opacity = '0';
this.style.transform = 'translateY(-20px)';
setTimeout(() => {
this.style.transition = 'all 0.3s ease';
this.style.opacity = '1';
this.style.transform = 'translateY(0)';
}, 50);
});
</script>
</body>
</html>

View File

@@ -67,7 +67,7 @@
<div class="content">
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">🏪 Registro de Agencias</h4>
<div class="card p-3 shadow-sm card-hover position-relative h-auto">
<div class="card p-3 shadow-sm card-hover position-relative h-auto fade-in-up">
<div class="table-responsive">
<table class="table table-striped" id="tabla-registros-agencias">
<thead class="table-dark">

View File

@@ -67,7 +67,7 @@
<div class="content">
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">🔄 Cambios de Usuario</h4>
<div class="card p-3 shadow-sm card-hover position-relative h-auto">
<div class="card p-3 shadow-sm card-hover position-relative h-auto fade-in-up">
<div class="table-responsive">
<table class="table table-striped align-middle" id="tabla-bitacora-usuarios">
<thead class="table-dark">

View File

@@ -67,7 +67,7 @@
<div class="content">
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">👥 Acceso de Usuarios</h4>
<div class="card p-3 shadow-sm card-hover position-relative h-auto">
<div class="card p-3 shadow-sm card-hover position-relative h-auto fade-in-up">
<div class="table-responsive">
<table class="table table-striped" id="tabla-logins">
<thead class="table-dark">

Some files were not shown because too many files have changed in this diff Show More