""" DTOs (Data Transfer Objects) para módulo de clases SCAII y SCAF Reemplaza schemas.py siguiendo enfoque DDD y estilo NestJS """ from datetime import datetime from decimal import Decimal from typing import Optional from pydantic import BaseModel, ConfigDict, Field class ClassCreateDTO(BaseModel): """DTO para crear una clase""" class_code: str = Field(..., max_length=8, description="Class code") description_es: str = Field( ..., max_length=500, description="Description in Spanish (required)" ) description_en: Optional[str] = Field( None, max_length=500, description="Description in English" ) material_key: str = Field( ..., max_length=10, description="Material key - Fixed Asset Type (required)", ) unit_of_measure: str = Field( ..., max_length=5, description="Unit of measure - U.M. comercial (required)" ) stock_unit_of_measure: Optional[str] = Field( None, max_length=5, description="UM Existencia - solo SCAII/inventory (UMEXISTENCIA)", ) fraction: str = Field( ..., max_length=20, description="Mexican tariff fraction (required)" ) us_fraction: Optional[str] = Field( None, max_length=16, description="US tariff fraction" ) sub_key: Optional[str] = Field( None, max_length=5, description="Sub classification key" ) physical_review: Optional[int] = Field( None, description="Physical review indicator" ) iva_exempt_fraction: Optional[str] = Field( None, max_length=4, description="IVA exempt fraction" ) is_active: Optional[bool] = Field( True, description="Indicates if the class is active (default: true)" ) system: str = Field( default="fixed_asset", max_length=12, description="Sistema: 'fixed_asset' (SCAF) o 'inventory' (SCAII)" ) class Config: from_attributes = True class ClassCreateDTOFA(ClassCreateDTO): """DTO para crear una clase de activo fijo (clase base + extensión FA)""" # Campos específicos de activos fijos (a24.fa_classes) import_tariff_code: Optional[str] = Field( None, max_length=10, description="Código de fracción de importación" ) import_tariff_type: Optional[str] = Field( None, max_length=6, description="Tipo de fracción de importación" ) export_tariff_code: Optional[str] = Field( None, max_length=10, description="Código de fracción de exportación" ) export_tariff_type: Optional[str] = Field( None, max_length=6, description="Tipo de fracción de exportación" ) depreciation_rate: Optional[Decimal] = Field( None, ge=0, le=100, description="Tasa de depreciación anual (%)" ) fda_code: Optional[str] = Field( None, max_length=20, description="Código FDA" ) eccn_code: Optional[str] = Field( None, max_length=20, description="Código ECCN" ) class_enabled: Optional[bool] = Field( True, description="Indica si la clase está habilitada" ) is_active: Optional[bool] = Field( True, description="Indicates if the class is active (default: true)" ) class Config: from_attributes = True class ClassUpdateDTO(BaseModel): """DTO para actualizar una clase""" class_code: Optional[str] = Field( None, max_length=8, description="Class code" ) description_es: Optional[str] = Field( None, max_length=500, description="Description in Spanish" ) description_en: Optional[str] = Field( None, max_length=500, description="Description in English" ) material_key: Optional[str] = Field( None, max_length=10, description="Material key (homologated TIPOMAT/TIPOMATEQUIPO)", ) unit_of_measure: Optional[str] = Field( None, max_length=5, description="Unit of measure (homologated UNIMEDIDA)" ) stock_unit_of_measure: Optional[str] = Field( None, max_length=5, description="UM Existencia - solo SCAII/inventory (UMEXISTENCIA)", ) fraction: Optional[str] = Field( None, max_length=20, description="Mexican tariff fraction" ) us_fraction: Optional[str] = Field( None, max_length=16, description="US tariff fraction" ) sub_key: Optional[str] = Field( None, max_length=5, description="Sub classification key" ) physical_review: Optional[int] = Field( None, description="Physical review indicator" ) iva_exempt_fraction: Optional[str] = Field( None, max_length=4, description="IVA exempt fraction" ) is_active: Optional[bool] = Field( True, description="Indicates if the class is active (default: true)" ) system: Optional[str] = Field( None, max_length=12, description="Sistema: 'fixed_asset' (SCAF) o 'inventory' (SCAII)" ) model_config = ConfigDict(from_attributes=True, extra='forbid') # Explicitly forbid extra fields class ClassResponseDTO(BaseModel): """DTO para respuesta de clase""" id: int tenant_id: int company_id: int class_code: str description_es: Optional[str] = None description_en: Optional[str] = None material_key: Optional[str] = None unit_of_measure: Optional[str] = None stock_unit_of_measure: Optional[str] = None fraction: Optional[str] = None us_fraction: Optional[str] = None sub_key: Optional[str] = None physical_review: Optional[int] = None iva_exempt_fraction: Optional[str] = None is_active: Optional[bool] = None system: str = "fixed_asset" created_at: datetime updated_at: datetime model_config = ConfigDict(from_attributes=True) class ClassResponseDTOFA(ClassResponseDTO): """DTO para respuesta de clase de activo fijo (incluye campos FA)""" # Campos de a24.fa_classes fa_id: Optional[int] = None import_tariff_code: Optional[str] = None import_tariff_type: Optional[str] = None export_tariff_code: Optional[str] = None export_tariff_type: Optional[str] = None depreciation_rate: Optional[Decimal] = None fda_code: Optional[str] = None eccn_code: Optional[str] = None class_enabled: Optional[bool] = None model_config = ConfigDict(from_attributes=True) class ClassBasicDTO(BaseModel): """DTO para información básica de clase""" class_code: str description_es: Optional[str] = None description_en: Optional[str] = None material_key: Optional[str] = None fraction: Optional[str] = None class Config: from_attributes = True class ClassListDTO(BaseModel): """DTO para lista de clases""" classes: list[ClassBasicDTO] total: int page: int size: int class Config: from_attributes = True class ClassSearchDTO(BaseModel): """DTO para búsqueda de clases""" class_code: Optional[str] = Field(None, description="Search by class code") description: Optional[str] = Field(None, description="Search in descriptions") material_key: Optional[str] = Field(None, description="Filter by material key") fraction: Optional[str] = Field(None, description="Filter by tariff fraction") physical_review: Optional[int] = Field( None, description="Filter by physical review indicator" ) class Config: from_attributes = True class ClassWithFADataResponse(BaseModel): """DTO para respuesta de clase con datos FA embebidos (para fixed-asset-classes)""" # Base class fields id: int tenant_id: int company_id: int class_code: str description_es: Optional[str] = None description_en: Optional[str] = None material_key: Optional[str] = None unit_of_measure: Optional[str] = None stock_unit_of_measure: Optional[str] = None fraction: Optional[str] = None us_fraction: Optional[str] = None sub_key: Optional[str] = None physical_review: Optional[int] = None iva_exempt_fraction: Optional[str] = None system: str = "fixed_asset" created_at: datetime updated_at: datetime # FA-specific fields (embedded from a24.fa_classes) fa_class_id: Optional[int] = None import_tariff_code: Optional[str] = None import_tariff_type: Optional[str] = None export_tariff_code: Optional[str] = None export_tariff_type: Optional[str] = None depreciation_rate: Optional[Decimal] = None fda_code: Optional[str] = None eccn_code: Optional[str] = None class_enabled: Optional[bool] = None model_config = ConfigDict(from_attributes=True) class ClassWithFADataPaginatedResponse(BaseModel): """Lista paginada de clases con datos FA (fixed-asset-classes)""" items: list[ClassWithFADataResponse] total: int page: int page_size: int model_config = ConfigDict(from_attributes=True)