Merge branch 'development' of https://git.aduanasoft.com/ADUANASOFT/anexo76 into feature/saldos_temporales_nueva_tabla
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
from pydantic import BaseModel, ConfigDict, field_validator
|
||||
from typing import Optional, List, Dict, Any
|
||||
from datetime import date
|
||||
|
||||
|
||||
class AphisCatalogDTO(BaseModel):
|
||||
id: Optional[int] = None
|
||||
|
||||
# --- Pestaña 1: General ---
|
||||
program_code: Optional[str] = None
|
||||
processing_code: Optional[str] = None
|
||||
aphis_type: Optional[str] = None
|
||||
disclaimer: Optional[str] = None
|
||||
electronic_image: Optional[str] = None
|
||||
confidential: Optional[str] = None
|
||||
global_product_id: Optional[str] = None
|
||||
intended_use_code: Optional[str] = None
|
||||
intended_use_description: Optional[str] = None
|
||||
item_type: Optional[str] = None
|
||||
product_code: Optional[str] = None
|
||||
product_code_2: Optional[str] = None
|
||||
product_code_3: Optional[str] = None
|
||||
scientific_genus_name: Optional[str] = None
|
||||
scientific_species_name: Optional[str] = None
|
||||
scientific_sub_species_name: Optional[str] = None
|
||||
common_name_specific: Optional[str] = None
|
||||
common_name_general: Optional[str] = None
|
||||
signed_doc: Optional[str] = None
|
||||
signed_doc_date: Optional[date] = None
|
||||
signed_doc_id: Optional[str] = None
|
||||
invoice_number: Optional[str] = None
|
||||
quantity_1: Optional[str] = None
|
||||
quantity_2: Optional[str] = None
|
||||
quantity_3: Optional[str] = None
|
||||
inspection: Optional[str] = None
|
||||
inspection_date: Optional[date] = None
|
||||
inspection_loc_date: Optional[date] = None
|
||||
inspection_location: Optional[str] = None
|
||||
country_production: Optional[str] = None
|
||||
country_source: Optional[str] = None
|
||||
|
||||
# --- Pestañas 2-7: Detalles (Listas de objetos) ---
|
||||
characteristics: Optional[List[Dict[str, Any]]] = []
|
||||
pitems: Optional[List[Dict[str, Any]]] = []
|
||||
lpcos: Optional[List[Dict[str, Any]]] = []
|
||||
entities: Optional[List[Dict[str, Any]]] = []
|
||||
containers: Optional[List[Dict[str, Any]]] = []
|
||||
routing: Optional[List[Dict[str, Any]]] = []
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
@field_validator("signed_doc_date", "inspection_date", "inspection_loc_date", mode="before")
|
||||
@classmethod
|
||||
def empty_to_none(cls, v):
|
||||
if v == "":
|
||||
return None
|
||||
return v
|
||||
@@ -0,0 +1,61 @@
|
||||
from typing import Optional, List, Dict, Any
|
||||
from datetime import date
|
||||
from sqlalchemy import Integer, String, Date, PrimaryKeyConstraint, JSON
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from core.database import Base
|
||||
|
||||
|
||||
class AphisCatalog(Base):
|
||||
"""
|
||||
Catálogo global de registros APHIS por empresa.
|
||||
Soporta las 7 pestañas de información (General + 6 detalles via JSON).
|
||||
"""
|
||||
__tablename__ = "inv_aphis_catalog"
|
||||
__table_args__ = (
|
||||
PrimaryKeyConstraint("id", name="inv_aphis_catalog_pkey"),
|
||||
{"schema": "a24", "extend_existing": True},
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
company_id: Mapped[int] = mapped_column(Integer, nullable=False, index=True)
|
||||
|
||||
# --- Pestaña 1: General (Campos principales) ---
|
||||
program_code: Mapped[Optional[str]] = mapped_column(String(10))
|
||||
processing_code: Mapped[Optional[str]] = mapped_column(String(10))
|
||||
aphis_type: Mapped[Optional[str]] = mapped_column(String(10))
|
||||
disclaimer: Mapped[Optional[str]] = mapped_column(String(10))
|
||||
electronic_image: Mapped[Optional[str]] = mapped_column(String(50))
|
||||
confidential: Mapped[Optional[str]] = mapped_column(String(1))
|
||||
global_product_id: Mapped[Optional[str]] = mapped_column(String(100))
|
||||
intended_use_code: Mapped[Optional[str]] = mapped_column(String(10))
|
||||
intended_use_description: Mapped[Optional[str]] = mapped_column(String(200))
|
||||
item_type: Mapped[Optional[str]] = mapped_column(String(20))
|
||||
product_code: Mapped[Optional[str]] = mapped_column(String(20))
|
||||
product_code_2: Mapped[Optional[str]] = mapped_column(String(20))
|
||||
product_code_3: Mapped[Optional[str]] = mapped_column(String(20))
|
||||
scientific_genus_name: Mapped[Optional[str]] = mapped_column(String(100))
|
||||
scientific_species_name: Mapped[Optional[str]] = mapped_column(String(100))
|
||||
scientific_sub_species_name: Mapped[Optional[str]] = mapped_column(String(100))
|
||||
common_name_specific: Mapped[Optional[str]] = mapped_column(String(200))
|
||||
common_name_general: Mapped[Optional[str]] = mapped_column(String(200))
|
||||
signed_doc: Mapped[Optional[str]] = mapped_column(String(100))
|
||||
signed_doc_date: Mapped[Optional[date]] = mapped_column(Date)
|
||||
signed_doc_id: Mapped[Optional[str]] = mapped_column(String(50))
|
||||
invoice_number: Mapped[Optional[str]] = mapped_column(String(50))
|
||||
quantity_1: Mapped[Optional[str]] = mapped_column(String(50))
|
||||
quantity_2: Mapped[Optional[str]] = mapped_column(String(50))
|
||||
quantity_3: Mapped[Optional[str]] = mapped_column(String(50))
|
||||
inspection: Mapped[Optional[str]] = mapped_column(String(200))
|
||||
inspection_date: Mapped[Optional[date]] = mapped_column(Date)
|
||||
inspection_loc_date: Mapped[Optional[date]] = mapped_column(Date)
|
||||
inspection_location: Mapped[Optional[str]] = mapped_column(String(200))
|
||||
country_production: Mapped[Optional[str]] = mapped_column(String(3))
|
||||
country_source: Mapped[Optional[str]] = mapped_column(String(3))
|
||||
|
||||
# --- Pestañas 2-7: Detalles (Almacenados como JSON por flexibilidad) ---
|
||||
characteristics: Mapped[Optional[List[Dict[str, Any]]]] = mapped_column(JSON, default=list)
|
||||
pitems: Mapped[Optional[List[Dict[str, Any]]]] = mapped_column(JSON, default=list)
|
||||
lpcos: Mapped[Optional[List[Dict[str, Any]]]] = mapped_column(JSON, default=list)
|
||||
entities: Mapped[Optional[List[Dict[str, Any]]]] = mapped_column(JSON, default=list)
|
||||
containers: Mapped[Optional[List[Dict[str, Any]]]] = mapped_column(JSON, default=list)
|
||||
routing: Mapped[Optional[List[Dict[str, Any]]]] = mapped_column(JSON, default=list)
|
||||
@@ -0,0 +1,62 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List
|
||||
from core.database import get_core_db
|
||||
from .models import AphisCatalog
|
||||
from .dto import AphisCatalogDTO
|
||||
|
||||
router = APIRouter(prefix="/aphis-catalog", tags=["APHIS Catalog"])
|
||||
|
||||
|
||||
@router.get("/", response_model=List[AphisCatalogDTO])
|
||||
def list_aphis_catalog(company_id: int, db: Session = Depends(get_core_db)):
|
||||
records = (
|
||||
db.query(AphisCatalog)
|
||||
.filter(AphisCatalog.company_id == company_id)
|
||||
.order_by(AphisCatalog.id)
|
||||
.all()
|
||||
)
|
||||
return records
|
||||
|
||||
|
||||
@router.post("/", response_model=AphisCatalogDTO)
|
||||
def create_aphis_catalog(data: AphisCatalogDTO, company_id: int, db: Session = Depends(get_core_db)):
|
||||
payload = data.model_dump(exclude={"id"})
|
||||
record = AphisCatalog(**payload, company_id=company_id)
|
||||
db.add(record)
|
||||
db.commit()
|
||||
db.refresh(record)
|
||||
return record
|
||||
|
||||
|
||||
@router.put("/{record_id}", response_model=AphisCatalogDTO)
|
||||
def update_aphis_catalog(
|
||||
record_id: int, data: AphisCatalogDTO, company_id: int, db: Session = Depends(get_core_db)
|
||||
):
|
||||
record = (
|
||||
db.query(AphisCatalog)
|
||||
.filter(AphisCatalog.id == record_id, AphisCatalog.company_id == company_id)
|
||||
.first()
|
||||
)
|
||||
if not record:
|
||||
raise HTTPException(status_code=404, detail="Registro no encontrado")
|
||||
|
||||
for field, value in data.model_dump(exclude={"id"}).items():
|
||||
setattr(record, field, value)
|
||||
|
||||
db.commit()
|
||||
db.refresh(record)
|
||||
return record
|
||||
|
||||
|
||||
@router.delete("/{record_id}", status_code=204)
|
||||
def delete_aphis_catalog(record_id: int, company_id: int, db: Session = Depends(get_core_db)):
|
||||
record = (
|
||||
db.query(AphisCatalog)
|
||||
.filter(AphisCatalog.id == record_id, AphisCatalog.company_id == company_id)
|
||||
.first()
|
||||
)
|
||||
if not record:
|
||||
raise HTTPException(status_code=404, detail="Registro no encontrado")
|
||||
db.delete(record)
|
||||
db.commit()
|
||||
@@ -8,6 +8,10 @@ from fastapi import APIRouter
|
||||
from .fa.fa_classes.routes import router as fa_classes_router
|
||||
from .fa.fa_item_lines.routes import router as fa_item_lines_router
|
||||
from .inv.part_countries.routes import router as part_countries_router
|
||||
from .inv.inv_aphis.inv_aphis_catalog.router import router as aphis_catalog_router
|
||||
|
||||
# Importar modelo para que SQLAlchemy cree la tabla automáticamente
|
||||
import api.v1.modules.a24.inv.inv_aphis.inv_aphis_catalog.models # noqa: F401
|
||||
|
||||
|
||||
# Router principal de A24
|
||||
@@ -21,3 +25,5 @@ router.include_router(
|
||||
|
||||
# Registrar routers de INV (Inventory)
|
||||
router.include_router(part_countries_router, prefix="/a24", tags=["a24 / inv / part-countries"])
|
||||
router.include_router(aphis_catalog_router, prefix="/a24", tags=["a24 / inv / aphis-catalog"])
|
||||
|
||||
|
||||
150
backend/api/v1/modules/a76/audit_log/register.py
Normal file
150
backend/api/v1/modules/a76/audit_log/register.py
Normal file
@@ -0,0 +1,150 @@
|
||||
# Importar modelos para Audit Log
|
||||
from api.v1.modules.a76.pedmientos.models.pedimentos import Pedimentos
|
||||
from api.v1.modules.a76.invoices.models import InvoiceHeader, InvoiceSalesDetails
|
||||
from api.v1.modules.a76.audit_log.events import register_audit_listeners
|
||||
|
||||
# Core Modules
|
||||
from api.v1.modules.a76.clients_and_providers.models import ClientProvider
|
||||
from api.v1.modules.a76.customs_brokers.models import CustomsBroker
|
||||
from api.v1.modules.a76.parts.models import Part
|
||||
from api.v1.modules.a76.items.models import LineItem
|
||||
from api.v1.modules.a76.general_catalogs.company.models import Company
|
||||
|
||||
# Reference Data
|
||||
from api.v1.modules.public.reference_data.countries.models import Country
|
||||
from api.v1.modules.public.reference_data.currency_types.models import CurrencyType
|
||||
from api.v1.modules.public.reference_data.customs_sections.models import CustomsSection
|
||||
from api.v1.modules.public.reference_data.customs_warehouses.models import (
|
||||
CustomsWarehouse,
|
||||
)
|
||||
from api.v1.modules.public.reference_data.incoterms.models import Incoterm
|
||||
from api.v1.modules.public.reference_data.invoice_types.models import InvoiceType
|
||||
from api.v1.modules.public.reference_data.material_types.models import MaterialType
|
||||
from api.v1.modules.public.reference_data.payment_methods.models import PaymentMethod
|
||||
from api.v1.modules.public.reference_data.pedimento_transport_catalog.models import (
|
||||
PedimentoTransportCatalog,
|
||||
)
|
||||
from api.v1.modules.public.reference_data.pedimento_codes.models import PedimentoCode
|
||||
from api.v1.modules.public.reference_data.pedimento_regimens.models import (
|
||||
RegimenPedimento,
|
||||
)
|
||||
from api.v1.modules.public.reference_data.states.models import State
|
||||
from api.v1.modules.public.reference_data.transport_modes.models import TransportMode
|
||||
from api.v1.modules.public.reference_data.transport_types.models import TransportType
|
||||
from api.v1.modules.public.reference_data.valuation_methods.models import (
|
||||
ValuationMethod,
|
||||
)
|
||||
from api.v1.modules.public.reference_data.license_exceptions.models import LicenseException
|
||||
from api.v1.modules.public.reference_data.agency_tariff_codes.models import AgencyTariffCode
|
||||
from api.v1.modules.public.reference_data.identifiers.models import IdentifierCatalog
|
||||
from api.v1.modules.public.reference_data.carta_porte_codes.models import CartaPorte
|
||||
from api.v1.modules.a76.general_catalogs.units_of_measure.models import UnitOfMeasure
|
||||
from api.v1.modules.a76.general_catalogs.exchange_rate.models import ExchangeRate
|
||||
from api.v1.modules.a76.general_catalogs.identifiers.models import Identifier
|
||||
from api.v1.modules.a76.classes.models import Class
|
||||
from api.v1.modules.a76.general_catalogs.classification_concepts.models import (
|
||||
ClassificationConcept,
|
||||
)
|
||||
from api.v1.modules.a76.general_catalogs.concepts.models import Concept
|
||||
from api.v1.modules.a76.general_catalogs.customs_broker_concepts.models import (
|
||||
CustomsBrokerConcept,
|
||||
)
|
||||
from api.v1.modules.a76.general_catalogs.depreciation_catalog.models import (
|
||||
DepreciationCatalog,
|
||||
)
|
||||
from api.v1.modules.a76.general_catalogs.doda.models import Doda
|
||||
from api.v1.modules.a76.general_catalogs.electronic_notices.models import (
|
||||
ElectronicNotice,
|
||||
)
|
||||
from api.v1.modules.a76.general_catalogs.equivalencies.models import Equivalency
|
||||
from api.v1.modules.a76.general_catalogs.error_catalogs.models import ErrorCatalog
|
||||
from api.v1.modules.a76.general_catalogs.fda_catalog.models import FDACatalog
|
||||
from api.v1.modules.a76.general_catalogs.inpc.models import INPC
|
||||
from api.v1.modules.a76.general_catalogs.legends.models import Legend
|
||||
from api.v1.modules.a76.general_catalogs.multi_currency_types.models import (
|
||||
MultiCurrencyType,
|
||||
)
|
||||
from api.v1.modules.a76.general_catalogs.packages.models import Package
|
||||
from api.v1.modules.a76.general_catalogs.ports.models import Port
|
||||
from api.v1.modules.a76.general_catalogs.location.models import Location, FaLocationExt
|
||||
from api.v1.modules.a76.general_catalogs.prevalidators.models import Prevalidator
|
||||
from api.v1.modules.a76.general_catalogs.seal.models import Seal
|
||||
from api.v1.modules.a76.general_catalogs.signatures.models import Signature
|
||||
from api.v1.modules.a76.general_catalogs.fractions.tariff_fractions.models import (
|
||||
TariffFraction,
|
||||
)
|
||||
from api.v1.modules.a76.general_catalogs.unit_conversions.models import UnitConversion
|
||||
from api.v1.modules.a76.general_catalogs.fractions.us_tariff_fractions.models import (
|
||||
USTariffFraction,
|
||||
)
|
||||
from api.v1.modules.a76.general_catalogs.sectors.models import Sector
|
||||
from api.v1.modules.a76.transportation.trailers.models import Trailer
|
||||
from api.v1.modules.a76.transportation.transporters.models import Transporter
|
||||
from api.v1.modules.a76.transportation.vehicles.models import Vehicle
|
||||
|
||||
|
||||
# Registrar Listeners de Auditoría
|
||||
def register_audit():
|
||||
register_audit_listeners(
|
||||
[
|
||||
# Core Transactions
|
||||
Pedimentos,
|
||||
InvoiceHeader,
|
||||
InvoiceSalesDetails,
|
||||
LineItem,
|
||||
# Sidebar Core Modules
|
||||
ClientProvider,
|
||||
CustomsBroker,
|
||||
Part,
|
||||
Company,
|
||||
# Transportation Modules
|
||||
Trailer,
|
||||
Transporter,
|
||||
Vehicle,
|
||||
# Reference Data
|
||||
Country,
|
||||
CurrencyType,
|
||||
CustomsSection,
|
||||
CustomsWarehouse,
|
||||
Incoterm,
|
||||
InvoiceType,
|
||||
MaterialType,
|
||||
PaymentMethod,
|
||||
PedimentoTransportCatalog,
|
||||
PedimentoCode,
|
||||
RegimenPedimento,
|
||||
Sector,
|
||||
State,
|
||||
TransportMode,
|
||||
TransportType,
|
||||
ValuationMethod,
|
||||
LicenseException,
|
||||
AgencyTariffCode,
|
||||
IdentifierCatalog,
|
||||
CartaPorte,
|
||||
UnitOfMeasure,
|
||||
ExchangeRate,
|
||||
Identifier,
|
||||
Class,
|
||||
ClassificationConcept,
|
||||
Concept,
|
||||
CustomsBrokerConcept,
|
||||
DepreciationCatalog,
|
||||
Doda,
|
||||
ElectronicNotice,
|
||||
Equivalency,
|
||||
ErrorCatalog,
|
||||
FDACatalog,
|
||||
INPC,
|
||||
Legend,
|
||||
MultiCurrencyType,
|
||||
Package,
|
||||
Port,
|
||||
Prevalidator,
|
||||
Seal,
|
||||
Signature,
|
||||
TariffFraction,
|
||||
UnitConversion,
|
||||
USTariffFraction,
|
||||
]
|
||||
)
|
||||
@@ -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:
|
||||
|
||||
@@ -44,15 +44,15 @@ def scan_file(self, job_id: str, model_target: str, config: str = None):
|
||||
|
||||
if model_target == "invoice_header":
|
||||
from api.v1.modules.a76.layouts_csv.facturas.tasks import _do_scan_file
|
||||
return _do_scan_file(job_id, "invoice_header", config, job_type_override="exp")
|
||||
return _do_scan_file(self, job_id, "invoice_header", config, job_type_override="exp")
|
||||
|
||||
if model_target == "invoice_details":
|
||||
from api.v1.modules.a76.layouts_csv.facturas.tasks import _do_scan_file
|
||||
return _do_scan_file(job_id, "invoice_details", config, job_type_override="exp")
|
||||
return _do_scan_file(self, job_id, "invoice_details", config, job_type_override="exp")
|
||||
|
||||
if model_target == "invoice_series":
|
||||
from api.v1.modules.a76.layouts_csv.facturas.tasks import _do_scan_file
|
||||
return _do_scan_file(job_id, "invoice_series", config, job_type_override="exp")
|
||||
return _do_scan_file(self, job_id, "invoice_series", config, job_type_override="exp")
|
||||
|
||||
# Fallback (e.g. unknown model_target)
|
||||
file_path = _ensure_file(job_id)
|
||||
|
||||
@@ -185,10 +185,10 @@ def _validate_customs_broker_ref(
|
||||
@celery_app.task(bind=True)
|
||||
def scan_file(self, job_id: str, model_target: str, config: str = None, job_type_override: Optional[str] = None):
|
||||
"""Pass 1: Read CSV, Validate types, Write Errors to JSONL. Delegates to _do_scan_file."""
|
||||
return _do_scan_file(job_id, model_target, config, job_type_override)
|
||||
return _do_scan_file(self, job_id, model_target, config, job_type_override)
|
||||
|
||||
|
||||
def _do_scan_file(job_id: str, model_target: str, config: Optional[str] = None, job_type_override: Optional[str] = None) -> Dict[str, Any]:
|
||||
def _do_scan_file(self, job_id: str, model_target: str, config: Optional[str] = None, job_type_override: Optional[str] = None) -> Dict[str, Any]:
|
||||
"""Pass 1 body: load file/meta from storage, run validations, store error lines. Uses effective_job_type for storage."""
|
||||
effective_job_type = job_type_override if job_type_override is not None else JOB_TYPE
|
||||
log_prefix = "Exportación import" if effective_job_type else "Invoices import"
|
||||
@@ -4487,6 +4487,7 @@ def _do_insert_valid_rows(job_id: str, model_target: str, job_type_override: Opt
|
||||
InvoiceFinancials,
|
||||
InvoiceLogistics,
|
||||
InvoiceSalesDetails,
|
||||
InvoiceStatus,
|
||||
OperationType,
|
||||
TransportType,
|
||||
WeightUnit,
|
||||
@@ -4925,7 +4926,10 @@ def _do_insert_valid_rows(job_id: str, model_target: str, job_type_override: Opt
|
||||
header = existing_header
|
||||
header.invoice_date = invoice_date
|
||||
header.operation_type = op_type_value
|
||||
header.status = True # Mark as updated
|
||||
# CSV import only captures data; "processed" is set by the manual/import processing flow.
|
||||
# Legacy CSV imports may have persisted booleans ('True'/'False') into status.
|
||||
if header.status not in (InvoiceStatus.PENDING, InvoiceStatus.PROCESSED, InvoiceStatus.REVERSED):
|
||||
header.status = InvoiceStatus.PENDING
|
||||
header.updated_date = datetime.utcnow()
|
||||
capture_user = meta.get("capture_user") or "CSV"
|
||||
header.who_processed = capture_user
|
||||
@@ -4964,7 +4968,7 @@ def _do_insert_valid_rows(job_id: str, model_target: str, job_type_override: Opt
|
||||
invoice_number=invoice_number,
|
||||
invoice_date=invoice_date,
|
||||
operation_type=op_type_value,
|
||||
status=False,
|
||||
status=InvoiceStatus.PENDING,
|
||||
system="CSV",
|
||||
capture_date=datetime.utcnow(),
|
||||
capture_user=capture_user,
|
||||
|
||||
|
Can't render this file because it is too large.
|
@@ -4,7 +4,7 @@ from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
|
||||
class CartaPorte(Base):
|
||||
__tablename__ = "carta_porte"
|
||||
__tablename__ = "carta_porte_codes"
|
||||
__table_args__ = (
|
||||
{"schema": "public", "extend_existing": True},
|
||||
)
|
||||
@@ -39,10 +39,8 @@ def seed_carta_porte(db: Session):
|
||||
if len(batch) >= batch_size:
|
||||
db.bulk_save_objects(batch)
|
||||
db.commit()
|
||||
batch = []
|
||||
print(f"Inserted {batch_size} records...")
|
||||
batch = []
|
||||
|
||||
if batch:
|
||||
db.bulk_save_objects(batch)
|
||||
db.commit()
|
||||
print(f"Finished seeding with {len(batch)} remaining records.")
|
||||
db.commit()
|
||||
@@ -6,7 +6,7 @@ Agrega todos los módulos de la aplicación
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .agency_tariff_codes.routes import router as agency_tariff_codes_router
|
||||
from .carta_porte.routes import router as carta_porte_router
|
||||
from .carta_porte_codes.routes import router as carta_porte_router
|
||||
from .code_pedimento_regimens.routes import router as code_pedimento_regimens_router
|
||||
from .containers.routes import router as containers_router
|
||||
from .countries.routes import router as countries_router
|
||||
@@ -108,7 +108,7 @@ router.include_router(
|
||||
router.include_router(
|
||||
carta_porte_router,
|
||||
prefix="/reference_data",
|
||||
tags=["public / reference_data / carta_porte"],
|
||||
tags=["public / reference_data / carta_porte_codes"],
|
||||
)
|
||||
router.include_router(
|
||||
customs_sections_router,
|
||||
|
||||
Reference in New Issue
Block a user