From 50504be30b4c833d8b74807f6f7bc47a7508a2ec Mon Sep 17 00:00:00 2001 From: Kevin_Ramirez Date: Tue, 27 Jan 2026 10:26:07 -0600 Subject: [PATCH] Se pusieron los datos correctos en la tabla --- .../importacion/packing_list/schemas.py | 5 + .../importacion/packing_list/service.py | 59 ++- .../packing_list/templates/packing_list.html | 369 ++++++++---------- .../dropdown-menu/dropdown-menu-root.svelte | 7 + .../ui/dropdown-menu/dropdown-menu-sub.svelte | 7 + .../lib/components/ui/dropdown-menu/index.ts | 6 +- 6 files changed, 223 insertions(+), 230 deletions(-) create mode 100644 frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-root.svelte create mode 100644 frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte diff --git a/backend/api/v1/modules/a76/reports/importacion/packing_list/schemas.py b/backend/api/v1/modules/a76/reports/importacion/packing_list/schemas.py index cbbd0958..fcaaae40 100644 --- a/backend/api/v1/modules/a76/reports/importacion/packing_list/schemas.py +++ b/backend/api/v1/modules/a76/reports/importacion/packing_list/schemas.py @@ -54,6 +54,7 @@ class PartidaSchema(BaseModel): numero_parte: str descripcion: str fraccion: str + fraccion_americana: Optional[str] = "" origen: str advalorem:Optional[str] = "" @@ -65,6 +66,8 @@ class PartidaSchema(BaseModel): clave_bultos: str peso_neto: Union[float, str] peso_bruto: Union[float, str] + peso_neto_lbs: Union[float, str] = 0.0 + peso_bruto_lbs: Union[float, str] = 0.0 valor_costo_unitario: Union[float, str] = "" valor_total: Union[float, str] = "" @@ -74,6 +77,8 @@ class TotalesSchema(BaseModel): clave_bultos: str = "" peso_neto_total: Union[float, str] peso_bruto_total: Union[float, str] + peso_neto_total_lbs: Union[float, str] = 0.0 + peso_bruto_total_lbs: Union[float, str] = 0.0 valor_total_total: Union[float, str] = "" valor_total_dolares: Union[float, str] = "" diff --git a/backend/api/v1/modules/a76/reports/importacion/packing_list/service.py b/backend/api/v1/modules/a76/reports/importacion/packing_list/service.py index a31a1eb2..43f0036d 100644 --- a/backend/api/v1/modules/a76/reports/importacion/packing_list/service.py +++ b/backend/api/v1/modules/a76/reports/importacion/packing_list/service.py @@ -3,7 +3,7 @@ import base64 import pdfkit from pathlib import Path from decimal import Decimal -from typing import Tuple, List, Callable, Optional, Dict +from typing import Tuple, List, Callable, Optional from jinja2 import Environment, FileSystemLoader, select_autoescape from fastapi import HTTPException @@ -12,9 +12,9 @@ from sqlalchemy.orm import Session # --- MODELOS --- from api.v1.modules.a76.invoices.models import InvoiceHeader, InvoiceLogistics -from api.v1.modules.a76.items.line_financials.models import LineFinancial from api.v1.modules.a76.items.line_quantities.models import LineQuantity from api.v1.modules.a76.items.line_items.models import LineItem +from api.v1.modules.a76.items.line_customs.models import LineCustom from api.v1.modules.a76.clients_and_providers.models import ( ClientProvider, ClientProviderAddress, ClientProviderPrograms ) @@ -60,7 +60,6 @@ class PackingListService: path = next((p for p in paths if p and Path(p).exists()), None) if not path: - # If we are in dev and cannot find it, try to mock it or raise clearer error if shutil.which("echo"): print("WARNING: wkhtmltopdf not found, PDF generation will fail.") raise RuntimeError(f"wkhtmltopdf binary not found. Searched in: {paths}") @@ -133,7 +132,7 @@ class PackingListService: company = db.query(Company).filter(Company.id == header.company_id).first() # Datos Default (Company/Importer) cliente_default = ClienteSchema( - header="Importador / consignatario:", + header="Importer / Consignee:", nombre=getattr(company, 'name', "Empresa Local"), direccion="DOMICILIO FISCAL", num_exterior="", colonia="", codigo_postal="", ciudad="", estado="", pais="MEX", @@ -144,15 +143,18 @@ class PackingListService: # Left Side Logic (Consignatario / Sold To) cliente_vendido = cliente_default if compliance and compliance.sold_to_id: - raw_header = compliance.sold_to_header or "CONSIGNATARIO" - clean_header = raw_header.replace("_", " ").capitalize() + ":" + raw = (compliance.sold_to_header or "").upper() + if "CONSIGN" in raw: + clean_header = "Consignee / Consignatario:" + else: + clean_header = "Sold To / Vendido a:" + cliente_vendido = self._obtener_datos_cliente(db, compliance.sold_to_id, clean_header) # Right Side Logic (Enviado A / Shipped To) cliente_enviado = cliente_default if compliance and compliance.shipped_to_id: - raw_header_shipped = compliance.shipped_to_header or "DESTINATARIO" - clean_header_shipped = raw_header_shipped.replace("_", " ").capitalize() + ":" + clean_header_shipped = "Shipped To / Enviado a:" cliente_enviado = self._obtener_datos_cliente(db, compliance.shipped_to_id, clean_header_shipped) remesa_valor = str(compliance.remesa) if (compliance and compliance.remesa) else "" @@ -241,17 +243,45 @@ class PackingListService: for line in lines: qty = db.query(LineQuantity).filter(LineQuantity.item_line_id == line.id).first() + + # --- WEIGHT CALCULATION LOGIC --- + peso_neto_kg = 0.0 + peso_bruto_kg = 0.0 + peso_neto_lb = 0.0 + peso_bruto_lb = 0.0 + + if qty: + raw_net = float(qty.net_weight or 0) + raw_gross = float(qty.gross_weight or 0) + unit = (qty.weight_unit or "KG").upper() + + if unit == "LB" or unit == "LBS": + peso_neto_lb = raw_net + peso_bruto_lb = raw_gross + peso_neto_kg = raw_net / 2.20462 + peso_bruto_kg = raw_gross / 2.20462 + else: # Default KG + peso_neto_kg = raw_net + peso_bruto_kg = raw_gross + peso_neto_lb = raw_net * 2.20462 + peso_bruto_lb = raw_gross * 2.20462 + # -------------------------------- + + custom_obj = db.query(LineCustom).filter(LineCustom.item_line_id == line.id).first() part_master = db.query(Part).filter(Part.id == line.part_number).first() desc_final = "S/D" num_parte_final = str(line.part_number or "S/N") fraccion_raw = "" origen_final = "MEX" + uom_comercial = "PZA" # Default UOM if part_master: desc_final = part_master.description_spanish or part_master.description_english or "Sin Desc." num_parte_final = part_master.part_number fraccion_raw = part_master.fraction if part_master.fraction else "" + # Commercial UOM from Part Master + uom_comercial = part_master.unit_of_measure or "PZA" if part_master.fa_data and part_master.fa_data.origin_country: origen_final = part_master.fa_data.origin_country @@ -276,15 +306,18 @@ class PackingListService: numero_parte=num_parte_final, descripcion=desc_final, fraccion=fraccion_imprimir, + fraccion_americana=custom_obj.american_fraction if custom_obj and custom_obj.american_fraction else "", origen=origen_final, advalorem="", # Hidden preferencia="", # Hidden cantidad_importacion=qty.quantity if qty else 0, - unidad_medida=qty.weight_unit if qty else "PZA", + unidad_medida=uom_comercial, # Commercial UOM (PCS, EA) cantidad_bultos=int(qty.package_quantity) if qty and qty.package_quantity else 0, clave_bultos=(qty.package_key or "") if qty else "", - peso_neto=qty.net_weight if qty else 0, - peso_bruto=qty.gross_weight if qty else 0, + peso_neto=self.formatear_numero(peso_neto_kg), + peso_bruto=self.formatear_numero(peso_bruto_kg), + peso_neto_lbs=self.formatear_numero(peso_neto_lb), + peso_bruto_lbs=self.formatear_numero(peso_bruto_lb), valor_costo_unitario=v_unitario, # Hidden valor_total=v_total # Hidden )) @@ -309,6 +342,9 @@ class PackingListService: # Financial totals hidden peso_n = sum(float(p.peso_neto) for p in partidas) peso_b = sum(float(p.peso_bruto) for p in partidas) + peso_n_lbs = sum(float(p.peso_neto_lbs) for p in partidas) + peso_b_lbs = sum(float(p.peso_bruto_lbs) for p in partidas) + bultos = sum(p.cantidad_bultos for p in partidas) claves = [p.clave_bultos for p in partidas if p.clave_bultos] @@ -318,6 +354,7 @@ class PackingListService: return TotalesSchema( cantidad_total=self.formatear_numero(cant), bultos_total=bultos, clave_bultos=clave_comun, peso_neto_total=self.formatear_numero(peso_n), peso_bruto_total=self.formatear_numero(peso_b), + peso_neto_total_lbs=self.formatear_numero(peso_n_lbs), peso_bruto_total_lbs=self.formatear_numero(peso_b_lbs), valor_total_total="", valor_total_dolares="" ) diff --git a/backend/api/v1/modules/a76/reports/importacion/packing_list/templates/packing_list.html b/backend/api/v1/modules/a76/reports/importacion/packing_list/templates/packing_list.html index 4ed057d9..a82f2aaa 100644 --- a/backend/api/v1/modules/a76/reports/importacion/packing_list/templates/packing_list.html +++ b/backend/api/v1/modules/a76/reports/importacion/packing_list/templates/packing_list.html @@ -278,8 +278,26 @@

