Files
plantillas-proyectos/backend/api/v1/modules/a76/classes/routes.py
acazares e0e38526b0 Refactor database models to include company_id and update unique constraints
- Updated PermissionRuleOct model to include company_id and modified unique constraint.
- Updated Seal model to include company_id and modified unique constraint.
- Added TYPE_CHECKING imports in reference data models for better type hinting.
- Created new models for QClasses and SClasses in the a24 module.
- Enhanced init_first_time.sh script for comprehensive system initialization, including Keycloak and PostgreSQL setup.
- Updated documentation to reflect changes in API endpoints and database relationships.
2025-11-07 18:47:41 -06:00

259 lines
8.0 KiB
Python

"""
Endpoints API para gestión de clases SCAII y SCAF
"""
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy.orm import Session
from typing import List, Optional
from core.database import get_core_db
from core.security import get_current_user, has_role
from .service import ClassService
from .dto import (
ClassCreateDTO,
ClassUpdateDTO,
ClassResponseDTO,
ClassBasicDTO,
ClassListDTO,
ClassSearchDTO
)
router = APIRouter(prefix="/classes", tags=["Classes"])
@router.get("/", response_model=ClassListDTO)
async def list_classes(
skip: int = Query(0, ge=0, description="Number of records to skip"),
limit: int = Query(100, ge=1, le=1000, description="Maximum number of records to return"),
client_id: Optional[int] = Query(None, description="Filter by client key"),
class_code: Optional[str] = Query(None, description="Search by class code"),
description: Optional[str] = Query(None, description="Search in descriptions"),
material_key: Optional[str] = Query(None, description="Filter by material key"),
fraction: Optional[str] = Query(None, description="Filter by tariff fraction"),
physical_review: Optional[int] = Query(None, description="Filter by physical review indicator"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""
List classes with optional filters and pagination
"""
service = ClassService(db)
search_params = ClassSearchDTO(
client_id=client_id,
class_code=class_code,
description=description,
material_key=material_key,
fraction=fraction,
physical_review=physical_review
)
return service.list_classes(skip, limit, search_params)
@router.get("/client/{client_id}", response_model=List[ClassBasicDTO])
async def get_classes_by_client(
client_id: int,
skip: int = Query(0, ge=0),
limit: int = Query(100, ge=1, le=1000),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""
Get all classes for a specific client
"""
service = ClassService(db)
return service.search_by_client(client_id, skip, limit)
@router.get("/search/fraction/{fraction}", response_model=List[ClassBasicDTO])
async def search_by_fraction(
fraction: str,
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""
Search classes by tariff fraction
"""
service = ClassService(db)
return service.search_by_fraction(fraction)
@router.get("/search/material/{material_key}", response_model=List[ClassBasicDTO])
async def search_by_material(
material_key: str,
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""
Search classes by material key
"""
service = ClassService(db)
return service.search_by_material(material_key)
@router.get("/search/unit-measure/{unit_of_measure}", response_model=List[ClassBasicDTO])
async def get_classes_by_unit_measure(
unit_of_measure: str,
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""
Get classes by unit of measure
"""
service = ClassService(db)
return service.get_classes_by_unit_measure(unit_of_measure)
@router.get("/search/physical-review/{physical_review}", response_model=List[ClassBasicDTO])
async def get_classes_by_physical_review(
physical_review: int,
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""
Get classes by physical review indicator
"""
service = ClassService(db)
return service.get_classes_by_physical_review(physical_review)
@router.get("/statistics", response_model=dict)
async def get_classes_statistics(
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""
Get basic classes statistics
"""
service = ClassService(db)
return service.get_classes_statistics()
@router.get("/{client_id}/{class_code}", response_model=ClassResponseDTO)
async def get_class(
client_id: int,
class_code: str,
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""
Get class by composite key (client_id + class_code)
"""
service = ClassService(db)
class_obj = service.get_class(client_id, class_code)
if not class_obj:
raise HTTPException(
status_code=404,
detail=f"Class with client_id '{client_id}' and class_code '{class_code}' not found"
)
return class_obj
@router.post("/", response_model=ClassResponseDTO, status_code=status.HTTP_201_CREATED)
async def create_class(
class_data: ClassCreateDTO,
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""
Create a new class in the system
"""
service = ClassService(db)
return service.create_class(class_data)
@router.put("/{client_id}/{class_code}", response_model=ClassResponseDTO)
async def update_class(
client_id: int,
class_code: str,
class_data: ClassUpdateDTO,
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""
Update class information
"""
service = ClassService(db)
class_obj = service.update_class(client_id, class_code, class_data)
if not class_obj:
raise HTTPException(
status_code=404,
detail=f"Class with client_id '{client_id}' and class_code '{class_code}' not found"
)
return class_obj
@router.delete("/{client_id}/{class_code}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_class(
client_id: int,
class_code: str,
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""
Delete class from the system
Note: This will completely remove the class from the system.
"""
service = ClassService(db)
if not service.delete_class(client_id, class_code):
raise HTTPException(
status_code=404,
detail=f"Class with client_id '{client_id}' and class_code '{class_code}' not found"
)
# Endpoints específicos para información detallada
@router.get("/{client_id}/{class_code}/basic", response_model=ClassBasicDTO)
async def get_class_basic_info(
client_id: int,
class_code: str,
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""
Get basic information for a class
"""
service = ClassService(db)
class_obj = service.get_class(client_id, class_code)
if not class_obj:
raise HTTPException(
status_code=404,
detail=f"Class with client_id '{client_id}' and class_code '{class_code}' not found"
)
return ClassBasicDTO(
client_id=class_obj.client_id,
class_code=class_obj.class_code,
description_spanish=class_obj.description_spanish,
description_english=class_obj.description_english,
material_key=class_obj.material_key,
fraction=class_obj.fraction
)
@router.get("/{client_id}/{class_code}/tariff", response_model=dict)
async def get_class_tariff_info(
client_id: int,
class_code: str,
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""
Get tariff information for a class (fractions, IVA exempt, etc.)
"""
service = ClassService(db)
class_obj = service.get_class(client_id, class_code)
if not class_obj:
raise HTTPException(
status_code=404,
detail=f"Class with client_id '{client_id}' and class_code '{class_code}' not found"
)
return {
"client_id": class_obj.client_id,
"class_code": class_obj.class_code,
"fraction": class_obj.fraction,
"us_fraction": class_obj.us_fraction,
"iva_exempt_fraction": class_obj.iva_exempt_fraction,
"sub_key": class_obj.sub_key,
"physical_review": class_obj.physical_review
}