Merge pull request 'feauture-pedimentos' (#10) from feauture-pedimentos into development

Reviewed-on: ADUANASOFT/anexo76#10
This commit is contained in:
2025-11-07 16:11:10 +00:00
51 changed files with 2275 additions and 1182 deletions

View File

@@ -1,11 +1,10 @@
"""
Modelos ORM para gestión de clientes y proveedores
"""
from sqlalchemy import Column, Integer, String, DateTime, Boolean, Text, SmallInteger, Numeric, ForeignKey
from sqlalchemy import Column, Integer, String, SmallInteger, Numeric, ForeignKey
from sqlalchemy.sql import func
from sqlalchemy.orm import relationship
from core.database import Base
import enum
class ClientProvider(Base):
@@ -17,7 +16,7 @@ class ClientProvider(Base):
# Primary key
client_id = Column(String(8), primary_key=True, nullable=False)
# Basic information
type_nat_foreign = Column(String(1), nullable=True) # TIPO NACIONAL/EXTRANJERO
name = Column(String(256), nullable=True)
@@ -37,15 +36,15 @@ class ClientProvider(Base):
tenant_id = Column(String, ForeignKey("a76.tenants.id"), nullable=False)
# Relationships
address = relationship("GClientProviderAddress", back_populates="client_provider", uselist=False, cascade="all, delete-orphan")
programs = relationship("GClientProviderPrograms", back_populates="client_provider", uselist=False, cascade="all, delete-orphan")
address = relationship("ClientProviderAddress", back_populates="client_provider", uselist=False, cascade="all, delete-orphan")
programs = relationship("ClientProviderPrograms", back_populates="client_provider", uselist=False, cascade="all, delete-orphan")
class GClientProviderAddress(Base):
class ClientProviderAddress(Base):
"""
Modelo para la tabla GClientesPro_Direccion - Dirección de clientes y proveedores
"""
__tablename__ = "gclient_provider_address"
__tablename__ = "client_provider_address"
__table_args__ = {"schema": "a76"}
# Primary key (foreign key)
@@ -71,11 +70,11 @@ class GClientProviderAddress(Base):
client_provider = relationship("ClientProvider", back_populates="address")
class GClientProviderPrograms(Base):
class ClientProviderPrograms(Base):
"""
Modelo para la tabla GClientesPro_Programas - Programas de clientes y proveedores
"""
__tablename__ = "gclient_provider_programs"
__tablename__ = "client_provider_programs"
__table_args__ = {"schema": "a76"}
# Primary key (foreign key)

View File

@@ -8,7 +8,7 @@ from fastapi import HTTPException
from typing import List, Optional
import logging
from .models import ClientProvider, GClientProviderAddress, GClientProviderPrograms
from .models import ClientProvider, ClientProviderAddress, ClientProviderPrograms
from .dto import (
ClientProviderCreateDTO,
ClientProviderUpdateDTO,
@@ -72,7 +72,7 @@ class ClientProviderService:
# Crear dirección si se proporciona
if client_data.address:
db_address = GClientProviderAddress(
db_address = ClientProviderAddress(
client_id=client_data.client_id,
**client_data.address.model_dump(exclude_unset=True)
)
@@ -80,7 +80,7 @@ class ClientProviderService:
# Crear programas si se proporciona
if client_data.programs:
db_programs = GClientProviderPrograms(
db_programs = ClientProviderPrograms(
client_id=client_data.client_id,
**client_data.programs.model_dump(exclude_unset=True)
)
@@ -207,7 +207,7 @@ class ClientProviderService:
# Actualizar dirección
if client_data.address:
address = self.db.query(GClientProviderAddress).filter(GClientProviderAddress.client_id == client_id).first()
address = self.db.query(ClientProviderAddress).filter(ClientProviderAddress.client_id == client_id).first()
if address:
# Actualizar dirección existente
address_data = client_data.address.model_dump(exclude_unset=True)
@@ -215,7 +215,7 @@ class ClientProviderService:
setattr(address, field, value)
else:
# Crear nueva dirección
address = GClientProviderAddress(
address = ClientProviderAddress(
client_id=client_id,
**client_data.address.model_dump(exclude_unset=True)
)
@@ -223,7 +223,7 @@ class ClientProviderService:
# Actualizar programas
if client_data.programs:
programs = self.db.query(GClientProviderPrograms).filter(GClientProviderPrograms.client_id == client_id).first()
programs = self.db.query(ClientProviderPrograms).filter(ClientProviderPrograms.client_id == client_id).first()
if programs:
# Actualizar programas existentes
programs_data = client_data.programs.model_dump(exclude_unset=True)
@@ -231,7 +231,7 @@ class ClientProviderService:
setattr(programs, field, value)
else:
# Crear nuevos programas
programs = GClientProviderPrograms(
programs = ClientProviderPrograms(
client_id=client_id,
**client_data.programs.model_dump(exclude_unset=True)
)

View File

@@ -7,11 +7,11 @@ from core.database import Base
import enum
class GCompany(Base):
class Company(Base):
"""
Modelo para la tabla GCompany - Información de la empresa
Modelo para la tabla Company - Información de la empresa
"""
__tablename__ = "gcompany"
__tablename__ = "company"
__table_args__ = {"schema": "a76"}
# Primary key

View File

@@ -7,7 +7,7 @@ from fastapi import HTTPException
from typing import List, Optional
import logging
from .models import GCompany
from .models import Company
from .dto import CompanyCreateDTO, CompanyUpdateDTO, CompanyResponseDTO
logger = logging.getLogger(__name__)
@@ -34,12 +34,12 @@ class CompanyService:
"""
try:
# Verificar que no exista ya una empresa (solo puede haber una por el consecutivo único)
existing = self.db.query(GCompany).filter(GCompany.consecutive == True).first()
existing = self.db.query(Company).filter(Company.consecutive == True).first()
if existing:
raise HTTPException(status_code=400, detail="A company is already registered in the system")
# Crear empresa
db_company = GCompany(
db_company = Company(
id=company_data.id,
consecutive=company_data.consecutive,
name=company_data.name,
@@ -98,7 +98,7 @@ class CompanyService:
Returns:
CompanyResponseDTO o None si no existe
"""
company = self.db.query(GCompany).filter(GCompany.consecutive == True).first()
company = self.db.query(Company).filter(Company.consecutive == True).first()
if not company:
return None
return CompanyResponseDTO.model_validate(company)
@@ -113,7 +113,7 @@ class CompanyService:
Returns:
CompanyResponseDTO o None si no existe
"""
company = self.db.query(GCompany).filter(GCompany.id == company_id).first()
company = self.db.query(Company).filter(Company.id == company_id).first()
if not company:
return None
return CompanyResponseDTO.model_validate(company)
@@ -129,7 +129,7 @@ class CompanyService:
Returns:
CompanyResponseDTO actualizada o None si no existe
"""
company = self.db.query(GCompany).filter(GCompany.id == company_id).first()
company = self.db.query(Company).filter(Company.id == company_id).first()
if not company:
return None
@@ -158,7 +158,7 @@ class CompanyService:
Returns:
True si se eliminó, False si no existe
"""
company = self.db.query(GCompany).filter(GCompany.id == company_id).first()
company = self.db.query(Company).filter(Company.id == company_id).first()
if not company:
return False
@@ -179,6 +179,6 @@ class CompanyService:
Returns:
True si existe una empresa, False en caso contrario
"""
return self.db.query(GCompany).filter(GCompany.consecutive == True).first() is not None
return self.db.query(Company).filter(Company.consecutive == True).first() is not None

View File

@@ -5,8 +5,6 @@ from datetime import datetime, time
class PedimentoDatesBase(BaseModel):
"""Base schema for Pedimento Dates"""
pedimento_id: int = Field(..., description="Pedimento ID")
tenant_id: int = Field(..., description="Tenant ID")
entry_date: Optional[datetime] = Field(None, description="Entry date")
pedimento_date: Optional[datetime] = Field(None, description="Pedimento date")
payment_date: Optional[datetime] = Field(None, description="Payment date")
@@ -45,6 +43,8 @@ class PedimentoDatesUpdate(BaseModel):
class PedimentoDatesResponse(PedimentoDatesBase):
"""Schema for Pedimento Dates response"""
id: int
pedimento_id: int = Field(..., description="Pedimento ID")
tenant_id: int = Field(..., description="Tenant ID")
created_at: datetime
model_config = ConfigDict(from_attributes=True)

View File

@@ -11,7 +11,7 @@ class PedimentoRectificationOriginBase(BaseModel):
original_customs_office: Optional[str] = Field(None, max_length=3, description="Original customs office")
original_license: Optional[str] = Field(None, max_length=4, description="Original license")
original_pedimento_number: Optional[str] = Field(None, max_length=7, description="Original pedimento number")
original_pedimento_key: Optional[str] = Field(None, max_length=2, description="Original pedimento key")
original_pedimento_code: Optional[str] = Field(None, max_length=2, description="Original pedimento key")
original_payment_date: Optional[datetime] = Field(None, description="Original payment date")
total_cash: Optional[int] = Field(None, description="Total cash")
total_others: Optional[int] = Field(None, description="Total others")
@@ -33,7 +33,7 @@ class PedimentoRectificationOriginUpdate(BaseModel):
original_customs_office: Optional[str] = Field(None, max_length=3)
original_license: Optional[str] = Field(None, max_length=4)
original_pedimento_number: Optional[str] = Field(None, max_length=7)
original_pedimento_key: Optional[str] = Field(None, max_length=2)
original_pedimento_code: Optional[str] = Field(None, max_length=2)
original_payment_date: Optional[datetime] = None
total_cash: Optional[int] = None
total_others: Optional[int] = None

View File

@@ -13,7 +13,7 @@ class PedimentosBase(BaseModel):
client_id: Optional[int] = Field(None, description="Client ID")
operation_type: Optional[int] = Field(None, description="Operation type")
pedimento_type: Optional[int] = Field(None, description="Pedimento type")
pedimento_key: Optional[str] = Field(None, max_length=2, description="Pedimento key")
pedimento_code: Optional[str] = Field(None, max_length=2, description="Pedimento key")
regime: Optional[str] = Field(None, max_length=3, description="Regime")
status: Optional[str] = Field(None, max_length=30, description="Status")
usd_value: Optional[Decimal] = Field(None, description="USD value")
@@ -36,7 +36,7 @@ class PedimentosUpdate(BaseModel):
client_id: Optional[int] = None
operation_type: Optional[int] = None
pedimento_type: Optional[int] = None
pedimento_key: Optional[str] = Field(None, max_length=2)
pedimento_code: Optional[str] = Field(None, max_length=2)
regime: Optional[str] = Field(None, max_length=3)
status: Optional[str] = Field(None, max_length=30)
usd_value: Optional[Decimal] = None

View File

@@ -20,7 +20,7 @@ class PedimentoRectificationOrigin(Base):
original_customs_office = mapped_column(String(3))
original_license = mapped_column(String(4))
original_pedimento_number = mapped_column(String(7))
original_pedimento_key = mapped_column(String(2))
original_pedimento_code = mapped_column(String(2))
original_payment_date = mapped_column(DateTime)
total_cash = mapped_column(Integer)
total_others = mapped_column(Integer)

View File

@@ -7,6 +7,8 @@ from core.database import Base
class Pedimentos(Base):
__tablename__ = 'pedimentos'
__table_args__ = (
ForeignKeyConstraint(['regime'], ['public.pedimento_regimens.code']),
ForeignKeyConstraint(['pedimento_code'], ['public.pedimento_codes.code']),
ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id']),
PrimaryKeyConstraint('id', name='pedimentos_pkey'),
Index('idx_pedimentos_client_id', 'client_id'),
@@ -24,7 +26,7 @@ class Pedimentos(Base):
client_id = mapped_column(Integer)
operation_type = mapped_column(Integer)
pedimento_type = mapped_column(Integer)
pedimento_key = mapped_column(String(2))
pedimento_code = mapped_column(String(2))
regime = mapped_column(String(3))
status = mapped_column(String(30))
usd_value = mapped_column(Numeric(17, 6))

View File

@@ -1,6 +1,7 @@
"""
Routes for PedimentoDates CRUD operations
"""
import logging
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from core.database import get_core_db
@@ -15,7 +16,7 @@ from ..dtos.pedimento_dates import (
router = APIRouter(prefix="/{pedimento_id}/dates")
logger = logging.getLogger(__name__)
@router.get("/", response_model=PedimentoDatesResponse)
async def get_dates(
@@ -36,8 +37,7 @@ async def get_dates(
@router.post("/", response_model=PedimentoDatesResponse, status_code=201)
async def create_dates(
pedimento_id: int,
async def create_dates(
data: PedimentoDatesCreate,
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
@@ -46,11 +46,7 @@ async def create_dates(
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
# Ensure pedimento_id matches
if data.pedimento_id != pedimento_id:
raise HTTPException(status_code=400, detail="Pedimento ID mismatch")
dates = PedimentoDatesService.create(db, data, tenant_id)
return dates

View File

@@ -1,12 +1,14 @@
"""
Service layer for PedimentoDates CRUD operations
"""
import logging
from typing import Optional
from sqlalchemy.orm import Session
from ..models.pedimento_dates import PedimentoDates
from ..dtos.pedimento_dates import PedimentoDatesCreate, PedimentoDatesUpdate
logger = logging.getLogger(__name__)
class PedimentoDatesService:
"""Service class for PedimentoDates business logic"""