-

-


+ + + + + + + + + + + +
+

PACKING LIST / LISTA DE EMPAQUE:

+
+

{{ factura.numero }}

+
+

MX CUSTOM BROKER / AGENTE ADUANAL MEXICANO:

+
+

{{ factura.agente_aduanal or '' }}

+
@@ -289,7 +307,7 @@
{% endif %} -
+

{{ cliente_proveedor.header }}

{{ cliente_proveedor.nombre }}

{{ cliente_proveedor.direccion }} @@ -308,97 +326,7 @@

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

PACKING LIST / LISTA DE EMPAQUE:

-
-

{{ factura.numero }}

-
-

Fecha:

-
-

{{ factura.fecha }}

-
-

T. Cambio:

-
-

{{ factura.tipo_cambio }}

-
-

Pedimento:

-
-

{{ factura.pedimento or '' }}

-
-

Clave:

-
-

{{ factura.clave_pedimento or '' }}

-
-

Remesa:

-
-

{{ factura.remesa or '' }}

-
-

Acuse:

-
-

{{ factura.acuse_electronico or 'N/A' }}

-
-

Mx custom broker / agente aduanal mexicano:

-

{{ factura.agente_aduanal or '' }}

-
-

Patente: {{ factura.patente or '' }}

-
- Regimen:{{ - factura.regimen or '' }} - -

