Se agrego un modal provicional para el fomrulario y creacion de los registros en la base de datos. Aun faltan 3 catalogos para la integracion con la base de datos
This commit is contained in:
@@ -14,5 +14,6 @@ class ClassificationConcept(Base, TenantScopedMixin, TimestampMixin):
|
||||
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
|
||||
classification: Mapped[str] = mapped_column(
|
||||
String(30), nullable=False) # CLASIFICACION
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Rutas para gestión de empresa
|
||||
"""
|
||||
|
||||
from typing import List
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -17,7 +17,29 @@ from .service import CompanyService
|
||||
# Main router that includes base CRUD
|
||||
router = APIRouter(prefix="/company")
|
||||
|
||||
# Custom endpoints
|
||||
@router.post(
|
||||
"", # Se suma al prefix, queda POST /api/v1/a76/company
|
||||
response_model=CompanyResponseDTO,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
summary="Create a new company",
|
||||
)
|
||||
async def create_company(
|
||||
data: CompanyCreateDTO,
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
if not tenant_id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Tenant ID not found in user data",
|
||||
)
|
||||
|
||||
service = CompanyService(db)
|
||||
return service.create_company_manually(data, tenant_id=tenant_id)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/my-companies",
|
||||
response_model=List[CompanyResponseDTO],
|
||||
@@ -166,11 +188,10 @@ async def get_program_info(
|
||||
"prosec": company.prosec,
|
||||
"prosec_authorization": company.prosec_authorization,
|
||||
}
|
||||
|
||||
# Base CRUD routes using TenantCRUDRoutes
|
||||
|
||||
base_router = TenantCRUDRoutes(
|
||||
service=CompanyService,
|
||||
create_schema=CompanyCreateDTO,
|
||||
create_schema=None,
|
||||
update_schema=CompanyUpdateDTO,
|
||||
response_schema=CompanyResponseDTO,
|
||||
prefix="",
|
||||
@@ -179,4 +200,5 @@ base_router = TenantCRUDRoutes(
|
||||
enable_list=True,
|
||||
enable_filters=True,
|
||||
).router
|
||||
|
||||
router.include_router(base_router)
|
||||
@@ -64,6 +64,7 @@ class CompanyService:
|
||||
.first()
|
||||
)
|
||||
|
||||
# ESTE ES EL MÉTODO VIEJO QUE CAUSABA PROBLEMAS (Lo dejamos por si acaso)
|
||||
@staticmethod
|
||||
def create(
|
||||
db: Session,
|
||||
@@ -71,7 +72,7 @@ class CompanyService:
|
||||
tenant_id: int,
|
||||
company_id: int,
|
||||
) -> Company:
|
||||
"""Create a new company"""
|
||||
"""Create a new company (MÉTODO GENÉRICO - NO USAR PARA CREACIÓN MANUAL)"""
|
||||
try:
|
||||
db_company = Company(
|
||||
**company_data.model_dump(exclude_unset=True),
|
||||
@@ -159,3 +160,31 @@ class CompanyService:
|
||||
.first()
|
||||
is not None
|
||||
)
|
||||
|
||||
def create_company_manually(self, data: CompanyCreateDTO, tenant_id: int) -> Company:
|
||||
|
||||
try:
|
||||
# 1. Preparar datos
|
||||
obj_data = data.model_dump(exclude_unset=True)
|
||||
|
||||
# 2. Crear objeto SQLAlchemy
|
||||
db_obj = Company(**obj_data, tenant_id=tenant_id)
|
||||
|
||||
# 3. Guardar
|
||||
self.db.add(db_obj)
|
||||
self.db.commit()
|
||||
self.db.refresh(db_obj)
|
||||
|
||||
return db_obj
|
||||
|
||||
except IntegrityError as e:
|
||||
self.db.rollback()
|
||||
logger.error(f"IntegrityError creating company manually: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Error de integridad: Es posible que esta empresa ya exista.",
|
||||
)
|
||||
except Exception as e:
|
||||
self.db.rollback()
|
||||
logger.error(f"Error creating company manually: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Error creando empresa: {str(e)}")
|
||||
@@ -4,6 +4,7 @@ from pydantic import BaseModel, Field, ConfigDict
|
||||
|
||||
class ConceptBase(BaseModel):
|
||||
code: str = Field(..., max_length=15, description="Concept Code (CLAVE)")
|
||||
company_id: int = Field(..., description="Company ID")
|
||||
description: Optional[str] = Field(
|
||||
None, max_length=120, description="Description")
|
||||
description_en: Optional[str] = Field(
|
||||
@@ -18,7 +19,6 @@ class ConceptBase(BaseModel):
|
||||
section: Optional[int] = Field(None, description="Section")
|
||||
classification: Optional[str] = Field(
|
||||
None, max_length=30, description="Classification")
|
||||
company_id: int = Field(..., description="Company ID")
|
||||
|
||||
|
||||
class ConceptCreate(ConceptBase):
|
||||
@@ -32,7 +32,7 @@ class ConceptUpdate(BaseModel):
|
||||
detailed_description: Optional[str] = Field(None, max_length=1000)
|
||||
priority: Optional[int] = None
|
||||
priority_ame: Optional[int] = None
|
||||
first_total: Optional[bool] = None
|
||||
first_total: Optional[bool] = None
|
||||
type: Optional[str] = Field(None, max_length=9)
|
||||
is_printed: Optional[bool] = None
|
||||
section: Optional[int] = None
|
||||
|
||||
@@ -18,25 +18,39 @@ class Concept(Base, TenantScopedMixin, TimestampMixin):
|
||||
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
|
||||
company_id: Mapped[int] = mapped_column(
|
||||
Integer, nullable=False) # IDEMPRESA
|
||||
|
||||
code: Mapped[str] = mapped_column(String(15), nullable=False) # CLAVE
|
||||
|
||||
description: Mapped[Optional[str]] = mapped_column(
|
||||
String(120), nullable=True) # DESCRIPCION
|
||||
|
||||
description_en: Mapped[Optional[str]] = mapped_column(
|
||||
String(120), nullable=True) # DESCRIPCIONINGLES
|
||||
|
||||
detailed_description: Mapped[Optional[str]] = mapped_column(
|
||||
String(1000), nullable=True) # DESCRIPCIONDETALLADA
|
||||
|
||||
priority: Mapped[Optional[int]] = mapped_column(
|
||||
Integer, nullable=True) # PRIORIDAD
|
||||
|
||||
priority_ame: Mapped[Optional[int]] = mapped_column(
|
||||
Integer, nullable=True) # PRIORIDADAME
|
||||
|
||||
first_total: Mapped[Optional[bool]] = mapped_column(
|
||||
Boolean, nullable=True) # PRIMERTOTAL
|
||||
|
||||
type: Mapped[Optional[str]] = mapped_column(
|
||||
String(9), nullable=True) # TIPO
|
||||
|
||||
is_printed: Mapped[Optional[bool]] = mapped_column(
|
||||
Boolean, nullable=True) # SEIMPRIME
|
||||
|
||||
section: Mapped[Optional[int]] = mapped_column(
|
||||
Integer, nullable=True) # SECCION
|
||||
|
||||
classification: Mapped[Optional[str]] = mapped_column(String(30), ForeignKey(
|
||||
"a76.classification_concepts.classification"), nullable=True) # CLASIFICACION
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ from pydantic import BaseModel, Field, ConfigDict
|
||||
|
||||
|
||||
class CustomsBrokerConceptBase(BaseModel):
|
||||
broker_key: str = Field(..., max_length=5,
|
||||
description="Customs Broker Key (CLAVEAA)")
|
||||
broker_key: str = Field(..., max_length=5, description="Customs Broker Key (CLAVEAA)")
|
||||
concept: str = Field(..., max_length=15, description="Concept")
|
||||
amount: Optional[Decimal] = Field(None, description="Amount")
|
||||
priority: Optional[int] = Field(None, description="Priority")
|
||||
|
||||
@@ -9,17 +9,23 @@ from core.database import Base
|
||||
class CustomsBrokerConcept(Base, TenantScopedMixin, TimestampMixin):
|
||||
__tablename__ = "customs_broker_concepts"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("broker_key", "concept", name="uq_broker_concept"),
|
||||
UniqueConstraint("broker_key", "concept", "company_id", name="uq_broker_concept"),
|
||||
{"schema": "a76"}
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
|
||||
company_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
|
||||
broker_key: Mapped[str] = mapped_column(
|
||||
String(5), nullable=False) # CLAVEAA
|
||||
|
||||
concept: Mapped[str] = mapped_column(
|
||||
String(15), nullable=False) # CONCEPTO
|
||||
|
||||
amount: Mapped[Optional[Decimal]] = mapped_column(
|
||||
Numeric(11, 2), nullable=True) # IMPORTE
|
||||
|
||||
priority: Mapped[Optional[int]] = mapped_column(
|
||||
Integer, nullable=True) # PRIORIDAD
|
||||
|
||||
@@ -7,7 +7,7 @@ router = TenantCRUDRoutes(
|
||||
create_schema=CustomsBrokerConceptCreate,
|
||||
update_schema=CustomsBrokerConceptUpdate,
|
||||
response_schema=CustomsBrokerConceptResponse,
|
||||
prefix="/customs-broker-concepts",
|
||||
prefix="/customs-broker-concepts",
|
||||
tags=["a76.general_catalogs.customs_broker_concepts"],
|
||||
resource_name="Customs Broker Concept",
|
||||
enable_list=True,
|
||||
|
||||
@@ -39,11 +39,15 @@ class EquivalencyItem(Base, TenantScopedMixin, TimestampMixin):
|
||||
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
|
||||
equivalency_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("a76.equivalencies.id"), nullable=False)
|
||||
|
||||
original_field: Mapped[str] = mapped_column(
|
||||
String(100), nullable=False) # Relation to Unit of Measure
|
||||
|
||||
external_field: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
|
||||
equivalency: Mapped["Equivalency"] = relationship(back_populates="items")
|
||||
|
||||
unit_of_measure: Mapped["UnitOfMeasure"] = relationship()
|
||||
|
||||
@@ -24,7 +24,8 @@ class ErrorClassification(Base, TenantScopedMixin, TimestampMixin):
|
||||
|
||||
# Primary key
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
Integer, primary_key=True, autoincrement=True
|
||||
)
|
||||
|
||||
# Classification code (unique)
|
||||
code: Mapped[str] = mapped_column(String(100), nullable=False, unique=True)
|
||||
@@ -60,7 +61,8 @@ class ErrorCatalog(Base, TenantScopedMixin, TimestampMixin):
|
||||
|
||||
# Primary key
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
Integer, primary_key=True, autoincrement=True
|
||||
)
|
||||
|
||||
# Error code (unique)
|
||||
code: Mapped[str] = mapped_column(String(15), nullable=False, unique=True)
|
||||
@@ -77,4 +79,4 @@ class ErrorCatalog(Base, TenantScopedMixin, TimestampMixin):
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ErrorCatalog(id={self.id}, code={self.code}, description={self.description}, classification_id={self.classification_id})>"
|
||||
return f"<ErrorCatalog(id={self.id}, code={self.code}, description={self.description}, classification_id={self.classification_id})>"
|
||||
@@ -30,6 +30,9 @@ class ExchangeRate(Base, TenantScopedMixin, TimestampMixin):
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
|
||||
date: Mapped[datetime] = mapped_column(DateTime)
|
||||
|
||||
value: Mapped[Optional[Decimal]] = mapped_column(DECIMAL(13, 6))
|
||||
|
||||
local_currency: Mapped[Optional[str]] = mapped_column(String(7))
|
||||
|
||||
foreign_currency: Mapped[Optional[str]] = mapped_column(String(7))
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
|
||||
# Identifier DTOs
|
||||
|
||||
|
||||
class IdentifierBase(BaseModel):
|
||||
code: str = Field(..., max_length=2, description="Identifier Code (CLAVE)")
|
||||
description: Optional[str] = Field(
|
||||
@@ -11,28 +8,23 @@ class IdentifierBase(BaseModel):
|
||||
level: Optional[str] = Field(None, max_length=1, description="Level")
|
||||
complement: Optional[str] = Field(
|
||||
None, max_length=5000, description="Complement")
|
||||
company_id: int = Field(..., description="Company ID")
|
||||
|
||||
|
||||
class IdentifierCreate(IdentifierBase):
|
||||
pass
|
||||
|
||||
|
||||
class IdentifierUpdate(BaseModel):
|
||||
code: Optional[str] = Field(None, max_length=2)
|
||||
description: Optional[str] = Field(None, max_length=1000)
|
||||
level: Optional[str] = Field(None, max_length=1)
|
||||
complement: Optional[str] = Field(None, max_length=5000)
|
||||
|
||||
|
||||
class IdentifierResponse(IdentifierBase):
|
||||
id: int
|
||||
tenant_id: int
|
||||
company_id: int
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
# Identifier Detail DTOs
|
||||
|
||||
|
||||
class IdentifierDetailBase(BaseModel):
|
||||
invoice_consecutive: Optional[int] = Field(
|
||||
@@ -47,13 +39,10 @@ class IdentifierDetailBase(BaseModel):
|
||||
None, max_length=51, description="Complement 2")
|
||||
complement3: Optional[str] = Field(
|
||||
None, max_length=50, description="Complement 3")
|
||||
company_id: int = Field(..., description="Company ID")
|
||||
|
||||
|
||||
class IdentifierDetailCreate(IdentifierDetailBase):
|
||||
pass
|
||||
|
||||
|
||||
class IdentifierDetailUpdate(BaseModel):
|
||||
invoice_consecutive: Optional[int] = None
|
||||
part_line: Optional[int] = None
|
||||
@@ -63,9 +52,9 @@ class IdentifierDetailUpdate(BaseModel):
|
||||
complement2: Optional[str] = Field(None, max_length=51)
|
||||
complement3: Optional[str] = Field(None, max_length=50)
|
||||
|
||||
|
||||
class IdentifierDetailResponse(IdentifierDetailBase):
|
||||
id: int
|
||||
tenant_id: int
|
||||
company_id: int
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
@@ -14,11 +14,15 @@ class Identifier(Base, TenantScopedMixin, TimestampMixin):
|
||||
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
|
||||
code: Mapped[str] = mapped_column(String(2), nullable=False) # CLAVE
|
||||
|
||||
description: Mapped[Optional[str]] = mapped_column(
|
||||
String(1000), nullable=True) # DESCRIPCION
|
||||
|
||||
level: Mapped[Optional[str]] = mapped_column(
|
||||
String(1), nullable=True) # NIVEL
|
||||
|
||||
complement: Mapped[Optional[str]] = mapped_column(
|
||||
String(5000), nullable=True) # COMPLEMENTO
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ from typing import Optional
|
||||
from decimal import Decimal
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
|
||||
|
||||
class INPCBase(BaseModel):
|
||||
year: str = Field(..., max_length=4, description="Year (YYYY)")
|
||||
month: str = Field(..., max_length=2, description="Month (MM)")
|
||||
@@ -12,7 +11,6 @@ class INPCBase(BaseModel):
|
||||
class INPCCreate(INPCBase):
|
||||
pass
|
||||
|
||||
|
||||
class INPCUpdate(BaseModel):
|
||||
year: Optional[str] = Field(None, max_length=4)
|
||||
month: Optional[str] = Field(None, max_length=2)
|
||||
@@ -21,5 +19,7 @@ class INPCUpdate(BaseModel):
|
||||
|
||||
class INPCResponse(INPCBase):
|
||||
id: int
|
||||
tenant_id: int
|
||||
company_id: int
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
@@ -16,7 +16,10 @@ class INPC(Base, TenantScopedMixin, TimestampMixin):
|
||||
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
|
||||
year: Mapped[str] = mapped_column(String(4), nullable=False) # ANIO
|
||||
|
||||
month: Mapped[str] = mapped_column(String(2), nullable=False) # MES
|
||||
|
||||
value: Mapped[Optional[Decimal]] = mapped_column(
|
||||
Numeric(19, 8), nullable=True) # VALOR
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
from fastapi import APIRouter
|
||||
from api.v1.common.tenant_crud_routes import TenantCRUDRoutes
|
||||
from .models import INPC
|
||||
from .dto import INPCCreate, INPCResponse, INPCUpdate
|
||||
from .service import INPCService
|
||||
|
||||
router = APIRouter(prefix="/inpc", tags=["a76.general_catalogs.inpc"])
|
||||
|
||||
inpc_crud = TenantCRUDRoutes(
|
||||
# Usamos TenantCRUDRoutes directamente
|
||||
router = TenantCRUDRoutes(
|
||||
service=INPCService,
|
||||
create_schema=INPCCreate,
|
||||
update_schema=INPCUpdate,
|
||||
response_schema=INPCResponse,
|
||||
prefix="",
|
||||
tags=["INPC"],
|
||||
prefix="/inpc",
|
||||
tags=["a76.general_catalogs.inpc"],
|
||||
resource_name="INPC",
|
||||
enable_list=True,
|
||||
)
|
||||
|
||||
router.include_router(inpc_crud.router)
|
||||
).router
|
||||
@@ -21,3 +21,5 @@ class LegendResponse(LegendBase):
|
||||
id: int
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
tenant_id : int
|
||||
company_id : int
|
||||
|
||||
@@ -15,6 +15,8 @@ class Legend(Base, TenantScopedMixin, TimestampMixin):
|
||||
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
|
||||
code: Mapped[int] = mapped_column(Integer, nullable=False) # CLAVELEY
|
||||
|
||||
description: Mapped[Optional[str]] = mapped_column(
|
||||
String(2000), nullable=True) # DESCLEYENDA
|
||||
|
||||
@@ -11,7 +11,7 @@ legend_crud = TenantCRUDRoutes(
|
||||
create_schema=LegendCreate,
|
||||
update_schema=LegendUpdate,
|
||||
response_schema=LegendResponse,
|
||||
prefix="",
|
||||
prefix="/legends",
|
||||
tags=["Legends"],
|
||||
resource_name="Legend",
|
||||
enable_list=True,
|
||||
|
||||
@@ -27,5 +27,7 @@ class MultiCurrencyTypeUpdate(BaseModel):
|
||||
|
||||
class MultiCurrencyTypeResponse(MultiCurrencyTypeBase):
|
||||
id: int
|
||||
|
||||
company_id: int
|
||||
tenant_id: int
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
@@ -18,13 +18,18 @@ class MultiCurrencyType(Base, TenantScopedMixin, TimestampMixin):
|
||||
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
|
||||
currency_type_code: Mapped[str] = mapped_column(
|
||||
String(3), ForeignKey("public.currency_types.code"), nullable=False)
|
||||
|
||||
country_key: Mapped[Optional[str]] = mapped_column(
|
||||
String(3), ForeignKey("public.countries.m3_key"), nullable=True)
|
||||
|
||||
conversion_factor: Mapped[Optional[Decimal]] = mapped_column(
|
||||
Numeric(13, 6), nullable=True)
|
||||
|
||||
publication_date: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
|
||||
currency_type: Mapped["CurrencyType"] = relationship()
|
||||
|
||||
country: Mapped[Optional["Country"]] = relationship()
|
||||
|
||||
@@ -31,4 +31,4 @@ class Package(Base, TenantScopedMixin, TimestampMixin):
|
||||
plurals: Mapped[Optional[str]] = mapped_column(String(4))
|
||||
plural_in: Mapped[Optional[str]] = mapped_column(String(4))
|
||||
code_ace: Mapped[Optional[str]] = mapped_column(String(4))
|
||||
code_aamex: Mapped[Optional[str]] = mapped_column(String(9))
|
||||
code_aamex: Mapped[Optional[str]] = mapped_column(String(9))
|
||||
@@ -11,6 +11,7 @@ class SignatureCreateDTO(BaseModel):
|
||||
"""DTO para crear una firma"""
|
||||
|
||||
code: str = Field(..., max_length=10, description="Signature code")
|
||||
|
||||
signature: Optional[str] = Field(
|
||||
None, max_length=1000, description="Signature")
|
||||
photo_path: Optional[str] = Field(
|
||||
@@ -39,6 +40,8 @@ class SignatureResponseDTO(BaseModel):
|
||||
code: str
|
||||
signature: Optional[str] = None
|
||||
photo_path: Optional[str] = None
|
||||
intenant_id: int
|
||||
company_id: int
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
@@ -32,6 +32,7 @@ class Signature(Base, TenantScopedMixin, TimestampMixin):
|
||||
|
||||
# Signature information
|
||||
signature: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
|
||||
photo_path: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
@@ -2,27 +2,23 @@ from typing import Optional
|
||||
from decimal import Decimal
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
|
||||
|
||||
class UnitConversionBase(BaseModel):
|
||||
from_unit_code: str = Field(..., max_length=5,
|
||||
description="Source Unit Code")
|
||||
to_unit_code: str = Field(..., max_length=5,
|
||||
description="Target Unit Code")
|
||||
conversion_factor: Optional[Decimal] = Field(
|
||||
None, description="Conversion Factor")
|
||||
|
||||
# 👇 OJO: Son códigos (Strings), no IDs
|
||||
from_unit_code: str = Field(..., max_length=5, description="Source Unit Code")
|
||||
to_unit_code: str = Field(..., max_length=5, description="Target Unit Code")
|
||||
conversion_factor: Optional[Decimal] = Field(None, description="Conversion Factor")
|
||||
|
||||
class UnitConversionCreate(UnitConversionBase):
|
||||
pass
|
||||
|
||||
|
||||
class UnitConversionUpdate(BaseModel):
|
||||
from_unit_code: Optional[str] = Field(None, max_length=5)
|
||||
to_unit_code: Optional[str] = Field(None, max_length=5)
|
||||
conversion_factor: Optional[Decimal] = None
|
||||
|
||||
|
||||
class UnitConversionResponse(UnitConversionBase):
|
||||
id: int
|
||||
tenant_id: int
|
||||
company_id: int
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
@@ -27,8 +27,11 @@ class UnitConversion(Base, TenantScopedMixin, TimestampMixin):
|
||||
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
|
||||
from_unit_code: Mapped[str] = mapped_column(String(5), nullable=False)
|
||||
|
||||
to_unit_code: Mapped[str] = mapped_column(String(5), nullable=False)
|
||||
|
||||
conversion_factor: Mapped[Optional[Decimal]] = mapped_column(
|
||||
Numeric(13, 6), nullable=True)
|
||||
|
||||
|
||||
@@ -181,4 +181,4 @@ class UnitOfMeasureGeneral(Base, TenantScopedMixin, TimestampMixin):
|
||||
String(4), nullable=True) # CLAVEACE
|
||||
|
||||
customs_unit: Mapped[Optional["UnitOfMeasureCustoms"]] = relationship()
|
||||
ace_unit: Mapped[Optional["UnitOfMeasureACE"]] = relationship()
|
||||
ace_unit: Mapped[Optional["UnitOfMeasureACE"]] = relationship(overlaps="customs_unit")
|
||||
|
||||
Reference in New Issue
Block a user