Compare commits

...

10 Commits

Author SHA1 Message Date
040af3a802 merge: integrar feature BOM, reintegro y diagnostico paso 12 (upstream)
Merge de upstream/main (commit c80f6c7):
- Funciones BOM: UNIMED_BOM, VALORIVAMN en queries
- generar_plantilla_reintegro + widgets UI reintegro (WIP)
- Diagnostico Paso 12 (% KGS)
- Manual_Reintegro.docx
- tab_bom nuevo

Fix conflictos de nombres:
- btn_frac_um / bar_frac_um para evitar colision con analytics frac

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 15:42:10 -05:00
Ernesto Herrera
c80f6c75a4 feature /bom y descagras % kgs 2026-06-02 08:23:38 -06:00
7631909606 feat: generador de Manual de Instalacion (docx)
Script Python que genera Manual_Instalacion.docx con:
- Portada y requisitos
- Instalacion paso a paso (ejecutar como administrador)
- Configuracion .env
- Primer uso y FAQ

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 09:12:13 -05:00
2670771aa2 merge: integrar features %incremento, analiticas saldos y descarga fraccion+UM
Merge de origin/main (commits c450cfa, e77813a):
- feature/Descargas por % de incremento de peso o cantidad
- feature/analiticas de saldos
- Manual_Descargas.docx

Mas cambios locales:
- Nueva funcion descarga por fraccion + UM (8 digitos sin BOM)
- _saldos_idx_fraccion, _consumir_fraccion, _build_cobertura_fraccion
- _build_descarga_fraccion_df, calcular_pronostico_fraccion_um,
  ejecutar_fraccion_um (modos NATURAL/DIRIGIDA, DRY_RUN)
- UI Frac/UM agregada al tab_desc
- DISTINCT en encabezado_facturas_ds y cat_pedimentos_ds

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 09:11:30 -05:00
Ernesto Herrera
e77813a45c feature/analiticas de saldos 2026-05-27 06:58:50 -06:00
Ernesto Herrera
c450cfa150 feature/Descargas por % de incremento de peso o cantidad 2026-05-25 08:20:43 -06:00
4c85daf0f2 feat: fixes estructuras SCAII partidas y encabezado (requerimiento Marisol)
- Fracción arancelaria 10 dígitos (Fraccion+SubdivisionFraccion)
- LINEA = SecuenciaFraccion del R551
- Peso neto/bruto calculado desde PesoBrutoMercancia R501 con distribución proporcional
- UM mapeada al catálogo SCAII (22 claves)
- NUMERO FACTURA formato SS-PPPP-NNNNNNN en partidas y encabezado
- NOT EXISTS en encabezado para excluir pedimentos rectificados (R701)
- DISTINCT en encabezado_facturas_ds y cat_pedimentos_ds para evitar duplicados

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 17:48:51 -05:00
71ef0b390a feat: empaquetado .exe con instalador y fix conexion SCAII
- launcher.py reescrito para PyInstaller (Voila in-process, kernel router)
- build_exe.bat: script PyInstaller (onedir + collect-all dependencias Jupyter)
- build_installer.bat + installer.iss: instalador Inno Setup
- schema_registro_sqlite.sql: schema DataStage en SQLite
- _make_conn_str ahora respeta SCAII_TRUSTED (Windows Auth)
- .env.example: quitar variables DB_* (Postgres ya no usado por DataStage)
- requirements.txt: quitar psycopg2-binary

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 15:57:25 -05:00
Ernesto Herrera
a65f34d852 fix: ajustes parametros tecma 2026-05-22 12:43:21 -06:00
Ernesto Herrera
1afe23182f fix: titulos y descarga%kgs 2026-05-21 09:09:17 -06:00
11 changed files with 24303 additions and 4770 deletions

View File

@@ -1,12 +1,8 @@
# Copia este archivo como .env y completa los valores reales. # Copia este archivo como .env y completa los valores reales.
# NO subas el .env al repositorio (esta en .gitignore). # NO subas el .env al repositorio (esta en .gitignore).
# Configuracion de conexion a PostgreSQL (DataStage) # DataStage corre embebido en SQLite (archivo datastage.db junto al .exe).
DB_HOST=localhost # No requiere configuracion. Para cambiar la ruta usa: DATASTAGE_DB=ruta\completa\datastage.db
DB_PORT=5432
DB_NAME=dbSat
DB_USER=servicemanager
DB_PASSWORD=cambia_esto
# Configuracion de conexion a SQL Server (SCAII) # Configuracion de conexion a SQL Server (SCAII)
# SCAII_TRUSTED=yes -> usa Windows Authentication (ignora SCAII_USER/PASSWORD) # SCAII_TRUSTED=yes -> usa Windows Authentication (ignora SCAII_USER/PASSWORD)

BIN
app/Manual_Descargas.docx Normal file

Binary file not shown.

BIN
app/Manual_Reintegro.docx Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

131
app/build_exe.bat Executable file
View File

