feature/correccion-de-carga-csv-falta-de-datos-fk

This commit is contained in:
hreyes
2026-03-12 11:41:11 -06:00
parent a57b01b190
commit e1d4ef4a68
13 changed files with 852 additions and 345 deletions

View File

@@ -69,9 +69,10 @@ def apply_calculations(
line.depreciation_date = invoice_date
if (not line.description.description_spanish and not line.description.description_english) and (line.part_info.description_spanish and line.part_info.description_english):
line.description.description_spanish = line.part_info.description_spanish
line.description.description_english = line.part_info.description_english
part_info = getattr(line, "part_info", None)
if (not line.description.description_spanish and not line.description.description_english) and part_info and (part_info.description_spanish and part_info.description_english):
line.description.description_spanish = part_info.description_spanish
line.description.description_english = part_info.description_english
else:
if not line.description.description_spanish:
class_desc = (
@@ -184,10 +185,14 @@ def calculate_values(
# ==========================================
# CÁLCULOS DE VALORES EN MONEDA
# foreign=ME, local=MN, manual=MC
# Una sola fuente: currency_type (USD/ME, MXN/MN) cuando está presente, paridad con import y CSV.
# ==========================================
result = (
db.query(InvoiceFinancials.currency, InvoiceFinancials.exchange_rate)
db.query(
InvoiceFinancials.currency,
InvoiceFinancials.currency_type,
InvoiceFinancials.exchange_rate,
)
.filter(
InvoiceFinancials.invoice_id == line.invoice_id,
InvoiceFinancials.tenant_id == tenant_id,
@@ -198,23 +203,27 @@ def calculate_values(
if not result:
return
currency, exchange_rate = result
currency, currency_type, exchange_rate = result
if currency_type in ("USD", "ME"):
currency = "foreign"
elif currency_type in ("MXN", "MN"):
currency = "local"
if currency == "foreign": # ME
line.financial.unit_cost_usd = line.financial.unit_cost_capture
line.financial.value_usd = line.financial.unit_cost_usd * line.quantity.quantity
line.financial.unit_cost_mxn = line.financial.unit_cost_capture * exchange_rate
line.financial.unit_cost_mxn = line.financial.unit_cost_capture * (exchange_rate or 1)
line.financial.value_mxn = line.financial.unit_cost_mxn * line.quantity.quantity
line.financial.value_mc = line.financial.unit_cost_usd * line.quantity.quantity
elif currency == "local": # MN
line.financial.unit_cost_mxn = line.financial.unit_cost_capture
line.financial.value_mxn = line.financial.unit_cost_mxn * line.quantity.quantity
line.financial.unit_cost_usd = line.financial.unit_cost_capture / exchange_rate
line.financial.unit_cost_usd = line.financial.unit_cost_capture / (exchange_rate or 1)
line.financial.value_usd = line.financial.unit_cost_usd * line.quantity.quantity
line.financial.value_mc = line.financial.unit_cost_usd * line.quantity.quantity
elif currency == "manual": # MC
line.financial.unit_cost_usd = line.financial.unit_cost_capture / exchange_rate
line.financial.unit_cost_usd = line.financial.unit_cost_capture / (exchange_rate or 1)
line.financial.value_usd = line.financial.unit_cost_usd * line.quantity.quantity
line.financial.unit_cost_mxn = line.financial.unit_cost_usd * exchange_rate
line.financial.unit_cost_mxn = line.financial.unit_cost_usd * (exchange_rate or 1)
line.financial.value_mxn = line.financial.unit_cost_mxn * line.quantity.quantity
line.financial.value_mc = line.financial.unit_cost_capture * line.quantity.quantity

View File

@@ -17,6 +17,17 @@ from api.v1.modules.public.reference_data.payment_methods.models import PaymentM
from .common import validate_common
def _normalize_weight_type(logistics) -> str:
"""Paridad con CSV: enum o string a 'KGS'/'LBS' para comparaciones."""
if logistics is None:
return "KGS"
wt = getattr(logistics, "weight_type", None) or "KGS"
if hasattr(wt, "value"):
wt = wt.value
weight_str = str(wt).upper() if wt else "KGS"
return weight_str if weight_str in ("KGS", "LBS") else "KGS"
def validate_create(
db: Session,
line: LineItem,
@@ -212,16 +223,28 @@ def validate_create(
line.financial.value_temp_material_usd = line.financial.value_usd
line.financial.value_temp_material_mxn = line.financial.value_mxn
# value_mc: paridad con calculate_values y cargas CSV
currency = getattr(invoice.financials, "currency", None)
if currency == "foreign":
line.financial.value_mc = line.financial.value_usd
elif currency == "local":
line.financial.value_mc = line.financial.value_usd
elif currency == "manual":
line.financial.value_mc = unit_cost_capture * quantity
else:
line.financial.value_mc = line.financial.value_usd
# ==========================================
# VALIDAR Y CONVERTIR PESOS NETOS
# ==========================================
invoice_weight_type = invoice.logistics.weight_type # 'kgs' o 'lbs'
invoice_weight_type = _normalize_weight_type(invoice.logistics)
quantity = line.quantity.quantity or Decimal("0")
net_weight_input = line.quantity.net_weight or Decimal("0")
# Determinar si la unidad de medida es de peso
unit_is_kgs = line.unit_of_measure and line.unit_of_measure == "24" #KGS
unit_is_lbs = line.unit_of_measure and line.unit_of_measure == "25" #LBS
# UOM peso: aceptar 24/"24" y 25/"25" (paridad con CSV)
uom = line.unit_of_measure
unit_is_kgs = uom is not None and (str(uom) == "24" or uom == 24)
unit_is_lbs = uom is not None and (str(uom) == "25" or uom == 25)
# Calcular peso neto en kilogramos (estándar interno)
if unit_is_kgs:

View File

@@ -1,8 +1,7 @@
from sqlalchemy.orm import Session
from api.v1.modules.a76.invoices.models import InvoiceFinancials, InvoiceHeader, InvoiceLogistics
from api.v1.modules.a76.invoices.models import InvoiceFinancials, InvoiceHeader
from core.exceptions import ErrorCollector
from ...models import LineItem
from ...models import LineItem
from api.v1.modules.a76.classes.models import Class
@@ -13,57 +12,97 @@ def apply_calculations(
#TODO: SSisGen Logic
# if ssisgen.calcularcostounitarioenbaseavalortotalscaf = 1:
# unit_cost_capture = line.financial.total_value / line.financial.total_value <-- habria que revisar por que esta asi, por que para mi no tiene sentido, pero es lo que esta en clarion
caluclate_values(db, line, tenant_id, company_id)
if not line.fa_data.is_subitem:
line.fa_data.subitem_number = None
invoice_date = db.query(InvoiceHeader.invoice_date).filter(InvoiceHeader.id == line.invoice_id, InvoiceHeader.tenant_id == tenant_id, InvoiceHeader.company_id == company_id).scalar()
line.depreciation_date = invoice_date
if (not line.description.description_spanish and not line.description.description_english) and (line.part_info.description_spanish and line.part_info.description_english):
line.description.description_spanish = line.part_info.description_spanish
line.description.description_english = line.part_info.description_english
else:
if not line.description.description_spanish:
class_desc = (
db.query(Class.description_es, Class.description_en)
.filter(Class.id == line.class_id, Class.tenant_id == tenant_id, Class.company_id == company_id)
.first()
)
if class_desc:
line.description.description_spanish, line.description.description_english = class_desc
calculate_values(db, line, tenant_id, company_id)
apply_calculations_after_values(db, line, tenant_id, company_id, line_number)
def caluclate_values(
def apply_calculations_after_values(
db: Session, line: LineItem, tenant_id: int, company_id: int, line_number: int
):
"""
Aplica solo depreciation_date, descripción desde part/class y subitem_number.
Usado por el flujo CSV para no sobrescribir los valores ya calculados por currency_type.
"""
fa_data = getattr(line, "fa_data", None)
if fa_data is not None and not getattr(fa_data, "is_subitem", True):
fa_data.subitem_number = None
invoice_date = db.query(InvoiceHeader.invoice_date).filter(
InvoiceHeader.id == line.invoice_id,
InvoiceHeader.tenant_id == tenant_id,
InvoiceHeader.company_id == company_id,
).scalar()
if invoice_date is not None:
line.depreciation_date = invoice_date
if not getattr(line.description, "description_spanish", None) and not getattr(
line.description, "description_english", None
):
part_info = getattr(line, "part_info", None)
if part_info and getattr(part_info, "description_spanish", None) and getattr(part_info, "description_english", None):
line.description.description_spanish = part_info.description_spanish
line.description.description_english = part_info.description_english
else:
if not getattr(line.description, "description_spanish", None):
class_desc = (
db.query(Class.description_es, Class.description_en)
.filter(
Class.id == line.class_id,
Class.tenant_id == tenant_id,
Class.company_id == company_id,
)
.first()
)
if class_desc:
line.description.description_spanish, line.description.description_english = class_desc
def calculate_values(
db: Session, line: LineItem, tenant_id: int, company_id: int
):
"""
Una sola fuente de verdad: usa currency_type (USD/ME, MXN/MN) cuando está presente,
para paridad con create.py y cargas CSV. Si no hay currency_type, usa currency (foreign/local/manual).
"""
result = (
db.query(InvoiceFinancials.currency, InvoiceFinancials.exchange_rate)
.filter(InvoiceFinancials.invoice_id == line.invoice_id, InvoiceFinancials.tenant_id == tenant_id, InvoiceFinancials.company_id == company_id)
db.query(
InvoiceFinancials.currency,
InvoiceFinancials.currency_type,
InvoiceFinancials.exchange_rate,
)
.filter(
InvoiceFinancials.invoice_id == line.invoice_id,
InvoiceFinancials.tenant_id == tenant_id,
InvoiceFinancials.company_id == company_id,
)
.first()
)
if not result:
return
currency, exchange_rate = result
currency, currency_type, exchange_rate = result
# Prioridad: currency_type para alinear con create.py y CSV
if currency_type in ("USD", "ME"):
currency = "foreign"
elif currency_type in ("MXN", "MN"):
currency = "local"
# si currency_type es otro o None, se usa currency tal cual
if currency == "foreign":
line.financial.unit_cost_usd = line.financial.unit_cost_capture
line.financial.value_usd = line.financial.unit_cost_usd * line.quantity.quantity
line.financial.unit_cost_mxn = line.financial.unit_cost_capture * exchange_rate
line.financial.unit_cost_mxn = line.financial.unit_cost_capture * (exchange_rate or 1)
line.financial.value_mxn = line.financial.unit_cost_mxn * line.quantity.quantity
line.financial.value_mc = line.financial.unit_cost_usd * line.quantity.quantity
elif currency == "local":
line.financial.unit_cost_mxn = line.financial.unit_cost_capture
line.financial.value_mxn = line.financial.unit_cost_mxn * line.quantity.quantity
line.financial.unit_cost_usd = line.financial.unit_cost_capture / exchange_rate
line.financial.unit_cost_usd = line.financial.unit_cost_capture / (exchange_rate or 1)
line.financial.value_usd = line.financial.unit_cost_usd * line.quantity.quantity
line.financial.value_mc = line.financial.unit_cost_usd * line.quantity.quantity
elif currency == "manual":
line.financial.unit_cost_usd = line.financial.unit_cost_capture/exchange_rate
line.financial.unit_cost_usd = line.financial.unit_cost_capture / (exchange_rate or 1)
line.financial.value_usd = line.financial.unit_cost_usd * line.quantity.quantity
line.financial.unit_cost_mxn = line.financial.unit_cost_usd * exchange_rate
line.financial.unit_cost_mxn = line.financial.unit_cost_usd * (exchange_rate or 1)
line.financial.value_mxn = line.financial.unit_cost_mxn * line.quantity.quantity
line.financial.value_mc = line.financial.unit_cost_capture * line.quantity.quantity

View File

@@ -16,6 +16,17 @@ from api.v1.modules.a76.general_catalogs.fractions.us_tariff_fractions.models im
from .common import validate_common
def _normalize_weight_type(logistics) -> str:
"""Paridad con CSV: enum o string a 'KGS'/'LBS' para comparaciones."""
if logistics is None:
return "KGS"
wt = getattr(logistics, "weight_type", None) or "KGS"
if hasattr(wt, "value"):
wt = wt.value
weight_str = str(wt).upper() if wt else "KGS"
return weight_str if weight_str in ("KGS", "LBS") else "KGS"
def validate_create(
db: Session,
line: LineItem,
@@ -196,16 +207,28 @@ def validate_create(
line.financial.value_temp_material_usd = line.financial.value_usd
line.financial.value_temp_material_mxn = line.financial.value_mxn
# value_mc: paridad con calculate_values y cargas CSV
currency = getattr(invoice.financials, "currency", None)
if currency == "foreign":
line.financial.value_mc = line.financial.value_usd
elif currency == "local":
line.financial.value_mc = line.financial.value_usd
elif currency == "manual":
line.financial.value_mc = unit_cost_capture * quantity
else:
line.financial.value_mc = line.financial.value_usd
# ==========================================
# VALIDAR Y CONVERTIR PESOS NETOS
# ==========================================
invoice_weight_type = invoice.logistics.weight_type # 'kgs' o 'lbs'
invoice_weight_type = _normalize_weight_type(invoice.logistics)
quantity = line.quantity.quantity or Decimal("0")
net_weight_input = line.quantity.net_weight or Decimal("0")
# Determinar si la unidad de medida es de peso
unit_is_kgs = line.unit_of_measure and line.unit_of_measure == "24" #KGS
unit_is_lbs = line.unit_of_measure and line.unit_of_measure == "25" #LBS
# UOM peso: aceptar 24/"24" y 25/"25" (paridad con CSV)
uom = line.unit_of_measure
unit_is_kgs = uom is not None and (str(uom) == "24" or uom == 24)
unit_is_lbs = uom is not None and (str(uom) == "25" or uom == 25)
# Calcular peso neto en kilogramos (estándar interno)
if unit_is_kgs: