Merge pull request 'fix/linea-scaii-551-partidas' (#1) from fix/linea-scaii-551-partidas into development

Reviewed-on: #1
This commit is contained in:
2026-07-13 20:49:31 +00:00
7 changed files with 94 additions and 64 deletions

2
.gitignore vendored
View File

@@ -1,6 +1,8 @@
venv/ venv/
venv32/
build/ build/
dist/ dist/
__pycache__/ __pycache__/
*.pyc *.pyc
*.spec.bak *.spec.bak
*.log

View File

@@ -1,12 +1,19 @@
# -*- mode: python ; coding: utf-8 -*- # -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_all
# flet necesita sus archivos de datos (icons.json, etc.); el hook por defecto no los trae completos
datas, binaries, hiddenimports = [], [], []
for pkg in ('flet', 'flet_desktop'):
d, b, h = collect_all(pkg)
datas += d; binaries += b; hiddenimports += h
a = Analysis( a = Analysis(
['main.py'], ['main.py'],
pathex=[], pathex=[],
binaries=[], binaries=binaries,
datas=[], datas=datas,
hiddenimports=[], hiddenimports=hiddenimports,
hookspath=[], hookspath=[],
hooksconfig={}, hooksconfig={},
runtime_hooks=[], runtime_hooks=[],
@@ -35,5 +42,4 @@ exe = EXE(
target_arch=None, target_arch=None,
codesign_identity=None, codesign_identity=None,
entitlements_file=None, entitlements_file=None,
version='C:\\Users\\PC\\AppData\\Local\\Temp\\6dd1fff1-457d-40b5-9d54-2ef8d8c29463',
) )

View File

@@ -60,7 +60,7 @@ class ConfiguracionApp:
log.error("FALTA PARÁMETRO: No se recibió DBServer. El proceso no puede continuar.") log.error("FALTA PARÁMETRO: No se recibió DBServer. El proceso no puede continuar.")
# 4. Lógica de URL Dinámica (Solo SCAII o SCAF) # 4. Lógica de URL Dinámica (Solo SCAII o SCAF)
self.datos["url_api"] = "https://104.192.7.152:3529/" self.datos["url_api"] = "https://anexo46.aduanasoft.com/api/v1/"
# 5. Valores por defecto finales # 5. Valores por defecto finales
if not self.datos["puerto"] or self.datos["puerto"] == 0: if not self.datos["puerto"] or self.datos["puerto"] == 0:

View File

@@ -174,7 +174,7 @@ class GestorBaseDatos:
col_fac = "FACCMIMPODEF" if regimen == "IMD" else "FACTURAIMPO" col_fac = "FACCMIMPODEF" if regimen == "IMD" else "FACTURAIMPO"
query = f""" query = f"""
SELECT p.Consecutivo AS Consecutivo, p.{col_linea} AS Linea, p.NumParte AS NumParte, SELECT p.Consecutivo AS Consecutivo, p.{col_linea} AS Linea, p.LOTE AS Lote, p.NumParte AS NumParte,
p.CantImpo AS Cantidad, p.ValorImpoMC AS ValorComercial, p.UniMed AS UnidadMedida, p.CantImpo AS Cantidad, p.ValorImpoMC AS ValorComercial, p.UniMed AS UnidadMedida,
p.FraccionImpo AS Fraccion, p.PesoNetoKGS AS PesoNeto, p.TipoFraccImpo AS TipoFracc, p.FraccionImpo AS Fraccion, p.PesoNetoKGS AS PesoNeto, p.TipoFraccImpo AS TipoFracc,
p.CostoUnitarioME AS CostoUnitarioME, p.ValorImpoME AS ValorDolares, p.CostoUnitarioME AS CostoUnitarioME, p.ValorImpoME AS ValorDolares,
@@ -196,7 +196,7 @@ class GestorBaseDatos:
""" """
else: # EXPO SCAII else: # EXPO SCAII
query = f""" query = f"""
SELECT p.FacturaExpo AS Consecutivo, p.Linea AS Linea, p.NumParte AS NumParte, SELECT p.FacturaExpo AS Consecutivo, p.Linea AS Linea, p.LOTE AS Lote, p.NumParte AS NumParte,
p.CantExpo AS Cantidad, p.ValorTotalMN AS ValorComercial, p.UniMed AS UnidadMedida, p.CantExpo AS Cantidad, p.ValorTotalMN AS ValorComercial, p.UniMed AS UnidadMedida,
p.FraccionExpo AS Fraccion, p.PesoNetoKGS AS PesoNeto, p.TipoFracExpo AS TipoFracc, p.FraccionExpo AS Fraccion, p.PesoNetoKGS AS PesoNeto, p.TipoFracExpo AS TipoFracc,
p.CostoUnitarioME AS CostoUnitarioME, p.ValorTotalME AS ValorDolares, p.CostoUnitarioME AS CostoUnitarioME, p.ValorTotalME AS ValorDolares,
@@ -224,7 +224,7 @@ class GestorBaseDatos:
col_vmn = "ValorMN" if regimen == "IMD" else "ValorImpoMN" col_vmn = "ValorMN" if regimen == "IMD" else "ValorImpoMN"
query = f""" query = f"""
SELECT p.Consecutivo AS Consecutivo, p.{col_linea} AS Linea, p.NumParte AS NumParte, SELECT p.Consecutivo AS Consecutivo, p.{col_linea} AS Linea, p.LOTE AS Lote, p.NumParte AS NumParte,
p.{col_cant} AS Cantidad, p.ValorImpoMC AS ValorComercial, p.UnidadMedida AS UnidadMedida, p.{col_cant} AS Cantidad, p.ValorImpoMC AS ValorComercial, p.UnidadMedida AS UnidadMedida,
p.Fraccion AS Fraccion, p.PesoNetoKGS AS PesoNeto, p.TipoFraccion AS TipoFracc, p.Fraccion AS Fraccion, p.PesoNetoKGS AS PesoNeto, p.TipoFraccion AS TipoFracc,
p.CostoUnitarioDlls AS CostoUnitarioME, p.{col_vme} AS ValorDolares, p.CostoUnitarioDlls AS CostoUnitarioME, p.{col_vme} AS ValorDolares,
@@ -245,7 +245,7 @@ class GestorBaseDatos:
""" """
else: # EXPO SCAF else: # EXPO SCAF
query = """ query = """
SELECT p.Consecutivo AS Consecutivo, p.LineaExpo AS Linea, p.FacturaImpo AS NumParte, SELECT p.Consecutivo AS Consecutivo, p.LineaExpo AS Linea, p.LOTE AS Lote, p.FacturaImpo AS NumParte,
p.CantExpo AS Cantidad, 0 AS ValorComercial, p.UnidadMedida AS UnidadMedida, p.CantExpo AS Cantidad, 0 AS ValorComercial, p.UnidadMedida AS UnidadMedida,
p.FraccionExpo AS Fraccion, p.PesoNetoKGS AS PesoNeto, p.TipoFraccion AS TipoFracc, p.FraccionExpo AS Fraccion, p.PesoNetoKGS AS PesoNeto, p.TipoFraccion AS TipoFracc,
p.CostoUnitarioDlls AS CostoUnitarioME, p.ValorExpoME AS ValorDolares, p.CostoUnitarioDlls AS CostoUnitarioME, p.ValorExpoME AS ValorDolares,

View File

@@ -1,6 +1,7 @@
import flet as ft import flet as ft
import threading import threading
from typing import Optional from typing import Optional
from logger_utils import log
class InterfazFlet: class InterfazFlet:
def __init__(self, cliente_sinc=None): def __init__(self, cliente_sinc=None):
@@ -72,65 +73,70 @@ class InterfazFlet:
# ------------------------------------------------------------------ # # ------------------------------------------------------------------ #
def agregar_fila(self, pedimento, factura, remesa, datos_completos, estatus): def agregar_fila(self, pedimento, factura, remesa, datos_completos, estatus):
log.info(f"[UI] agregar_fila llamado (page={'OK' if self.page else 'None'}): pedi={pedimento} fac={factura}")
async def _do(): async def _do():
# Panel de detalles (oculto inicialmente) try:
campos_relevantes = { # Panel de detalles (oculto inicialmente)
"NumFactura": "Factura", campos_relevantes = {
"Remesa": "Remesa", "NumFactura": "Factura",
"IdProveedor": "ID Proveedor", "Remesa": "Remesa",
"ValorDolares": "Valor Dólares", "IdProveedor": "ID Proveedor",
"PesoBruto": "Peso Bruto", "ValorDolares": "Valor Dólares",
"Transportista": "Transportista", "PesoBruto": "Peso Bruto",
"NumTrailer": "Caja/Trailer", "Transportista": "Transportista",
"Incoterm": "Incoterm", "NumTrailer": "Caja/Trailer",
"Consecutivo": "Folio Interno" "Incoterm": "Incoterm",
} "Consecutivo": "Folio Interno"
}
filas_detalles = [ filas_detalles = [
ft.Row([ ft.Row([
ft.Text(f"{etiqueta}:", weight="bold", width=120, size=12, color=ft.Colors.GREY_700), ft.Text(f"{etiqueta}:", weight="bold", width=120, size=12, color=ft.Colors.GREY_700),
ft.Text(str(datos_completos.get(clave, "N/A")), size=12, color=ft.Colors.BLUE_700), ft.Text(str(datos_completos.get(clave, "N/A")), size=12, color=ft.Colors.BLUE_700),
], spacing=10) ], spacing=10)
for clave, etiqueta in campos_relevantes.items() for clave, etiqueta in campos_relevantes.items()
] ]
panel_detalles = ft.Container( panel_detalles = ft.Container(
content=ft.Column(filas_detalles, spacing=6), content=ft.Column(filas_detalles, spacing=6),
visible=False, # <-- oculto por defecto visible=False, # <-- oculto por defecto
bgcolor=ft.Colors.BLUE_50, bgcolor=ft.Colors.BLUE_50,
padding=ft.padding.only(left=30, top=10, right=20, bottom=10), padding=ft.padding.only(left=30, top=10, right=20, bottom=10),
border=ft.border.only( border=ft.border.only(
bottom=ft.BorderSide(1, ft.Colors.BLUE_100), bottom=ft.BorderSide(1, ft.Colors.BLUE_100),
left=ft.BorderSide(3, ft.Colors.BLUE_400), # acento visual izquierdo left=ft.BorderSide(3, ft.Colors.BLUE_400), # acento visual izquierdo
), ),
) )
icono_expand = ft.Icon(ft.Icons.KEYBOARD_ARROW_DOWN, size=16, color=ft.Colors.GREY_400) icono_expand = ft.Icon(ft.Icons.KEYBOARD_ARROW_DOWN, size=16, color=ft.Colors.GREY_400)
def toggle_detalles(e, p=panel_detalles, i=icono_expand): def toggle_detalles(e, p=panel_detalles, i=icono_expand):
p.visible = not p.visible p.visible = not p.visible
i.name = ft.Icons.KEYBOARD_ARROW_UP if p.visible else ft.Icons.KEYBOARD_ARROW_DOWN i.name = ft.Icons.KEYBOARD_ARROW_UP if p.visible else ft.Icons.KEYBOARD_ARROW_DOWN
self.page.update()
fila_header = ft.Container(
content=ft.Row([
ft.Text(pedimento, width=150, size=12, color=ft.Colors.BLACK),
ft.Text(factura, width=120, size=12, color=ft.Colors.BLACK),
ft.Text(remesa, width=80, size=12, color=ft.Colors.BLACK),
ft.Text(estatus, expand=True, size=12, color=ft.Colors.BLUE_700, weight="bold"),
icono_expand,
]),
padding=ft.padding.symmetric(horizontal=20, vertical=8),
border=ft.border.only(bottom=ft.BorderSide(1, ft.Colors.GREY_100)),
on_click=toggle_detalles,
ink=True,
tooltip="Clic para ver detalles"
)
# Agrupamos header + panel en una Column
fila_completa = ft.Column([fila_header, panel_detalles], spacing=0)
self.lista_resultados.controls.append(fila_completa)
self.page.update() self.page.update()
log.info(f"[UI] fila agregada OK. Total filas en tabla: {len(self.lista_resultados.controls)}")
fila_header = ft.Container( except Exception as ex:
content=ft.Row([ log.error(f"[UI] Error en agregar_fila._do: {ex}")
ft.Text(pedimento, width=150, size=12, color=ft.Colors.BLACK),
ft.Text(factura, width=120, size=12, color=ft.Colors.BLACK),
ft.Text(remesa, width=80, size=12, color=ft.Colors.BLACK),
ft.Text(estatus, expand=True, size=12, color=ft.Colors.BLUE_700, weight="bold"),
icono_expand,
]),
padding=ft.padding.symmetric(horizontal=20, vertical=8),
border=ft.border.only(bottom=ft.BorderSide(1, ft.Colors.GREY_100)),
on_click=toggle_detalles,
ink=True,
tooltip="Clic para ver detalles"
)
# Agrupamos header + panel en una Column
fila_completa = ft.Column([fila_header, panel_detalles], spacing=0)
self.lista_resultados.controls.append(fila_completa)
self.page.update()
if self.page: if self.page:
self.page.run_task(_do) self.page.run_task(_do)

View File

@@ -45,6 +45,13 @@ class Generador551SCAF:
else: else:
um_comercial_num = map_um_num.get(oma_raw, oma_raw) um_comercial_num = map_um_num.get(oma_raw, oma_raw)
# linea_scaii_551 es entero opcional (llave de cruce con WINSAAI); sale del LOTE.
# Si el LOTE viene vacio o no es numerico -> None (el API lo acepta como null).
try:
linea_scaii = int(str(par.get("Lote")).strip())
except (ValueError, TypeError):
linea_scaii = None
registro = { registro = {
"id_factura": id_factura, "id_factura": id_factura,
"fraccion_551": str(frac_final), "fraccion_551": str(frac_final),
@@ -82,6 +89,7 @@ class Generador551SCAF:
"numero_remesa_551": str(num_remesa or " "), "numero_remesa_551": str(num_remesa or " "),
"numero_partida_remesa_551": str(num_partida_remesa), "numero_partida_remesa_551": str(num_partida_remesa),
"linea_scaii_551": linea_scaii, # Entero (o null) desde LOTE de la tabla de partidas
"cantidad_alterna_551": float(par.get("CantidadAlterna") or 0), "cantidad_alterna_551": float(par.get("CantidadAlterna") or 0),
"unidad_medida_alterna_551": str(par.get("UnidadAlterna", " ")), "unidad_medida_alterna_551": str(par.get("UnidadAlterna", " ")),
"descripcion_ingles_551": " ", "descripcion_ingles_551": " ",

View File

@@ -88,6 +88,13 @@ class Generador551SCAII:
if not metodo_val: if not metodo_val:
metodo_val = str(met_val) if met_val else "" metodo_val = str(met_val) if met_val else ""
# linea_scaii_551 es entero opcional (llave de cruce con WINSAAI); sale del LOTE.
# Si el LOTE viene vacio o no es numerico -> None (el API lo acepta como null).
try:
linea_scaii = int(str(par.get("Lote")).strip())
except (ValueError, TypeError):
linea_scaii = None
registro = { registro = {
"id_factura": id_factura, "id_factura": id_factura,
"fraccion_551": str(frac_final), "fraccion_551": str(frac_final),
@@ -125,6 +132,7 @@ class Generador551SCAII:
"numero_remesa_551": str(num_remesa or " "), "numero_remesa_551": str(num_remesa or " "),
"numero_partida_remesa_551": str(par.get("Linea") or num_partida_remesa), "numero_partida_remesa_551": str(par.get("Linea") or num_partida_remesa),
"linea_scaii_551": linea_scaii, # Entero (o null) desde LOTE de la tabla de partidas
"cantidad_alterna_551": float(par.get("CantidadAlterna") or 0), "cantidad_alterna_551": float(par.get("CantidadAlterna") or 0),
"unidad_medida_alterna_551": str(par.get("UnidadAlterna") or " "), "unidad_medida_alterna_551": str(par.get("UnidadAlterna") or " "),
"descripcion_ingles_551": str(par.get("DescripcionIngles") or " ").strip()[:80], "descripcion_ingles_551": str(par.get("DescripcionIngles") or " ").strip()[:80],