Se pusieron los datos correctos en la tabla

This commit is contained in:
2026-01-27 10:26:07 -06:00
parent 75fc4d56f9
commit 50504be30b
6 changed files with 223 additions and 230 deletions

View File

@@ -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] = ""

View File

@@ -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=""
)

View File

@@ -278,8 +278,26 @@
<p class="cliente p-t-1 p-b-1"></p>
</div>
<div class="width-48-right">
<p class="p-l-5 line-1"><span></span></p>
<p class="p-t-2"><br></p>
<table cellspacing="0" class="m-l-3" style="float: right; text-align: left;">
<tbody>
<tr class="h-22">
<td bgcolor="#E4E4E4" class="border" style="width:100pt">
<p class="medio-bold p-l-3 line-9">PACKING LIST / LISTA DE EMPAQUE:</p>
</td>
<td class="border" colspan="3" style="width:128pt">
<p class="grande p-l-5">{{ factura.numero }}</p>
</td>
</tr>
<tr class="h-22">
<td bgcolor="#E4E4E4" class="border">
<p class="small-bold p-l-3 line-9">MX CUSTOM BROKER / AGENTE ADUANAL MEXICANO:</p>
</td>
<td class="border" colspan="3">
<p class="normal p-l-5">{{ factura.agente_aduanal or '' }}</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="flex-container-end clearfix" style="overflow: visible;">
@@ -289,7 +307,7 @@
<img src="{{ logo_b64 }}" style="max-height: 70pt; max-width: 120pt;" />
</div>
{% endif %}
<div style="margin-left: 130pt; padding-top: 25pt; text-align: left;">
<div style="margin-left: 130pt; padding-top: 5pt; text-align: left;">
<h1 class="cliente">{{ cliente_proveedor.header }}</h1>
<p>{{ cliente_proveedor.nombre }}</p>
<p>{{ cliente_proveedor.direccion }}
@@ -308,97 +326,7 @@
</div>
</div>
<div style="position: relative;">
<table cellspacing="0" class="m-l-3" style="float: right; text-align: left;">
<tbody>
<tr class="h-18">
<td bgcolor="#E4E4E4" class="border" style="width:52pt">
<p class="medio-bold">PACKING LIST / LISTA DE EMPAQUE:</p>
</td>
<td class="border" colspan="3" style="width:176pt">
<p class="grande">{{ factura.numero }}</p>
</td>
</tr>
<tr class="h-11">
<td bgcolor="#E4E4E4" class="border" style="width:52pt">
<p class="normal p-l-3 line-9">Fecha:</p>
</td>
<td class="border" style="width:72pt">
<p class="normal p-l-2 line-9">{{ factura.fecha }}</p>
</td>
<td bgcolor="#E4E4E4" class="border" style="width:52pt">
<p class="normal p-l-2 line-9">T. Cambio:</p>
</td>
<td class="border" style="width:52pt">
<p class="normal center line-9">{{ factura.tipo_cambio }}</p>
</td>
</tr>
<tr class="h-11">
<td bgcolor="#E4E4E4" class="border" style="width:52pt">
<p class="small-bold p-l-3 line-9">Pedimento:</p>
</td>
<td class="border" colspan="1" style="width:109pt">
<p class="normal p-l-3 line-9">{{ factura.pedimento or '' }}</p>
</td>
<td bgcolor="#E4E4E4" class="border">
<p class="normal p-l-3 line-9">Clave:</p>
</td>
<td class="border" style="width:37pt">
<p class="normal p-r-2 line-9 center">{{ factura.clave_pedimento or '' }}</p>
</td>
</tr>
<tr class="h-11">
<td bgcolor="#E4E4E4" class="border" style="width:52pt">
<p class="normal p-l-3 line-9">Remesa:</p>
</td>
<td class="border" style="width:38pt">
<p class="normal p-l-10 line-9">{{ factura.remesa or '' }}</p>
</td>
<td bgcolor="#E4E4E4" class="border" style="width:79pt">
<p class="normal p-l-6 line-9">Acuse:</p>
</td>
<td class="border" style="width:59pt">
<p class="normal p-l-6 line-9">{{ factura.acuse_electronico or 'N/A' }}</p>
</td>
</tr>
<tr class="h-22">
<td class="border" colspan="4" style="width:228pt">
<p class="small-bold p-l-3 line-7">Mx custom broker / agente aduanal mexicano:</p>
<p class="normal p-l-3 line-7">{{ factura.agente_aduanal or '' }}</p>
</td>
</tr>
<tr class="h-15">
<td class="border" colspan="2">
<p><span class="small-bold p-l-3 line-7">Patente: </span><span
class="normal p-l-3 line-7">{{ factura.patente or '' }}</span></p>
</td>
<td class="border">
<span class="tiny-bold p-l-3 line-7">Regimen:</span><span class="tiny p-l-3 line-7">{{
factura.regimen or '' }}</span>
</td>
<td class="border">
<p class="tiny-bold p-l-3 line-7">INCOTERM:</p>
<p class="tiny p-l-3 line-7">{{ factura.incoterm or '' }}</p>
</td>
</tr>
<tr>
<td class="border" colspan="2">
{% if factura.precinto %}
<p class="tiny-bold p-l-3 line-7">Precinto: {{ factura.precinto }}</p>
{% endif %}
</td>
<td class="border">
<p class="tiny-bold p-l-3 line-7">Aduana: {{ factura.aduana or '' }}</p>
</td>
<td class="border">
{% if factura.destino %}
<p class="tiny-bold p-l-3 line-7">Destino: {{ factura.destino }}</p>
{% endif %}
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="flex-container clearfix">
@@ -451,131 +379,140 @@
<p class="p-t-8"><br /></p>
</header>
<table cellspacing="0" class="m-l-5" style="width: 99%; max-width: 580pt;">
<thead>
<!-- TABLA DE TRANSPORTES ELIMINADA POR SOLICITUD DEL USUARIO -->
<tr class="h-10">
<td class="border" rowspan="2">
<p class="tiny-bold p-t-5 center">Line / Línea</p>
</td>
<td class="border" rowspan="2" colspan="2">
<p class="tiny-bold p-l-2 line-10">Part Number / Número de Parte</p>
<p class="tiny-bold p-l-2 line-10">Description / Descripción</p>
</td>
<td class="border" colspan="2">
<p class="tiny-bold center line-9">Quantity / Cantidad</p>
</td>
<td class="border" colspan="2" style="width:60pt">
<p class="tiny-bold center line-9">Packing / Empaque</p>
</td>
<td class="border" colspan="2">
<p class="tiny-bold center line-9">Weight / Peso (KGS)</p>
</td>
<!-- COLUMNA VALORES REMOVIDA -->
</tr>
<tr class="h-11">
<!-- Subheaders -->
<td class="border center">
<p class="mini p-l-2">Qty / Cant.</p>
</td>
<td class="border center">
<p class="mini p-l-1">U.M.</p>
</td>
<td class="border">
<p class="mini center">Qty / Cant.</p>
</td>
<td class="border">
<p class="mini center">Type / Tipo</p>
</td>
<td class="border">
<p class="mini center">Net / Neto</p>
<p class="mini center font-bold" style="font-size: 4pt;">(LBS / KGS)</p>
</td>
<td class="border">
<p class="mini center">Gross / Bruto</p>
<p class="mini center font-bold" style="font-size: 4pt;">(LBS / KGS)</p>
</td>
</tr>
</thead>
<tr class="h-10">
<td class="border" rowspan="2">
<p class="tiny-bold p-t-5 center">Línea</p>
</td>
<td class="border" rowspan="2" colspan="2">
<p class="tiny-bold p-l-2 line-10">Número de Parte</p>
<p class="tiny-bold p-l-2 line-10">Descripción</p>
</td>
<td class="border" colspan="2">
<p class="tiny-bold center line-9">Comercial</p>
</td>
<td class="border" colspan="2" style="width:60pt">
<p class="tiny-bold center line-9">Empaque</p>
</td>
<td class="border" colspan="2">
<p class="tiny-bold center line-9">Peso (KGS)</p>
</td>
<!-- COLUMNA VALORES REMOVIDA -->
</tr>
<tr class="h-11">
<!-- Subheaders -->
<td class="border center">
<p class="mini p-l-2">Cantidad</p>
</td>
<td class="border center">
<p class="mini p-l-1">U.M.</p>
</td>
<td class="border">
<p class="mini center">Cant.</p>
</td>
<td class="border">
<p class="mini center">Tipo</p>
</td>
<td class="border">
<p class="mini center">Neto</p>
</td>
<td class="border">
<p class="mini center">Bruto</p>
</td>
</tr>
</thead>
<tbody class="h-384" style="width: 100%;">
{% for partida in partidas %}
<tr>
<td class="border" style="width: 25pt;">
<p class="mini p-t-3 center">{{ loop.index }}</p>
</td>
<td class="border" colspan="2">
<p class="mini p-t-1" style="font-weight: bold;">{{ partida.numero_parte }}</p>
<p class="mini">{{ partida.descripcion }} / {{ partida.fraccion_americana }} / {{ partida.origen }}
</p>
</td>
<td class="border">
<p class="mini p-t-2 center">{{ partida.cantidad_importacion }}</p>
</td>
<td class="border">
<p class="mini p-t-2 center">{{ partida.unidad_medida }}</p>
</td>
<td class="border">
<p class="mini p-t-2 center">
{% if partida.cantidad_bultos != 0 %}{{ partida.cantidad_bultos }}{% endif %}
</p>
</td>
<td class="border">
<p class="mini p-t-2 center">
{{ partida.clave_bultos }}
</p>
</td>
<td class="border">
<p class="mini p-t-1 center">{{ partida.peso_neto_lbs }}</p>
<p class="mini center font-bold">{{ partida.peso_neto }}</p>
</td>
<td class="border">
<p class="mini p-t-1 center">{{ partida.peso_bruto_lbs }}</p>
<p class="mini center font-bold">{{ partida.peso_bruto }}</p>
</td>
</tr>
{% endfor %}
</tbody>
<tbody class="h-384" style="width: 100%;">
{% for partida in partidas %}
<tr>
<td class="border" style="width: 25pt;">
<p class="mini p-t-3 center">{{ loop.index }}</p>
</td>
<td class="border" colspan="2">
<p class="mini p-t-1" style="font-weight: bold;">{{ partida.numero_parte }}</p>
<p class="mini">{{ partida.descripcion }}</p>
</td>
<td class="border">
<p class="mini p-t-2 center">{{ partida.cantidad_importacion }}</p>
</td>
<td class="border">
<p class="mini p-t-2 center">{{ partida.unidad_medida }}</p>
</td>
<td class="border">
<p class="mini p-t-2 center">
{% if partida.cantidad_bultos != 0 %}{{ partida.cantidad_bultos }}{% endif %}
</p>
</td>
<td class="border">
<p class="mini p-t-2 center">
{{ partida.clave_bultos }}
</p>
</td>
<td class="border">
<p class="mini p-t-3 center">{{ partida.peso_neto }}</p>
</td>
<td class="border">
<p class="mini p-t-3 center">{{ partida.peso_bruto }}</p>
</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr class="h-14">
<td bgcolor="#E4E4E4" class="border" colspan="3" style="width:123pt">
<p class="small-bold p-t-2 p-l-3 line-10">
<span>Observaciones:</span>
<span class="small-bold" style="float: right; margin-right: 2pt;">TOTALES</span>
</p>
</td>
<td class="border" style="width:40pt">
<p class="tiny p-t-5 p-r-1 line-8 center">{{ totales.cantidad_total }}</p>
</td>
<td class="border" style="width:22pt"></td>
<td class="border" style="width:30pt">
<p class="tiny p-t-4 center line-8">
{% if totales.bultos_total != 0 %}{{ totales.bultos_total }}{% endif %}
</p>
</td>
<td class="border" style="width:30pt">
<p class="tiny p-t-4 center line-8">
<span>{{ totales.clave_bultos or '' }}</span>
</p>
</td>
<td class="border" style="width:40pt">
<p class="tiny p-t-5 center line-8">{{ totales.peso_neto_total }}</p>
</td>
<td class="border" style="width:40pt">
<p class="tiny p-t-5 center line-8">{{ totales.peso_bruto_total }}</p>
</td>
</tr>
<tr>
<td colspan="3" class="border" style="height: 100pt; text-align:start; vertical-align: top;">
<p class="mini p-l-2 p-t-2">{{ factura.observaciones }}</p>
</td>
<td colspan="5" style="width:336pt; vertical-align: bottom; height: 100%;">
<p style="border-bottom: 1pt solid black; width: 80%; margin: 0 auto 2pt auto;"></p>
<p class="normal center" style="margin-bottom: 0;">{{ cliente_proveedor.nombre }}</p>
<p style="margin-bottom: 0;"><br /></p>
</td>
</tr>
<tr>
<td colspan="10" style="width:580pt; vertical-align: top; height: 100%;">
<p class="p-t-8"><br /></p>
</td>
</tr>
</tfoot>
<tfoot>
<tr class="h-14">
<td bgcolor="#E4E4E4" class="border" colspan="3" style="width:123pt">
<p class="small-bold p-t-2 p-l-3 line-10">
<span>Observaciones:</span>
<span class="small-bold" style="float: right; margin-right: 2pt;">TOTALES</span>
</p>
</td>
<td class="border" style="width:40pt">
<p class="mini p-t-3 p-r-1 line-8 center">{{ totales.cantidad_total }}</p>
</td>
<td class="border" style="width:22pt"></td>
<td class="border" style="width:30pt">
<p class="mini p-t-3 center line-8">
{% if totales.bultos_total != 0 %}{{ totales.bultos_total }}{% endif %}
</p>
</td>
<td class="border" style="width:30pt">
<p class="mini p-t-3 center line-8">
<span>{{ totales.clave_bultos or '' }}</span>
</p>
</td>
<td class="border" style="width:40pt">
<p class="mini p-t-2 center line-8">{{ totales.peso_neto_total_lbs }} LBS</p>
<p class="mini center line-8 font-bold">{{ totales.peso_neto_total }} KGS</p>
</td>
<td class="border" style="width:40pt">
<p class="mini p-t-2 center line-8">{{ totales.peso_bruto_total_lbs }} LBS</p>
<p class="mini center line-8 font-bold">{{ totales.peso_bruto_total }} KGS</p>
</td>
</tr>
<tr>
<td colspan="3" class="border" style="height: 100pt; text-align:start; vertical-align: top;">
<p class="mini p-l-2 p-t-2">{{ factura.observaciones }}</p>
</td>
<td colspan="5" style="width:336pt; vertical-align: bottom; height: 100%;">
<p style="border-bottom: 1pt solid black; width: 80%; margin: 0 auto 2pt auto;"></p>
<p class="normal center" style="margin-bottom: 0;">{{ cliente_proveedor.nombre }}</p>
<p style="margin-bottom: 0;"><br /></p>
</td>
</tr>
<tr>
<td colspan="10" style="width:580pt; vertical-align: top; height: 100%;">
<p class="p-t-8"><br /></p>
</td>
</tr>
</tfoot>
</table>
</body>