feat: add DODA module with DTOs, models, and service layer for managing DODA operations
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Módulo de DODA (Documentos de Operación de Aduana)
|
||||
"""
|
||||
423
backend/api/v1/modules/a76/general_catalogs/doda/dto.py
Normal file
423
backend/api/v1/modules/a76/general_catalogs/doda/dto.py
Normal file
@@ -0,0 +1,423 @@
|
||||
"""
|
||||
DTOs (Data Transfer Objects) para módulo de DODA
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from typing import Optional, List
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
# ============ DODA CONTAINER SEAL DTOS ============
|
||||
class DodaContainerSealCreateDTO(BaseModel):
|
||||
"""DTO para crear un candado de contenedor"""
|
||||
|
||||
seal_value: Optional[str] = Field(
|
||||
None, max_length=21, description="Seal value")
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class DodaContainerSealResponseDTO(BaseModel):
|
||||
"""DTO para responder con datos de un candado"""
|
||||
|
||||
id: int
|
||||
doda_sys_id: int
|
||||
seal_line: int
|
||||
seal_value: Optional[str] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# ============ DODA CONTAINER DTOS ============
|
||||
class DodaContainerCreateDTO(BaseModel):
|
||||
"""DTO para crear un contenedor"""
|
||||
|
||||
container_value: Optional[str] = Field(
|
||||
None, max_length=20, description="Container value")
|
||||
seals: Optional[str] = Field(None, max_length=254, description="Seals")
|
||||
seals_detail: Optional[List[DodaContainerSealCreateDTO]] = Field(
|
||||
None, description="Container seals"
|
||||
)
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class DodaContainerUpdateDTO(BaseModel):
|
||||
"""DTO para actualizar un contenedor"""
|
||||
|
||||
container_value: Optional[str] = Field(
|
||||
None, max_length=20, description="Container value")
|
||||
seals: Optional[str] = Field(None, max_length=254, description="Seals")
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class DodaContainerResponseDTO(BaseModel):
|
||||
"""DTO para responder con datos de un contenedor"""
|
||||
|
||||
id: int
|
||||
doda_sys_id: int
|
||||
container_line: int
|
||||
container_value: Optional[str] = None
|
||||
seals: Optional[str] = None
|
||||
seals_detail: Optional[List[DodaContainerSealResponseDTO]] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# ============ DODA AMERICAN PEDIMENTO DTOS ============
|
||||
class DodaAmericanPedimentoCreateDTO(BaseModel):
|
||||
"""DTO para crear un pedimento americano"""
|
||||
|
||||
american_pedimento_type: Optional[str] = Field(
|
||||
None, max_length=2, description="American pedimento type"
|
||||
)
|
||||
american_pedimento_value: Optional[str] = Field(
|
||||
None, max_length=20, description="American pedimento value"
|
||||
)
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class DodaAmericanPedimentoUpdateDTO(BaseModel):
|
||||
"""DTO para actualizar un pedimento americano"""
|
||||
|
||||
american_pedimento_type: Optional[str] = Field(
|
||||
None, max_length=2, description="American pedimento type"
|
||||
)
|
||||
american_pedimento_value: Optional[str] = Field(
|
||||
None, max_length=20, description="American pedimento value"
|
||||
)
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class DodaAmericanPedimentoResponseDTO(BaseModel):
|
||||
"""DTO para responder con datos de un pedimento americano"""
|
||||
|
||||
id: int
|
||||
doda_sys_id: int
|
||||
american_pedimento_line: int
|
||||
american_pedimento_type: Optional[str] = None
|
||||
american_pedimento_value: Optional[str] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# ============ DODA PEDIMENTO DTOS ============
|
||||
class DodaPedimentoCreateDTO(BaseModel):
|
||||
"""DTO para crear un pedimento DODA"""
|
||||
|
||||
authorization_patent: Optional[str] = Field(
|
||||
None, max_length=10, description="Authorization patent"
|
||||
)
|
||||
document: Optional[str] = Field(
|
||||
None, max_length=50, description="Document")
|
||||
shipment: Optional[str] = Field(
|
||||
None, max_length=11, description="Shipment")
|
||||
cove: Optional[str] = Field(None, max_length=50, description="COVE")
|
||||
umc: Optional[str] = Field(None, max_length=20, description="UMC")
|
||||
effective_amount_usd: Optional[Decimal] = Field(
|
||||
None, description="Effective amount USD")
|
||||
difference_amount_usd: Optional[Decimal] = Field(
|
||||
None, description="Difference amount USD")
|
||||
dta_niu: Optional[str] = Field(None, max_length=20, description="DTA NIU")
|
||||
article_7: Optional[bool] = Field(None, description="Article 7")
|
||||
pedimento_sys_id: Optional[int] = Field(
|
||||
None, description="Pedimento system ID")
|
||||
invoice_line: Optional[int] = Field(None, description="Invoice line")
|
||||
part_ii_line: Optional[int] = Field(None, description="Part II line")
|
||||
pedimento_type: Optional[str] = Field(
|
||||
None, max_length=20, description="Pedimento type")
|
||||
zero_packaging_validation: Optional[bool] = Field(
|
||||
None, description="Zero packaging validation"
|
||||
)
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class DodaPedimentoUpdateDTO(BaseModel):
|
||||
"""DTO para actualizar un pedimento DODA"""
|
||||
|
||||
authorization_patent: Optional[str] = Field(
|
||||
None, max_length=10, description="Authorization patent"
|
||||
)
|
||||
document: Optional[str] = Field(
|
||||
None, max_length=50, description="Document")
|
||||
shipment: Optional[str] = Field(
|
||||
None, max_length=11, description="Shipment")
|
||||
cove: Optional[str] = Field(None, max_length=50, description="COVE")
|
||||
umc: Optional[str] = Field(None, max_length=20, description="UMC")
|
||||
effective_amount_usd: Optional[Decimal] = Field(
|
||||
None, description="Effective amount USD")
|
||||
difference_amount_usd: Optional[Decimal] = Field(
|
||||
None, description="Difference amount USD")
|
||||
dta_niu: Optional[str] = Field(None, max_length=20, description="DTA NIU")
|
||||
article_7: Optional[bool] = Field(None, description="Article 7")
|
||||
pedimento_sys_id: Optional[int] = Field(
|
||||
None, description="Pedimento system ID")
|
||||
invoice_line: Optional[int] = Field(None, description="Invoice line")
|
||||
part_ii_line: Optional[int] = Field(None, description="Part II line")
|
||||
pedimento_type: Optional[str] = Field(
|
||||
None, max_length=20, description="Pedimento type")
|
||||
zero_packaging_validation: Optional[bool] = Field(
|
||||
None, description="Zero packaging validation"
|
||||
)
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class DodaPedimentoResponseDTO(BaseModel):
|
||||
"""DTO para responder con datos de un pedimento DODA"""
|
||||
|
||||
id: int
|
||||
doda_sys_id: int
|
||||
pedimento_line: int
|
||||
authorization_patent: Optional[str] = None
|
||||
document: Optional[str] = None
|
||||
shipment: Optional[str] = None
|
||||
cove: Optional[str] = None
|
||||
umc: Optional[str] = None
|
||||
effective_amount_usd: Optional[Decimal] = None
|
||||
difference_amount_usd: Optional[Decimal] = None
|
||||
dta_niu: Optional[str] = None
|
||||
article_7: Optional[bool] = None
|
||||
pedimento_sys_id: Optional[int] = None
|
||||
invoice_line: Optional[int] = None
|
||||
part_ii_line: Optional[int] = None
|
||||
pedimento_type: Optional[str] = None
|
||||
zero_packaging_validation: Optional[bool] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# ============ MAIN DODA DTOS ============
|
||||
class DodaCreateDTO(BaseModel):
|
||||
"""DTO para crear un DODA"""
|
||||
|
||||
integration_number: Optional[str] = Field(
|
||||
None, max_length=30, description="Integration number")
|
||||
doda_date: Optional[int] = Field(None, description="DODA date")
|
||||
doda_time: Optional[int] = Field(None, description="DODA time")
|
||||
dispatch_customs: Optional[str] = Field(
|
||||
None, max_length=3, description="Dispatch customs")
|
||||
customs_sections: Optional[str] = Field(
|
||||
None, max_length=3, description="Customs sections")
|
||||
patent: Optional[str] = Field(None, max_length=4, description="Patent")
|
||||
pedimentos: Optional[str] = Field(
|
||||
None, max_length=80, description="Pedimentos")
|
||||
caat: Optional[str] = Field(None, max_length=10, description="CAAT")
|
||||
transport_identification: Optional[str] = Field(
|
||||
None, max_length=20, description="Transport identification"
|
||||
)
|
||||
fast_id: Optional[str] = Field(None, max_length=20, description="FAST ID")
|
||||
operation_type: Optional[str] = Field(
|
||||
None, max_length=1, description="Operation type")
|
||||
selected: Optional[bool] = Field(None, description="Selected")
|
||||
user_selected: Optional[str] = Field(
|
||||
None, max_length=30, description="User selected")
|
||||
last_user: Optional[str] = Field(
|
||||
None, max_length=30, description="Last user")
|
||||
responsible: Optional[str] = Field(
|
||||
None, max_length=14, description="Responsible")
|
||||
carrier: Optional[str] = Field(None, max_length=8, description="Carrier")
|
||||
shipments: Optional[str] = Field(
|
||||
None, max_length=80, description="Shipments")
|
||||
pedimento_type: Optional[str] = Field(
|
||||
None, max_length=30, description="Pedimento type")
|
||||
original_chain: Optional[str] = Field(
|
||||
None, max_length=5000, description="Original chain")
|
||||
serial_number: Optional[str] = Field(
|
||||
None, max_length=21, description="Serial number")
|
||||
electronic_signature: Optional[str] = Field(
|
||||
None, max_length=2000, description="Electronic signature"
|
||||
)
|
||||
transaction_number: Optional[str] = Field(
|
||||
None, max_length=30, description="Transaction number")
|
||||
status: Optional[str] = Field(None, max_length=30, description="Status")
|
||||
linq_sat_qr: Optional[str] = Field(
|
||||
None, max_length=1000, description="LINQ SAT QR")
|
||||
sat_certificate: Optional[str] = Field(
|
||||
None, max_length=2001, description="SAT certificate")
|
||||
sat_digital_seal: Optional[str] = Field(
|
||||
None, description="SAT digital seal")
|
||||
xml_doda_sent_path: Optional[str] = Field(
|
||||
None, max_length=1000, description="XML DODA sent path")
|
||||
xml_doda_response_path: Optional[str] = Field(
|
||||
None, max_length=1000, description="XML DODA response path"
|
||||
)
|
||||
sat_original_chain: Optional[str] = Field(
|
||||
None, description="SAT original chain")
|
||||
customs_clearance: Optional[int] = Field(
|
||||
None, description="Customs clearance")
|
||||
unique_badge_number: Optional[str] = Field(
|
||||
None, max_length=250, description="Unique badge number"
|
||||
)
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class DodaUpdateDTO(BaseModel):
|
||||
"""DTO para actualizar un DODA"""
|
||||
|
||||
integration_number: Optional[str] = Field(
|
||||
None, max_length=30, description="Integration number")
|
||||
doda_date: Optional[int] = Field(None, description="DODA date")
|
||||
doda_time: Optional[int] = Field(None, description="DODA time")
|
||||
dispatch_customs: Optional[str] = Field(
|
||||
None, max_length=3, description="Dispatch customs")
|
||||
customs_sections: Optional[str] = Field(
|
||||
None, max_length=3, description="Customs sections")
|
||||
patent: Optional[str] = Field(None, max_length=4, description="Patent")
|
||||
pedimentos: Optional[str] = Field(
|
||||
None, max_length=80, description="Pedimentos")
|
||||
caat: Optional[str] = Field(None, max_length=10, description="CAAT")
|
||||
transport_identification: Optional[str] = Field(
|
||||
None, max_length=20, description="Transport identification"
|
||||
)
|
||||
fast_id: Optional[str] = Field(None, max_length=20, description="FAST ID")
|
||||
operation_type: Optional[str] = Field(
|
||||
None, max_length=1, description="Operation type")
|
||||
selected: Optional[bool] = Field(None, description="Selected")
|
||||
user_selected: Optional[str] = Field(
|
||||
None, max_length=30, description="User selected")
|
||||
last_user: Optional[str] = Field(
|
||||
None, max_length=30, description="Last user")
|
||||
responsible: Optional[str] = Field(
|
||||
None, max_length=14, description="Responsible")
|
||||
carrier: Optional[str] = Field(None, max_length=8, description="Carrier")
|
||||
shipments: Optional[str] = Field(
|
||||
None, max_length=80, description="Shipments")
|
||||
pedimento_type: Optional[str] = Field(
|
||||
None, max_length=30, description="Pedimento type")
|
||||
original_chain: Optional[str] = Field(
|
||||
None, max_length=5000, description="Original chain")
|
||||
serial_number: Optional[str] = Field(
|
||||
None, max_length=21, description="Serial number")
|
||||
electronic_signature: Optional[str] = Field(
|
||||
None, max_length=2000, description="Electronic signature"
|
||||
)
|
||||
transaction_number: Optional[str] = Field(
|
||||
None, max_length=30, description="Transaction number")
|
||||
status: Optional[str] = Field(None, max_length=30, description="Status")
|
||||
linq_sat_qr: Optional[str] = Field(
|
||||
None, max_length=1000, description="LINQ SAT QR")
|
||||
sat_certificate: Optional[str] = Field(
|
||||
None, max_length=2001, description="SAT certificate")
|
||||
sat_digital_seal: Optional[str] = Field(
|
||||
None, description="SAT digital seal")
|
||||
xml_doda_sent_path: Optional[str] = Field(
|
||||
None, max_length=1000, description="XML DODA sent path")
|
||||
xml_doda_response_path: Optional[str] = Field(
|
||||
None, max_length=1000, description="XML DODA response path"
|
||||
)
|
||||
sat_original_chain: Optional[str] = Field(
|
||||
None, description="SAT original chain")
|
||||
customs_clearance: Optional[int] = Field(
|
||||
None, description="Customs clearance")
|
||||
unique_badge_number: Optional[str] = Field(
|
||||
None, max_length=250, description="Unique badge number"
|
||||
)
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class DodaResponseDTO(BaseModel):
|
||||
"""DTO para responder con datos de un DODA"""
|
||||
|
||||
sys_id: int
|
||||
integration_number: Optional[str] = None
|
||||
doda_date: Optional[int] = None
|
||||
doda_time: Optional[int] = None
|
||||
dispatch_customs: Optional[str] = None
|
||||
customs_sections: Optional[str] = None
|
||||
patent: Optional[str] = None
|
||||
pedimentos: Optional[str] = None
|
||||
caat: Optional[str] = None
|
||||
transport_identification: Optional[str] = None
|
||||
fast_id: Optional[str] = None
|
||||
operation_type: Optional[str] = None
|
||||
selected: Optional[bool] = None
|
||||
user_selected: Optional[str] = None
|
||||
last_user: Optional[str] = None
|
||||
responsible: Optional[str] = None
|
||||
carrier: Optional[str] = None
|
||||
shipments: Optional[str] = None
|
||||
pedimento_type: Optional[str] = None
|
||||
original_chain: Optional[str] = None
|
||||
serial_number: Optional[str] = None
|
||||
electronic_signature: Optional[str] = None
|
||||
transaction_number: Optional[str] = None
|
||||
status: Optional[str] = None
|
||||
linq_sat_qr: Optional[str] = None
|
||||
sat_certificate: Optional[str] = None
|
||||
sat_digital_seal: Optional[str] = None
|
||||
xml_doda_sent_path: Optional[str] = None
|
||||
xml_doda_response_path: Optional[str] = None
|
||||
sat_original_chain: Optional[str] = None
|
||||
customs_clearance: Optional[int] = None
|
||||
unique_badge_number: Optional[str] = None
|
||||
containers: Optional[List[DodaContainerResponseDTO]] = None
|
||||
american_pedimentos: Optional[List[DodaAmericanPedimentoResponseDTO]] = None
|
||||
pedimentos_detail: Optional[List[DodaPedimentoResponseDTO]] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class DodaDetailResponseDTO(BaseModel):
|
||||
"""DTO detallado para responder con todos los datos de un DODA"""
|
||||
|
||||
sys_id: int
|
||||
integration_number: Optional[str] = None
|
||||
doda_date: Optional[int] = None
|
||||
doda_time: Optional[int] = None
|
||||
dispatch_customs: Optional[str] = None
|
||||
customs_sections: Optional[str] = None
|
||||
patent: Optional[str] = None
|
||||
pedimentos: Optional[str] = None
|
||||
caat: Optional[str] = None
|
||||
transport_identification: Optional[str] = None
|
||||
fast_id: Optional[str] = None
|
||||
operation_type: Optional[str] = None
|
||||
selected: Optional[bool] = None
|
||||
user_selected: Optional[str] = None
|
||||
last_user: Optional[str] = None
|
||||
responsible: Optional[str] = None
|
||||
carrier: Optional[str] = None
|
||||
shipments: Optional[str] = None
|
||||
pedimento_type: Optional[str] = None
|
||||
original_chain: Optional[str] = None
|
||||
serial_number: Optional[str] = None
|
||||
electronic_signature: Optional[str] = None
|
||||
transaction_number: Optional[str] = None
|
||||
status: Optional[str] = None
|
||||
linq_sat_qr: Optional[str] = None
|
||||
sat_certificate: Optional[str] = None
|
||||
sat_digital_seal: Optional[str] = None
|
||||
xml_doda_sent_path: Optional[str] = None
|
||||
xml_doda_response_path: Optional[str] = None
|
||||
sat_original_chain: Optional[str] = None
|
||||
customs_clearance: Optional[int] = None
|
||||
unique_badge_number: Optional[str] = None
|
||||
containers: List[DodaContainerResponseDTO] = []
|
||||
american_pedimentos: List[DodaAmericanPedimentoResponseDTO] = []
|
||||
pedimentos_detail: List[DodaPedimentoResponseDTO] = []
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
263
backend/api/v1/modules/a76/general_catalogs/doda/models.py
Normal file
263
backend/api/v1/modules/a76/general_catalogs/doda/models.py
Normal file
@@ -0,0 +1,263 @@
|
||||
"""
|
||||
Modelos ORM para gestión de DODA (Documentos de Operación de Aduana)
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from api.v1.common.base_models import TenantScopedMixin
|
||||
from core.database import Base
|
||||
from sqlalchemy import (
|
||||
ForeignKeyConstraint,
|
||||
Integer,
|
||||
PrimaryKeyConstraint,
|
||||
String,
|
||||
Text,
|
||||
LargeBinary,
|
||||
Numeric,
|
||||
Boolean,
|
||||
)
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
|
||||
class Doda(Base, TenantScopedMixin):
|
||||
"""
|
||||
Modelo para la tabla Doda - Documentos de Operación de Aduana
|
||||
"""
|
||||
|
||||
__tablename__ = "doda" # gDODA
|
||||
__table_args__ = (
|
||||
PrimaryKeyConstraint("id", name="doda_pkey"),
|
||||
{"schema": "a76"},
|
||||
)
|
||||
|
||||
# Primary key
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
|
||||
# Integration and timestamps
|
||||
integration_number: Mapped[Optional[str]] = mapped_column(String(30))
|
||||
doda_date: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
doda_time: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
|
||||
# Customs information
|
||||
dispatch_customs: Mapped[Optional[str]] = mapped_column(String(3))
|
||||
customs_sections: Mapped[Optional[str]] = mapped_column(String(3))
|
||||
patent: Mapped[Optional[str]] = mapped_column(String(4))
|
||||
pedimentos: Mapped[Optional[str]] = mapped_column(String(80))
|
||||
caat: Mapped[Optional[str]] = mapped_column(String(10))
|
||||
|
||||
# Transport and identifiers
|
||||
transport_identification: Mapped[Optional[str]] = mapped_column(String(20))
|
||||
fast_id: Mapped[Optional[str]] = mapped_column(String(20))
|
||||
operation_type: Mapped[Optional[str]] = mapped_column(String(1))
|
||||
|
||||
# Selection info
|
||||
selected: Mapped[Optional[bool]] = mapped_column(Boolean)
|
||||
user_selected: Mapped[Optional[str]] = mapped_column(String(30))
|
||||
last_user: Mapped[Optional[str]] = mapped_column(String(30))
|
||||
|
||||
# Responsible parties
|
||||
responsible: Mapped[Optional[str]] = mapped_column(String(14))
|
||||
carrier: Mapped[Optional[str]] = mapped_column(String(8))
|
||||
shipments: Mapped[Optional[str]] = mapped_column(String(80))
|
||||
pedimento_type: Mapped[Optional[str]] = mapped_column(String(30))
|
||||
|
||||
# Digital signatures and certificates
|
||||
original_chain: Mapped[Optional[str]] = mapped_column(String(5000))
|
||||
serial_number: Mapped[Optional[str]] = mapped_column(String(21))
|
||||
electronic_signature: Mapped[Optional[str]] = mapped_column(String(2000))
|
||||
|
||||
# Transaction info
|
||||
transaction_number: Mapped[Optional[str]] = mapped_column(String(30))
|
||||
status: Mapped[Optional[str]] = mapped_column(String(30))
|
||||
|
||||
# SAT information
|
||||
linq_sat_qr: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
sat_certificate: Mapped[Optional[str]] = mapped_column(String(2001))
|
||||
sat_digital_seal: Mapped[Optional[Text]] = mapped_column(Text)
|
||||
|
||||
# XML Paths
|
||||
xml_doda_sent_path: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
xml_doda_response_path: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
|
||||
# SAT original chain
|
||||
sat_original_chain: Mapped[Optional[Text]] = mapped_column(Text)
|
||||
|
||||
# Customs clearance
|
||||
customs_clearance: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
unique_badge_number: Mapped[Optional[str]] = mapped_column(String(250))
|
||||
|
||||
# Relationships
|
||||
containers: Mapped[list["DodaContainer"]] = relationship(
|
||||
"DodaContainer", back_populates="doda", cascade="all, delete-orphan"
|
||||
)
|
||||
american_pedimentos: Mapped[list["DodaAmericanPedimento"]] = relationship(
|
||||
"DodaAmericanPedimento", back_populates="doda", cascade="all, delete-orphan"
|
||||
)
|
||||
pedimentos_detail: Mapped[list["DodaPedimento"]] = relationship(
|
||||
"DodaPedimento", back_populates="doda", cascade="all, delete-orphan"
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Doda(id={self.id}, integration_number={self.integration_number}, status={self.status})>"
|
||||
|
||||
|
||||
class DodaContainer(Base, TenantScopedMixin):
|
||||
"""
|
||||
Modelo para la tabla DodaContainer - Contenedores en DODA
|
||||
"""
|
||||
|
||||
__tablename__ = "doda_containers" # gDoda_Contenedores
|
||||
__table_args__ = (
|
||||
PrimaryKeyConstraint("id", name="doda_containers_pkey"),
|
||||
ForeignKeyConstraint(
|
||||
["doda_id"], ["a76.doda.id"], name="fk_doda_containers_doda"
|
||||
),
|
||||
{"schema": "a76"},
|
||||
)
|
||||
|
||||
# Primary key
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
|
||||
# Foreign key and line number
|
||||
doda_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
container_line: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
|
||||
# Container information
|
||||
container_value: Mapped[Optional[str]] = mapped_column(String(20))
|
||||
seals: Mapped[Optional[str]] = mapped_column(String(254))
|
||||
|
||||
# Relationships
|
||||
doda: Mapped["Doda"] = relationship("Doda", back_populates="containers")
|
||||
seals_detail: Mapped[list["DodaContainerSeal"]] = relationship(
|
||||
"DodaContainerSeal", back_populates="container", cascade="all, delete-orphan"
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<DodaContainer(id={self.id}, doda_id={self.doda_id}, container_line={self.container_line})>"
|
||||
|
||||
|
||||
class DodaContainerSeal(Base, TenantScopedMixin):
|
||||
"""
|
||||
Modelo para la tabla DodaContainerSeal - Candados de Contenedores
|
||||
"""
|
||||
|
||||
__tablename__ = "doda_container_seals" # gDoda_Contenedores_Candados
|
||||
__table_args__ = (
|
||||
PrimaryKeyConstraint("id", name="doda_container_seals_pkey"),
|
||||
ForeignKeyConstraint(
|
||||
["container_id"], ["a76.doda_containers.id"], name="fk_doda_container_seals_container"
|
||||
),
|
||||
{"schema": "a76"},
|
||||
)
|
||||
|
||||
# Primary key
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
|
||||
# Foreign key and line info
|
||||
container_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
doda_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
seal_line: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
|
||||
# Seal information
|
||||
seal_value: Mapped[Optional[str]] = mapped_column(String(21))
|
||||
|
||||
# Relationships
|
||||
container: Mapped["DodaContainer"] = relationship(
|
||||
"DodaContainer", back_populates="seals_detail"
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<DodaContainerSeal(id={self.id}, doda_id={self.doda_id}, seal_line={self.seal_line})>"
|
||||
|
||||
|
||||
class DodaAmericanPedimento(Base, TenantScopedMixin):
|
||||
"""
|
||||
Modelo para la tabla DodaAmericanPedimento - Pedimentos Americanos en DODA
|
||||
"""
|
||||
|
||||
__tablename__ = "doda_american_pedimentos" # gDoda_PedimentoAmericano
|
||||
__table_args__ = (
|
||||
PrimaryKeyConstraint("id", name="doda_american_pedimentos_pkey"),
|
||||
ForeignKeyConstraint(
|
||||
["doda_id"], ["a76.doda.id"], name="fk_doda_american_pedimentos_doda"
|
||||
),
|
||||
{"schema": "a76"},
|
||||
)
|
||||
|
||||
# Primary key
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
|
||||
# Foreign key and line number
|
||||
doda_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
american_pedimento_line: Mapped[int] = mapped_column(
|
||||
Integer, nullable=False)
|
||||
|
||||
# American pedimento information
|
||||
american_pedimento_type: Mapped[Optional[str]] = mapped_column(String(2))
|
||||
american_pedimento_value: Mapped[Optional[str]] = mapped_column(String(20))
|
||||
|
||||
# Relationships
|
||||
doda: Mapped["Doda"] = relationship(
|
||||
"Doda", back_populates="american_pedimentos")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<DodaAmericanPedimento(id={self.id}, doda_id={self.doda_id}, american_pedimento_line={self.american_pedimento_line})>"
|
||||
|
||||
|
||||
class DodaPedimento(Base, TenantScopedMixin):
|
||||
"""
|
||||
Modelo para la tabla DodaPedimento - Pedimentos en DODA
|
||||
"""
|
||||
|
||||
__tablename__ = "doda_pedimentos" # gDoda_Pedimentos
|
||||
__table_args__ = (
|
||||
PrimaryKeyConstraint("id", name="doda_pedimentos_pkey"),
|
||||
ForeignKeyConstraint(
|
||||
["doda_id"], ["a76.doda.id"], name="fk_doda_pedimentos_doda"
|
||||
),
|
||||
{"schema": "a76"},
|
||||
)
|
||||
|
||||
# Primary key
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
|
||||
# Foreign key and line number
|
||||
doda_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
pedimento_line: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
|
||||
# Authorization and document info
|
||||
authorization_patent: Mapped[Optional[str]] = mapped_column(String(10))
|
||||
document: Mapped[Optional[str]] = mapped_column(String(50))
|
||||
shipment: Mapped[Optional[str]] = mapped_column(String(11))
|
||||
|
||||
# Commercial information
|
||||
cove: Mapped[Optional[str]] = mapped_column(String(50))
|
||||
umc: Mapped[Optional[str]] = mapped_column(String(20))
|
||||
|
||||
# Financial information
|
||||
effective_amount_usd: Mapped[Optional[Numeric]
|
||||
] = mapped_column(Numeric(15, 2))
|
||||
difference_amount_usd: Mapped[Optional[Numeric]
|
||||
] = mapped_column(Numeric(15, 2))
|
||||
|
||||
# Additional identifiers
|
||||
dta_niu: Mapped[Optional[str]] = mapped_column(String(20))
|
||||
article_7: Mapped[Optional[bool]] = mapped_column(Boolean)
|
||||
pedimento_id: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
invoice_line: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
part_ii_line: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
|
||||
# Type and validation
|
||||
pedimento_type: Mapped[Optional[str]] = mapped_column(String(20))
|
||||
zero_packaging_validation: Mapped[Optional[bool]] = mapped_column(Boolean)
|
||||
|
||||
# Relationships
|
||||
doda: Mapped["Doda"] = relationship(
|
||||
"Doda", back_populates="pedimentos_detail")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<DodaPedimento(id={self.id}, doda_id={self.doda_id}, pedimento_line={self.pedimento_line})>"
|
||||
288
backend/api/v1/modules/a76/general_catalogs/doda/service.py
Normal file
288
backend/api/v1/modules/a76/general_catalogs/doda/service.py
Normal file
@@ -0,0 +1,288 @@
|
||||
"""
|
||||
Capa de servicio para lógica de negocio de DODA
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from fastapi import HTTPException
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from .dto import (
|
||||
DodaCreateDTO,
|
||||
DodaResponseDTO,
|
||||
DodaUpdateDTO,
|
||||
DodaContainerCreateDTO,
|
||||
DodaContainerUpdateDTO,
|
||||
DodaAmericanPedimentoCreateDTO,
|
||||
DodaAmericanPedimentoUpdateDTO,
|
||||
DodaPedimentoCreateDTO,
|
||||
DodaPedimentoUpdateDTO,
|
||||
)
|
||||
from .models import (
|
||||
Doda,
|
||||
DodaContainer,
|
||||
DodaContainerSeal,
|
||||
DodaAmericanPedimento,
|
||||
DodaPedimento,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DodaService:
|
||||
"""Servicio para gestión de DODA"""
|
||||
|
||||
def __init__(self, db: Session):
|
||||
self.db = db
|
||||
|
||||
# ============ DODA MAIN CRUD ============
|
||||
@staticmethod
|
||||
def get_all(
|
||||
db: Session,
|
||||
skip: int = 0,
|
||||
limit: int = 50,
|
||||
filters: Optional[Dict[str, Any]] = None,
|
||||
) -> Tuple[List[Doda], int]:
|
||||
"""Get all DODAs with pagination"""
|
||||
query = db.query(Doda)
|
||||
|
||||
if filters:
|
||||
if filters.get("integration_number"):
|
||||
query = query.filter(
|
||||
Doda.integration_number.ilike(
|
||||
f"%{filters['integration_number']}%")
|
||||
)
|
||||
if filters.get("status"):
|
||||
query = query.filter(
|
||||
Doda.status.ilike(f"%{filters['status']}%"))
|
||||
if filters.get("patent"):
|
||||
query = query.filter(
|
||||
Doda.patent.ilike(f"%{filters['patent']}%"))
|
||||
|
||||
total = query.count()
|
||||
dodas = query.offset(skip).limit(limit).all()
|
||||
return dodas, total
|
||||
|
||||
@staticmethod
|
||||
def get_by_id(db: Session, sys_id: int) -> Optional[Doda]:
|
||||
"""Get DODA by ID"""
|
||||
return db.query(Doda).filter(Doda.sys_id == sys_id).first()
|
||||
|
||||
@staticmethod
|
||||
def create(db: Session, doda_data: DodaCreateDTO) -> Doda:
|
||||
"""Create a new DODA"""
|
||||
try:
|
||||
db_doda = Doda(**doda_data.model_dump(exclude_unset=True))
|
||||
db.add(db_doda)
|
||||
db.commit()
|
||||
db.refresh(db_doda)
|
||||
return db_doda
|
||||
except IntegrityError as e:
|
||||
db.rollback()
|
||||
logger.error(f"IntegrityError creating DODA: {str(e)}")
|
||||
raise HTTPException(status_code=400, detail="Error creating DODA")
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error creating DODA: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Error creating DODA")
|
||||
|
||||
@staticmethod
|
||||
def update(db: Session, sys_id: int, doda_data: DodaUpdateDTO) -> Optional[Doda]:
|
||||
"""Update a DODA"""
|
||||
try:
|
||||
db_doda = db.query(Doda).filter(Doda.sys_id == sys_id).first()
|
||||
if not db_doda:
|
||||
return None
|
||||
|
||||
for key, value in doda_data.model_dump(exclude_unset=True).items():
|
||||
setattr(db_doda, key, value)
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_doda)
|
||||
return db_doda
|
||||
except IntegrityError as e:
|
||||
db.rollback()
|
||||
logger.error(f"IntegrityError updating DODA: {str(e)}")
|
||||
raise HTTPException(status_code=400, detail="Error updating DODA")
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error updating DODA: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Error updating DODA")
|
||||
|
||||
@staticmethod
|
||||
def delete(db: Session, sys_id: int) -> bool:
|
||||
"""Delete a DODA"""
|
||||
try:
|
||||
db_doda = db.query(Doda).filter(Doda.sys_id == sys_id).first()
|
||||
if not db_doda:
|
||||
return False
|
||||
|
||||
db.delete(db_doda)
|
||||
db.commit()
|
||||
return True
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error deleting DODA: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Error deleting DODA")
|
||||
|
||||
# ============ CONTAINERS ============
|
||||
@staticmethod
|
||||
def add_container(
|
||||
db: Session, sys_id: int, container_data: DodaContainerCreateDTO
|
||||
) -> Optional[DodaContainer]:
|
||||
"""Add a container to a DODA"""
|
||||
try:
|
||||
doda = db.query(Doda).filter(Doda.sys_id == sys_id).first()
|
||||
if not doda:
|
||||
return None
|
||||
|
||||
# Get max line number
|
||||
max_line = (
|
||||
db.query(DodaContainer)
|
||||
.filter(DodaContainer.doda_sys_id == sys_id)
|
||||
.count()
|
||||
)
|
||||
|
||||
db_container = DodaContainer(
|
||||
doda_sys_id=sys_id,
|
||||
container_line=max_line + 1,
|
||||
**{
|
||||
k: v
|
||||
for k, v in container_data.model_dump(exclude_unset=True).items()
|
||||
if k != "seals_detail"
|
||||
},
|
||||
)
|
||||
db.add(db_container)
|
||||
db.commit()
|
||||
db.refresh(db_container)
|
||||
return db_container
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error adding container: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=500, detail="Error adding container")
|
||||
|
||||
@staticmethod
|
||||
def update_container(
|
||||
db: Session,
|
||||
sys_id: int,
|
||||
container_line: int,
|
||||
container_data: DodaContainerUpdateDTO,
|
||||
) -> Optional[DodaContainer]:
|
||||
"""Update a container"""
|
||||
try:
|
||||
db_container = (
|
||||
db.query(DodaContainer)
|
||||
.filter(
|
||||
DodaContainer.doda_sys_id == sys_id,
|
||||
DodaContainer.container_line == container_line,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not db_container:
|
||||
return None
|
||||
|
||||
for key, value in container_data.model_dump(exclude_unset=True).items():
|
||||
setattr(db_container, key, value)
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_container)
|
||||
return db_container
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error updating container: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=500, detail="Error updating container")
|
||||
|
||||
@staticmethod
|
||||
def get_containers(db: Session, sys_id: int) -> List[DodaContainer]:
|
||||
"""Get all containers for a DODA"""
|
||||
return (
|
||||
db.query(DodaContainer)
|
||||
.filter(DodaContainer.doda_sys_id == sys_id)
|
||||
.all()
|
||||
)
|
||||
|
||||
# ============ AMERICAN PEDIMENTOS ============
|
||||
@staticmethod
|
||||
def add_american_pedimento(
|
||||
db: Session, sys_id: int, pedimento_data: DodaAmericanPedimentoCreateDTO
|
||||
) -> Optional[DodaAmericanPedimento]:
|
||||
"""Add an American pedimento to a DODA"""
|
||||
try:
|
||||
doda = db.query(Doda).filter(Doda.sys_id == sys_id).first()
|
||||
if not doda:
|
||||
return None
|
||||
|
||||
max_line = (
|
||||
db.query(DodaAmericanPedimento)
|
||||
.filter(DodaAmericanPedimento.doda_sys_id == sys_id)
|
||||
.count()
|
||||
)
|
||||
|
||||
db_pedimento = DodaAmericanPedimento(
|
||||
doda_sys_id=sys_id,
|
||||
american_pedimento_line=max_line + 1,
|
||||
**pedimento_data.model_dump(exclude_unset=True),
|
||||
)
|
||||
db.add(db_pedimento)
|
||||
db.commit()
|
||||
db.refresh(db_pedimento)
|
||||
return db_pedimento
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error adding American pedimento: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=500, detail="Error adding American pedimento"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_american_pedimentos(db: Session, sys_id: int) -> List[DodaAmericanPedimento]:
|
||||
"""Get all American pedimentos for a DODA"""
|
||||
return (
|
||||
db.query(DodaAmericanPedimento)
|
||||
.filter(DodaAmericanPedimento.doda_sys_id == sys_id)
|
||||
.all()
|
||||
)
|
||||
|
||||
# ============ PEDIMENTOS ============
|
||||
@staticmethod
|
||||
def add_pedimento(
|
||||
db: Session, sys_id: int, pedimento_data: DodaPedimentoCreateDTO
|
||||
) -> Optional[DodaPedimento]:
|
||||
"""Add a pedimento to a DODA"""
|
||||
try:
|
||||
doda = db.query(Doda).filter(Doda.sys_id == sys_id).first()
|
||||
if not doda:
|
||||
return None
|
||||
|
||||
max_line = (
|
||||
db.query(DodaPedimento)
|
||||
.filter(DodaPedimento.doda_sys_id == sys_id)
|
||||
.count()
|
||||
)
|
||||
|
||||
db_pedimento = DodaPedimento(
|
||||
doda_sys_id=sys_id,
|
||||
pedimento_line=max_line + 1,
|
||||
**pedimento_data.model_dump(exclude_unset=True),
|
||||
)
|
||||
db.add(db_pedimento)
|
||||
db.commit()
|
||||
db.refresh(db_pedimento)
|
||||
return db_pedimento
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error adding pedimento: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=500, detail="Error adding pedimento")
|
||||
|
||||
@staticmethod
|
||||
def get_pedimentos(db: Session, sys_id: int) -> List[DodaPedimento]:
|
||||
"""Get all pedimentos for a DODA"""
|
||||
return (
|
||||
db.query(DodaPedimento).filter(
|
||||
DodaPedimento.doda_sys_id == sys_id).all()
|
||||
)
|
||||
Reference in New Issue
Block a user