178 lines
6.5 KiB
Python
178 lines
6.5 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 import or_
|
|
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"),
|
|
name: Optional[str] = Query(None, description="Filter by name (contains)"),
|
|
rfc: Optional[str] = Query(None, description="Filter by RFC/TAX-ID (contains)"),
|
|
type: Optional[ClientOrProviderEnum] = Query(
|
|
None, description="Type of entity (client or provider)"
|
|
),
|
|
active: Optional[bool] = Query(None, description="Active status"),
|
|
page: Optional[int] = Query(None, ge=1, description="Page number (1-based)"),
|
|
page_size: Optional[int] = Query(
|
|
None, ge=1, le=1000, description="Page size when using page-based pagination"
|
|
),
|
|
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, ["partners_mgmt.view"])
|
|
|
|
resolved_limit = page_size if page_size is not None else limit
|
|
resolved_skip = ((page - 1) * resolved_limit) if page is not None else skip
|
|
|
|
query = db.query(ClientProvider).options(
|
|
joinedload(ClientProvider.address),
|
|
joinedload(ClientProvider.programs)
|
|
).filter(
|
|
ClientProvider.tenant_id == tenant_id,
|
|
ClientProvider.company_id == company_id,
|
|
)
|
|
|
|
if name:
|
|
query = query.filter(ClientProvider.name.ilike(f"%{name.strip()}%"))
|
|
|
|
if rfc:
|
|
query = query.filter(ClientProvider.rfc.ilike(f"%{rfc.strip()}%"))
|
|
|
|
if type is not None:
|
|
# Include 'both' type when filtering by client or provider
|
|
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(resolved_skip).limit(resolved_limit).all()
|
|
|
|
return {
|
|
"items": [ClientProviderResponseDTO.model_validate(c) for c in clients],
|
|
"total": total,
|
|
"page": (resolved_skip // resolved_limit) + 1,
|
|
"page_size": resolved_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, ["partners_mgmt.view"])
|
|
|
|
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, ["partners_mgmt.create"])
|
|
|
|
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, ["partners_mgmt.edit"])
|
|
|
|
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, ["partners_mgmt.view"])
|
|
|
|
# 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, ["partners_mgmt.delete"])
|
|
|
|
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
|