""" 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 func, 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 (contains)"), tax_id: Optional[str] = Query(None, description="Filter by TAX-ID (contains)"), short_name: Optional[str] = Query( None, description="Filter by short name / clave (exact match, case-insensitive)" ), 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 tax_id: query = query.filter(ClientProvider.tax_id.ilike(f"%{tax_id.strip()}%")) if short_name: sn = short_name.strip().upper() query = query.filter(func.upper(ClientProvider.short_name) == sn) 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