INCOTERM:

-

{{ factura.incoterm or '' }}

-
- {% if factura.precinto %} -

Precinto: {{ factura.precinto }}

- {% endif %} -
-

Aduana: {{ factura.aduana or '' }}

-
- {% if factura.destino %} -

Destino: {{ factura.destino }}

- {% endif %} -
-
+
@@ -451,131 +379,140 @@


+ + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + {% for partida in partidas %} + + + + + + + + + + + {% endfor %} + - - {% for partida in partidas %} - - - - - - - - - - - {% endfor %} - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + +
+

Line / Línea

+
+

Part Number / Número de Parte

+

Description / Descripción

+
+

Quantity / Cantidad

+
+

Packing / Empaque

+
+

Weight / Peso (KGS)

+
+

Qty / Cant.

+
+

U.M.

+
+

Qty / Cant.

+
+

Type / Tipo

+
+

Net / Neto

+

(LBS / KGS)

+
+

Gross / Bruto

+

(LBS / KGS)

+
-

Línea

-
-

Número de Parte

-

Descripción

-
-

Comercial

-
-

Empaque

-
-

Peso (KGS)

-
-

Cantidad

-
-

U.M.

-
-

Cant.

-
-

Tipo

-
-

Neto

-
-

Bruto

-
+

