273 lines
8.5 KiB
Python
273 lines
8.5 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
|
|
"""
|
|
# Validate access to the tenant and company
|
|
tenant_id = current_user.get("tenant_id")
|
|
company_id = current_user.get("company_id")
|
|
|
|
if not tenant_id or not company_id:
|
|
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
|
|
|
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
|
|
"""
|
|
# Validate access to the tenant and company
|
|
tenant_id = current_user.get("tenant_id")
|
|
company_id = current_user.get("company_id")
|
|
|
|
if not tenant_id or not company_id:
|
|
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
|
|
|
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
|
|
}
|
|
|
|
|