feature/catalog-importation
This commit is contained in:
@@ -10,6 +10,7 @@ from api.v1.modules.public.reference_data.customs_sections.models import Customs
|
||||
from api.v1.modules.public.reference_data.pedimento_codes.models import PedimentoCode
|
||||
from api.v1.modules.public.reference_data.code_pedimento_regimens.models import CodePedimentoRegimen
|
||||
from api.v1.modules.public.reference_data.incoterms.models import Incoterm
|
||||
from api.v1.modules.public.reference_data.valuation_methods.models import ValuationMethod
|
||||
from api.v1.modules.public.reference_data.transport_modes.models import TransportMode
|
||||
|
||||
# Import A76 Services
|
||||
@@ -31,6 +32,7 @@ from api.v1.modules.public.reference_data.transport_types.dto import TransportTy
|
||||
from api.v1.modules.public.reference_data.customs_sections.dto import CustomsSectionDTO
|
||||
from api.v1.modules.public.reference_data.code_pedimento_regimens.dto import CodePedimentoRegimenDTO
|
||||
from api.v1.modules.public.reference_data.incoterms.dto import IncotermDTO
|
||||
from api.v1.modules.public.reference_data.valuation_methods.dto import ValuationMethodDTO
|
||||
from api.v1.modules.public.reference_data.transport_modes.dto import TransportModeDTO
|
||||
|
||||
# Additional DTOs
|
||||
@@ -72,6 +74,9 @@ class InvoiceCatalogService:
|
||||
response.incoterms = [
|
||||
IncotermDTO.model_validate(obj) for obj in db.query(Incoterm).all()
|
||||
]
|
||||
response.valuation_methods = [
|
||||
ValuationMethodDTO.model_validate(obj) for obj in db.query(ValuationMethod).all()
|
||||
]
|
||||
response.transport_modes = [
|
||||
TransportModeDTO.model_validate(obj) for obj in db.query(TransportMode).all()
|
||||
]
|
||||
|
||||
@@ -3,9 +3,10 @@ from api.v1.common.tenant_crud_routes import TenantCRUDRoutes
|
||||
from core.database import get_core_db
|
||||
from core.security import get_current_user, validate_access_to_resource
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Path
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from . import schemas, services
|
||||
from . import schemas, services, models
|
||||
from .catalog_service import InvoiceCatalogService
|
||||
|
||||
# Create main router
|
||||
@@ -36,6 +37,27 @@ def get_edition_data(
|
||||
return data
|
||||
|
||||
|
||||
@router.get("/invoices/remesa-suggestion", response_model=Dict[str, int])
|
||||
def get_remesa_suggestion(
|
||||
pedimento_id: int = Query(..., description="Pedimento ID"),
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: Dict[str, Any] = Depends(get_current_user),
|
||||
):
|
||||
"""Suggest next remesa for selected pedimento (max+1)."""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
max_rem = (
|
||||
db.query(func.max(models.InvoiceComplianceMx.remesa))
|
||||
.filter(
|
||||
models.InvoiceComplianceMx.pedimento_id == pedimento_id,
|
||||
models.InvoiceComplianceMx.tenant_id == tenant_id,
|
||||
models.InvoiceComplianceMx.company_id == company_id,
|
||||
)
|
||||
.scalar()
|
||||
)
|
||||
return {"next_remesa": int((max_rem or 0) + 1)}
|
||||
|
||||
|
||||
# Create CRUD routes for Invoice Header using TenantCRUDRoutes
|
||||
invoice_crud = TenantCRUDRoutes(
|
||||
service=services.InvoiceService,
|
||||
|
||||
@@ -605,6 +605,7 @@ from api.v1.modules.public.reference_data.currency_types.dto import CurrencyType
|
||||
from api.v1.modules.public.reference_data.transport_types.dto import TransportTypeDTO
|
||||
from api.v1.modules.public.reference_data.customs_sections.dto import CustomsSectionDTO
|
||||
from api.v1.modules.public.reference_data.incoterms.dto import IncotermDTO
|
||||
from api.v1.modules.public.reference_data.valuation_methods.dto import ValuationMethodDTO
|
||||
from api.v1.modules.public.reference_data.transport_modes.dto import TransportModeDTO
|
||||
from api.v1.modules.public.reference_data.code_pedimento_regimens.dto import CodePedimentoRegimenDTO
|
||||
|
||||
@@ -634,6 +635,7 @@ class InvoiceCatalogsResponse(BaseModel):
|
||||
code_pedimento_regimens: List[CodePedimentoRegimenDTO] = []
|
||||
seals: List[dict] = [] # Placeholder, refine with actual DTO
|
||||
incoterms: List[IncotermDTO] = []
|
||||
valuation_methods: List[ValuationMethodDTO] = []
|
||||
pedimentos: List[dict] = [] # Placeholder, refine with actual DTO
|
||||
transport_modes: List[TransportModeDTO] = []
|
||||
default_settings: Optional[dict] = None
|
||||
|
||||
@@ -34,6 +34,60 @@ def _get_current_username() -> str:
|
||||
return "System"
|
||||
|
||||
|
||||
def _autofill_remesa_if_needed(db: Session, invoice_data, tenant_id: int, company_id: int) -> None:
|
||||
"""
|
||||
Autocalcula remesa cuando hay pedimento consolidado y remesa viene vacía.
|
||||
Se hace ANTES de validar para que cumpla reglas de required en validators.
|
||||
"""
|
||||
try:
|
||||
compliance = getattr(invoice_data, "compliance_mx", None)
|
||||
if not compliance:
|
||||
return
|
||||
|
||||
pedimento_id = getattr(compliance, "pedimento_id", None)
|
||||
remesa = getattr(compliance, "remesa", None)
|
||||
|
||||
if not pedimento_id or remesa:
|
||||
return
|
||||
|
||||
# No aplicar a MEX (por consistencia con CSV import donde remesa es None para MEX)
|
||||
invoice_type = getattr(invoice_data, "invoice_type", None)
|
||||
if invoice_type == "MEX":
|
||||
return
|
||||
|
||||
from api.v1.modules.a76.pedmientos.models.pedimentos import Pedimentos, PedimentoType
|
||||
|
||||
ped = (
|
||||
db.query(Pedimentos)
|
||||
.filter(
|
||||
Pedimentos.id == pedimento_id,
|
||||
Pedimentos.tenant_id == tenant_id,
|
||||
Pedimentos.company_id == company_id,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not ped:
|
||||
return
|
||||
|
||||
if getattr(ped, "pedimento_type", None) != PedimentoType.CONSOLIDATED:
|
||||
return
|
||||
|
||||
max_rem = (
|
||||
db.query(func.max(models.InvoiceComplianceMx.remesa))
|
||||
.filter(
|
||||
models.InvoiceComplianceMx.pedimento_id == pedimento_id,
|
||||
models.InvoiceComplianceMx.tenant_id == tenant_id,
|
||||
models.InvoiceComplianceMx.company_id == company_id,
|
||||
)
|
||||
.scalar()
|
||||
)
|
||||
next_rem = (max_rem or 0) + 1
|
||||
compliance.remesa = next_rem
|
||||
except Exception:
|
||||
# No bloquear guardado por fallo de autocalculo; validación normal aplicará.
|
||||
return
|
||||
|
||||
|
||||
class InvoiceService:
|
||||
"""Service for Invoice Header operations"""
|
||||
|
||||
@@ -126,6 +180,9 @@ class InvoiceService:
|
||||
# Validaciones con ErrorCollector
|
||||
errors = ErrorCollector()
|
||||
|
||||
# Autocalculo remesa (si aplica) ANTES de validar
|
||||
_autofill_remesa_if_needed(db, invoice_data, tenant_id, company_id)
|
||||
|
||||
# Validar si la factura ya existe
|
||||
invoice_exists(db, invoice_data.invoice_number, tenant_id, company_id, errors)
|
||||
if invoice_data.operation_type == "exp":
|
||||
@@ -284,6 +341,8 @@ class InvoiceService:
|
||||
if invoice_data.operation_type == "exp":
|
||||
validate_update_export(db, invoice_data, invoice, tenant_id, company_id, errors)
|
||||
else:
|
||||
# Autocalculo remesa (si aplica) ANTES de validar
|
||||
_autofill_remesa_if_needed(db, invoice_data, tenant_id, company_id)
|
||||
validate_update_import(db, invoice_data, invoice, tenant_id, company_id, errors)
|
||||
|
||||
# Si hay errores, lanzar excepción ANTES de actualizar
|
||||
|
||||
@@ -78,7 +78,7 @@ async def delete_incoterm(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(has_role("admin")),
|
||||
):
|
||||
obj = db.query(Incoterm).filter(Incoterm.key == key).first()
|
||||
obj = db.query(Incoterm).filter(Incoterm.code == key).first()
|
||||
if not obj:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
db.delete(obj)
|
||||
|
||||
Reference in New Issue
Block a user