{{ loop.index }}

+
+

{{ partida.numero_parte }}

+

{{ partida.descripcion }} / {{ partida.fraccion_americana }} / {{ partida.origen }} +

+
+

{{ partida.cantidad_importacion }}

+
+

{{ partida.unidad_medida }}

+
+

+ {% if partida.cantidad_bultos != 0 %}{{ partida.cantidad_bultos }}{% endif %} +

+
+

+ {{ partida.clave_bultos }} +

+
+

{{ partida.peso_neto_lbs }}

+

{{ partida.peso_neto }}

+
+

{{ partida.peso_bruto_lbs }}

+

{{ partida.peso_bruto }}

+
-

{{ loop.index }}

-
-

{{ partida.numero_parte }}

-

{{ partida.descripcion }}

-
-

{{ partida.cantidad_importacion }}

-
-

{{ partida.unidad_medida }}

-
-

- {% if partida.cantidad_bultos != 0 %}{{ partida.cantidad_bultos }}{% endif %} -

-
-

- {{ partida.clave_bultos }} -

-
-

{{ partida.peso_neto }}

-
-

{{ partida.peso_bruto }}

-
-

- Observaciones: - TOTALES -

-
-

{{ totales.cantidad_total }}

-
-

- {% if totales.bultos_total != 0 %}{{ totales.bultos_total }}{% endif %} -

-
-

- {{ totales.clave_bultos or '' }} -

-
-

{{ totales.peso_neto_total }}

-
-

{{ totales.peso_bruto_total }}

-
-

{{ factura.observaciones }}

-
-

-

{{ cliente_proveedor.nombre }}

-


-
-


-
+

+ Observaciones: + TOTALES +

+
+

{{ totales.cantidad_total }}

+
+

+ {% if totales.bultos_total != 0 %}{{ totales.bultos_total }}{% endif %} +

+
+

+ {{ totales.clave_bultos or '' }} +

+
+

{{ totales.peso_neto_total_lbs }} LBS

+

{{ totales.peso_neto_total }} KGS

+
+

{{ totales.peso_bruto_total_lbs }} LBS

+

{{ totales.peso_bruto_total }} KGS

+
+

{{ factura.observaciones }}

+
+

+

{{ cliente_proveedor.nombre }}

+


+
+


+
diff --git a/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-root.svelte b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-root.svelte new file mode 100644 index 00000000..7ac64712 --- /dev/null +++ b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-root.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte new file mode 100644 index 00000000..9b14b3ec --- /dev/null +++ b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/dropdown-menu/index.ts b/frontend/src/lib/components/ui/dropdown-menu/index.ts index 1cf9f701..9ac1bdd1 100644 --- a/frontend/src/lib/components/ui/dropdown-menu/index.ts +++ b/frontend/src/lib/components/ui/dropdown-menu/index.ts @@ -1,4 +1,4 @@ -import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; +// import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import CheckboxItem from "./dropdown-menu-checkbox-item.svelte"; import Content from "./dropdown-menu-content.svelte"; import Group from "./dropdown-menu-group.svelte"; @@ -12,8 +12,8 @@ import Trigger from "./dropdown-menu-trigger.svelte"; import SubContent from "./dropdown-menu-sub-content.svelte"; import SubTrigger from "./dropdown-menu-sub-trigger.svelte"; import GroupHeading from "./dropdown-menu-group-heading.svelte"; -const Sub = DropdownMenuPrimitive.Sub; -const Root = DropdownMenuPrimitive.Root; +import Sub from "./dropdown-menu-sub.svelte"; +import Root from "./dropdown-menu-root.svelte"; export { CheckboxItem,