feat: Enhance fixed asset class management with new fields, refactor forms, and update invoice-related services.

This commit is contained in:
Galindo97
2026-02-24 08:17:38 -06:00
parent f9f6a17c4a
commit 3898d3a1e0
25 changed files with 910 additions and 769 deletions

View File

@@ -223,6 +223,7 @@ class ClassWithFADataResponse(BaseModel):
fa_class_id: Optional[int] = None
depreciation_rate: Optional[Decimal] = None
fda_code: Optional[str] = None
eccn_code: Optional[str] = None
class_enabled: Optional[bool] = None
model_config = ConfigDict(from_attributes=True)

View File

@@ -152,6 +152,7 @@ class ClassService:
"fa_class_id": fa_class.id if fa_class else None,
"depreciation_rate": fa_class.depreciation_rate if fa_class else None,
"fda_code": fa_class.fda_code if fa_class else None,
"eccn_code": fa_class.eccn_code if fa_class else None,
"class_enabled": fa_class.class_enabled if fa_class else None,
}
combined.append(class_dict)

View File

@@ -134,12 +134,14 @@ class InvoiceCatalogService:
# Drivers
try:
drivers, _ = DriverService.get_all(db, tenant_id, company_id, limit=1000)
drivers = DriverService.list_drivers(db, str(company_id), str(tenant_id))
response.drivers = [
DriverResponseDTO.model_validate(d) for d in drivers
]
except Exception as e:
print(f"Error fetching drivers: {e}")
# Initialize drivers as empty list if an error occurs
drivers = []
# Trailers
try:

View File