@@ -0,0 +1,131 @@
@echo off
REM ============================================================
REM Build .exe de Utilerias RECON 2 con PyInstaller (modo onedir)
REM Usa un conda env limpio para empaquetado reproducible.
REM ============================================================
setlocal
cd /d "%~dp0"
set ENV_NAME=utilerias-recon-build
set PY_VERSION=3.11
REM ------------------------------------------------------------
REM 1) Verificar conda
REM ------------------------------------------------------------
where conda >nul 2>nul
if errorlevel 1 (
echo [ERROR] Conda no esta en PATH. Abre "Anaconda Prompt" y vuelve a ejecutar.
goto :end
)
REM ------------------------------------------------------------
REM 2) Crear env si no existe
REM ------------------------------------------------------------
call conda env list | findstr /B "%ENV_NAME% " >nul
if errorlevel 1 (
echo [INFO] Creando env conda "%ENV_NAME%" con Python %PY_VERSION%...
call conda create -y -n %ENV_NAME% python=%PY_VERSION%
if errorlevel 1 (
echo [ERROR] No se pudo crear el env.
goto :end
)
)
REM ------------------------------------------------------------
REM 3) Activar env e instalar dependencias
REM ------------------------------------------------------------
call conda activate %ENV_NAME%
if errorlevel 1 (
echo [ERROR] No se pudo activar el env.
goto :end
)
echo [INFO] Instalando dependencias...
call pip install --upgrade pip
call pip install -r requirements.txt
if errorlevel 1 (
echo [ERROR] Fallo la instalacion de dependencias.
goto :end
)
REM ------------------------------------------------------------
REM 4) Limpiar builds previos
REM ------------------------------------------------------------
if exist build rmdir /s /q build
if exist dist rmdir /s /q dist
if exist UtileriasReconV2.spec del /q UtileriasReconV2.spec
REM ------------------------------------------------------------
REM 5) Ejecutar PyInstaller (onedir, noconsole)
REM ------------------------------------------------------------
echo [INFO] Construyendo .exe (esto tarda 3-8 minutos)...
pyinstaller launcher.py ^
--name UtileriasReconV2 ^
--onedir ^
--noconsole ^
--noconfirm ^
--collect-all voila ^
--collect-all jupyter_server ^
--collect-all jupyter_client ^
--collect-all jupyter_core ^
--collect-all nbformat ^
--collect-all nbconvert ^
--collect-all ipykernel ^
--collect-all ipywidgets ^
--collect-all jupyterlab_pygments ^
--collect-all notebook ^
--collect-all rfc3987_syntax ^
--collect-all jsonschema_specifications ^
--collect-all jsonschema ^
--collect-all jupyter_events ^
--collect-all nbclient ^
--collect-all terminado ^
--collect-all debugpy ^
--collect-all matplotlib_inline ^
--collect-all matplotlib ^
--collect-all dotenv ^
--collect-all sklearn ^
--collect-all scipy ^
--collect-all pandas ^
--collect-all numpy ^
--collect-all sqlalchemy ^
--collect-all openpyxl ^
--hidden-import pyodbc ^
--hidden-import sqlalchemy.dialects.sqlite ^
--hidden-import sqlalchemy.dialects.mssql ^
--hidden-import sklearn.utils._typedefs ^
--hidden-import sklearn.neighbors._partition_nodes
if errorlevel 1 (
echo.
echo [ERROR] PyInstaller fallo. Revisa el log arriba.
goto :end
)
REM ------------------------------------------------------------
REM 6) Copiar app.ipynb y .env.example al lado del .exe
REM ------------------------------------------------------------
copy /Y app.ipynb dist\UtileriasReconV2\app.ipynb >nul
echo [INFO] app.ipynb copiado a dist\UtileriasReconV2\
if exist ..\.env.example (
copy /Y ..\.env.example dist\UtileriasReconV2\.env.example >nul
echo [INFO] .env.example copiado a dist\UtileriasReconV2\
)
echo.
echo ============================================================
echo BUILD EXITOSO (--onedir)
echo Carpeta lista: dist\UtileriasReconV2\
echo Ejecutable: dist\UtileriasReconV2\UtileriasReconV2.exe
echo.
echo Siguiente paso: empaquetar como instalador con Inno Setup
echo .\build_installer.bat
echo ============================================================
:end
echo.
pause
endlocal

72
app/build_installer.bat Executable file
View File

@@ -0,0 +1,72 @@
@echo off
REM ============================================================
REM Build completo: PyInstaller (onedir) + Inno Setup
REM Genera installer_output\UtileriasReconV2_Setup_X.X.X.exe
REM ============================================================
setlocal
cd /d "%~dp0"
REM ------------------------------------------------------------
REM 1) Localizar el compilador de Inno Setup (ISCC.exe)
REM ------------------------------------------------------------
set "ISCC="
if exist "%ProgramFiles(x86)%\Inno Setup 6\ISCC.exe" set "ISCC=%ProgramFiles(x86)%\Inno Setup 6\ISCC.exe"
if exist "%ProgramFiles%\Inno Setup 6\ISCC.exe" set "ISCC=%ProgramFiles%\Inno Setup 6\ISCC.exe"
if "%ISCC%"=="" (
echo [ERROR] No se encontro Inno Setup 6.
echo Descargalo de: https://jrsoftware.org/isdl.php
echo Instala la version "Stable Release", luego vuelve a ejecutar este script.
goto :end
)
echo [INFO] Inno Setup encontrado en: %ISCC%
REM ------------------------------------------------------------
REM 2) Construir el .exe con PyInstaller (onedir)
REM ------------------------------------------------------------
echo.
echo [INFO] Paso 1/2: PyInstaller...
call build_exe.bat
if errorlevel 1 (
echo [ERROR] PyInstaller fallo. Revisa el log arriba.
goto :end
)
if not exist "dist\UtileriasReconV2\UtileriasReconV2.exe" (
echo [ERROR] No se genero dist\UtileriasReconV2\UtileriasReconV2.exe
goto :end
)
REM ------------------------------------------------------------
REM 3) Limpiar build previo del instalador
REM ------------------------------------------------------------
if exist installer_output rmdir /s /q installer_output
REM ------------------------------------------------------------
REM 4) Compilar el instalador
REM ------------------------------------------------------------
echo.
echo [INFO] Paso 2/2: Inno Setup...
"%ISCC%" installer.iss
if errorlevel 1 (
echo [ERROR] Inno Setup fallo.
goto :end
)
echo.
echo ============================================================
echo INSTALADOR LISTO
echo Archivo: installer_output\UtileriasReconV2_Setup_1.0.0.exe
echo.
echo Distribuye SOLO ese .exe. El usuario:
echo 1. Ejecuta el instalador (1 vez, ~30 seg)
echo 2. Edita .env desde el menu Inicio (acceso directo)
echo 3. Doble clic en el icono del escritorio
echo 4. La app arranca en ~3 seg (sin re-extraer)
echo ============================================================
:end
echo.
pause
endlocal

429
app/generar_manual_instalacion.py Executable file
View File

