Files
plantillas-proyectos/backend/api/v1/modules/a76/clients_and_providers/routes.py
2026-03-03 16:45:12 -07:00

163 lines
5.7 KiB
Python

"""
Endpoints API para gestión de clientes y proveedores
"""
from typing import List, Optional
from core.database import get_core_db
from core.security import get_current_user, validate_access_to_resource
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session, joinedload
from api.v1.common.tenant_crud_routes import TenantCRUDRoutes
from .models import ClientOrProviderEnum
from .dto import (
ClientProviderBasicDTO,
ClientProviderCreateDTO,
ClientProviderResponseDTO,
ClientProviderUpdateDTO,
ClientProviderPaginatedResponseDTO,
)
from .service import ClientProviderService
from .models import ClientProvider
from api.v1.modules.a76.layouts_csv.clients_and_providers.routes import router as imports_router
# Create main router to add custom endpoints
router = APIRouter(prefix="/clients-providers")
# CSV import (mismo flujo que customs_brokers/imports: upload → scan → commit)
router.include_router(imports_router, prefix="/imports", tags=["clients_and_providers / csv_import"])
@router.get("/", response_model=ClientProviderPaginatedResponseDTO)
async def get_clients_and_providers(
company_id: int = Query(..., description="Company ID"),
type: Optional[ClientOrProviderEnum] = Query(
None, description="Type of entity (client or provider)"
),
active: Optional[bool] = Query(None, description="Active status"),
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 clients and providers"""
tenant_id = validate_access_to_resource(db, company_id, current_user)
query = db.query(ClientProvider).options(
joinedload(ClientProvider.address),
joinedload(ClientProvider.programs)
).filter(
ClientProvider.tenant_id == tenant_id,
ClientProvider.company_id == company_id,
)
if type is not None:
# Include 'both' type when filtering by client or provider
from sqlalchemy import or_
query = query.filter(
or_(
ClientProvider.client_or_provider == type,
ClientProvider.client_or_provider == ClientOrProviderEnum.BOTH
)
)
if active is not None:
query = query.filter(ClientProvider.is_active == active)
total = query.count()
clients = query.offset(skip).limit(limit).all()
return {
"items": [ClientProviderResponseDTO.model_validate(c) for c in clients],
"total": total,
"page": (skip // limit) + 1,
"page_size": limit,
}
@router.get("/{client_id}/basic", response_model=ClientProviderBasicDTO)
async def get_clients_and_providers_basic_info(
client_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user),
):
"""Get basic information for a client/provider (without address and programs)"""
tenant_id = validate_access_to_resource(db, company_id, current_user)
client = ClientProviderService.get_by_id(db, client_id, tenant_id, company_id)
if not client:
raise HTTPException(status_code=404, detail="Client/Provider not found")
return ClientProviderBasicDTO.model_validate(client)
@router.post("/", response_model=ClientProviderResponseDTO)
async def create_client_provider(
client_data: ClientProviderCreateDTO,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user),
):
"""Create a new client/provider"""
tenant_id = validate_access_to_resource(db, company_id, current_user)
return ClientProviderService.create(db, client_data, tenant_id, company_id)
@router.patch("/{client_id}", response_model=ClientProviderResponseDTO)
async def update_client_provider(
client_id: int,
client_data: ClientProviderUpdateDTO,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user),
):
"""Update a client/provider"""
tenant_id = validate_access_to_resource(db, company_id, current_user)
client = ClientProviderService.update(
db, client_id, tenant_id, company_id, client_data
)
if not client:
raise HTTPException(status_code=404, detail="Client/Provider not found")
return client
@router.get("/{client_id}", response_model=ClientProviderResponseDTO)
async def get_client_provider_detail(
client_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user),
):
"""
Obtener un cliente/proveedor completo por ID.
Esta es la ruta que tu formulario necesita para cargar los datos.
"""
tenant_id = validate_access_to_resource(db, company_id, current_user)
# Usamos el servicio para buscar por ID
client = ClientProviderService.get_by_id(db, client_id, tenant_id, company_id)
if not client:
raise HTTPException(status_code=404, detail="Client/Provider not found")
return client
@router.delete("/{client_id}", response_model=bool)
async def delete_client_provider(
client_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user),
):
"""Delete a client/provider"""
tenant_id = validate_access_to_resource(db, company_id, current_user)
success = ClientProviderService.delete(db, client_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Client/Provider not found")
return success