@@ -188,6 +188,23 @@ def validate_create(
line.financial.unit_cost_mxn = unit_cost_capture
# Si es otro tipo de moneda, dejamos el costo como está
# Calcular valores totales basados en cantidad y costo unitario
quantity = line.quantity.quantity or Decimal("0")
# Valor Comercial
if line.financial.unit_cost_usd is not None:
line.financial.value_usd = line.financial.unit_cost_usd * quantity
if line.financial.unit_cost_mxn is not None:
line.financial.value_mxn = line.financial.unit_cost_mxn * quantity
# Valor Aduanas (asumiendo que es igual al Valor Comercial por defecto)
line.financial.customs_value_usd = line.financial.value_usd
line.financial.customs_value_mxn = line.financial.value_mxn
# Valor MP Temp (Materia Prima Temporal)
line.financial.value_temp_material_usd = line.financial.value_usd
line.financial.value_temp_material_mxn = line.financial.value_mxn
# ==========================================
# VALIDAR Y CONVERTIR PESOS NETOS
# ==========================================

View File

@@ -64,6 +64,30 @@ def validate_update(
# Costo unitario
if line.financial.unit_cost_capture is None:
line.financial.unit_cost_capture = existing_line.financial.unit_cost_capture
# Recalcular valores monetarios si el costo o la cantidad cambian
currency_type = invoice.financials.currency_type
unit_cost_capture = line.financial.unit_cost_capture or Decimal("0")
if currency_type in ["USD", "ME"]:
line.financial.unit_cost_usd = unit_cost_capture
line.financial.unit_cost_mxn = unit_cost_capture * exchange_rate
elif currency_type in ["MXN", "MN"]:
line.financial.unit_cost_usd = (unit_cost_capture / exchange_rate) if exchange_rate else Decimal("0")
line.financial.unit_cost_mxn = unit_cost_capture
quantity = line.quantity.quantity if line.quantity.quantity is not None else existing_line.quantity.quantity
if line.financial.unit_cost_usd is not None:
line.financial.value_usd = line.financial.unit_cost_usd * quantity
if line.financial.unit_cost_mxn is not None:
line.financial.value_mxn = line.financial.unit_cost_mxn * quantity
line.financial.customs_value_usd = line.financial.value_usd
line.financial.customs_value_mxn = line.financial.value_mxn
line.financial.value_temp_material_usd = line.financial.value_usd
line.financial.value_temp_material_mxn = line.financial.value_mxn
# Convertir peso neto si se proporcionó
invoice_weight_type = invoice.logistics.weight_type

View File

@@ -4,7 +4,7 @@ Complete nested one-to-one structure:
LineItem -> LineFinancial -> LineQuantity -> LineCustoms -> LineDescription -> LineReference
"""
from typing import Any, Optional
from typing import Any, Optional, Union
from datetime import datetime
from decimal import Decimal
from pydantic import BaseModel, Field, ConfigDict, model_validator
@@ -58,13 +58,13 @@ class LineItemBase(BaseModel):
line_number: int = Field(..., description="Line number")
# Part identification
part_number_id: Optional[int] = Field(
part_number_id: Union[int, str, None] = Field(
None,
description="Part number",
alias="part_number",
serialization_alias="part_number_id",
)
component_part_number_id: Optional[int] = Field(
component_part_number_id: Union[int, str, None] = Field(
None,
description="Component part number",
alias="component_part_number",

View File

@@ -36,6 +36,7 @@ from .line_references.models import LineReference
from api.v1.modules.a24.fa.fa_item_lines.models import FaLineItem
from .models import LineItem
from api.v1.modules.a76.invoices.models import InvoiceHeader
from api.v1.modules.a76.parts.models import Part
logger = logging.getLogger(__name__)
@@ -45,6 +46,33 @@ class ItemService:
Service for managing Items and related entities with tenant/company isolation
"""
@staticmethod
def _resolve_part_number(
db: Session,
part_number: Optional[str],
tenant_id: int,
company_id: int,
) -> Optional[int]:
"""Try to resolve a part number string to its database ID."""
if not part_number:
return None
# If it's already an integer (or a string representing an integer), it might be the ID
try:
return int(part_number)
except (ValueError, TypeError):
# It's a string part number (e.g., "MAQ-001"), look it up
part = (
db.query(Part)
.filter(
Part.part_number == part_number,
Part.tenant_id == tenant_id,
Part.company_id == company_id,
)
.first()
)
return part.id if part else None
@staticmethod
def _get_next_line_number(db: Session, invoice_id: int) -> int:
"""Calculate the next line_number for a given invoice based on database."""
@@ -282,6 +310,24 @@ class ItemService:
# Calculate the next line number for this single item
line_number = ItemService._get_next_line_number(db, item_data.invoice_id)
# Resolve part ID if a string is provided in part_number (alias for part_number_id)
if item_data.part_number_id and not isinstance(item_data.part_number_id, int):
resolved_id = ItemService._resolve_part_number(
db, str(item_data.part_number_id), tenant_id, company_id
)
if resolved_id:
item_data.part_number_id = resolved_id
# Resolve component part ID
if item_data.component_part_number_id and not isinstance(
item_data.component_part_number_id, int
):
resolved_id = ItemService._resolve_part_number(
db, str(item_data.component_part_number_id), tenant_id, company_id
)
if resolved_id:
item_data.component_part_number_id = resolved_id
# Validar el item
validate_create(
db,
@@ -378,6 +424,24 @@ class ItemService:
):
errors.raise_if_errors("Error al actualizar el item")
# Resolve part ID if a string is provided in part_number (alias for part_number_id)
if hasattr(item_data, 'part_number_id') and item_data.part_number_id and not isinstance(item_data.part_number_id, int):
resolved_id = ItemService._resolve_part_number(
db, str(item_data.part_number_id), tenant_id, company_id
)
if resolved_id:
item_data.part_number_id = resolved_id
# Resolve component part ID
if hasattr(item_data, 'component_part_number_id') and item_data.component_part_number_id and not isinstance(
item_data.component_part_number_id, int
):
resolved_id = ItemService._resolve_part_number(
db, str(item_data.component_part_number_id), tenant_id, company_id
)
if resolved_id:
item_data.component_part_number_id = resolved_id
# Validar el item que se va a actualizar
validate_update(
db,

View File

@@ -112,9 +112,10 @@ class DatabaseHelper:
try:
sql = text("""
SELECT name, rfc
FROM a76.clients_and_providers
WHERE id = :client_code AND client_or_provider = :client_type
SELECT cp.name, cp.rfc, cpp.tax_id
FROM a76.clients_and_providers cp
LEFT JOIN a76.clients_and_providers_programs cpp ON cpp.client_id = cp.id
WHERE cp.id = :client_code AND cp.client_or_provider = :client_type
""")
result = db.execute(sql, {"client_code": client_code, "client_type": client_type}).fetchone()
@@ -122,7 +123,7 @@ class DatabaseHelper:
return {
"name": result[0],
"rfc": result[1],
"tax_id": None # Column does not exist in this table
"tax_id": result[2]
}
else:
logger.debug(f"Client {client_code} not found as {client_type}")
@@ -233,8 +234,7 @@ class DatabaseHelper:
SELECT serial_numbers, model, brand
FROM a76.item_line_series ils
INNER JOIN a76.item_lines il ON ils.line_item_id = il.id
INNER JOIN a76.items i ON il.item_id = i.id
WHERE i.invoice_id = :invoice_id
WHERE il.invoice_id = :invoice_id
AND il.line_number = :linea
ORDER BY ils.id
LIMIT 1
@@ -290,8 +290,7 @@ class DatabaseHelper:
SELECT serial_numbers, model, expo_brad
FROM a76.item_line_series ils
INNER JOIN a76.item_lines il ON ils.line_item_id = il.id
INNER JOIN a76.items i ON il.item_id = i.id
WHERE i.invoice_id = :invoice_id
WHERE il.invoice_id = :invoice_id
AND il.line_number = :linea
ORDER BY ils.id
LIMIT 1

View File

@@ -89,6 +89,11 @@ class DefinitiveImportService:
# Totals come directly from GROUP BY query (no N+1 problem)
total_me = to_float(row[28]) # total_me from SUM aggregation
total_mn = to_float(row[29]) # total_mn from SUM aggregation
sum_value_usd = to_float(row[30])
sum_value_mxn = to_float(row[31])
total_me = sum_value_usd if sum_value_usd > 0 else total_me
total_mn = sum_value_mxn if sum_value_mxn > 0 else total_mn
# Calculate exchange rate and value
valor_comercial, tipo_cambio = ExchangeRateCalculator.calculate_for_aggregated(
@@ -215,58 +220,58 @@ class DefinitiveImportService:
valor_comercial, tipo_cambio = ExchangeRateCalculator.calculate_exchange_rate_and_value(
db=db,
db_name=filters.database_name,
es_subpartida=row[39], # EsSubPartida
valor_me=row[26],
valor_mn_direct=row[24],
fecha_pago=row[12],
fecha_inicio=row[10],
clave_ped=row[58],
tipo_cambio_partida=row[50],
es_subpartida=row[39], # C40 - EsSubPartida
valor_me=row[26], # C27 - ValorImpoME
valor_mn_direct=row[24], # C25 - ValorImpoMN
fecha_pago=row[12], # C13 - Fecha_Pago
fecha_inicio=row[10], # C1 entry_date
clave_ped=row[57] if len(row) > 57 else '', # C58 - TIPOPEDIMENTOTRANSPORTEE
tipo_cambio_partida=row[49], # C50 - TipoCambio
currency_type=filters.currency_type.value,
exchange_rate_type=filters.exchange_rate_type.value,
met_trans=met_trans
)
# Set peso values based on subpartida flag
if row[39] == 'P':
peso_neto = float(row[28]) if row[28] else 0.0
peso_bruto = float(row[29]) if row[29] else 0.0
if row[39] == 'P': # C40 - EsSubPartida
peso_neto = float(row[28]) if row[28] else 0.0 # C29
peso_bruto = float(row[29]) if row[29] else 0.0 # C30
else:
peso_neto = 0.0
peso_bruto = 0.0
series_info = DatabaseHelper.get_series_info(
db, filters.database_name, row[34], row[44], filters.is_shelter
db, filters.database_name, row[38], row[43], filters.is_shelter # C39, C44
)
simbolo_ex = None
if row[19]:
if row[48]: # C49 - Part Number
simbolo_ex = DatabaseHelper.get_part_export_symbol(
db, filters.database_name, row[19], filters.is_shelter
db, filters.database_name, row[48], filters.is_shelter
)
pedimento_r1 = DatabaseHelper.get_rectification_pedimento(
db, row[1], row[41], filters.is_shelter
db, row[1], row[40], filters.is_shelter # C2, C41
)
num_gaf_uni = DatabaseHelper.get_driver_badge(
db, filters.database_name, row[0]
db, filters.database_name, row[0] # C1
)
movement = MovementItemDetailed(
Linea=row[44],
Factura=row[0],
Pedimento=row[1],
FechaFactura=row[2],
Estatus=row[3],
ClavePed=row[4],
Linea=row[43], # C44
Factura=row[0], # C1
Pedimento=row[1], # C2
FechaFactura=parse_yyyymmdd_date(row[2]), # C3
Estatus=row[3], # C4
ClavePed=row[4], # C5
TipoMovTemDef='IMPDF',
EsCambioRegimen='N',
Regimen=row[9],
Fecha_Inicio=row[10],
Fecha_Fin=row[11],
Fecha_Pago=row[12],
Remesa=row[13],
Regimen=row[9], # C10
Fecha_Inicio=parse_yyyymmdd_date(row[10]), # C11
Fecha_Fin=parse_yyyymmdd_date(row[11]), # C12
Fecha_Pago=parse_yyyymmdd_date(row[12]), # C13
Remesa=str(row[13]) if row[13] is not None else None, # C14
Proveedor=row[7], # C8 - Provider name (from JOIN)
RFCProveedor=proveedor_info.get('rfc'),
ProveedorTaxID=proveedor_info.get('tax_id'),
@@ -275,42 +280,42 @@ class DefinitiveImportService:
VendidoATaxID=vendido_info.get('tax_id'),
AgenteAduanal=agente_info.get('name'),
Patente=agente_info.get('license'),
NumParte=row[19],
DescripcionE=StringHelper.clean_text(row[20]),
DescripcionI=StringHelper.clean_text(row[21]),
CantidadIE=float(row[22]) if row[22] else 0.0,
UniMed=row[23],
NumParte=row[48], # C49
DescripcionE=StringHelper.clean_text(row[20]), # C21
DescripcionI=StringHelper.clean_text(row[21]), # C22
CantidadIE=float(row[22]) if row[22] else 0.0, # C23
UniMed=row[23], # C24
ValorComercialMN=valor_comercial,
TipoCambio=tipo_cambio,
PesoNeto=peso_neto,
PesoBruto=peso_bruto,
OrdenCompraVenta=row[30],
FraccionArancelaria=row[31],
Preferencia=row[32],
Sector=None, # row[34] is invoice ID, Sector not in query
PaisOrigen=row[37],
OrdenCompraVenta=row[30], # C31
FraccionArancelaria=row[31], # C32
Preferencia=row[32], # C33
Sector=row[34], # C35
PaisOrigen=row[36], # C37
Aduana=aduana_nombre,
Advalorem=row[39],
Advalorem='P' if row[39] == 'P' else 'S', # C40
TipoExpo='',
PedimentoR1=pedimento_r1,
EDocument=row[42],
NumOperacionVU=row[43],
EDocument=row[41], # C42
NumOperacionVU=row[42], # C43
Series=series_info,
Marca=StringHelper.clean_text(row[45]),
Modelo=StringHelper.clean_text(row[46]),
FraccionAmericana=row[47],
ECCN=row[48],
Marca=StringHelper.clean_text(row[44]), # C45
Modelo=StringHelper.clean_text(row[45]), # C46
FraccionAmericana=row[46], # C47
ECCN=row[47], # C48
SimboloEx=simbolo_ex,
FechaEmision=row[51],
FechaEmision=parse_yyyymmdd_date(row[50]) if row[50] else None, # C51
BaseDeDatos=filters.database_name,
NumGafUni=num_gaf_uni,
UsuarioCap=row[52],
UsuarioAcr=row[53],
Transportista=row[54],
NumCaja=row[55],
Pedimento18=row[56],
AduanaCru=row[38],
Lote=row[57]
UsuarioCap=row[51], # C52
UsuarioAcr=row[52], # C53
Transportista=row[53], # C54
NumCaja=row[54], # C55
Pedimento18=row[55] if len(row) > 55 else '', # C56
AduanaCru=row[37], # C38
Lote=row[56] if len(row) > 56 else '' # C57
)
movements.append(movement)
@@ -337,7 +342,7 @@ class DefinitiveImportService:
if filters.range_type.value == "FF":
where_conditions.append(f"ih.invoice_date >= TO_DATE('{filters.start_date}', 'YYYYMMDD') AND ih.invoice_date <= TO_DATE('{filters.end_date}', 'YYYYMMDD')")
else:
where_conditions.append(f"log.payment_date >= TO_DATE('{filters.start_date}', 'YYYYMMDD') AND log.payment_date <= TO_DATE('{filters.end_date}', 'YYYYMMDD')")
where_conditions.append(f"pd.payment_date >= TO_DATE('{filters.start_date}', 'YYYYMMDD') AND pd.payment_date <= TO_DATE('{filters.end_date}', 'YYYYMMDD')")
# Note: Status filter applied at Python level after CASE WHEN in SELECT
# because is_updated doesn't directly represent AC/NA status
@@ -367,8 +372,7 @@ class DefinitiveImportService:
COALESCE(SUM(lf.value_mxn), 0)
FROM a76.item_line_financials lf
INNER JOIN a76.item_lines il ON il.id = lf.item_line_id
INNER JOIN a76.items itm ON itm.id = il.item_id
WHERE itm.invoice_id = :consecutivo
WHERE il.invoice_id = :consecutivo
AND COALESCE(il.is_subpartida, false) = false
""")

View File

@@ -65,13 +65,14 @@ class ExchangeRateCalculator:
fecha_pago=fecha_pago,
fecha_inicio=fecha_inicio,
tipo_pedimento=clave_ped,
use_transport_method=True, # Always use for detailed calculations
use_transport_method=True,
met_trans=met_trans
)
tc_value = DatabaseHelper.get_exchange_rate(db, db_name, fecha_tc)
if tc_value:
tipo_cambio_final = tc_value
# For ME, the value is always in foreign currency (USD)
return (valor_comercial, tipo_cambio_final)
# Handle local currency (MN) case
@@ -86,14 +87,14 @@ class ExchangeRateCalculator:
tc_value = DatabaseHelper.get_exchange_rate(db, db_name, fecha_tc)
if tc_value and valor_me is not None:
# Calculate MN value from ME * exchange rate
# Calculate MN value from ME * payment date exchange rate
return (valor_me * tc_value, tc_value)
else:
# Fall back to direct MN value and partida exchange rate
if tc_value is None:
logger.warning(f"Exchange rate not found for date {fecha_tc}, using partida values")
return (valor_mn_direct or 0.0, tipo_cambio_partida)
else:
# exchange_rate_type == "FT" (Invoice Date)
# Use direct MN value and partida exchange rate
return (valor_mn_direct or 0.0, tipo_cambio_partida)
@@ -151,22 +152,42 @@ class ExchangeRateCalculator:
use_transport_method=use_transport_method,
met_trans=met_trans
)
# For Shelter + FP: validate exchange rate exists (Clarion logic)
tc_value = DatabaseHelper.get_exchange_rate(
db, db_name, fecha_tc,
is_shelter=is_shelter,
raise_on_missing=is_shelter # Raise error if shelter and not found
raise_on_missing=is_shelter
)
if tc_value:
return (valor_me * tc_value, tc_value)
# For ME, the value is always in foreign currency (USD). Just return the new exchange rate.
return (valor_comercial, tc_value)
else:
logger.warning(f"Exchange rate not found for date {fecha_tc}, using DB values")
return (valor_mn, tipo_cambio_db)
else:
# FT or no payment date: use DB values
return (valor_comercial, tipo_cambio)
return (valor_comercial, tipo_cambio)
# Local currency case
else:
return (valor_mn, tipo_cambio_db)
if exchange_rate_type == "FP" and fecha_pago:
fecha_tc = DateHelper.get_fecha_tipo_cambio(
fecha_pago=fecha_pago,
fecha_inicio=fecha_inicio,
tipo_pedimento=tipo_pedimento,
use_transport_method=use_transport_method,
met_trans=met_trans
)
tc_value = DatabaseHelper.get_exchange_rate(
db, db_name, fecha_tc,
is_shelter=is_shelter,
raise_on_missing=is_shelter
)
if tc_value and valor_me is not None:
# Calculate MN value from ME * payment date exchange rate
return (valor_me * tc_value, tc_value)
else:
if tc_value is None:
logger.warning(f"Exchange rate not found for date {fecha_tc}, using DB values")
return (valor_mn, tipo_cambio_db)
else:
return (valor_mn, tipo_cambio_db)

View File

@@ -73,8 +73,18 @@ class ExportService:
consecutivo = row[16] # C35 - Consecutivo
# Totals come directly from GROUP BY query (no N+1 problem)
total_me = row[24] # total_me from SUM aggregation
total_mn = row[25] # total_mn from SUM aggregation
def to_float(val):
if val is None or val == '': return 0.0
try: return float(val)
except (ValueError, TypeError): return 0.0
total_me = to_float(row[24]) # total_me from SUM aggregation
total_mn = to_float(row[25]) # total_mn from SUM aggregation
sum_value_usd = to_float(row[27])
sum_value_mxn = to_float(row[28])
total_me = sum_value_usd if sum_value_usd > 0 else total_me
total_mn = sum_value_mxn if sum_value_mxn > 0 else total_mn
# Calculate exchange rate and value
valor_comercial, tipo_cambio = ExchangeRateCalculator.calculate_for_aggregated(
@@ -243,19 +253,19 @@ class ExportService:
# Build detailed movement item
movement = MovementItemDetailed(
Linea=row[41], # C42 - LineaExpo
Factura=row[0], # C1 - FacturaExpo
Pedimento=row[1], # C2 - PedimentoExpo
FechaFactura=row[2], # C3 - FechaFactura
Estatus=row[5], # C6 - Estatus
ClavePed=row[6], # C7 - ClavePed
TipoMovTemDef=row[33], # C34 - TipoFactura
Linea=row[39], # C42 - LineaExpo
Factura=row[0], # C1 - FacturaExpo
Pedimento=row[1], # C2 - PedimentoExpo
FechaFactura=row[2], # C3 - FechaFactura
Estatus=row[5], # C6 - Estatus
ClavePed=row[4], # C5 - ClavePed
TipoMovTemDef=row[31], # C34 - TipoFactura
EsCambioRegimen='N',
Regimen=row[7], # C8 - Regimen
Fecha_Inicio=row[8], # C9 - Fecha_Inicio
Fecha_Fin=row[9], # C10 - Fecha_Fin
Fecha_Pago=row[10], # C11 - Fecha_Pago
Remesa=row[11], # C12 - Remesa
Regimen=row[5], # C6 - Regime (Shared index with Estatus in this query)
Fecha_Inicio=parse_yyyymmdd_date(row[6]), # C7 - Fecha_Inicio
Fecha_Fin=parse_yyyymmdd_date(row[7]), # C8 - Fecha_Fin
Fecha_Pago=parse_yyyymmdd_date(row[8]), # C9 - Fecha_Pago
Remesa=row[9], # C12 - Remesa
TipoCambio=tipo_cambio_final,
Proveedor=proveedor_info.get("name"),
RFCProveedor=proveedor_info.get("rfc"),
@@ -265,41 +275,42 @@ class ExportService:
VendidoATaxID=vendido_info.get("tax_id"),
AgenteAduanal=agente_info.get("name"),
Patente=agente_info.get("license"),
NumParte=row[17], # C18 - Clase
NumParte=row[17], # C18 - NumParte
DescripcionE=StringHelper.remove_commas(row[18]), # C19 - DescripcionE
DescripcionI=StringHelper.remove_commas(row[19]), # C20 - DescripcionI
CantidadIE=row[20], # C21 - CantExpo
UniMed=row[21], # C22 - UnidadMedida
CantidadIE=row[20], # C21 - CantExpo
UniMed=row[21], # C22 - UnidadMedida
ValorComercialMN=valor_mn,
PesoNeto=peso_neto_final,
PesoBruto=peso_bruto_final,
OrdenCompraVenta=row[26], # C27 - OrdenCompra
FraccionArancelaria=row[27], # C28 - FraccionExpo
Preferencia=row[28], # C29 - TipoFraccion
Sector=row[30], # C31 - Sector
PaisOrigen=row[31], # C32 - PaisOrigen
OrdenCompraVenta=row[26], # C27 - OrdenCompra
FraccionArancelaria=row[27], # C28 - FraccionExpo
Preferencia=row[28], # C29 - TipoFraccion
Sector=row[30], # C31 - Sector
PaisOrigen=row[31], # C32 - PaisOrigen
Aduana=customs_name,
Advalorem=row[37], # C38 - EsSubPartida
Advalorem=row[27], # C30 - Advalorem
TipoExpo='EXPO DEF',
PedimentoR1=rectified_pedimento,
EDocument=row[39], # C40 - EDocument
NumOperacionVU=row[40], # C41 - NumOperacionVU
EDocument=row[37], # C40 - EDocument
NumOperacionVU=row[38], # C41 - NumOperacionVU
Series=series_info,
Marca=row[42], # C43 - Marca
Modelo=row[43], # C44 - Modelo
FraccionAmericana=row[44], # C45 - FraccionAme
ECCN=row[45], # C46 - ECCN
FechaEmision=row[48], # C49 - FechaEmision
Marca=StringHelper.clean_text(row[40]), # C43 - Marca
Modelo=StringHelper.clean_text(row[41]), # C44 - Modelo
FraccionAmericana=row[42], # C45 - FraccionAme
ECCN=row[43], # C46 - ECCN
FechaEmision=parse_yyyymmdd_date(row[46]) if row[46] else None, # C49 - FechaEmision
BaseDeDatos=filters.database_name,
NumGafUni=driver_badge,
UsuarioCap=row[49], # C50 - UsuarioCap
UsuarioAcr=row[50], # C51 - UsuarioAct
Transportista=row[51], # C52 - Transportista
NumCaja=row[52], # C53 - Transporte + NumTrasporte
Pedimento18=row[53], # C54 - Pedimento18
AduanaCru=row[32], # C33 - Aduana_Cruce
Lote=row[54] # C55 - Lote
UsuarioCap=row[47], # C50 - UsuarioCap
UsuarioAcr=row[48], # C51 - UsuarioAct
Transportista=row[49], # C52 - Carrier ID (derived from log.transport_id)
NumCaja=row[50], # C53 - log.transport_id || log.transport_num
Pedimento18=row[51], # C54 - empty
AduanaCru=row[30], # C33 - Aduana_Cruce
Lote=row[52] if len(row) > 52 else '' # C55 - Lote
)
movements.append(movement)
@@ -342,7 +353,7 @@ class ExportService:
logger.debug("Including cancelled invoices (include_cancelled = true)")
# Date range
date_field = "ih.invoice_date" if filters.range_type.value == "FF" else "log.payment_date"
date_field = "ih.invoice_date" if filters.range_type.value == "FF" else "pd.payment_date"
conditions.append(f"{date_field} >= TO_DATE('{filters.start_date}', 'YYYYMMDD')")
conditions.append(f"{date_field} <= TO_DATE('{filters.end_date}', 'YYYYMMDD')")

View File

@@ -102,8 +102,18 @@ class ExportRepairService:
consecutivo = row[15] # C35 - Consecutivo
# Totals come directly from GROUP BY query (no N+1 problem)
total_me = row[24] # total_me
total_mn = row[25] # total_mn
def to_float(val):
if val is None or val == '': return 0.0
try: return float(val)
except (ValueError, TypeError): return 0.0
total_me = to_float(row[24]) # total_me
total_mn = to_float(row[25]) # total_mn
sum_value_usd = to_float(row[27])
sum_value_mxn = to_float(row[28])
total_me = sum_value_usd if sum_value_usd > 0 else total_me
total_mn = sum_value_mxn if sum_value_mxn > 0 else total_mn
# Calculate exchange rate and value
valor_comercial, tipo_cambio = ExchangeRateCalculator.calculate_for_aggregated(
@@ -294,9 +304,9 @@ class ExportRepairService:
TipoMovTemDef=row[33], # C34 - TipoFactura
EsCambioRegimen='N',
Regimen=row[7], # C8 - Regimen
Fecha_Inicio=row[8], # C9 - Fecha_Inicio
Fecha_Fin=row[9], # C10 - Fecha_Fin
Fecha_Pago=row[10], # C11 - Fecha_Pago
Fecha_Inicio=parse_yyyymmdd_date(row[8]), # C9 - Fecha_Inicio
Fecha_Fin=parse_yyyymmdd_date(row[9]), # C10 - Fecha_Fin
Fecha_Pago=parse_yyyymmdd_date(row[10]), # C11 - Fecha_Pago
Remesa=row[11], # C12 - Remesa
Proveedor=proveedor_info.get('name'),
RFCProveedor=proveedor_info.get('rfc'),
@@ -306,7 +316,7 @@ class ExportRepairService:
VendidoATaxID=vendido_info.get('tax_id'),
AgenteAduanal=agente_info.get('name'),
Patente=agente_info.get('license'),
NumParte=row[17], # C18 - Clase (NumParte)
NumParte=row[46], # C47 - NumParte
DescripcionE=StringHelper.clean_text(row[18]), # C19
DescripcionI=StringHelper.clean_text(row[19]), # C20
CantidadIE=float(row[20]) if row[20] else 0.0, # C21
@@ -332,7 +342,7 @@ class ExportRepairService:
FraccionAmericana=row[44], # C45 - FraccionAme
ECCN=row[45], # C46 - ECCN
SimboloEx=simbolo_ex,
FechaEmision=row[48], # C49 - FechaFactura
FechaEmision=parse_yyyymmdd_date(row[48]) if row[48] else None, # C49 - FechaFactura
BaseDeDatos=filters.database_name,
NumGafUni=num_gaf_uni,
UsuarioCap=row[49], # C50 - UsuarioCap
@@ -341,7 +351,7 @@ class ExportRepairService:
NumCaja=row[52], # C53 - Transporte + NumTrasporte
Pedimento18=row[53], # C54 - Pedimento18
AduanaCru=row[32], # C33 - Aduana_Cruce
Lote=row[54] # C55 - Lote
Lote=row[54] if len(row) > 54 else '' # C55 - Lote
)
movements.append(movement)
@@ -369,7 +379,7 @@ class ExportRepairService:
if filters.range_type.value == "FF":
where_conditions.append(f"ih.invoice_date >= TO_DATE('{filters.start_date}', 'YYYYMMDD') AND ih.invoice_date <= TO_DATE('{filters.end_date}', 'YYYYMMDD')")
else:
where_conditions.append(f"log.payment_date >= TO_DATE('{filters.start_date}', 'YYYYMMDD') AND log.payment_date <= TO_DATE('{filters.end_date}', 'YYYYMMDD')")
where_conditions.append(f"pd.payment_date >= TO_DATE('{filters.start_date}', 'YYYYMMDD') AND pd.payment_date <= TO_DATE('{filters.end_date}', 'YYYYMMDD')")
# Provider filter
if filters.provider:
@@ -391,30 +401,6 @@ class ExportRepairService:
return " AND ".join(where_conditions)
def _calculate_totals(self, db: Session, db_name: str, consecutivo: int, discharge_filter: str) -> tuple:
"""Calculate totals for main partidas with discharge filter."""
discharge_clause = ""
if discharge_filter == "SiDes":
discharge_clause = " AND il.is_discharged = true"
elif discharge_filter == "NoDes":
discharge_clause = " AND il.is_discharged = false"
sql = text(f"""
SELECT
COALESCE(SUM(lf.value_usd), 0),
COALESCE(SUM(lf.value_mxn), 0)
FROM a76.item_line_financials lf
INNER JOIN a76.item_lines il ON il.id = lf.item_line_id
INNER JOIN a76.items itm ON itm.id = il.item_id
WHERE itm.invoice_id = :consecutivo
AND il.is_subpart = false
{discharge_clause}
""")
result = db.execute(sql, {"consecutivo": consecutivo}).fetchone()
total_me = float(result[0]) if result and result[0] is not None else 0.0
total_mn = float(result[1]) if result and result[1] is not None else 0.0
return total_me, total_mn
def _get_driver_badge(self, db: Session, db_name: str, factura: str) -> str:

View File

@@ -19,9 +19,9 @@ class TemporaryImportQueries:
CASE WHEN ih.is_updated THEN 'AC' ELSE 'NA' END AS C4,
COALESCE(ped.pedimento_code, '') AS C5,
COALESCE(ped.regime, '') AS C10,
COALESCE(TO_CHAR(log.entry_exit_date, 'YYYYMMDD'), '') AS C11,
COALESCE(TO_CHAR(log.delivery_date, 'YYYYMMDD'), '') AS C12,
COALESCE(TO_CHAR(log.payment_date, 'YYYYMMDD'), '') AS C13,
COALESCE(TO_CHAR(pd.entry_date, 'YYYYMMDD'), '') AS C11,
COALESCE(TO_CHAR(pd.end_date, 'YYYYMMDD'), TO_CHAR(pd.payment_date, 'YYYYMMDD'), '') AS C12,
COALESCE(TO_CHAR(pd.payment_date, 'YYYYMMDD'), '') AS C13,
COALESCE(cmp.remesa, 0) AS C14,
COALESCE(fin.exchange_rate, 0) AS C15,
COALESCE(cmp.provider_id::text, '') AS C16,
@@ -49,7 +49,9 @@ class TemporaryImportQueries:
COALESCE(fin.value_mn, 0) AS total_mn,
COALESCE(lf_agg.sum_value_mxn, 0) AS valor_comercial_mn,
COALESCE(lf_agg.sum_value_temp_mxn, 0) AS valor_mp_temp_mn,
COALESCE(lf_agg.sum_value_added_mxn, 0) AS valor_agre_mn
COALESCE(lf_agg.sum_value_added_mxn, 0) AS valor_agre_mn,
COALESCE(lf_agg.sum_value_temp_usd, 0) AS valor_mp_temp_usd,
COALESCE(lf_agg.sum_value_usd, 0) AS sum_value_usd
FROM a76.invoice_header ih
LEFT JOIN a76.invoice_compliance_mx cmp ON cmp.invoice_id = ih.id
LEFT JOIN a76.invoice_financials fin ON fin.invoice_id = ih.id
@@ -62,15 +64,17 @@ class TemporaryImportQueries:
pro_rect.original_license = ped.license AND
pro_rect.original_pedimento_number = ped.pedimento_number
LEFT JOIN a76.pedimentos ped_r1 ON ped_r1.id = pro_rect.pedimento_id
LEFT JOIN a76.pedimento_dates pd ON pd.pedimento_id = ped.id
LEFT JOIN (
SELECT i.invoice_id,
SELECT il.invoice_id,
SUM(COALESCE(lf.value_mxn, 0)) AS sum_value_mxn,
SUM(COALESCE(lf.value_temp_material_mxn, 0)) AS sum_value_temp_mxn,
SUM(COALESCE(lf.value_added_mxn, 0)) AS sum_value_added_mxn
FROM a76.items i
JOIN a76.item_lines il ON il.item_id = i.id
SUM(COALESCE(lf.value_added_mxn, 0)) AS sum_value_added_mxn,
SUM(COALESCE(lf.value_temp_material_usd, 0)) AS sum_value_temp_usd,
SUM(COALESCE(lf.value_usd, 0)) AS sum_value_usd
FROM a76.item_lines il
JOIN a76.item_line_financials lf ON lf.item_line_id = il.id
GROUP BY i.invoice_id
GROUP BY il.invoice_id
) lf_agg ON lf_agg.invoice_id = ih.id
WHERE ih.operation_type = 'imp'
AND ih.invoice_type = 'TEM'
@@ -95,9 +99,9 @@ class TemporaryImportQueries:
COALESCE(prov.name, '') AS C8,
COALESCE(client.name, '') AS C9,
COALESCE(ped.regime, '') AS C10,
COALESCE(TO_CHAR(log.entry_exit_date, 'YYYYMMDD'), '') AS C11,
COALESCE(TO_CHAR(log.delivery_date, 'YYYYMMDD'), '') AS C12,
COALESCE(TO_CHAR(log.payment_date, 'YYYYMMDD'), '') AS C13,
COALESCE(TO_CHAR(pd.entry_date, 'YYYYMMDD'), '') AS C11,
COALESCE(TO_CHAR(pd.end_date, 'YYYYMMDD'), TO_CHAR(pd.payment_date, 'YYYYMMDD'), '') AS C12,
COALESCE(TO_CHAR(pd.payment_date, 'YYYYMMDD'), '') AS C13,
COALESCE(cmp.remesa, 0) AS C14,
COALESCE(fin.exchange_rate, 0) AS C15,
COALESCE(cmp.provider_id::text, '') AS C16,
@@ -115,16 +119,16 @@ class TemporaryImportQueries:
COALESCE(lf.customs_value_usd, 0) AS C28,
COALESCE(lq.net_weight, 0) AS C29,
COALESCE(lq.gross_weight, 0) AS C30,
COALESCE(ih.purchase_order, '') AS C31,
COALESCE(il.order, ih.purchase_order, '') AS C31,
COALESCE(lc.fraction, '') AS C32,
COALESCE(lc.fraction_type, '') AS C33,
COALESCE(lc.advalorem_numeric, 0) AS C34,
COALESCE(lc.sector, '') AS C35,
COALESCE(NULLIF(lc.sector, ''), NULLIF(fap.sector, ''), '') AS C35,
COALESCE(lf.igi_amount_usd, 0) AS C36,
COALESCE(lc.origin_country, '') AS C37,
COALESCE(cmp.aduana, '') AS C38,
ih.id AS C39,
FALSE AS C40,
COALESCE(il.material_type, 'P') AS C40,
COALESCE(
ped_r1.year || '-' || ped_r1.customs_office || '-' || ped_r1.license || '-' || ped_r1.pedimento_number,
pro_origin.original_pedimento_year || '-' || pro_origin.original_customs_office || '-' || pro_origin.original_license || '-' || pro_origin.original_pedimento_number,
@@ -133,11 +137,11 @@ class TemporaryImportQueries:
cmp.edocument AS C42, -- [41]
cmp.vucem_operation_num AS C43, -- [42]
COALESCE(il.line_number, 0) AS C44,
'' AS C45,
'' AS C46,
REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(ld.brand, ''), CHR(44), ' '), CHR(9), ' '), CHR(10), ' '), CHR(13), ' ') AS C45,
REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(ld.model, ''), CHR(44), ' '), CHR(9), ' '), CHR(10), ' '), CHR(13), ' ') AS C46,
COALESCE(cls.us_fraction, '') AS C47,
COALESCE(prt.eccn, '') AS C48,
COALESCE(il.part_number::text, '') AS C49,
COALESCE(prt.eccn, fac.eccn_code, '') AS C48,
COALESCE(prt.part_number::text, '') AS C49,
COALESCE(fin.exchange_rate, 0) AS C50,
COALESCE(TO_CHAR(ih.emission_date, 'YYYYMMDD'), '') AS C51,
COALESCE(ih.capture_user, '') AS C52,
@@ -145,8 +149,10 @@ class TemporaryImportQueries:
COALESCE(log.carrier_id, '') AS C54,
COALESCE(log.transport_num || ' ' || log.license_plate, '') AS C55,
'' AS C56,
'' AS C57,
'' AS C58
COALESCE(ld.lot, '') AS C57,
'' AS C58,
COALESCE(lf.value_temp_material_mxn, 0) AS C59,
COALESCE(lf.value_temp_material_usd, 0) AS C60
FROM a76.invoice_header ih
LEFT JOIN a76.invoice_compliance_mx cmp ON cmp.invoice_id = ih.id
LEFT JOIN a76.invoice_financials fin ON fin.invoice_id = ih.id
@@ -159,16 +165,18 @@ class TemporaryImportQueries:
pro_rect.original_license = ped.license AND
pro_rect.original_pedimento_number = ped.pedimento_number
LEFT JOIN a76.pedimentos ped_r1 ON ped_r1.id = pro_rect.pedimento_id
LEFT JOIN a76.pedimento_dates pd ON pd.pedimento_id = ped.id
LEFT JOIN a76.clients_and_providers prov ON prov.id = cmp.provider_id
LEFT JOIN a76.clients_and_providers client ON client.id = cmp.sold_to_id
LEFT JOIN a76.items i ON i.invoice_id = ih.id
LEFT JOIN a76.item_lines il ON il.item_id = i.id
LEFT JOIN a76.item_lines il ON il.invoice_id = ih.id
LEFT JOIN a76.item_line_descriptions ld ON ld.item_line_id = il.id
LEFT JOIN a76.item_line_quantities lq ON lq.item_line_id = il.id
LEFT JOIN a76.item_line_financials lf ON lf.item_line_id = il.id
LEFT JOIN a76.item_line_customs lc ON lc.item_line_id = il.id
LEFT JOIN a76.classes cls ON cls.id = il.class_id
LEFT JOIN a76.parts prt ON prt.id = il.part_number
LEFT JOIN a24.fa_classes fac ON fac.class_id = cls.id
LEFT JOIN a76.parts prt ON prt.id = il.part_number_id
LEFT JOIN a24.fa_partes fap ON fap.id = prt.id
LEFT JOIN a76.units_of_measure um ON um.id = il.unit_of_measure
WHERE ih.operation_type = 'imp'
AND ih.invoice_type = 'TEM'
@@ -225,9 +233,9 @@ class DefinitiveImportQueries:
CASE WHEN ih.is_updated THEN 'AC' ELSE 'NA' END AS C4,
COALESCE(ped.pedimento_code, '') AS C5,
COALESCE(ped.regime, '') AS C10,
COALESCE(TO_CHAR(log.entry_exit_date, 'YYYYMMDD'), '') AS C11,
COALESCE(TO_CHAR(log.delivery_date, 'YYYYMMDD'), '') AS C12,
COALESCE(TO_CHAR(log.payment_date, 'YYYYMMDD'), '') AS C13,
COALESCE(TO_CHAR(pd.entry_date, 'YYYYMMDD'), '') AS C11,
COALESCE(TO_CHAR(pd.end_date, 'YYYYMMDD'), TO_CHAR(pd.payment_date, 'YYYYMMDD'), '') AS C12,
COALESCE(TO_CHAR(pd.payment_date, 'YYYYMMDD'), '') AS C13,
COALESCE(log.payment_receipt_num, '') AS C14,
COALESCE(fin.exchange_rate, 0) AS C15,
COALESCE(cmp.provider_id::text, '') AS C16,
@@ -252,7 +260,9 @@ class DefinitiveImportQueries:
'' AS C58,
'' AS C59,
COALESCE(fin.value_me, 0) AS total_me,
COALESCE(fin.value_mn, 0) AS total_mn
COALESCE(fin.value_mn, 0) AS total_mn,
COALESCE(lf_agg.sum_value_usd, 0) AS sum_value_usd,
COALESCE(lf_agg.sum_value_mxn, 0) AS sum_value_mxn
FROM a76.invoice_header ih
LEFT JOIN a76.invoice_compliance_mx cmp ON cmp.invoice_id = ih.id
LEFT JOIN a76.invoice_financials fin ON fin.invoice_id = ih.id
@@ -265,6 +275,16 @@ class DefinitiveImportQueries:
pro_rect.original_license = ped.license AND
pro_rect.original_pedimento_number = ped.pedimento_number
LEFT JOIN a76.pedimentos ped_r1 ON ped_r1.id = pro_rect.pedimento_id
LEFT JOIN a76.pedimento_dates pd ON pd.pedimento_id = ped.id
LEFT JOIN (
SELECT il.invoice_id,
SUM(CASE WHEN COALESCE(fil.is_subitem, false) THEN 0 ELSE COALESCE(lf.value_mxn, 0) END) AS sum_value_mxn,
SUM(CASE WHEN COALESCE(fil.is_subitem, false) THEN 0 ELSE COALESCE(lf.value_usd, 0) END) AS sum_value_usd
FROM a76.item_lines il
JOIN a76.item_line_financials lf ON lf.item_line_id = il.id
LEFT JOIN a24.fa_item_lines fil ON fil.id = il.id
GROUP BY il.invoice_id
) lf_agg ON lf_agg.invoice_id = ih.id
WHERE ih.operation_type = 'imp'
AND ih.invoice_type IN ('DEF', 'EXDEF', 'MATDE')
AND {where_clause}
@@ -273,70 +293,73 @@ class DefinitiveImportQueries:
@staticmethod
def build_main_query(db_name: str, where_clause: str) -> str:
"""Build main SQL query for DETAILED mode (all partidas) from PostgreSQL for definitive imports."""
return f"""
SELECT
ih.invoice_number AS C1, -- [0]
ped.pedimento_number AS C2, -- [1]
ih.invoice_date AS C3, -- [2]
ped.status AS C4, -- [3]
ped.pedimento_code AS C5, -- [4]
'' AS C6, -- [5]
'' AS C7, -- [6]
COALESCE(prov.name, '') AS C8, -- [7] Provider name
COALESCE(client.name, '') AS C9, -- [8] Client name
ped.regime AS C10, -- [9]
log.entry_exit_date AS C11, -- [10]
log.delivery_date AS C12, -- [11]
log.payment_date AS C13, -- [12]
log.payment_receipt_num AS C14, -- [13]
'' AS C15, -- [14]
cmp.provider_id AS C16, -- [15]
cmp.sold_to_id AS C17, -- [16]
cmp.customs_broker_id AS C18, -- [17]
'' AS C19, -- [18]
prt.part_number AS C20, -- [19]
ld.description_spanish AS C21, -- [20]
ld.description_english AS C22, -- [21]
lq.quantity AS C23, -- [22]
um.code AS C24, -- [23]
lf.value_mxn AS C25, -- [24]
'' AS C26, -- [25]
lf.value_usd AS C27, -- [26]
'' AS C28, -- [27]
lq.net_weight AS C29, -- [28]
lq.gross_weight AS C30, -- [29]
ih.purchase_order AS C31, -- [30]
lc.fraction AS C32, -- [31]
'' AS C33, '' AS C34, -- [32-33]
ih.id AS C35, -- [34]
'' AS C36, '' AS C37, -- [35-36]
lc.origin_country AS C38, -- [37]
cmp.aduana AS C39, -- [38]
il.material_type AS C40, -- [39]
il.id AS C41, -- [40]
ih.invoice_number AS C1,
COALESCE(ped.pedimento_number, '') AS C2,
TO_CHAR(ih.invoice_date, 'YYYYMMDD') AS C3,
CASE WHEN ih.is_updated THEN 'AC' ELSE 'NA' END AS C4,
COALESCE(ped.pedimento_code, '') AS C5,
COALESCE(fin.value_me, 0) AS C6,
COALESCE(fin.value_mn, 0) AS C7,
COALESCE(prov.name, '') AS C8,
COALESCE(client.name, '') AS C9,
COALESCE(ped.regime, '') AS C10,
COALESCE(TO_CHAR(pd.entry_date, 'YYYYMMDD'), '') AS C11,
COALESCE(TO_CHAR(pd.end_date, 'YYYYMMDD'), TO_CHAR(pd.payment_date, 'YYYYMMDD'), '') AS C12,
COALESCE(TO_CHAR(pd.payment_date, 'YYYYMMDD'), '') AS C13,
COALESCE(log.payment_receipt_num, '') AS C14,
COALESCE(fin.exchange_rate, 0) AS C15,
COALESCE(cmp.provider_id::text, '') AS C16,
COALESCE(cmp.sold_to_id::text, '') AS C17,
COALESCE(cmp.customs_broker_id::text, '') AS C18,
'' AS C19,
COALESCE(il.class_id::text, '') AS C20,
REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(ld.description_spanish, ''), CHR(44), ' '), CHR(9), ' '), CHR(10), ' '), CHR(13), ' ') AS C21,
REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(ld.description_english, ''), CHR(44), ' '), CHR(9), ' '), CHR(10), ' '), CHR(13), ' ') AS C22,
COALESCE(lq.quantity, 0) AS C23,
COALESCE(um.code, '') AS C24,
COALESCE(lf.value_mxn, 0) AS C25,
COALESCE(lf.customs_value_mxn, 0) AS C26,
COALESCE(lf.value_usd, 0) AS C27,
COALESCE(lf.customs_value_usd, 0) AS C28,
COALESCE(lq.net_weight, 0) AS C29,
COALESCE(lq.gross_weight, 0) AS C30,
COALESCE(il.order, ih.purchase_order, '') AS C31,
COALESCE(lc.fraction, '') AS C32,
COALESCE(lc.fraction_type, '') AS C33,
COALESCE(lc.advalorem_numeric, 0) AS C34,
COALESCE(NULLIF(lc.sector, ''), NULLIF(fap.sector, ''), '') AS C35,
COALESCE(lf.igi_amount_usd, 0) AS C36,
COALESCE(lc.origin_country, '') AS C37,
COALESCE(cmp.aduana, '') AS C38,
ih.id AS C39,
COALESCE(il.material_type, 'P') AS C40,
COALESCE(
ped_r1.year || '-' || ped_r1.customs_office || '-' || ped_r1.license || '-' || ped_r1.pedimento_number,
pro_origin.original_pedimento_year || '-' || pro_origin.original_customs_office || '-' || pro_origin.original_license || '-' || pro_origin.original_pedimento_number,
''
) AS C42, -- [41] rectification_id
cmp.edocument AS C43, -- [42]
cmp.vucem_operation_num AS C44, -- [43]
il.line_number AS C45, -- [44]
ld.brand AS C46, -- [45]
ld.model AS C47, -- [46]
prt.us_fraction AS C48, -- [47]
prt.eccn AS C49, -- [48]
prt.id AS C50, -- [49]
fin.exchange_rate AS C51, -- [50]
ih.emission_date AS C52, -- [51]
ih.capture_user AS C53, -- [52]
ih.who_updated AS C54, -- [53]
'' AS C55, -- [54]
COALESCE(log.transport_id || ' ' || log.transport_num, '') AS C56, -- [55]
'' AS C57, -- [56] Pedimento18 (row[56])
COALESCE(ld.lot, '') AS C58, -- [57] Lote (row[57])
'' AS C59, -- [58] TipoPed (row[58])
'' AS C60 -- [59] Relleno final
) AS C41,
cmp.edocument AS C42,
cmp.vucem_operation_num AS C43,
COALESCE(il.line_number, 0) AS C44,
REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(ld.brand, ''), CHR(44), ' '), CHR(9), ' '), CHR(10), ' '), CHR(13), ' ') AS C45,
REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(ld.model, ''), CHR(44), ' '), CHR(9), ' '), CHR(10), ' '), CHR(13), ' ') AS C46,
COALESCE(cls.us_fraction, '') AS C47,
COALESCE(prt.eccn, fac.eccn_code, '') AS C48,
COALESCE(prt.part_number::text, '') AS C49,
COALESCE(fin.exchange_rate, 0) AS C50,
COALESCE(TO_CHAR(ih.emission_date, 'YYYYMMDD'), '') AS C51,
COALESCE(ih.capture_user, '') AS C52,
COALESCE(ih.who_updated, '') AS C53,
COALESCE(log.carrier_id, '') AS C54,
COALESCE(log.transport_id || ' ' || log.transport_num, '') AS C55,
'' AS C56,
COALESCE(ld.lot, '') AS C57,
'' AS C58,
0 AS C59,
0 AS C60
FROM a76.invoice_header ih
LEFT JOIN a76.invoice_compliance_mx cmp ON cmp.invoice_id = ih.id
LEFT JOIN a76.invoice_financials fin ON fin.invoice_id = ih.id
@@ -349,16 +372,18 @@ class DefinitiveImportQueries:
pro_rect.original_license = ped.license AND
pro_rect.original_pedimento_number = ped.pedimento_number
LEFT JOIN a76.pedimentos ped_r1 ON ped_r1.id = pro_rect.pedimento_id
LEFT JOIN a76.pedimento_dates pd ON pd.pedimento_id = ped.id
LEFT JOIN a76.clients_and_providers prov ON prov.id = cmp.provider_id
LEFT JOIN a76.clients_and_providers client ON client.id = cmp.sold_to_id
LEFT JOIN a76.items itm ON itm.invoice_id = ih.id
LEFT JOIN a76.item_lines il ON il.item_id = itm.id
LEFT JOIN a76.item_lines il ON il.invoice_id = ih.id
LEFT JOIN a76.item_line_descriptions ld ON ld.item_line_id = il.id
LEFT JOIN a76.item_line_quantities lq ON lq.item_line_id = il.id
LEFT JOIN a76.item_line_financials lf ON lf.item_line_id = il.id
LEFT JOIN a76.item_line_customs lc ON lc.item_line_id = il.id
LEFT JOIN a76.classes cls ON cls.id = il.class_id
LEFT JOIN a76.parts prt ON prt.id = il.part_number
LEFT JOIN a24.fa_classes fac ON fac.class_id = cls.id
LEFT JOIN a76.parts prt ON prt.id = il.part_number_id
LEFT JOIN a24.fa_partes fap ON fap.id = prt.id
LEFT JOIN a76.units_of_measure um ON um.id = il.unit_of_measure
WHERE {where_clause}
ORDER BY ih.invoice_number, il.line_number
@@ -374,8 +399,7 @@ class DefinitiveImportQueries:
FROM a76.item_line_financials lf
INNER JOIN a76.item_lines il ON il.id = lf.item_line_id
LEFT JOIN a24.fa_item_lines fil ON fil.id = il.id
INNER JOIN a76.items itm ON itm.id = il.item_id
WHERE itm.invoice_id = :consecutivo
WHERE il.invoice_id = :consecutivo
"""
@@ -425,7 +449,7 @@ class RepairImportQueries:
CASE WHEN ih.is_updated THEN 'AC' ELSE 'NA' END AS C5,
COALESCE(ped.pedimento_code, '') AS C6,
COALESCE(ped.regime, '') AS C7,
COALESCE(TO_CHAR(log.payment_date, 'YYYYMMDD'), '') AS C9,
COALESCE(TO_CHAR(pd.payment_date, 'YYYYMMDD'), '') AS C9,
COALESCE(cmp.remesa::text, '') AS C10,
COALESCE(fin.exchange_rate, 0) AS C11,
COALESCE(cmp.provider_id::text, '') AS C12,
@@ -449,7 +473,9 @@ class RepairImportQueries:
ped_r1.year || '-' || ped_r1.customs_office || '-' || ped_r1.license || '-' || ped_r1.pedimento_number,
pro_origin.original_pedimento_year || '-' || pro_origin.original_customs_office || '-' || pro_origin.original_license || '-' || pro_origin.original_pedimento_number,
''
) AS C48
) AS C48,
COALESCE(lf_agg.sum_value_usd, 0) AS sum_value_usd,
COALESCE(lf_agg.sum_value_mxn, 0) AS sum_value_mxn
FROM a76.invoice_header ih
LEFT JOIN a76.invoice_compliance_mx cmp ON cmp.invoice_id = ih.id
LEFT JOIN a76.invoice_financials fin ON fin.invoice_id = ih.id
@@ -462,13 +488,23 @@ class RepairImportQueries:
pro_rect.original_license = ped.license AND
pro_rect.original_pedimento_number = ped.pedimento_number
LEFT JOIN a76.pedimentos ped_r1 ON ped_r1.id = pro_rect.pedimento_id
LEFT JOIN a76.pedimento_dates pd ON pd.pedimento_id = ped.id
LEFT JOIN (
SELECT il.invoice_id,
SUM(CASE WHEN COALESCE(fil.is_subitem, false) THEN 0 ELSE COALESCE(lf.value_mxn, 0) END) AS sum_value_mxn,
SUM(CASE WHEN COALESCE(fil.is_subitem, false) THEN 0 ELSE COALESCE(lf.value_usd, 0) END) AS sum_value_usd
FROM a76.item_lines il
JOIN a76.item_line_financials lf ON lf.item_line_id = il.id
LEFT JOIN a24.fa_item_lines fil ON fil.id = il.id
WHERE 1=1 {discharge_filter}
GROUP BY il.invoice_id
) lf_agg ON lf_agg.invoice_id = ih.id
WHERE ih.operation_type = 'imp'
AND COALESCE(cmp.is_regime_change, false) = false
AND EXISTS (
SELECT 1 FROM a76.items i2
INNER JOIN a76.item_lines il2 ON il2.item_id = i2.id
SELECT 1 FROM a76.item_lines il2
INNER JOIN a24.fa_item_lines fil2 ON fil2.id = il2.id
WHERE i2.invoice_id = ih.id AND fil2.search_invoice IS NOT NULL
WHERE il2.invoice_id = ih.id AND fil2.search_invoice IS NOT NULL
{discharge_filter}
)
{"AND " + where_str if where_str else ""}
@@ -494,14 +530,15 @@ class RepairImportQueries:
CASE WHEN ih.is_updated THEN 'AC' ELSE 'NA' END,
COALESCE(ped.pedimento_code, ''),
COALESCE(ped.regime, ''),
'',
COALESCE(TO_CHAR(log.payment_date, 'YYYYMMDD'), ''),
COALESCE(TO_CHAR(pd.entry_date, 'YYYYMMDD'), ''),
COALESCE(TO_CHAR(pd.end_date, 'YYYYMMDD'), TO_CHAR(pd.payment_date, 'YYYYMMDD'), ''),
COALESCE(TO_CHAR(pd.payment_date, 'YYYYMMDD'), ''),
COALESCE(cmp.remesa::text, ''),
COALESCE(fin.exchange_rate, 0),
COALESCE(cmp.provider_id::text, ''),
COALESCE(cmp.sold_to_id::text, ''),
COALESCE(cmp.customs_broker_id::text, ''),
COALESCE(il.part_number::text, ''),
COALESCE(prt.part_number::text, ''),
REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(ld.description_spanish, ''), CHR(44), ' '), CHR(9), ' '), CHR(10), ' '), CHR(13), ' '),
REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(ld.description_english, ''), CHR(44), ' '), CHR(9), ' '), CHR(10), ' '), CHR(13), ' '),
COALESCE(lq.quantity, 0),
@@ -510,14 +547,14 @@ class RepairImportQueries:
COALESCE(lf.value_usd, 0),
COALESCE(lq.net_weight, 0),
COALESCE(lq.gross_weight, 0),
COALESCE(ih.purchase_order, ''),
COALESCE(il.order, ih.purchase_order, ''),
COALESCE(lc.fraction, ''),
'',
COALESCE(lc.sector, ''),
COALESCE(NULLIF(lc.sector, ''), NULLIF(fap.sector, ''), ''),
COALESCE(lc.origin_country, ''),
COALESCE(ped.customs_office, ''),
ih.id,
'P',
COALESCE(il.material_type, 'P'),
COALESCE(
ped_r1.year || '-' || ped_r1.customs_office || '-' || ped_r1.license || '-' || ped_r1.pedimento_number,
pro_origin.original_pedimento_year || '-' || pro_origin.original_customs_office || '-' || pro_origin.original_license || '-' || pro_origin.original_pedimento_number,
@@ -528,7 +565,7 @@ class RepairImportQueries:
REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(ld.brand, ''), CHR(44), ' '), CHR(9), ' '), CHR(10), ' '), CHR(13), ' '),
REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(ld.model, ''), CHR(44), ' '), CHR(9), ' '), CHR(10), ' '), CHR(13), ' '),
COALESCE(lc.american_fraction, ''),
COALESCE(prt.eccn, ''),
COALESCE(prt.eccn, fac.eccn_code, ''),
COALESCE(fin.exchange_rate, 0),
COALESCE(TO_CHAR(ih.emission_date, 'YYYYMMDD'), ''),
COALESCE(ih.capture_user, ''),
@@ -549,14 +586,17 @@ class RepairImportQueries:
pro_rect.original_license = ped.license AND
pro_rect.original_pedimento_number = ped.pedimento_number
LEFT JOIN a76.pedimentos ped_r1 ON ped_r1.id = pro_rect.pedimento_id
LEFT JOIN a76.items itm ON itm.invoice_id = ih.id
LEFT JOIN a76.item_lines il ON il.item_id = itm.id
LEFT JOIN a76.pedimento_dates pd ON pd.pedimento_id = ped.id
LEFT JOIN a76.item_lines il ON il.invoice_id = ih.id
LEFT JOIN a76.item_line_descriptions ld ON ld.item_line_id = il.id
LEFT JOIN a76.item_line_quantities lq ON lq.item_line_id = il.id
LEFT JOIN a76.item_line_financials lf ON lf.item_line_id = il.id
LEFT JOIN a76.item_line_customs lc ON lc.item_line_id = il.id
LEFT JOIN a24.fa_item_lines fil ON fil.id = il.id
LEFT JOIN a76.parts prt ON prt.id = il.part_number
LEFT JOIN a76.classes cls ON cls.id = il.class_id
LEFT JOIN a24.fa_classes fac ON fac.class_id = cls.id
LEFT JOIN a76.parts prt ON prt.id = il.part_number_id
LEFT JOIN a24.fa_partes fap ON fap.id = prt.id
LEFT JOIN a76.units_of_measure um ON um.id = il.unit_of_measure
WHERE ih.operation_type = 'imp'
AND COALESCE(cmp.is_regime_change, false) = false
@@ -583,8 +623,7 @@ class RepairImportQueries:
FROM a76.item_line_financials lf
INNER JOIN a76.item_lines il ON il.id = lf.item_line_id
LEFT JOIN a24.fa_item_lines fil ON fil.id = il.id
INNER JOIN a76.items itm ON itm.id = il.item_id
WHERE itm.invoice_id = :consecutivo
WHERE il.invoice_id = :consecutivo
{discharge_filter}
"""
@@ -628,9 +667,8 @@ class ExportQueries:
CASE WHEN ih.is_updated THEN 'AC' ELSE 'NA' END AS C6,
COALESCE(ped.pedimento_code, '') AS C7,
COALESCE(ped.regime, '') AS C8,
COALESCE(TO_CHAR(log.entry_exit_date, 'YYYYMMDD'), '') AS C9,
COALESCE(TO_CHAR(log.delivery_date, 'YYYYMMDD'), '') AS C10,
COALESCE(TO_CHAR(log.payment_date, 'YYYYMMDD'), '') AS C11,
COALESCE(TO_CHAR(pd.end_date, 'YYYYMMDD'), TO_CHAR(pd.payment_date, 'YYYYMMDD'), '') AS C10,
COALESCE(TO_CHAR(pd.payment_date, 'YYYYMMDD'), '') AS C11,
COALESCE(log.payment_receipt_num, '') AS C12,
COALESCE(cmp.provider_id::text, '') AS C14,
COALESCE(cmp.sold_to_id::text, '') AS C15,
@@ -652,7 +690,9 @@ class ExportQueries:
ped_r1.year || '-' || ped_r1.customs_office || '-' || ped_r1.license || '-' || ped_r1.pedimento_number,
pro_origin.original_pedimento_year || '-' || pro_origin.original_customs_office || '-' || pro_origin.original_license || '-' || pro_origin.original_pedimento_number,
''
) AS C54
) AS C54,
COALESCE(lf_agg.sum_value_usd, 0) AS sum_value_usd,
COALESCE(lf_agg.sum_value_mxn, 0) AS sum_value_mxn
FROM a76.invoice_header ih
LEFT JOIN a76.invoice_compliance_mx cmp ON cmp.invoice_id = ih.id
LEFT JOIN a76.invoice_financials fin ON fin.invoice_id = ih.id
@@ -665,6 +705,16 @@ class ExportQueries:
pro_rect.original_license = ped.license AND
pro_rect.original_pedimento_number = ped.pedimento_number
LEFT JOIN a76.pedimentos ped_r1 ON ped_r1.id = pro_rect.pedimento_id
LEFT JOIN a76.pedimento_dates pd ON pd.pedimento_id = ped.id
LEFT JOIN (
SELECT il.invoice_id,
SUM(CASE WHEN COALESCE(fil.is_subitem, false) THEN 0 ELSE COALESCE(lf.value_mxn, 0) END) AS sum_value_mxn,
SUM(CASE WHEN COALESCE(fil.is_subitem, false) THEN 0 ELSE COALESCE(lf.value_usd, 0) END) AS sum_value_usd
FROM a76.item_lines il
JOIN a76.item_line_financials lf ON lf.item_line_id = il.id
LEFT JOIN a24.fa_item_lines fil ON fil.id = il.id
GROUP BY il.invoice_id
) lf_agg ON lf_agg.invoice_id = ih.id
WHERE {where_clause}
ORDER BY ih.invoice_number
"""
@@ -677,34 +727,32 @@ class ExportQueries:
ped.pedimento_number AS C2, -- [1]
ih.invoice_date AS C3, -- [2]
'' AS C4, -- [3]
'' AS C5, -- [4]
ped.status AS C6, -- [5]
ped.pedimento_code AS C7, -- [6]
ped.regime AS C8, -- [7]
log.entry_exit_date AS C9, -- [8]
log.delivery_date AS C10, -- [9]
log.payment_date AS C11, -- [10]
ped.pedimento_code AS C5, -- [4]
ped.regime AS C6, -- [5]
pd.entry_date AS C7, -- [6]
COALESCE(pd.end_date, pd.payment_date) AS C8, -- [7]
pd.payment_date AS C9, -- [8]
log.payment_receipt_num AS C12, -- [11]
'' AS C13, -- [12]
cmp.provider_id AS C14, -- [13]
cmp.sold_to_id AS C15, -- [14]
cmp.customs_broker_id AS C16, -- [15]
'' AS C17, -- [16]
prt.part_number AS C18, -- [17]
ld.description_spanish AS C19, -- [18]
ld.description_english AS C20, -- [19]
COALESCE(prt.part_number::text, '') AS C18, -- [17]
REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(ld.description_spanish, ''), CHR(44), ' '), CHR(9), ' '), CHR(10), ' '), CHR(13), ' ') AS C19, -- [18]
REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(ld.description_english, ''), CHR(44), ' '), CHR(9), ' '), CHR(10), ' '), CHR(13), ' ') AS C20, -- [19]
lq.quantity AS C21, -- [20]
um.code AS C22, -- [21]
'' AS C23, -- [22]
'' AS C24, -- [23]
lq.net_weight AS C25, -- [24]
lq.gross_weight AS C26, -- [25]
ih.purchase_order AS C27, -- [26]
lc.fraction AS C28, -- [27]
'' AS C29, -- [28]
'' AS C30, -- [29]
'' AS C31, -- [30]
'' AS C32, -- [31]
COALESCE(lq.net_weight, 0) AS C25, -- [24]
COALESCE(lq.gross_weight, 0) AS C26, -- [25]
COALESCE(il.order, ih.purchase_order, '') AS C27, -- [26]
COALESCE(lc.fraction, '') AS C28, -- [27]
COALESCE(lc.fraction_type, '') AS C29, -- [28]
COALESCE(lc.advalorem_numeric, 0) AS C30, -- [29]
COALESCE(NULLIF(lc.sector, ''), NULLIF(fap.sector, ''), '') AS C31, -- [30]
COALESCE(lc.origin_country, '') AS C32, -- [31]
cmp.aduana AS C33, -- [32]
ih.invoice_type AS C34, -- [33]
ih.id AS C35, -- [34]
@@ -719,10 +767,10 @@ class ExportQueries:
cmp.edocument AS C40, -- [39]
cmp.vucem_operation_num AS C41, -- [40]
il.line_number AS C42, -- [41]
ld.brand AS C43, -- [42]
ld.model AS C44, -- [43]
prt.us_fraction AS C45, -- [44]
prt.eccn AS C46, -- [45]
REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(ld.brand, ''), CHR(44), ' '), CHR(9), ' '), CHR(10), ' '), CHR(13), ' ') AS C43, -- [42]
REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(ld.model, ''), CHR(44), ' '), CHR(9), ' '), CHR(10), ' '), CHR(13), ' ') AS C44, -- [43]
COALESCE(cls.us_fraction, '') AS C45, -- [44]
COALESCE(prt.eccn, fac.eccn_code, '') AS C46, -- [45]
prt.id AS C47, -- [46]
fin.exchange_rate AS C48, -- [47]
ih.emission_date AS C49, -- [48]
@@ -749,14 +797,16 @@ class ExportQueries:
pro_rect.original_license = ped.license AND
pro_rect.original_pedimento_number = ped.pedimento_number
LEFT JOIN a76.pedimentos ped_r1 ON ped_r1.id = pro_rect.pedimento_id
LEFT JOIN a76.items itm ON itm.invoice_id = ih.id
LEFT JOIN a76.item_lines il ON il.item_id = itm.id
LEFT JOIN a76.pedimento_dates pd ON pd.pedimento_id = ped.id
LEFT JOIN a76.item_lines il ON il.invoice_id = ih.id
LEFT JOIN a76.item_line_descriptions ld ON ld.item_line_id = il.id
LEFT JOIN a76.item_line_quantities lq ON lq.item_line_id = il.id
LEFT JOIN a76.item_line_financials lf ON lf.item_line_id = il.id
LEFT JOIN a76.item_line_customs lc ON lc.item_line_id = il.id
LEFT JOIN a76.classes cls ON cls.id = il.class_id
LEFT JOIN a76.parts prt ON prt.id = il.part_number
LEFT JOIN a24.fa_classes fac ON fac.class_id = cls.id
LEFT JOIN a76.parts prt ON prt.id = il.part_number_id
LEFT JOIN a24.fa_partes fap ON fap.id = prt.id
LEFT JOIN a76.units_of_measure um ON um.id = il.unit_of_measure
WHERE {where_clause}
ORDER BY ih.invoice_number, il.line_number
@@ -782,8 +832,7 @@ class ExportQueries:
FROM a76.item_line_financials lf
INNER JOIN a76.item_lines il ON il.id = lf.item_line_id
LEFT JOIN a24.fa_item_lines fil ON fil.id = il.id
INNER JOIN a76.items itm ON itm.id = il.item_id
WHERE itm.invoice_id = :consecutivo
WHERE il.invoice_id = :consecutivo
{discharge_filter}
"""
@@ -828,7 +877,7 @@ class ExportRepairQueries:
CASE WHEN ih.is_updated THEN 'AC' ELSE 'NA' END AS C6,
COALESCE(ped.pedimento_code, '') AS C7,
COALESCE(ped.regime, '') AS C8,
COALESCE(TO_CHAR(log.payment_date, 'YYYYMMDD'), '') AS C11,
COALESCE(TO_CHAR(pd.payment_date, 'YYYYMMDD'), '') AS C11,
COALESCE(cmp.remesa::text, '') AS C12,
COALESCE(fin.exchange_rate, 0) AS C13,
COALESCE(cmp.provider_id::text, '') AS C14,
@@ -852,7 +901,9 @@ class ExportRepairQueries:
ped_r1.year || '-' || ped_r1.customs_office || '-' || ped_r1.license || '-' || ped_r1.pedimento_number,
pro_origin.original_pedimento_year || '-' || pro_origin.original_customs_office || '-' || pro_origin.original_license || '-' || pro_origin.original_pedimento_number,
''
) AS C54
) AS C54,
COALESCE(lf_agg.sum_value_usd, 0) AS sum_value_usd,
COALESCE(lf_agg.sum_value_mxn, 0) AS sum_value_mxn
FROM a76.invoice_header ih
LEFT JOIN a76.invoice_compliance_mx cmp ON cmp.invoice_id = ih.id
LEFT JOIN a76.invoice_financials fin ON fin.invoice_id = ih.id
@@ -865,6 +916,17 @@ class ExportRepairQueries:
pro_rect.original_license = ped.license AND
pro_rect.original_pedimento_number = ped.pedimento_number
LEFT JOIN a76.pedimentos ped_r1 ON ped_r1.id = pro_rect.pedimento_id
LEFT JOIN a76.pedimento_dates pd ON pd.pedimento_id = ped.id
LEFT JOIN (
SELECT il.invoice_id,
SUM(CASE WHEN COALESCE(fil.is_subitem, false) THEN 0 ELSE COALESCE(lf.value_mxn, 0) END) AS sum_value_mxn,
SUM(CASE WHEN COALESCE(fil.is_subitem, false) THEN 0 ELSE COALESCE(lf.value_usd, 0) END) AS sum_value_usd
FROM a76.item_lines il
JOIN a76.item_line_financials lf ON lf.item_line_id = il.id
LEFT JOIN a24.fa_item_lines fil ON fil.id = il.id
WHERE 1=1 {discharge_filter}
GROUP BY il.invoice_id
) lf_agg ON lf_agg.invoice_id = ih.id
WHERE ih.operation_type = 'exp'
AND ih.invoice_type = 'REPAR'
{"AND " + where_str if where_str else ""}
@@ -884,9 +946,9 @@ class ExportRepairQueries:
CASE WHEN ih.is_updated THEN 'AC' ELSE 'NA' END AS C6,
COALESCE(ped.pedimento_code, '') AS C7,
COALESCE(ped.regime, '') AS C8,
'' AS C9,
'' AS C10,
COALESCE(TO_CHAR(log.payment_date, 'YYYYMMDD'), '') AS C11,
COALESCE(TO_CHAR(pd.entry_date, 'YYYYMMDD'), '') AS C9,
COALESCE(TO_CHAR(pd.end_date, 'YYYYMMDD'), TO_CHAR(pd.payment_date, 'YYYYMMDD'), '') AS C10,
COALESCE(TO_CHAR(pd.payment_date, 'YYYYMMDD'), '') AS C11,
COALESCE(cmp.remesa::text, '') AS C12,
COALESCE(fin.exchange_rate, 0) AS C13,
COALESCE(cmp.provider_id::text, '') AS C14,
@@ -902,18 +964,18 @@ class ExportRepairQueries:
COALESCE(lf.customs_value_usd, 0) AS C24,
COALESCE(lq.net_weight, 0) AS C25,
COALESCE(lq.gross_weight, 0) AS C26,
COALESCE(ih.purchase_order, '') AS C27,
COALESCE(il.order, ih.purchase_order, '') AS C27,
COALESCE(lc.fraction, '') AS C28,
COALESCE(lc.fraction_type, '') AS C29,
COALESCE(lc.advalorem_numeric, 0) AS C30,
COALESCE(lc.sector, '') AS C31,
COALESCE(NULLIF(lc.sector, ''), NULLIF(fap.sector, ''), '') AS C31,
COALESCE(lc.origin_country, '') AS C32,
COALESCE(ped.customs_office, '') AS C33,
COALESCE(ih.document_type, '') AS C34,
ih.id AS C35,
COALESCE(lf.value_mxn, 0) AS C36,
COALESCE(lf.value_usd, 0) AS C37,
'P' AS C38,
COALESCE(il.material_type, 'P') AS C38,
COALESCE(
ped_r1.year || '-' || ped_r1.customs_office || '-' || ped_r1.license || '-' || ped_r1.pedimento_number,
pro_origin.original_pedimento_year || '-' || pro_origin.original_customs_office || '-' || pro_origin.original_license || '-' || pro_origin.original_pedimento_number,
@@ -925,8 +987,8 @@ class ExportRepairQueries:
REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(ld.brand, ''), CHR(44), ' '), CHR(9), ' '), CHR(10), ' '), CHR(13), ' ') AS C43,
REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(ld.model, ''), CHR(44), ' '), CHR(9), ' '), CHR(10), ' '), CHR(13), ' ') AS C44,
COALESCE(cls.us_fraction, '') AS C45,
COALESCE(prt.eccn, '') AS C46,
COALESCE(il.part_number::text, '') AS C47,
COALESCE(prt.eccn, fac.eccn_code, '') AS C46,
COALESCE(prt.part_number::text, '') AS C47,
COALESCE(fin.exchange_rate, 0) AS C48,
COALESCE(TO_CHAR(ih.invoice_date, 'YYYYMMDD'), '') AS C49,
COALESCE(ih.capture_user, '') AS C50,
@@ -951,14 +1013,16 @@ class ExportRepairQueries:
pro_rect.original_license = ped.license AND
pro_rect.original_pedimento_number = ped.pedimento_number
LEFT JOIN a76.pedimentos ped_r1 ON ped_r1.id = pro_rect.pedimento_id
LEFT JOIN a76.items itm ON itm.invoice_id = ih.id
LEFT JOIN a76.item_lines il ON il.item_id = itm.id
LEFT JOIN a76.pedimento_dates pd ON pd.pedimento_id = ped.id
LEFT JOIN a76.item_lines il ON il.invoice_id = ih.id
LEFT JOIN a76.item_line_descriptions ld ON ld.item_line_id = il.id
LEFT JOIN a76.item_line_quantities lq ON lq.item_line_id = il.id
LEFT JOIN a76.item_line_financials lf ON lf.item_line_id = il.id
LEFT JOIN a76.item_line_customs lc ON lc.item_line_id = il.id
LEFT JOIN a76.classes cls ON cls.id = il.class_id
LEFT JOIN a76.parts prt ON prt.id = il.part_number
LEFT JOIN a24.fa_classes fac ON fac.class_id = cls.id
LEFT JOIN a76.parts prt ON prt.id = il.part_number_id
LEFT JOIN a24.fa_partes fap ON fap.id = prt.id
LEFT JOIN a76.units_of_measure um ON um.id = il.unit_of_measure
WHERE UPPER(ih.operation_type) IN ('EXP', 'TRA', 'RET')
AND UPPER(ih.invoice_type) IN ('DEF', 'REPAR', 'EXDEF', 'MATDE')
@@ -983,7 +1047,7 @@ class ExportRepairQueries:
FROM a76.item_line_financials lf
INNER JOIN a76.item_lines il ON il.id = lf.item_line_id
LEFT JOIN a24.fa_item_lines fil ON il.id = lf.item_line_id
WHERE itm.invoice_id = :consecutivo
WHERE il.invoice_id = :consecutivo
{discharge_filter}
"""

View File

@@ -93,6 +93,11 @@ class RepairImportService:
# Totals come directly from GROUP BY query (no N+1 problem)
total_me = to_float(row[24]) # total_me from SUM aggregation
total_mn = to_float(row[25]) # total_mn from SUM aggregation
sum_value_usd = to_float(row[27])
sum_value_mxn = to_float(row[28])
total_me = sum_value_usd if sum_value_usd > 0 else total_me
total_mn = sum_value_mxn if sum_value_mxn > 0 else total_mn
# Calculate exchange rate and value
valor_comercial, tipo_cambio = ExchangeRateCalculator.calculate_for_aggregated(
@@ -228,8 +233,8 @@ class RepairImportService:
db=db,
db_name=filters.database_name,
es_subpartida=row[30], # 'P' or 'S'
valor_me=row[20],
valor_mn_direct=row[19],
valor_me=row[21],
valor_mn_direct=row[20],
fecha_pago=row[8],
fecha_inicio=row[7],
clave_ped=row[45],
@@ -240,9 +245,9 @@ class RepairImportService:
)
# Set peso values based on subpartida flag
if row[30] == 'P':
peso_neto = float(row[21]) if row[21] else 0.0
peso_bruto = float(row[22]) if row[22] else 0.0
if row[31] == 'P':
peso_neto = float(row[22]) if row[22] else 0.0
peso_bruto = float(row[23]) if row[23] else 0.0
else:
peso_neto = 0.0
peso_bruto = 0.0
@@ -252,9 +257,9 @@ class RepairImportService:
)
simbolo_ex = None
if row[14]:
if row[15]:
simbolo_ex = DatabaseHelper.get_part_export_symbol(
db, filters.database_name, row[14], filters.is_shelter
db, filters.database_name, row[15], filters.is_shelter
)
pedimento_r1 = DatabaseHelper.get_rectification_pedimento(
@@ -275,10 +280,10 @@ class RepairImportService:
TipoMovTemDef='IMPRE',
EsCambioRegimen='N',
Regimen=row[6],
Fecha_Inicio=row[7],
Fecha_Fin=row[7], # Using same valid column or empty
Fecha_Pago=row[8],
Remesa=row[9],
Fecha_Inicio=parse_yyyymmdd_date(row[7]),
Fecha_Fin=parse_yyyymmdd_date(row[8]),
Fecha_Pago=parse_yyyymmdd_date(row[9]),
Remesa=row[10],
Proveedor=proveedor_info.get('name'),
RFCProveedor=proveedor_info.get('rfc'),
ProveedorTaxID=proveedor_info.get('tax_id'),
@@ -287,42 +292,42 @@ class RepairImportService:
VendidoATaxID=vendido_info.get('tax_id'),
AgenteAduanal=agente_info.get('name'),
Patente=agente_info.get('license'),
NumParte=row[14],
DescripcionE=StringHelper.clean_text(row[15]),
DescripcionI=StringHelper.clean_text(row[16]),
CantidadIE=float(row[17]) if row[17] else 0.0,
UniMed=row[18],
NumParte=row[15],
DescripcionE=StringHelper.clean_text(row[16]),
DescripcionI=StringHelper.clean_text(row[17]),
CantidadIE=float(row[18]) if row[18] else 0.0,
UniMed=row[19],
ValorComercialMN=valor_comercial,
TipoCambio=tipo_cambio,
PesoNeto=peso_neto,
PesoBruto=peso_bruto,
OrdenCompraVenta=row[23],
FraccionArancelaria=row[24],
Preferencia=row[25],
Sector=row[26],
PaisOrigen=row[27],
OrdenCompraVenta=row[24],
FraccionArancelaria=row[25],
Preferencia=row[26],
Sector=row[27],
PaisOrigen=row[28],
Aduana=aduana_nombre,
Advalorem=row[30],
Advalorem='',
TipoExpo='',
PedimentoR1=pedimento_r1,
EDocument=row[32],
NumOperacionVU=row[33],
EDocument=row[33],
NumOperacionVU=row[34],
Series=series_info,
Marca=StringHelper.clean_text(row[34]),
Modelo=StringHelper.clean_text(row[35]),
FraccionAmericana=row[36],
ECCN=row[37],
Marca=StringHelper.clean_text(row[35]),
Modelo=StringHelper.clean_text(row[36]),
FraccionAmericana=row[37],
ECCN=row[38],
SimboloEx=simbolo_ex,
FechaEmision=row[39],
FechaEmision=parse_yyyymmdd_date(row[40]) if row[40] else None, # C41 - FechaEmision
BaseDeDatos=filters.database_name,
NumGafUni=num_gaf_uni,
UsuarioCap=row[40],
UsuarioAcr=row[41],
Transportista=row[42],
NumCaja=row[43],
Pedimento18=row[44],
AduanaCru=row[28],
Lote='' # Not in query
UsuarioCap=row[41],
UsuarioAcr=row[42],
Transportista=row[43],
NumCaja=row[44],
Pedimento18=row[45],
AduanaCru=row[29],
Lote=row[54] if len(row) > 54 else '' # Not in query but mapped safely
)
movements.append(movement)
@@ -347,7 +352,7 @@ class RepairImportService:
if filters.range_type.value == "FF":
where_conditions.append(f"ih.invoice_date >= TO_DATE('{filters.start_date}', 'YYYYMMDD') AND ih.invoice_date <= TO_DATE('{filters.end_date}', 'YYYYMMDD')")
else:
where_conditions.append(f"log.payment_date >= TO_DATE('{filters.start_date}', 'YYYYMMDD') AND log.payment_date <= TO_DATE('{filters.end_date}', 'YYYYMMDD')")
where_conditions.append(f"pd.payment_date >= TO_DATE('{filters.start_date}', 'YYYYMMDD') AND pd.payment_date <= TO_DATE('{filters.end_date}', 'YYYYMMDD')")
# Note: Status filter applied at Python level after CASE WHEN in SELECT
# because is_updated doesn't directly represent AC/NA status
@@ -383,8 +388,7 @@ class RepairImportService:
COALESCE(SUM(lf.value_mxn), 0)
FROM a76.item_line_financials lf
INNER JOIN a76.item_lines il ON il.id = lf.item_line_id
INNER JOIN a76.items itm ON itm.id = il.item_id
WHERE itm.invoice_id = :consecutivo
WHERE il.invoice_id = :consecutivo
AND il.is_subitem = false
{discharge_clause}
""")