@@ -0,0 +1,429 @@
"""
Genera el manual de instalación y primeros pasos de Utilerías RECON 2.
"""
from docx import Document
from docx.shared import Pt, RGBColor, Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
import datetime, os
doc = Document()
for sec in doc.sections:
sec.top_margin = Cm(2.5)
sec.bottom_margin = Cm(2.5)
sec.left_margin = Cm(2.2)
sec.right_margin = Cm(2.2)
style = doc.styles['Normal']
style.font.name = 'Calibri'
style.font.size = Pt(11)
# ── Helpers ──────────────────────────────────────────────────────────────────
def h1(text):
p = doc.add_heading(text, level=1)
for r in p.runs: r.font.color.rgb = RGBColor(0x15, 0x65, 0xC0)
def h2(text):
p = doc.add_heading(text, level=2)
for r in p.runs: r.font.color.rgb = RGBColor(0x1E, 0x88, 0xE5)
def h3(text):
p = doc.add_heading(text, level=3)
for r in p.runs: r.font.color.rgb = RGBColor(0x42, 0xA5, 0xF5)
def parr(text, bold=False):
p = doc.add_paragraph()
r = p.add_run(text)
r.bold = bold
return p
def code_block(text):
p = doc.add_paragraph()
p.paragraph_format.left_indent = Cm(0.4)
r = p.add_run(text)
r.font.name = 'Consolas'
r.font.size = Pt(9.5)
pPr = p._p.get_or_add_pPr()
shd = OxmlElement('w:shd')
shd.set(qn('w:val'), 'clear')
shd.set(qn('w:color'), 'auto')
shd.set(qn('w:fill'), 'F0F0F0')
pPr.append(shd)
return p
def bullet(text, level=0):
p = doc.add_paragraph(text, style='List Bullet')
p.paragraph_format.left_indent = Cm(0.6 + level * 0.6)
return p
def numbered(text):
p = doc.add_paragraph(text, style='List Number')
p.paragraph_format.left_indent = Cm(0.6)
return p
def nota(titulo, text):
p = doc.add_paragraph()
p.paragraph_format.left_indent = Cm(0.4)
pPr = p._p.get_or_add_pPr()
shd = OxmlElement('w:shd')
shd.set(qn('w:val'), 'clear'); shd.set(qn('w:color'), 'auto')
shd.set(qn('w:fill'), 'FFF8E1')
pPr.append(shd)
r = p.add_run(titulo + ': '); r.bold = True
r.font.color.rgb = RGBColor(0xE6, 0x5C, 0x00)
p.add_run(text)
def alerta(titulo, text):
p = doc.add_paragraph()
p.paragraph_format.left_indent = Cm(0.4)
pPr = p._p.get_or_add_pPr()
shd = OxmlElement('w:shd')
shd.set(qn('w:val'), 'clear'); shd.set(qn('w:color'), 'auto')
shd.set(qn('w:fill'), 'FFEBEE')
pPr.append(shd)
r = p.add_run(titulo + ': '); r.bold = True
r.font.color.rgb = RGBColor(0xC6, 0x28, 0x28)
p.add_run(text)
def tip(titulo, text):
p = doc.add_paragraph()
p.paragraph_format.left_indent = Cm(0.4)
pPr = p._p.get_or_add_pPr()
shd = OxmlElement('w:shd')
shd.set(qn('w:val'), 'clear'); shd.set(qn('w:color'), 'auto')
shd.set(qn('w:fill'), 'E8F5E9')
pPr.append(shd)
r = p.add_run(titulo + ': '); r.bold = True
r.font.color.rgb = RGBColor(0x2E, 0x7D, 0x32)
p.add_run(text)
def table_simple(headers, rows, col_widths_cm=None):
t = doc.add_table(rows=1 + len(rows), cols=len(headers))
t.style = 'Light Grid Accent 1'
hdr = t.rows[0].cells
for i, h in enumerate(headers):
hdr[i].text = h
for p in hdr[i].paragraphs:
for r in p.runs:
r.bold = True
r.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
tcPr = hdr[i]._tc.get_or_add_tcPr()
shd = OxmlElement('w:shd')
shd.set(qn('w:val'), 'clear')
shd.set(qn('w:fill'), '1565C0')
tcPr.append(shd)
for r_idx, row in enumerate(rows, start=1):
cells = t.rows[r_idx].cells
for c_idx, val in enumerate(row):
cells[c_idx].text = str(val)
if col_widths_cm:
for i, w in enumerate(col_widths_cm):
for row in t.rows:
row.cells[i].width = Cm(w)
return t
# ── PORTADA ───────────────────────────────────────────────────────────────────
title = doc.add_paragraph()
title.alignment = WD_ALIGN_PARAGRAPH.CENTER
r = title.add_run('\n\n\nSistema de Utilerías RECON 2')
r.bold = True; r.font.size = Pt(34); r.font.color.rgb = RGBColor(0x15, 0x65, 0xC0)
sub = doc.add_paragraph()
sub.alignment = WD_ALIGN_PARAGRAPH.CENTER
r = sub.add_run('Guía de Instalación y Primeros Pasos')
r.font.size = Pt(20); r.font.color.rgb = RGBColor(0x42, 0xA5, 0xF5)
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
r = p.add_run(f'\n\nVersión {datetime.date.today().isoformat()}')
r.font.size = Pt(12); r.italic = True
doc.add_page_break()
# ── ÍNDICE ────────────────────────────────────────────────────────────────────
h1('Contenido')
for item in [
'1. Requisitos previos',
'2. Instalación paso a paso',
'3. Configuración inicial (.env)',
'4. Primer uso de la aplicación',
'5. Preguntas frecuentes',
]:
doc.add_paragraph(item)
doc.add_page_break()
# ── 1. REQUISITOS ─────────────────────────────────────────────────────────────
h1('1. Requisitos previos')
parr('Antes de instalar, verifica que la computadora cumple con lo siguiente:')
table_simple(
['Requisito', 'Detalle'],
[
['Sistema operativo', 'Windows 10 / Windows 11 (64 bits)'],
['Permisos', 'Cuenta con permisos de Administrador local'],
['ODBC Driver', 'Microsoft ODBC Driver 17 o 18 for SQL Server'],
['Acceso a red', 'Conexión al servidor donde está la base de datos SCAII'],
['Espacio en disco', 'Mínimo 500 MB libres en la unidad de instalación'],
],
col_widths_cm=[4.5, 11]
)
doc.add_paragraph()
alerta(
'IMPORTANTE — Permisos de Administrador',
'La aplicación DEBE instalarse y ejecutarse como Administrador. '
'Sin este permiso, el instalador no podrá registrar los componentes necesarios '
'y la aplicación fallará al intentar conectarse a la base de datos.'
)
doc.add_paragraph()
h3('Cómo verificar si tienes el ODBC Driver instalado')
numbered('Presiona Windows + R, escribe odbcad32 y presiona Enter')
numbered('En la pestaña "Controladores" busca "ODBC Driver 17 for SQL Server" o "ODBC Driver 18 for SQL Server"')
numbered('Si no aparece, solicita al área de sistemas que lo instale antes de continuar')
# ── 2. INSTALACIÓN ────────────────────────────────────────────────────────────
doc.add_page_break()
h1('2. Instalación paso a paso')
alerta(
'Ejecutar como Administrador',
'Todos los pasos de esta sección deben realizarse con permisos de Administrador. '
'Si no lo haces, la instalación puede completarse pero la aplicación no funcionará correctamente.'
)
doc.add_paragraph()
h2('Paso 1 — Obtener el instalador')
parr('El archivo instalador tiene un nombre similar a:')
code_block('UtileriasReconV2_Setup.exe')
parr('Solicítalo al área técnica o descárgalo de la ubicación que te indiquen.')
doc.add_paragraph()
h2('Paso 2 — Ejecutar el instalador como Administrador')
numbered('Localiza el archivo UtileriasReconV2_Setup.exe en tu computadora')
numbered('Haz clic derecho sobre el archivo')
numbered('Selecciona "Ejecutar como administrador"')
numbered('Si Windows muestra una ventana de Control de Cuentas de Usuario (UAC), haz clic en ""')
doc.add_paragraph()
nota(
'Clic derecho → Ejecutar como administrador',
'Es fundamental este paso. Si haces doble clic normal y tu cuenta no es administrador, '
'la instalación puede parecer exitosa pero la app fallará al abrir.'
)
doc.add_paragraph()
h2('Paso 3 — Seguir el asistente de instalación')
numbered('Se abre el asistente de instalación. Haz clic en "Siguiente"')
numbered('Lee y acepta los términos de uso, luego haz clic en "Siguiente"')
numbered('Elige la carpeta de instalación (se recomienda dejar la carpeta predeterminada)')
code_block('C:\\Program Files\\UtileriasReconV2\\')
numbered('Haz clic en "Instalar" y espera a que termine el proceso (tarda aproximadamente 1-2 minutos)')
numbered('Al terminar, haz clic en "Finalizar"')
tip(
'Carpeta de instalación',
'Si instalas en una ruta diferente a la predeterminada, anota la ruta. '
'La necesitarás para el paso de configuración.'
)
doc.add_paragraph()
h2('Paso 4 — Verificar la instalación')
parr('Al finalizar, deberías ver en la carpeta de instalación los siguientes archivos principales:')
table_simple(
['Archivo / Carpeta', 'Descripción'],
[
['UtileriasReconV2.exe', 'El ejecutable principal de la aplicación'],
['app.ipynb', 'El notebook con toda la lógica de la app'],
['.env.example', 'Plantilla de configuración — debes renombrarlo a .env'],
['_internal/', 'Carpeta con dependencias internas (no modificar)'],
],
col_widths_cm=[5, 10]
)
# ── 3. CONFIGURACIÓN ─────────────────────────────────────────────────────────
doc.add_page_break()
h1('3. Configuración inicial (.env)')
parr(
'La aplicación usa un archivo llamado .env para saber a qué servidor conectarse '
'y con qué credenciales. Este archivo NO viene incluido en el instalador por seguridad — '
'debes crearlo tú a partir de la plantilla.'
)
doc.add_paragraph()
h2('Crear el archivo .env')
numbered('Ve a la carpeta de instalación')
numbered('Localiza el archivo .env.example')
numbered('Copia ese archivo y renombra la copia como .env (sin el .example)')
numbered('Abre el archivo .env con el Bloc de notas o cualquier editor de texto')
numbered('Modifica los valores según los datos de tu servidor:')
doc.add_paragraph()
code_block(
'# Servidor SCAII (SQL Server)\n'
'SCAII_SERVER=nombre-o-ip-de-tu-servidor\n'
'SCAII_USER=tu_usuario\n'
'SCAII_PASSWORD=tu_contraseña\n'
'\n'
'# Autenticación Windows (opcional)\n'
'# Si tu servidor usa Windows Auth en lugar de usuario/contraseña,\n'
'# cambia el valor a "yes"\n'
'SCAII_TRUSTED=no\n'
'\n'
'# Ruta donde están los archivos .asc del DataStage\n'
'DATASTAGE_PATH=C:\\ruta\\a\\tus\\archivos\\asc\n'
)
nota(
'Archivo sensible',
'El archivo .env contiene contraseñas. No lo compartas por correo ni lo subas a ningún '
'repositorio. Mantenlo solo en la computadora de trabajo.'
)
doc.add_paragraph()
h2('Autenticación Windows vs. usuario y contraseña')
table_simple(
['Escenario', 'Configuración en .env'],
[
['Tu servidor pide usuario y contraseña de SQL Server',
'SCAII_TRUSTED=no\nSCAII_USER=sa\nSCAII_PASSWORD=TuContraseña'],
['Tu servidor usa Windows Authentication (credenciales de Windows)',
'SCAII_TRUSTED=yes\n(SCAII_USER y SCAII_PASSWORD se ignoran)'],
],
col_widths_cm=[6, 9]
)
# ── 4. PRIMER USO ─────────────────────────────────────────────────────────────
doc.add_page_break()
h1('4. Primer uso de la aplicación')
alerta(
'Ejecutar como Administrador',
'Cada vez que abras la aplicación, hazlo con clic derecho → '
'"Ejecutar como administrador". De lo contrario puede fallar la conexión a la base de datos.'
)
doc.add_paragraph()
h2('Abrir la aplicación')
numbered('Localiza el acceso directo en el escritorio o ve a la carpeta de instalación')
numbered('Haz clic derecho sobre UtileriasReconV2.exe')
numbered('Selecciona "Ejecutar como administrador"')
numbered('Aparece una ventana negra de consola — NO la cierres, es normal')
numbered('Después de unos segundos, tu navegador (Edge o Chrome) abre automáticamente la aplicación')
tip(
'Si el navegador no abre solo',
'Escribe manualmente en la barra de direcciones: http://127.0.0.1:8866/'
)
doc.add_paragraph()
nota(
'La consola negra',
'La ventana negra debe permanecer abierta todo el tiempo que uses la aplicación. '
'Si la cierras, la app se apaga. Cuando termines de trabajar, primero cierra el '
'navegador y luego cierra la consola.'
)
doc.add_paragraph()
h2('Verificar la conexión a la base de datos')
parr(
'Al cargar la aplicación, en la parte superior verás una barra de estado. '
'Su color te indica si la conexión es correcta:'
)
table_simple(
['Color de la barra', 'Significado'],
[
['Azul / Verde', 'Conexión exitosa. Puedes trabajar normalmente.'],
['Rojo', 'Error de conexión. Revisa el archivo .env y que el servidor esté disponible.'],
],
col_widths_cm=[4, 11]
)
doc.add_paragraph()
h2('Navegar por las pestañas')
parr('La aplicación tiene varias pestañas en la parte superior. Cada una corresponde a una función:')
table_simple(
['Pestaña', 'Para qué sirve'],
[
['Conexión / Config', 'Seleccionar la base de datos activa y ver el estado de la conexión'],
['Descargas', 'Generar y complementar descargas de inventario contra facturas'],
['Análisis de Saldos', 'Ver el estado del inventario: saldos, comparativos, exportar a Excel'],
['Sustitutos NLP', 'Sugerir automáticamente números de parte similares'],
['Descarga % KGS', 'Descargas basadas en porcentaje de peso (BOMs con cantidades en %)'],
['DataStage', 'Cargar archivos .asc del SAAIM3 y generar estructuras SCAII'],
['Estructuras SCAII', 'Generar los Excel de partidas y encabezados para importar en SCAII'],
],
col_widths_cm=[4, 11]
)
# ── 5. FAQ ────────────────────────────────────────────────────────────────────
doc.add_page_break()
h1('5. Preguntas frecuentes')
h3('¿Por qué tengo que ejecutarlo como Administrador?')
parr(
'La aplicación necesita permisos de administrador para dos cosas: '
'(1) conectarse al servidor de SQL Server a través del driver ODBC, y '
'(2) escribir los archivos temporales que genera Jupyter internamente. '
'Sin estos permisos, la app puede abrir visualmente pero falla al intentar '
'cualquier operación con la base de datos.'
)
doc.add_paragraph()
h3('¿Puedo instalarla sin ser administrador?')
parr(
'No. El instalador requiere privilegios de administrador para registrar los '
'componentes de Jupyter y el driver ODBC. Si no tienes estos permisos en tu '
'equipo, solicita al área de sistemas que haga la instalación.'
)
doc.add_paragraph()
h3('La app abre pero dice "Error de conexión"')
bullet('Verifica que el archivo .env existe en la carpeta de instalación (no solo .env.example)')
bullet('Revisa que el servidor SCAII_SERVER sea correcto (nombre o IP)')
bullet('Confirma que el usuario y contraseña sean correctos')
bullet('Asegúrate de que el servidor SQL Server esté encendido y accesible desde tu red')
bullet('Si usas Windows Auth (SCAII_TRUSTED=yes), verifica que tu usuario de Windows tenga acceso al servidor')
doc.add_paragraph()
h3('¿Cómo sé que la instalación fue exitosa?')
parr('Si puedes abrir la app como Administrador y la barra superior muestra azul o verde sin errores, '
'la instalación está correcta.')
doc.add_paragraph()
h3('¿Tengo que instalarla cada vez que actualicen la app?')
parr(
'Depende del tipo de actualización. Si el área técnica entrega un nuevo instalador '
'(.exe de setup), sí hay que reinstalar. Si solo entregan un nuevo archivo app.ipynb, '
'basta con reemplazar ese archivo en la carpeta de instalación sin reinstalar todo.'
)
doc.add_paragraph()
alerta(
'Soporte',
'Para cualquier problema de instalación o configuración que no esté cubierto en este manual, '
'contacta al área técnica de Aduanasoft.'
)
# ── GUARDAR ───────────────────────────────────────────────────────────────────
out_path = 'Manual_Instalacion.docx'
try:
doc.save(out_path)
except PermissionError:
out_path = f'Manual_Instalacion_{datetime.datetime.now().strftime("%Y%m%d_%H%M%S")}.docx'
doc.save(out_path)
print('(El anterior estaba abierto en Word. Generado con nombre alternativo.)')
print(f'Manual generado: {os.path.abspath(out_path)}')

