Merge pull request 'feature/partidas-impo-bug-fix' (#227) from feature/partidas-impo-bug-fix into development

Reviewed-on: ADUANASOFT/anexo76#227
This commit is contained in:
2026-03-18 21:07:44 +00:00
3 changed files with 171 additions and 37 deletions

View File

@@ -9,6 +9,9 @@ from ...common.fractions import search_fraction_preference
from ...common.common_validators import item_exists
from ...models import LineItem
from ...line_customs.models import FractionType, LineCustom
from api.v1.modules.a76.general_catalogs.fractions.us_tariff_fractions.models import (
USTariffFraction,
)
from api.v1.modules.a76.items.schemas import LineItemCreate
from api.v1.modules.a76.invoices.models import InvoiceHeader
from api.v1.modules.a76.classes.models import Class
@@ -22,6 +25,8 @@ from api.v1.modules.public.reference_data.valuation_methods.models import (
from api.v1.modules.a76.parts.models import Part
from api.v1.modules.a76.general_catalogs.company.models import Company
import re
def validate_common(
db: Session,
@@ -284,18 +289,75 @@ def validate_common(
)
if line.customs.american_fraction:
american_fraction_exists = db.query(
exists().where(
LineCustom.american_fraction == line.customs.american_fraction
def _normalize_american_fraction_code(raw_code: str) -> list[str]:
"""
Attempts to map user input to the canonical USTariffFraction.code.
The catalog commonly stores dotted HTS codes (e.g. 3802.20.00.00),
but users may paste/enter digits-only or use different separators.
"""
normalized_raw = (raw_code or "").strip()
if not normalized_raw:
return []
digits_only = re.sub(r"[.\s\-]", "", normalized_raw)
candidates: list[str] = []
# 1) Exact input
candidates.append(normalized_raw)
# 2) Canonical with dots if length matches common patterns
if len(digits_only) == 10:
candidates.append(
f"{digits_only[:4]}.{digits_only[4:6]}.{digits_only[6:8]}.{digits_only[8:10]}"
)
elif len(digits_only) == 8:
candidates.append(
f"{digits_only[:4]}.{digits_only[4:6]}.{digits_only[6:8]}"
)
# 3) Digits-only (if catalog stores without dots)
candidates.append(digits_only)
# De-duplicate while preserving order
seen: set[str] = set()
deduped: list[str] = []
for c in candidates:
if not c or c in seen:
continue
seen.add(c)
deduped.append(c)
return deduped
raw_american_fraction = str(line.customs.american_fraction)
candidates = _normalize_american_fraction_code(raw_american_fraction)
us_fraction: USTariffFraction | None = None
for candidate in candidates:
us_fraction = (
db.query(USTariffFraction)
.filter(
USTariffFraction.code == candidate,
USTariffFraction.tenant_id == tenant_id,
USTariffFraction.company_id == company_id,
)
.first()
)
).scalar()
if not american_fraction_exists:
if us_fraction:
break
if not us_fraction:
errors.add_error(
field=f"line[{line_number}].customs.american_fraction",
message="La fracción americana especificada no existe.",
solution=["Proporciona una fracción americana valida."],
code="AMERICAN_FRACTION_NOT_FOUND",
)
else:
# Keep canonical value so downstream validators can use it safely.
line.customs.american_fraction = us_fraction.code
if line.order:
if len(line.order) > 20: