Enhance invoice processing and validation logic

- Updated `InvoiceService` to accurately reflect the number of line items in `party_count` for invoices.
- Modified weight calculations in the create and update validators to ensure consistent handling of weight types, now using lowercase comparison for "KGS".
- Adjusted invoice data structure in the frontend to include `total_items` and refined logistics handling.
- Improved table rendering in the invoice edit component for better sticky header behavior and item visibility.

These changes aim to improve data integrity and user experience in invoice management.
This commit is contained in:
2026-03-19 14:22:21 -05:00
parent 264188e112
commit cf56ca3a68
13 changed files with 197 additions and 80 deletions

View File

@@ -61,7 +61,7 @@ def review_qty_vs_weight(
continue
qty = line.quantity.quantity or Decimal(0)
net_weight = getattr(line.quantity.quantity, None) or Decimal(0)
net_weight = line.quantity.net_weight or Decimal(0)
if net_weight != qty:
errors.add_error(

View File

@@ -198,7 +198,7 @@ def _update_invoice_totals(invoice: InvoiceHeader) -> None:
# Marcar la factura como procesada
invoice.status = InvoiceStatus.PROCESSED
invoice.party_count = len(invoice.financials.__dict__) # se sobreescribirá con el conteo real
invoice.party_count = len(lines)
# TODO: SSisGen:ActSeguridad = 1 → invoice.updated_by = current_user
# TODO: SSisGen:CalValBaseTCPed = 1 →

View File

@@ -10,6 +10,7 @@ from .imports.validators.update import validate_update as validate_update_import
from .exports.validators.create import validate_create as validate_create_export
from .exports.validators.update import validate_update as validate_update_export
from .common.common_validators import invoice_exists
from api.v1.modules.a76.items.models import LineItem
from . import models, schemas
@@ -166,6 +167,25 @@ class InvoiceService:
total = query.count()
items = query.offset(skip).limit(limit).all()
# Keep party_count aligned with the real number of line items.
# This avoids stale values stored in invoice_header.party_count.
if items:
invoice_ids = [inv.id for inv in items]
counts = (
db.query(LineItem.invoice_id, func.count(LineItem.id))
.filter(
LineItem.invoice_id.in_(invoice_ids),
LineItem.tenant_id == tenant_id,
LineItem.company_id == company_id,
)
.group_by(LineItem.invoice_id)
.all()
)
count_map = {invoice_id: int(count) for invoice_id, count in counts}
for inv in items:
inv.party_count = count_map.get(inv.id, 0)
return items, total
@staticmethod

View File

@@ -248,18 +248,18 @@ def validate_create(
# Calcular peso neto en kilogramos (estándar interno)
if unit_is_kgs:
if invoice_weight_type == "KGS":
if invoice_weight_type.lower() == "kgs":
line.quantity.net_weight = quantity
else: # invoice en libras
line.quantity.net_weight = quantity * Decimal("2.204624")
elif unit_is_lbs:
if invoice_weight_type == "KGS":
if invoice_weight_type.lower() == "kgs":
line.quantity.net_weight = quantity / Decimal("2.204624")
else: # invoice en libras
line.quantity.net_weight = quantity
else:
# Otra unidad de medida - usar peso capturado y convertir si es necesario
if invoice_weight_type == "KGS":
if invoice_weight_type.lower() == "kgs":
# El peso capturado está en kilos
line.quantity.net_weight = net_weight_input
else:
@@ -289,7 +289,7 @@ def validate_create(
# Si no se proporcionó peso bruto, calcularlo
if not gross_weight_input or gross_weight_input == 0:
if invoice_weight_type == "KGS":
if invoice_weight_type.lower() == "kgs":
line.quantity.gross_weight = line.quantity.net_weight + (
package_weight_unit * package_quantity
)
@@ -299,7 +299,7 @@ def validate_create(
)
else:
# Convertir peso bruto capturado según tipo de factura
if invoice_weight_type == "KGS":
if invoice_weight_type.lower() == "kgs":
line.quantity.gross_weight = gross_weight_input
else: # libras
line.quantity.gross_weight = gross_weight_input / Decimal("2.204624")

View File

@@ -95,21 +95,19 @@ def validate_update(
# Se proporcionó nuevo peso neto, convertir según tipo
net_weight_input = line.quantity.net_weight
if invoice_weight_type == "KGS":
if invoice_weight_type.lower() == "kgs":
line.quantity.net_weight = net_weight_input
else: # libras, convertir a kilos
line.quantity.net_weight = net_weight_input / Decimal("2.204624")
else:
# Mantener peso existente
line.quantity.net_weight = existing_line.quantity.net_weight
print(f"After weight conversion: net_weight={line.quantity.net_weight}, gross_weight={line.quantity.gross_weight}, weight_type={invoice_weight_type}")
line.quantity.net_weight = existing_line.quantity.net_weight
# Convertir peso bruto si se proporcionó
if line.quantity.gross_weight is not None:
gross_weight_input = line.quantity.gross_weight
if invoice_weight_type == "KGS":
if invoice_weight_type.lower() == "kgs":
line.quantity.gross_weight = gross_weight_input
else: # libras, convertir a kilos
line.quantity.gross_weight = gross_weight_input / Decimal("2.204624")

View File

@@ -232,18 +232,18 @@ def validate_create(
# Calcular peso neto en kilogramos (estándar interno)
if unit_is_kgs:
if invoice_weight_type == "KGS":
if invoice_weight_type.lower() == "kgs":
line.quantity.net_weight = quantity
else: # invoice en libras
line.quantity.net_weight = quantity * Decimal("2.204624")
elif unit_is_lbs:
if invoice_weight_type == "KGS":
if invoice_weight_type.lower() == "kgs":
line.quantity.net_weight = quantity / Decimal("2.204624")
else: # invoice en libras
line.quantity.net_weight = quantity
else:
# Otra unidad de medida - usar peso capturado y convertir si es necesario
if invoice_weight_type == "KGS":
if invoice_weight_type.lower() == "kgs":
# El peso capturado está en kilos
line.quantity.net_weight = net_weight_input
else:
@@ -273,7 +273,7 @@ def validate_create(
# Si no se proporcionó peso bruto, calcularlo
if not gross_weight_input or gross_weight_input == 0:
if invoice_weight_type == "KGS":
if invoice_weight_type.lower() == "kgs":
line.quantity.gross_weight = line.quantity.net_weight + (
package_weight_unit * package_quantity
)
@@ -283,7 +283,7 @@ def validate_create(
)
else:
# Convertir peso bruto capturado según tipo de factura
if invoice_weight_type == "KGS":
if invoice_weight_type.lower() == "kgs":
line.quantity.gross_weight = gross_weight_input
else: # libras
line.quantity.gross_weight = gross_weight_input / Decimal("2.204624")

View File

@@ -94,21 +94,19 @@ def validate_update(
# Se proporcionó nuevo peso neto, convertir según tipo
net_weight_input = line.quantity.net_weight
if invoice_weight_type == "KGS":
if invoice_weight_type.lower() == "kgs":
line.quantity.net_weight = net_weight_input
else: # libras, convertir a kilos
line.quantity.net_weight = net_weight_input / Decimal("2.204624")
else:
# Mantener peso existente
line.quantity.net_weight = existing_line.quantity.net_weight
print(f"After weight conversion: net_weight={line.quantity.net_weight}, gross_weight={line.quantity.gross_weight}, weight_type={invoice_weight_type}")
line.quantity.net_weight = existing_line.quantity.net_weight
# Convertir peso bruto si se proporcionó
if line.quantity.gross_weight is not None:
gross_weight_input = line.quantity.gross_weight
if invoice_weight_type == "KGS":
if invoice_weight_type.lower() == "kgs":
line.quantity.gross_weight = gross_weight_input
else: # libras, convertir a kilos
line.quantity.gross_weight = gross_weight_input / Decimal("2.204624")