feat: Implement Document Types for Digitization module
- Added backend API for managing document types related to digitization, including CRUD operations. - Created DTOs and models for DocumentTypeDigitization with validation using Pydantic. - Updated frontend API service to interact with the new document types API. - Refactored existing forms in the frontend to load and manage document types effectively. - Enhanced data loading and state management in various components related to pedimentos.
This commit is contained in:
1
backend/api/v1/modules/a76/doc_types_dig/__init__.py
Normal file
1
backend/api/v1/modules/a76/doc_types_dig/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Document Types for Digitization module
|
||||
32
backend/api/v1/modules/a76/doc_types_dig/dto.py
Normal file
32
backend/api/v1/modules/a76/doc_types_dig/dto.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class DocumentTypeDigitizationBase(BaseModel):
|
||||
"""Base schema for Document Type Digitization"""
|
||||
|
||||
code: str = Field(..., max_length=10, description="Código del tipo de documento")
|
||||
description: str = Field(..., description="Descripción del tipo de documento")
|
||||
active: bool = Field(default=True, description="Indica si el tipo está activo")
|
||||
|
||||
|
||||
class DocumentTypeDigitizationCreate(DocumentTypeDigitizationBase):
|
||||
"""Schema for creating a Document Type Digitization"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class DocumentTypeDigitizationUpdate(BaseModel):
|
||||
"""Schema for updating a Document Type Digitization"""
|
||||
|
||||
code: str | None = Field(None, max_length=10)
|
||||
description: str | None = None
|
||||
active: bool | None = None
|
||||
|
||||
|
||||
class DocumentTypeDigitizationResponse(DocumentTypeDigitizationBase):
|
||||
"""Schema for Document Type Digitization response"""
|
||||
|
||||
id: int
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
25
backend/api/v1/modules/a76/doc_types_dig/models.py
Normal file
25
backend/api/v1/modules/a76/doc_types_dig/models.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
|
||||
from core.database import Base
|
||||
from sqlalchemy import Boolean, Integer, PrimaryKeyConstraint, String, Text, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
|
||||
class DocumentTypeDigitization(Base, TenantScopedMixin, TimestampMixin):
|
||||
"""Tipos de documentos para digitalización de pedimentos"""
|
||||
|
||||
__tablename__ = "document_types_digitization"
|
||||
__table_args__ = (
|
||||
PrimaryKeyConstraint("id", name="document_types_digitization_pkey"),
|
||||
UniqueConstraint(
|
||||
"tenant_id",
|
||||
"company_id",
|
||||
"code",
|
||||
name="document_types_digitization_code_key",
|
||||
),
|
||||
{"schema": "a76"},
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer)
|
||||
code: Mapped[str] = mapped_column(String(10), nullable=False, index=True)
|
||||
description: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
active: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
170
backend/api/v1/modules/a76/doc_types_dig/routes.py
Normal file
170
backend/api/v1/modules/a76/doc_types_dig/routes.py
Normal file
@@ -0,0 +1,170 @@
|
||||
from typing import List
|
||||
|
||||
from api.v1.modules.a76.doc_types_dig.dto import (
|
||||
DocumentTypeDigitizationCreate,
|
||||
DocumentTypeDigitizationResponse,
|
||||
DocumentTypeDigitizationUpdate,
|
||||
)
|
||||
from api.v1.modules.a76.doc_types_dig.models import DocumentTypeDigitization
|
||||
from core.database import get_core_db
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
router = APIRouter(prefix="/document-types-digitization", tags=["Document Types Digitization"])
|
||||
|
||||
|
||||
@router.get("", response_model=List[DocumentTypeDigitizationResponse])
|
||||
def get_all_document_types(
|
||||
active_only: bool = True,
|
||||
db: Session = Depends(get_core_db),
|
||||
):
|
||||
"""
|
||||
Obtener todos los tipos de documentos para digitalización
|
||||
|
||||
Args:
|
||||
active_only: Si es True, solo devuelve los tipos activos
|
||||
"""
|
||||
query = select(DocumentTypeDigitization)
|
||||
|
||||
if active_only:
|
||||
query = query.where(DocumentTypeDigitization.active == True)
|
||||
|
||||
query = query.order_by(DocumentTypeDigitization.code)
|
||||
|
||||
result = db.execute(query)
|
||||
document_types = result.scalars().all()
|
||||
|
||||
return document_types
|
||||
|
||||
|
||||
@router.get("/{document_type_id}", response_model=DocumentTypeDigitizationResponse)
|
||||
def get_document_type(
|
||||
document_type_id: int,
|
||||
db: Session = Depends(get_core_db),
|
||||
):
|
||||
"""Obtener un tipo de documento por ID"""
|
||||
result = db.execute(
|
||||
select(DocumentTypeDigitization).where(DocumentTypeDigitization.id == document_type_id)
|
||||
)
|
||||
document_type = result.scalar_one_or_none()
|
||||
|
||||
if not document_type:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Tipo de documento con ID {document_type_id} no encontrado"
|
||||
)
|
||||
|
||||
return document_type
|
||||
|
||||
|
||||
@router.get("/by-code/{code}", response_model=DocumentTypeDigitizationResponse)
|
||||
def get_document_type_by_code(
|
||||
code: str,
|
||||
db: Session = Depends(get_core_db),
|
||||
):
|
||||
"""Obtener un tipo de documento por código"""
|
||||
result = db.execute(
|
||||
select(DocumentTypeDigitization).where(DocumentTypeDigitization.code == code)
|
||||
)
|
||||
document_type = result.scalar_one_or_none()
|
||||
|
||||
if not document_type:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Tipo de documento con código {code} no encontrado"
|
||||
)
|
||||
|
||||
return document_type
|
||||
|
||||
|
||||
@router.post("", response_model=DocumentTypeDigitizationResponse, status_code=status.HTTP_201_CREATED)
|
||||
def create_document_type(
|
||||
document_type_data: DocumentTypeDigitizationCreate,
|
||||
db: Session = Depends(get_core_db),
|
||||
):
|
||||
"""Crear un nuevo tipo de documento"""
|
||||
# Verificar si el código ya existe
|
||||
result = db.execute(
|
||||
select(DocumentTypeDigitization).where(DocumentTypeDigitization.code == document_type_data.code)
|
||||
)
|
||||
existing = result.scalar_one_or_none()
|
||||
|
||||
if existing:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"Ya existe un tipo de documento con el código {document_type_data.code}"
|
||||
)
|
||||
|
||||
new_document_type = DocumentTypeDigitization(**document_type_data.model_dump())
|
||||
db.add(new_document_type)
|
||||
db.commit()
|
||||
db.refresh(new_document_type)
|
||||
|
||||
return new_document_type
|
||||
|
||||
|
||||
@router.put("/{document_type_id}", response_model=DocumentTypeDigitizationResponse)
|
||||
def update_document_type(
|
||||
document_type_id: int,
|
||||
document_type_data: DocumentTypeDigitizationUpdate,
|
||||
db: Session = Depends(get_core_db),
|
||||
):
|
||||
"""Actualizar un tipo de documento existente"""
|
||||
result = db.execute(
|
||||
select(DocumentTypeDigitization).where(DocumentTypeDigitization.id == document_type_id)
|
||||
)
|
||||
document_type = result.scalar_one_or_none()
|
||||
|
||||
if not document_type:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Tipo de documento con ID {document_type_id} no encontrado"
|
||||
)
|
||||
|
||||
# Actualizar solo los campos proporcionados
|
||||
update_data = document_type_data.model_dump(exclude_unset=True)
|
||||
|
||||
# Verificar si el nuevo código ya existe (si se está actualizando)
|
||||
if "code" in update_data and update_data["code"] != document_type.code:
|
||||
result = db.execute(
|
||||
select(DocumentTypeDigitization).where(DocumentTypeDigitization.code == update_data["code"])
|
||||
)
|
||||
existing = result.scalar_one_or_none()
|
||||
if existing:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"Ya existe un tipo de documento con el código {update_data['code']}"
|
||||
)
|
||||
|
||||
for field, value in update_data.items():
|
||||
setattr(document_type, field, value)
|
||||
|
||||
db.commit()
|
||||
db.refresh(document_type)
|
||||
|
||||
return document_type
|
||||
|
||||
|
||||
@router.delete("/{document_type_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def delete_document_type(
|
||||
document_type_id: int,
|
||||
db: Session = Depends(get_core_db),
|
||||
):
|
||||
"""Eliminar un tipo de documento (soft delete, marca como inactivo)"""
|
||||
result = db.execute(
|
||||
select(DocumentTypeDigitization).where(DocumentTypeDigitization.id == document_type_id)
|
||||
)
|
||||
document_type = result.scalar_one_or_none()
|
||||
|
||||
if not document_type:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Tipo de documento con ID {document_type_id} no encontrado"
|
||||
)
|
||||
|
||||
# Soft delete - solo marcar como inactivo
|
||||
document_type.active = False
|
||||
db.commit()
|
||||
|
||||
return None
|
||||
@@ -3,11 +3,10 @@ from typing import TYPE_CHECKING
|
||||
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
|
||||
from core.database import Base
|
||||
from sqlalchemy import (
|
||||
Boolean,
|
||||
ForeignKeyConstraint,
|
||||
Integer,
|
||||
PrimaryKeyConstraint,
|
||||
SmallInteger,
|
||||
String,
|
||||
UniqueConstraint,
|
||||
)
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
@@ -38,12 +37,12 @@ class PedimentoConfigAdditional(Base, TenantScopedMixin, TimestampMixin):
|
||||
id: Mapped[int] = mapped_column(Integer)
|
||||
pedimento_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
|
||||
add_po_identifier: Mapped[int] = mapped_column(SmallInteger)
|
||||
do_not_exempt_norms_complement_x: Mapped[int] = mapped_column(SmallInteger)
|
||||
manual_pedimento_year: Mapped[str] = mapped_column(String(2))
|
||||
enable_import_invoice_recipient: Mapped[int] = mapped_column(SmallInteger)
|
||||
send_502_validation_file_for_consolidated: Mapped[int] = mapped_column(SmallInteger)
|
||||
add_remove_norms: Mapped[int] = mapped_column(SmallInteger)
|
||||
add_po_identifier: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
do_not_exempt_norms_complement_x: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
manual_pedimento_year: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
enable_import_invoice_recipient: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
send_502_validation_file_for_consolidated: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
add_remove_norms: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
|
||||
pedimento: Mapped["Pedimentos"] = relationship(
|
||||
"Pedimentos", back_populates="pedimento_config_additional"
|
||||
|
||||
@@ -14,6 +14,7 @@ from .clients_and_providers import router as client_and_provider_router
|
||||
from .general_catalogs.company import router as company_router
|
||||
from .country_rule_oct.routes import router as country_rule_oct_router
|
||||
from .transportation.drivers.routes import router as drivers_router
|
||||
from .doc_types_dig.routes import router as doc_types_dig_router
|
||||
from .general_catalogs.exchange_rate.routes import router as exchange_rate_router
|
||||
from .general_catalogs.identifiers.routes import router as identifiers_router
|
||||
from .fraction_rule_octave.routes import router as fraction_rule_octave_router
|
||||
@@ -73,6 +74,7 @@ router.include_router(trailers_router, prefix="/a76", tags=["a76 / trailers"])
|
||||
router.include_router(
|
||||
customs_broker_router, prefix="/a76", tags=["a76 / customs_broker"]
|
||||
)
|
||||
router.include_router(doc_types_dig_router, prefix="/a76", tags=["a76 / document_types_digitization"])
|
||||
router.include_router(drivers_router, prefix="/a76", tags=["a76 / drivers"])
|
||||
router.include_router(transporters_router, prefix="/a76",
|
||||
tags=["a76 / transporters"])
|
||||
|
||||
Reference in New Issue
Block a user