67
app/installer.iss Executable file
View File

@@ -0,0 +1,67 @@
; ============================================================
; Instalador de Utilerias RECON 2
; Empaqueta dist\UtileriasReconV2\ en un instalador .exe unico.
; Requiere Inno Setup 6+: https://jrsoftware.org/isdl.php
; ============================================================
#define MyAppName "Utilerias RECON 2"
#define MyAppVersion "1.0.0"
#define MyAppPublisher "Tecma / CurtManufacturing"
#define MyAppExeName "UtileriasReconV2.exe"
[Setup]
AppId={{B3F1A7A8-4D2E-4A6B-9C5A-2C0D1F8E9A11}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppPublisher={#MyAppPublisher}
DefaultDirName={autopf}\UtileriasReconV2
DefaultGroupName={#MyAppName}
DisableProgramGroupPage=yes
OutputDir=installer_output
OutputBaseFilename=UtileriasReconV2_Setup_{#MyAppVersion}
Compression=lzma2/ultra64
SolidCompression=yes
ArchitecturesAllowed=x64compatible
ArchitecturesInstallIn64BitMode=x64compatible
PrivilegesRequired=lowest
PrivilegesRequiredOverridesAllowed=dialog
WizardStyle=modern
UninstallDisplayIcon={app}\{#MyAppExeName}
[Languages]
Name: "spanish"; MessagesFile: "compiler:Languages\Spanish.isl"
[Tasks]
Name: "desktopicon"; Description: "Crear acceso directo en el escritorio"; GroupDescription: "Iconos adicionales:"
[Files]
; Empaqueta TODA la carpeta dist\UtileriasReconV2\ generada por PyInstaller.
; Excluimos *.map (source maps de JS, solo para debug del browser, no se usan)
; porque tienen nombres absurdamente largos y rompen la compresion.
Source: "dist\UtileriasReconV2\*"; DestDir: "{app}"; Excludes: "*.map"; Flags: ignoreversion recursesubdirs createallsubdirs
; Plantilla .env (el usuario la copia como .env y edita)
Source: "..\.env.example"; DestDir: "{app}"; Flags: ignoreversion
[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{group}\Editar configuracion (.env)"; Filename: "notepad.exe"; Parameters: """{app}\.env"""
Name: "{group}\Desinstalar {#MyAppName}"; Filename: "{uninstallexe}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "Ejecutar {#MyAppName} ahora"; Flags: nowait postinstall skipifsilent
[Code]
// Si no existe .env, copia .env.example como .env tras instalar
procedure CurStepChanged(CurStep: TSetupStep);
var
EnvFile, EnvExample: string;
begin
if CurStep = ssPostInstall then
begin
EnvFile := ExpandConstant('{app}\.env');
EnvExample := ExpandConstant('{app}\.env.example');
if (not FileExists(EnvFile)) and FileExists(EnvExample) then
CopyFile(EnvExample, EnvFile, False);
end;
end;

View File

@@ -1,10 +1,9 @@
""" """
Launcher de la app GENPACT V2. Launcher de la app GENPACT V2 - Utilerias RECON 2.
Arranca un servidor Voila local en background y abre el browser apuntando al app.ipynb. Arranca Voila in-process (sin subprocess) para que funcione empaquetado con
Cuando se cierra la consola, el servidor de Voila se mata. PyInstaller. Si el .exe es invocado como kernel de IPython, enruta al
ipykernel_launcher en vez de relanzar Voila (evita el bucle infinito).
Funciona tanto desde Python directo como empaquetado con PyInstaller.
""" """
import os import os
import sys import sys
@@ -12,36 +11,113 @@ import time
import socket import socket
import threading import threading
import webbrowser import webbrowser
import subprocess
from pathlib import Path from pathlib import Path
# ------------------------------------------------------------------ # ============================================================
# Localizar app.ipynb y .env # Diagnostico: escribir log al disco apenas arranque el launcher.
# ------------------------------------------------------------------ # Si este archivo no aparece, el problema es PyInstaller / antivirus
def base_dir() -> Path: # bloqueando la extraccion antes de que Python siquiera empiece.
"""Carpeta donde estan los recursos de la app. # ============================================================
- En modo PyInstaller (--onedir), sys._MEIPASS apunta a la carpeta temporal. def _diag_log(msg):
- En modo dev, es la carpeta del script. try:
""" log_path = os.path.join(os.path.expanduser('~'), 'utilerias_recon_boot.log')
with open(log_path, 'a', encoding='utf-8') as f:
f.write(f'{time.strftime("%Y-%m-%d %H:%M:%S")} | {msg}\n')
except Exception:
pass
_diag_log(f'=== LAUNCHER START ===')
_diag_log(f'sys.executable={sys.executable}')
_diag_log(f'sys.argv={sys.argv}')
_diag_log(f'cwd={os.getcwd()}')
_diag_log(f'frozen={getattr(sys, "frozen", False)}')
try:
if sys.stdout is not None:
print(f'[boot] launcher iniciando, sys.executable={sys.executable}', flush=True)
print(f'[boot] argv={sys.argv}', flush=True)
print(f'[boot] cwd={os.getcwd()}', flush=True)
except Exception:
pass
# ============================================================
# Cargar .env ANTES de cualquier cosa para que las variables
# queden en os.environ y se hereden a Voila + kernels subprocess.
# (En --onefile el cwd del kernel apunta al temp de PyInstaller,
# no a la carpeta del .exe, asi que load_dotenv relativo falla.)
# ============================================================
def _exe_dir() -> Path:
if getattr(sys, 'frozen', False):
return Path(sys.executable).parent
return Path(__file__).resolve().parent
_env_file = _exe_dir() / '.env'
if _env_file.exists():
try:
from dotenv import load_dotenv
load_dotenv(_env_file, override=True)
except ImportError:
pass
# ============================================================
# Router de modo: detectar invocacion como kernel de Jupyter
# jupyter_client spawnea: sys.executable -m ipykernel_launcher -f conn.json
# En el .exe eso seria: UtileriasReconV2.exe -m ipykernel_launcher -f conn.json
# ============================================================
if 'ipykernel_launcher' in sys.argv:
# En modo empaquetado, sys.stdout/stderr heredados del padre (Voila)
# pueden ser invalidos para flush() y romper ipykernel.init_io.
# ipykernel los reemplaza por canales ZMQ despues, asi que devnull es seguro.
if getattr(sys, 'frozen', False):
try:
_devnull = open(os.devnull, 'w')
sys.stdout = _devnull
sys.stderr = _devnull
except Exception:
pass
sys.argv = [a for a in sys.argv if a not in ('-m', 'ipykernel_launcher')]
from ipykernel import kernelapp
kernelapp.launch_new_instance()
sys.exit(0)
# ============================================================
# Localizar app.ipynb y .env
# ============================================================
def resources_dir() -> Path:
"""Recursos embebidos (app.ipynb bundleado). En PyInstaller usa _MEIPASS."""
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
return Path(sys._MEIPASS)
return Path(sys.executable).parent if getattr(sys, 'frozen', False) \
else Path(__file__).resolve().parent
def user_dir() -> Path:
"""Archivos del usuario (.env). Siempre al lado del .exe."""
if getattr(sys, 'frozen', False): if getattr(sys, 'frozen', False):
# PyInstaller --onedir: los datos quedan al lado del .exe
return Path(sys.executable).parent return Path(sys.executable).parent
return Path(__file__).resolve().parent return Path(__file__).resolve().parent
BASE = base_dir() RES = resources_dir()
APP_NB = BASE / 'app.ipynb' USR = user_dir()
# Si el .env esta un nivel arriba (proyecto), copialo o setea el cwd # Buscar app.ipynb: primero al lado del .exe (override), luego en recursos
if not (BASE / '.env').exists() and (BASE.parent / '.env').exists(): APP_NB = USR / 'app.ipynb'
os.chdir(BASE.parent) if not APP_NB.exists():
APP_NB = RES / 'app.ipynb'
# cwd donde este el .env (para que python-dotenv lo encuentre)
if (USR / '.env').exists():
os.chdir(USR)
elif (USR.parent / '.env').exists():
os.chdir(USR.parent)
else: else:
os.chdir(BASE) os.chdir(USR)
# ------------------------------------------------------------------
# Buscar puerto libre
# ------------------------------------------------------------------
def find_free_port(preferido: int = 8866) -> int: def find_free_port(preferido: int = 8866) -> int:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try: try:
@@ -50,7 +126,6 @@ def find_free_port(preferido: int = 8866) -> int:
return preferido return preferido
except OSError: except OSError:
sock.close() sock.close()
# Si el preferido esta ocupado, deja que el SO asigne uno
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('127.0.0.1', 0)) s.bind(('127.0.0.1', 0))
p = s.getsockname()[1] p = s.getsockname()[1]
@@ -58,63 +133,66 @@ def find_free_port(preferido: int = 8866) -> int:
return p return p
# ------------------------------------------------------------------ def fatal(msg: str):
# Arrancar Voila """Muestra error y sale. Si no hay consola, usa MessageBox de Windows."""
# ------------------------------------------------------------------ try:
if sys.stdout is not None:
print(f'ERROR: {msg}')
except Exception:
pass
try:
import tkinter
from tkinter import messagebox
root = tkinter.Tk()
root.withdraw()
messagebox.showerror('Utilerias RECON 2', msg)
except Exception:
pass
sys.exit(1)
def main(): def main():
if not APP_NB.exists(): if not APP_NB.exists():
print(f'ERROR: no se encuentra {APP_NB}') fatal(f'No se encuentra app.ipynb en {APP_NB}')
sys.exit(1)
port = find_free_port(8866) port = find_free_port(8866)
# Cuando Voila apunta a un notebook especifico, lo sirve en la raiz "/"
url = f'http://127.0.0.1:{port}/' url = f'http://127.0.0.1:{port}/'
print('=' * 60) if sys.stdout is not None:
print(' GENPACT V2 - Sistema de Descargas SCAII') print('=' * 60)
print('=' * 60) print(' GENPACT V2 - Sistema de Descargas SCAII')
print(f' Iniciando servidor Voila en puerto {port}...') print('=' * 60)
print(f' URL: {url}') print(f' URL: {url}')
print(' Cierra esta ventana para detener la app.') print(' Cierra esta ventana para detener la app.')
print('=' * 60) print('=' * 60)
# Construir comando de Voila # Abrir browser cuando Voila acepte conexiones
if getattr(sys, 'frozen', False):
# En modo empaquetado, llamamos al binario de python integrado
cmd = [sys.executable, '-m', 'voila',
str(APP_NB),
f'--port={port}',
'--no-browser',
'--Voila.ip=127.0.0.1',
'--strip_sources=True']
else:
cmd = [sys.executable, '-m', 'voila',
str(APP_NB),
f'--port={port}',
'--no-browser',
'--Voila.ip=127.0.0.1',
'--strip_sources=True']
# Lanzar proceso
proc = subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr)
# Abrir browser cuando el server este listo
def _open(): def _open():
for _ in range(60): for _ in range(120):
try: try:
with socket.create_connection(('127.0.0.1', port), timeout=0.5): with socket.create_connection(('127.0.0.1', port), timeout=0.5):
break break
except OSError: except OSError:
time.sleep(0.5) time.sleep(0.5)
webbrowser.open(url) webbrowser.open(url)
threading.Thread(target=_open, daemon=True).start() threading.Thread(target=_open, daemon=True).start()
# Lanzar Voila in-process (sin subprocess)
sys.argv = [
'voila',
str(APP_NB),
f'--port={port}',
'--no-browser',
'--Voila.ip=127.0.0.1',
'--strip_sources=True',
]
try: try:
proc.wait() from voila.app import Voila
except KeyboardInterrupt: Voila.launch_instance()
print('\nDeteniendo Voila...') except SystemExit:
proc.terminate() pass
except Exception as e:
fatal(f'Voila fallo al iniciar: {e}')
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -11,5 +11,4 @@ jupyter_server>=2.0
notebook>=7.0 notebook>=7.0
scikit-learn>=1.3 scikit-learn>=1.3
pyinstaller>=6.0 pyinstaller>=6.0
psycopg2-binary>=2.9
sqlalchemy>=2.0 sqlalchemy>=2.0

384
app/schema_registro_sqlite.sql Executable file
View File

@@ -0,0 +1,384 @@
CREATE TABLE IF NOT EXISTS "Registro501" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"TipoOperacion" TEXT,
"ClaveDocumento" TEXT,
"SeccionAduaneraEntrada" TEXT,
"CurpContribuyente" TEXT,
"Rfc" TEXT,
"CurpAgenteA" TEXT,
"TipoCambio" NUMERIC,
"TotalFletes" NUMERIC,
"TotalSeguros" NUMERIC,
"TotalEmbalajes" NUMERIC,
"TotalIncrementables" NUMERIC,
"TotalDeducibles" NUMERIC,
"PesoBrutoMercancia" NUMERIC,
"MedioTransporteSalida" TEXT,
"MedioTransporteArribo" TEXT,
"MedioTransporteEntrada_Salida" TEXT,
"DestinoMercancia" TEXT,
"NombreContribuyente" TEXT,
"CalleContribuyente" TEXT,
"NumInteriorContribuyente" TEXT,
"NumExteriorContribuyente" TEXT,
"CPContribuyente" TEXT,
"MunicipioContribuyente" TEXT,
"EntidadFedContribuyente" TEXT,
"PaisContribuyente" TEXT,
"TipoPedimento" TEXT,
"FechaRecepcionPedimento" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro502" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"RfcTransportista" TEXT,
"CurpTransportista" TEXT,
"NombreTransportista" TEXT,
"PaisTransporte" TEXT,
"IdentificadorTransporte" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro503" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"NumeroGuia" TEXT,
"TipoGuia" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro504" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"NumContenedor" TEXT,
"TipoContenedor" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro505" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"FechaFacturacion" TEXT,
"NumeroFactura" TEXT,
"TerminoFacturacion" TEXT,
"MonedaFacturacion" TEXT,
"ValorDolares" NUMERIC,
"ValorMonedaExtranjera" NUMERIC,
"PaisFacturacion" TEXT,
"EntidadFedFacturacion" TEXT,
"IndentFiscalProveedor" TEXT,
"ProveedorMercancia" TEXT,
"CalleProveedor" TEXT,
"NumInteriorProveedor" TEXT,
"NumExteriorProveedor" TEXT,
"CpProveedor" TEXT,
"MunicipioProveedor" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro506" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"TipoFecha" TEXT,
"FechaOperacion" TEXT,
"FechaValidacionPagoR" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro507" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"ClaveCaso" TEXT,
"IdentificadorCaso" TEXT,
"TipoPedimento" TEXT,
"ComplementoCaso" TEXT,
"FechaValidacionPagoR" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro508" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"InstitucionEmisora" TEXT,
"NumeroCuenta" TEXT,
"FolioConstancia" TEXT,
"FechaConstancia" TEXT,
"TipoCuenta" TEXT,
"ClaveGarantia" TEXT,
"ValorUnitarioTitulo" NUMERIC,
"TotalGarantia" NUMERIC,
"CantidadUnidades" NUMERIC,
"TitulosAsignados" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro509" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"ClaveContribucion" TEXT,
"TasaContribucion" NUMERIC,
"TipoTasa" TEXT,
"TipoPedimento" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro510" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"ClaveContribucion" TEXT,
"FormaPago" TEXT,
"ImportePago" NUMERIC,
"TipoPedimento" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro511" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"SecuenciaObservacion" TEXT,
"Observaciones" TEXT,
"TipoPedimento" TEXT,
"FechaValidacionPagoR" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro512" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"PatenteAduanalOrig" TEXT,
"PedimentoOriginal" TEXT,
"SeccionAduaneraDespOrig" TEXT,
"DocumentoOriginal" TEXT,
"FechaOperacionOrig" TEXT,
"FraccionOriginal" TEXT,
"UnidadMedida" TEXT,
"MercanciaDescargada" NUMERIC,
"TipoPedimento" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro520" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"IndentFiscalDestinatario" TEXT,
"NombreDestinatarioMercancia" TEXT,
"CalleDestinatario" TEXT,
"NumInteriorDestinatario" TEXT,
"NumExteriorDestinatario" TEXT,
"CpDestinatario" TEXT,
"MunicpioDestinatario" TEXT,
"PaisDestinatario" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro551" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"Fraccion" TEXT,
"SecuenciaFraccion" TEXT,
"SubdivisionFraccion" TEXT,
"DescripcionMercancia" TEXT,
"PrecioUnitario" NUMERIC,
"ValorAduana" NUMERIC,
"ValorComercial" NUMERIC,
"ValorDolares" NUMERIC,
"CantidadUMComercial" NUMERIC,
"UnidadMedidaComercial" TEXT,
"CantidadUMTarifa" NUMERIC,
"UnidadMedidaTarifa" TEXT,
"ValorAgregado" NUMERIC,
"ClaveVinculacion" TEXT,
"MetodoValorizacion" TEXT,
"CodigoMercanciaProducto" TEXT,
"MarcaMercanciaProducto" TEXT,
"ModeloMercanciaProducto" TEXT,
"PaisOrigenDestino" TEXT,
"PaisCompradorVendedor" TEXT,
"EntidadFedOrigen" TEXT,
"EntidadFedDestino" TEXT,
"EntidadFedComprador" TEXT,
"EntidadFedVendedor" TEXT,
"TipoOperacion" TEXT,
"ClaveDocumento" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro552" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"Fraccion" TEXT,
"SecuenciaFraccion" TEXT,
"VinNumeroSerie" TEXT,
"KilometrajeVehiculo" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro553" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"Fraccion" TEXT,
"SecuenciaFraccion" TEXT,
"ClavePermiso" TEXT,
"FirmaDescargo" TEXT,
"NumeroPermiso" TEXT,
"ValorComercialDolares" NUMERIC,
"CantidadMUMTarifa" NUMERIC,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro554" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"Fraccion" TEXT,
"SecuenciaFraccion" TEXT,
"ClaveCaso" TEXT,
"IdentificadorCaso" TEXT,
"ComplementoCaso" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro555" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"Fraccion" TEXT,
"SecuenciaFraccion" TEXT,
"InstitucionEmisora" TEXT,
"NumeroCuenta" TEXT,
"FolioConstancia" TEXT,
"FechaConstancia" TEXT,
"ClaveGarantia" TEXT,
"ValorUnitarioTitulo" NUMERIC,
"TotalGarantia" NUMERIC,
"CantidadUnidadesMedida" NUMERIC,
"TitulosAsignados" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro556" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"Fraccion" TEXT,
"SecuenciaFraccion" TEXT,
"ClaveContribucion" TEXT,
"TasaContribucion" NUMERIC,
"TipoTasa" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro557" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"Fraccion" TEXT,
"SecuenciaFraccion" TEXT,
"ClaveContribucion" TEXT,
"FormaPago" TEXT,
"ImportePago" NUMERIC,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro558" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"Fraccion" TEXT,
"SecuenciaFraccion" TEXT,
"SecuenciaObservacion" TEXT,
"Observaciones" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro701" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"ClaveDocumento" TEXT,
"FechaPago" TEXT,
"PedimentoAnterior" TEXT,
"PatenteAnterior" TEXT,
"SeccionAduaneraAnterior" TEXT,
"DocumentoAnterior" TEXT,
"FechaOperacionAnterior" TEXT,
"PedimentoOriginal" TEXT,
"PatenteAduanalOrig" TEXT,
"SeccionAduaneraDespOrig" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "Registro702" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"ClaveContribucion" TEXT,
"FormaPago" TEXT,
"ImportePago" NUMERIC,
"TipoPedimento" TEXT,
"FechaPagoReal" TEXT
);
CREATE TABLE IF NOT EXISTS "RegistroInci" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"ConsecutivoRemesa" TEXT,
"NumeroSeleccion" TEXT,
"FechaInicioReconocimiento" TEXT,
"HoraInicioReconocimiento" TEXT,
"FechaFinReconocimiento" TEXT,
"HoraFinReconocimiento" TEXT,
"Fraccion" TEXT,
"SecuenciaFraccion" TEXT,
"ClaveDocumento" TEXT,
"TipoOperacion" TEXT,
"GradoIncidencia" TEXT,
"FechaSeleccion" TEXT
);
CREATE TABLE IF NOT EXISTS "RegistroResumen" (
"Folio" TEXT,
"RFCoPatenteAduanal" TEXT,
"Fecha_Inicial" TEXT,
"Fecha_Final" TEXT,
"Fecha_Ejecucion" TEXT,
"Total_Fracciones" INTEGER,
"Total_Contribuciones" INTEGER
);
CREATE TABLE IF NOT EXISTS "RegistroSel" (
"Patente" TEXT,
"Pedimento" TEXT,
"SeccionAduanera" TEXT,
"ConsecutivoRemesa" TEXT,
"NumeroSeleccion" TEXT,
"FechaSeleccion" TEXT,
"HoraSeleccion" TEXT,
"SemaforoFiscal" TEXT,
"ClaveDocumento" TEXT,
"TipoOperacion" TEXT
);
CREATE TABLE IF NOT EXISTS base_numpartes (
numparte TEXT PRIMARY KEY,
descripcion TEXT,
unimed TEXT,
fraccion TEXT
);