From cbb31dc1c94761529885150e9bf78acb8a8676bd Mon Sep 17 00:00:00 2001 From: Kevin_Ramirez Date: Tue, 28 Apr 2026 09:29:39 -0600 Subject: [PATCH] Arreglando las factruas --- .../a76/invoices/common/calculations.py | 55 ++++++++++++------- .../v1/modules/a76/invoices/common/mappers.py | 2 - .../a76/invoices/imports/validators/create.py | 2 +- .../a76/invoices/imports/validators/update.py | 2 +- .../api/v1/modules/a76/invoices/schemas.py | 2 +- .../api/v1/modules/a76/invoices/services.py | 12 +++- 6 files changed, 47 insertions(+), 28 deletions(-) diff --git a/backend/api/v1/modules/a76/invoices/common/calculations.py b/backend/api/v1/modules/a76/invoices/common/calculations.py index f3278732..0bbe1c5f 100644 --- a/backend/api/v1/modules/a76/invoices/common/calculations.py +++ b/backend/api/v1/modules/a76/invoices/common/calculations.py @@ -9,28 +9,41 @@ from .. import schemas def apply_calculations( invoice: schemas.InvoiceHeaderUpdate, ): - if invoice.invoice_type == "CR": - invoice.compliance_mx.is_regime_change = True - else: - invoice.compliance_mx.is_regime_change = False - - increments_me = (invoice.financials.freight or 0) + (invoice.financials.insurance or 0) + (invoice.financials.packaging or 0) + (invoice.financials.other_increments or 0) - if invoice.financials.currency == "foreign": - invoice.financials.total_increments_me = increments_me - invoice.financials.total_increments_mn = invoice.financials.total_increments_me * invoice.financials.exchange_rate - invoice.financials.currency_type = "USD" - elif invoice.financials.currency == "local": - invoice.financials.total_increments_mn = increments_me - invoice.financials.total_increments_me = invoice.financials.total_increments_mn / invoice.financials.exchange_rate - invoice.financials.currency_type = "MXN" - elif invoice.financials.currency == "manual": - invoice.financials.total_increments_me = (increments_me)/invoice.financials.exchange_rate - invoice.financials.total_increments_mn = invoice.financials.total_increments_me * invoice.financials.exchange_rate - + if invoice.compliance_mx: + if invoice.invoice_type == "CR": + invoice.compliance_mx.is_regime_change = True + else: + invoice.compliance_mx.is_regime_change = False - invoice.compliance_mx.is_pedimento_pending = False - if not invoice.compliance_mx.pedimento_id: - invoice.compliance_mx.is_pedimento_pending = True + if invoice.financials: + # Arithmetica defensiva: (val or 0) + freight = invoice.financials.freight or 0 + insurance = invoice.financials.insurance or 0 + packaging = invoice.financials.packaging or 0 + other = invoice.financials.other_increments or 0 + increments_me = freight + insurance + packaging + other + + # Tipo de cambio seguro + tc = invoice.financials.exchange_rate or 1 # Fallback a 1 para evitar division por cero + + if invoice.financials.currency == "foreign": + invoice.financials.total_increments_me = increments_me + invoice.financials.total_increments_mn = increments_me * tc + invoice.financials.currency_type = "USD" + elif invoice.financials.currency == "local": + invoice.financials.total_increments_mn = increments_me + invoice.financials.total_increments_me = increments_me / tc if tc != 0 else 0 + invoice.financials.currency_type = "MXN" + elif invoice.financials.currency == "manual": + # TC_MM para moneda manual + tc_mm = invoice.financials.exchange_rate_mm or 1 + invoice.financials.total_increments_me = increments_me / tc_mm if tc_mm != 0 else 0 + invoice.financials.total_increments_mn = invoice.financials.total_increments_me * tc + + if invoice.compliance_mx: + invoice.compliance_mx.is_pedimento_pending = False + if not invoice.compliance_mx.pedimento_id: + invoice.compliance_mx.is_pedimento_pending = True diff --git a/backend/api/v1/modules/a76/invoices/common/mappers.py b/backend/api/v1/modules/a76/invoices/common/mappers.py index 3bea0dd9..3b3fe57b 100644 --- a/backend/api/v1/modules/a76/invoices/common/mappers.py +++ b/backend/api/v1/modules/a76/invoices/common/mappers.py @@ -7,8 +7,6 @@ def clean_dict(data_dict: dict) -> dict: if isinstance(value, str) and not value.strip(): cleaned[key] = None - elif value == 0 and (key.endswith('_id') or key == 'remesa'): - cleaned[key] = None else: cleaned[key] = value return cleaned diff --git a/backend/api/v1/modules/a76/invoices/imports/validators/create.py b/backend/api/v1/modules/a76/invoices/imports/validators/create.py index 9afedbc2..3670ea35 100644 --- a/backend/api/v1/modules/a76/invoices/imports/validators/create.py +++ b/backend/api/v1/modules/a76/invoices/imports/validators/create.py @@ -14,7 +14,7 @@ def validate_create(db: Session, invoice: InvoiceHeaderCreate, tenant_id: int, c if not invoice.invoice_type: errors.add_required_error("invoice_type") - if not invoice.document_type and invoice.invoice_type != "MEX": + if not invoice.document_type and invoice.invoice_type not in ["MEX", "AME"]: errors.add_required_error("document_type") if not invoice.invoice_number: diff --git a/backend/api/v1/modules/a76/invoices/imports/validators/update.py b/backend/api/v1/modules/a76/invoices/imports/validators/update.py index 565d3722..a91b1676 100644 --- a/backend/api/v1/modules/a76/invoices/imports/validators/update.py +++ b/backend/api/v1/modules/a76/invoices/imports/validators/update.py @@ -235,7 +235,7 @@ def validate_update( invoice.compliance_mx.aduana = clean_str(invoice.compliance_mx.aduana) # Validar que aduana sea obligatorio (excepto para MEX) - if existing_invoice.invoice_type != "MEX": + if existing_invoice.invoice_type not in ["MEX", "AME"]: current_aduana = invoice.compliance_mx.aduana if invoice.compliance_mx else (existing_invoice.compliance_mx.aduana if existing_invoice.compliance_mx else None) if not current_aduana: errors.add_required_error("aduana") diff --git a/backend/api/v1/modules/a76/invoices/schemas.py b/backend/api/v1/modules/a76/invoices/schemas.py index e8df1724..57d25c75 100644 --- a/backend/api/v1/modules/a76/invoices/schemas.py +++ b/backend/api/v1/modules/a76/invoices/schemas.py @@ -511,7 +511,7 @@ class InvoiceHeaderUpdate(InvoiceHeaderBase): operation_type: Optional[OperationType] = None compliance_mx: Optional[InvoiceComplianceMxUpdate] = None - financials: Optional[InvoiceFinancialsUpdate] + financials: Optional[InvoiceFinancialsUpdate] = None logistics: Optional[InvoiceLogisticsUpdate] = None details: Optional[List[InvoiceSalesDetailsUpdate]] = None collections: Optional[List[InvoiceCollectionsUpdate]] = None diff --git a/backend/api/v1/modules/a76/invoices/services.py b/backend/api/v1/modules/a76/invoices/services.py index 3960adf8..f39a9833 100644 --- a/backend/api/v1/modules/a76/invoices/services.py +++ b/backend/api/v1/modules/a76/invoices/services.py @@ -407,6 +407,10 @@ class InvoiceService: # Autocalculo remesa (si aplica) ANTES de validar _autofill_remesa_if_needed(db, invoice_data, tenant_id, company_id) + # DEBUG: Log payload for analysis + print(f"DEBUG: Creating invoice {invoice_data.invoice_number} of type {invoice_data.invoice_type}") + print(f"DEBUG: Payload: {invoice_data.model_dump()}") + # Validar si la factura ya existe invoice_exists(db, invoice_data.invoice_number, tenant_id, company_id, errors) if invoice_data.operation_type == "exp": @@ -445,8 +449,8 @@ class InvoiceService: invoice_dict["capture_user"] = username invoice_dict["who_processed"] = username - # Ensure document_type respects DB constraints for MEX invoices (bypass clean_dict) - if invoice_dict.get("invoice_type") == "MEX" and not invoice_dict.get("document_type"): + # Ensure document_type respects DB constraints for MEX/AME invoices (bypass clean_dict) + if invoice_dict.get("invoice_type") in ["MEX", "AME"] and not invoice_dict.get("document_type"): invoice_dict["document_type"] = None new_invoice = models.InvoiceHeader(**invoice_dict) @@ -538,6 +542,10 @@ class InvoiceService: company_id: int, ) -> Optional[models.InvoiceHeader]: """Update an existing invoice with validation""" + + # DEBUG: Log payload for analysis + print(f"DEBUG: Updating invoice ID {invoice_id} of type {invoice_data.invoice_type}") + print(f"DEBUG: Payload: {invoice_data.model_dump(exclude_unset=True)}") # Validaciones con ErrorCollector errors = ErrorCollector()