Merge branch 'development' into feature/alembic-daf

This commit is contained in:
2026-05-12 09:23:19 -06:00
57 changed files with 2063 additions and 944 deletions

View File

@@ -138,7 +138,7 @@ def review_classes(
3a. If no class errors: validate fractions for ALL lines.
3b. If class errors exist: add a CLASE error per invalid line and validate
fractions only for those lines.
4. If the invoice has not been reviewed by the company, flag lines whose
4. If invoice logistics does not mark equipment_reviewed, flag lines whose
class requires physical review (Class.physical_review == 1).
"""
# 1. Lines whose class is not assigned / not found in the catalog
@@ -178,7 +178,7 @@ def review_classes(
_validate_line_fraction(db, line, errors)
# 4. Physical review check
if not invoice.compliance_mx.was_reviewed_by_company:
if not (invoice.logistics and invoice.logistics.equipment_reviewed):
review_required_lines = (
db.query(LineItem)
.join(Class, LineItem.class_id == Class.id)
@@ -201,7 +201,7 @@ def review_classes(
),
solution=[
f"Revisar el Equipo de la Partida: {line.line_number} "
"y Asignar Como Revisado a Nivel Factura."
"y marcar Equipo revisado en Continuación (logística) a nivel factura."
],
code="CLASE",
)

View File

@@ -3,6 +3,7 @@ Carga de conjuntos FK para validación de import CSV de transportistas.
Clarion: GTransportista (ClaveTrans), GPaises (Pais_Ame), GEstados (Descripcion), relación Estado-País.
"""
from typing import Set, Tuple, Optional
from sqlalchemy.orm import Session
import logging
from core.database import CoreSessionLocal
@@ -13,6 +14,7 @@ logger = logging.getLogger(__name__)
def load_transportistas_fk_sets(
tenant_id: Optional[int] = None,
company_id: Optional[int] = None,
db: Optional[Session] = None,
) -> Tuple[
Set[str],
Set[str],
@@ -32,50 +34,56 @@ def load_transportistas_fk_sets(
state_descriptions_upper: Set[str] = set()
state_country_set: Set[Tuple[str, str]] = set()
try:
with CoreSessionLocal() as session:
from api.v1.modules.a76.transportation.transporters.models import Transporter
from api.v1.modules.public.reference_data.countries.models import Country
from api.v1.modules.public.reference_data.states.models import State
def _load(session: Session):
from api.v1.modules.a76.transportation.transporters.models import Transporter
from api.v1.modules.public.reference_data.countries.models import Country
from api.v1.modules.public.reference_data.states.models import State
if tenant_id is not None and company_id is not None:
for row in (
session.query(Transporter.transporter_key)
.filter(
Transporter.tenant_id == tenant_id,
Transporter.company_id == company_id,
)
.all()
):
if row[0] and (row[0] or "").strip():
existing_transporter_keys.add((row[0] or "").strip().upper())
for row in session.query(Country.ame_key).all():
if row[0]:
valid_country_ame.add((row[0] or "").strip().upper())
for state in session.query(State).all():
country = (
session.query(Country)
.filter(Country.m3_key == state.m3_key)
.first()
if tenant_id is not None and company_id is not None:
for row in (
session.query(Transporter.transporter_key)
.filter(
Transporter.tenant_id == tenant_id,
Transporter.company_id == company_id,
)
ame = None
if country and (country.ame_key or "").strip():
ame = (country.ame_key or "").strip().upper()
.all()
):
if row[0] and (row[0] or "").strip():
existing_transporter_keys.add((row[0] or "").strip().upper())
desc = (state.description or "").strip()
if desc:
state_descriptions_upper.add(desc.upper())
if ame:
state_country_set.add((ame, desc.upper()))
for row in session.query(Country.ame_key).all():
if row[0]:
valid_country_ame.add((row[0] or "").strip().upper())
mex_key = (state.mex_key or "").strip()
if mex_key:
mk = mex_key.upper()
state_descriptions_upper.add(mk)
if ame:
state_country_set.add((ame, mk))
for state in session.query(State).all():
country = (
session.query(Country)
.filter(Country.m3_key == state.m3_key)
.first()
)
ame = None
if country and (country.ame_key or "").strip():
ame = (country.ame_key or "").strip().upper()
desc = (state.description or "").strip()
if desc:
state_descriptions_upper.add(desc.upper())
if ame:
state_country_set.add((ame, desc.upper()))
mex_key = (state.mex_key or "").strip()
if mex_key:
mk = mex_key.upper()
state_descriptions_upper.add(mk)
if ame:
state_country_set.add((ame, mk))
try:
if db:
_load(db)
else:
with CoreSessionLocal() as session:
_load(session)
except Exception as e:
logger.warning("Transportistas import: could not load FK sets: %s", e)

View File

@@ -37,24 +37,31 @@ def validate_row_transporter(
clave = (row.get("CLAVE TRANSPORTISTA") or "").strip().upper()
use_partial = actualizar and bool(clave and clave in existing)
if not use_partial and clave and clave in existing:
errors.append({
"line": line_num,
"col": "CLAVE TRANSPORTISTA",
"msg": f"La clave '{row.get('CLAVE TRANSPORTISTA')}' ya existe en el catálogo."
})
if use_partial:
errors.extend(
valida_parcial_transportistas(
row,
line_num,
valid_country_ame=valid_country_ame,
state_descriptions_upper=state_descriptions_upper,
state_country_set=state_country_set,
row,
line_num,
valid_country_ame=valid_country_ame,
state_descriptions_upper=state_descriptions_upper,
state_country_set=state_country_set,
)
)
else:
errors.extend(
valida_toda_transportistas(
row,
line_num,
valid_country_ame=valid_country_ame,
state_descriptions_upper=state_descriptions_upper,
state_country_set=state_country_set,
row,
line_num,
valid_country_ame=valid_country_ame,
state_descriptions_upper=state_descriptions_upper,
state_country_set=state_country_set,
)
)
return errors

View File

@@ -277,6 +277,7 @@ def transporter_model_to_row(t) -> Dict[str, Any]:
def validate_transporter_row_for_api(
db: Session,
tenant_id: int,
company_id: int,
row: Dict[str, Any],
@@ -292,7 +293,7 @@ def validate_transporter_row_for_api(
valid_country_ame,
state_descriptions_upper,
state_country_set,
) = load_transportistas_fk_sets(tenant_id, company_id)
) = load_transportistas_fk_sets(tenant_id, company_id, db=db)
clave = (row.get("CLAVE TRANSPORTISTA") or "").strip().upper()
# existing set from loader is uppercased keys for this company

View File

@@ -104,6 +104,7 @@ class TransporterService:
"""Create a new transporter"""
data = transporter_data.model_dump()
validate_transporter_row_for_api(
db,
tenant_id,
company_id,
transporter_fields_to_csv_row(data),
@@ -163,6 +164,7 @@ class TransporterService:
}
merged.update(update_data)
validate_transporter_row_for_api(
db,
tenant_id,
company_id,
transporter_fields_to_csv_row(merged),