View File

@@ -97,6 +97,12 @@ class TemporaryImportService:
valor_comercial_mn = to_float(row[30]) # valor_comercial_mn from item_line_financials
valor_mp_temp_mn = to_float(row[31]) # valor_mp_temp_mn from item_line_financials
valor_agre_mn = to_float(row[32]) # valor_agre_mn from item_line_financials
valor_mp_temp_usd = to_float(row[33]) # valor_mp_temp_usd from item_line_financials
sum_value_usd = to_float(row[34]) # sum_value_usd from item_line_financials (line item sum avoids zero-value header bug)
# Replace zero values with the computed sums
total_me = sum_value_usd if sum_value_usd > 0 else total_me
total_mn = valor_comercial_mn if valor_comercial_mn > 0 else total_mn
# Calculate exchange rate and value
valor_comercial, tipo_cambio = ExchangeRateCalculator.calculate_for_aggregated(
@@ -127,6 +133,24 @@ class TemporaryImportService:
num_gaf_uni = DatabaseHelper.get_driver_badge(
db, filters.database_name, factura
)
# Calculate exchange rate for MPTemp explicitly decoupled from Valor Comercial
valor_mp_temp_raw, _ = ExchangeRateCalculator.calculate_for_aggregated(
db=db,
db_name=filters.database_name,
valor_me=to_float(row[33]), # sum_value_temp_usd
valor_mn=to_float(row[31]), # sum_value_temp_mxn
tipo_cambio_db=to_float(row[19]),
fecha_pago=row[8],
fecha_inicio=row[6],
tipo_pedimento=row[4],
currency_type=filters.currency_type.value,
exchange_rate_type=filters.exchange_rate_type.value,
is_shelter=filters.is_shelter,
use_transport_method=False,
met_trans=met_trans
)
valor_mp_temp = float(valor_mp_temp_raw)
# Build movement item
movement = MovementItem(
@@ -137,8 +161,8 @@ class TemporaryImportService:
ClavePed=row[4], # C5 - ClavePed
TipoMovTemDef='IMTEM',
EsCambioRegimen='N',
ValorMPTemp=valor_mp_temp_mn,
ValorComercialMN=valor_comercial_mn,
ValorMPTemp=valor_mp_temp,
ValorComercialMN=valor_comercial,
TipoCambio=tipo_cambio,
ValorAgre=valor_agre_mn,
TipoExpo='',
@@ -212,6 +236,15 @@ class TemporaryImportService:
db, filters.database_name, row[17] # C18 - AAduanal
)
# Get provider and client details including RFC and TaxID
provider_info = DatabaseHelper.get_client_info(
db, filters.database_name, row[15], is_supplier=True
) if row[15] else {}
client_info = DatabaseHelper.get_client_info(
db, filters.database_name, row[16], is_supplier=False
) if row[16] else {}
# Get customs section name
aduana_nombre = DatabaseHelper.get_aduana_seccion_nombre(
db, filters.database_name, row[37] # C38 - Aduana_Cruce
@@ -233,13 +266,24 @@ class TemporaryImportService:
met_trans=met_trans
)
# ValorComercialMN should always be in MXN
# If currency_type is ME, valor_comercial is in USD, so multiply by tipo_cambio
if filters.currency_type.value == "ME" and tipo_cambio:
valor_comercial_mn = float(valor_comercial) * float(tipo_cambio)
else:
# If currency_type is MN, valor_comercial is already in MXN
valor_comercial_mn = float(valor_comercial)
valor_mp_temp_raw, _ = ExchangeRateCalculator.calculate_exchange_rate_and_value(
db=db,
db_name=filters.database_name,
es_subpartida=row[39], # C40 - EsSubPartida
valor_me=float(row[59]) if row[59] else 0.0,
valor_mn_direct=float(row[58]) if row[58] else 0.0,
fecha_pago=row[12], # C13 - Fecha_Pago
fecha_inicio=row[10], # C11 - Fecha_Inicio
clave_ped=row[57], # C58 - TIPOPEDIMENTOTRANSPORTEE
tipo_cambio_partida=row[49], # C50 - TipoCambio
currency_type=filters.currency_type.value,
exchange_rate_type=filters.exchange_rate_type.value,
met_trans=met_trans
)
valor_mp_temp_mn = float(valor_mp_temp_raw)
# Assign the properly converted commercial value directly
valor_comercial_mn = float(valor_comercial)
# Set peso values based on subpartida flag
if row[39] == 'P': # C40 - EsSubPartida
@@ -288,6 +332,8 @@ class TemporaryImportService:
return 'P' if val else 'S' # Convert bool to P/S for Advalorem
return str(val)
logger.error(f"DEBUG ROW: NumParte(17)='{row[17]}', Sector(30)='{row[30]}', Partida {row[43]}, Original Part ID(46)='{row[46]}'")
# Build detailed movement item
movement = MovementItemDetailed(
Linea=row[43], # C44 - LineaImpo
@@ -299,31 +345,32 @@ class TemporaryImportService:
TipoMovTemDef='IMTEM',
EsCambioRegimen='N',
Regimen=row[9], # C10 - Regimen
Fecha_Inicio=none_if_empty(row[10]), # C11 - Fecha_Inicio
Fecha_Fin=none_if_empty(row[11]), # C12 - Fecha_Fin
Fecha_Pago=none_if_empty(row[12]), # C13 - Fecha_Pago
Fecha_Inicio=parse_yyyymmdd_date(row[10]), # C11 - Fecha_Inicio
Fecha_Fin=parse_yyyymmdd_date(row[11]), # C12 - Fecha_Fin
Fecha_Pago=parse_yyyymmdd_date(row[12]), # C13 - Fecha_Pago
Remesa=to_str(row[13]), # C14 - Remesa
Proveedor=row[7], # C8 - Provider name (from JOIN)
RFCProveedor=None, # RFC not in detailed query
ProveedorTaxID=None, # Tax ID not in detailed query
RFCProveedor=provider_info.get('rfc'),
ProveedorTaxID=provider_info.get('tax_id'),
VendidoA=row[8], # C9 - Client name (from JOIN)
VendidoARFC=None, # RFC not in detailed query
VendidoATaxID=None, # Tax ID not in detailed query
VendidoARFC=client_info.get('rfc'),
VendidoATaxID=client_info.get('tax_id'),
AgenteAduanal=agente_info.get('name'),
Patente=agente_info.get('license'),
NumParte=row[19], # C20 - Clase (NumParte)
NumParte=row[48], # C49 - Part Number (from JOIN)
DescripcionE=StringHelper.clean_text(row[20]), # C21
DescripcionI=StringHelper.clean_text(row[21]), # C22
CantidadIE=float(row[22]) if row[22] else 0.0, # C23
UniMed=row[23], # C24
ValorComercialMN=valor_comercial_mn,
ValorMPTemp=valor_mp_temp_mn,
TipoCambio=tipo_cambio,
PesoNeto=peso_neto,
PesoBruto=peso_bruto,
OrdenCompraVenta=row[30], # C31 - OrdenCompra
FraccionArancelaria=row[31], # C32 - Fraccion
Preferencia=row[32], # C33 - TipoFraccion
Sector=row[34], # C35 - Sector
Sector=row[34], # C35 - Sector (from COALESCE)
PaisOrigen=row[36], # C37 - PaisOrigen
Aduana=aduana_nombre,
Advalorem=to_str(row[39]), # C40 - EsSubPartida (convert bool to str)
@@ -337,13 +384,13 @@ class TemporaryImportService:
FraccionAmericana=row[46], # C47 - FraccionAme
ECCN=row[47], # C48 - ECCN
SimboloEx=simbolo_ex,
FechaEmision=parse_yyyymmdd_date(row[50]), # C51 - FechaEmision (convert to datetime)
FechaEmision=parse_yyyymmdd_date(row[50]), # C51 - FechaEmision
BaseDeDatos=filters.database_name,
NumGafUni=num_gaf_uni,
UsuarioCap=row[51], # C52 - UsuarioCap
UsuarioAcr=row[52], # C53 - UsuarioAct
Transportista=row[53], # C54 - Transportista
NumCaja=row[54], # C55 - Transporte + NumTrasporte
Transportista=row[53], # C54 - Carrier ID
NumCaja=row[54], # C55 - Transport num
Pedimento18=row[55], # C56 - Pedimento18
AduanaCru=row[37], # C38 - Aduana_Cruce
Lote=row[56] # C57 - LOTE
@@ -371,7 +418,7 @@ class TemporaryImportService:
if filters.range_type.value == "FF":
where_conditions.append(f"ih.invoice_date >= TO_DATE('{filters.start_date}', 'YYYYMMDD') AND ih.invoice_date <= TO_DATE('{filters.end_date}', 'YYYYMMDD')")
else:
where_conditions.append(f"log.payment_date >= TO_DATE('{filters.start_date}', 'YYYYMMDD') AND log.payment_date <= TO_DATE('{filters.end_date}', 'YYYYMMDD')")
where_conditions.append(f"pd.payment_date >= TO_DATE('{filters.start_date}', 'YYYYMMDD') AND pd.payment_date <= TO_DATE('{filters.end_date}', 'YYYYMMDD')")
# Note: Status filter applied at Python level after CASE WHEN in SELECT
# because is_updated doesn't directly represent AC/NA status
@@ -401,8 +448,7 @@ class TemporaryImportService:
COALESCE(SUM(lf.value_mxn), 0)
FROM a76.item_line_financials lf
JOIN a76.item_lines il ON il.id = lf.item_line_id
JOIN a76.items i ON i.id = il.item_id
WHERE i.invoice_id = :consecutivo
WHERE il.invoice_id = :consecutivo
AND COALESCE(il.is_subpartida, false) = false
""")