- Added data table component for displaying clients and providers with infinite scroll functionality. - Created dropdown actions for each client/provider including copy ID, copy RFC, view details, edit, toggle status, and delete. - Implemented dialogs for creating, editing, viewing details, and deleting clients/providers. - Integrated API calls for fetching, creating, editing, deleting, and toggling status of clients/providers. - Enhanced error handling and loading states for better user experience. - Updated server-side logic to handle pagination and company selection for client/provider data retrieval.
159 lines
5.3 KiB
Python
159 lines
5.3 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
|
|
from api.v1.common.tenant_crud_routes import TenantCRUDRoutes
|
|
|
|
from .dto import (
|
|
ClientProviderBasicDTO,
|
|
ClientProviderCreateDTO,
|
|
ClientProviderResponseDTO,
|
|
ClientProviderUpdateDTO,
|
|
)
|
|
from .service import ClientProviderService
|
|
from .models import ClientProvider
|
|
|
|
# Create base CRUD router using TenantCRUDRoutes factory
|
|
base_router = TenantCRUDRoutes(
|
|
service=ClientProviderService,
|
|
create_schema=ClientProviderCreateDTO,
|
|
update_schema=ClientProviderUpdateDTO,
|
|
response_schema=ClientProviderResponseDTO,
|
|
prefix="",
|
|
tags=[],
|
|
resource_name="Client/Provider",
|
|
id_name="id", # Using numeric ID
|
|
enable_list=True, # Enable GET /clients-providers with pagination
|
|
enable_filters=True, # Enable filtering
|
|
default_page_size=50,
|
|
max_page_size=100,
|
|
).router
|
|
|
|
# Create main router to add custom endpoints
|
|
router = APIRouter(prefix="/clients-providers")
|
|
|
|
# Include base CRUD routes
|
|
router.include_router(base_router, prefix="")
|
|
|
|
|
|
# Custom endpoints
|
|
@router.get("/clients", response_model=List[ClientProviderBasicDTO])
|
|
async def get_clients_only(
|
|
company_id: int = Query(..., description="Company ID"),
|
|
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 only clients (client_or_provider = 'client')"""
|
|
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
|
|
|
clients = (
|
|
db.query(ClientProvider)
|
|
.filter(
|
|
ClientProvider.tenant_id == tenant_id,
|
|
ClientProvider.company_id == company_id,
|
|
ClientProvider.client_or_provider == "client"
|
|
)
|
|
.offset(skip)
|
|
.limit(limit)
|
|
.all()
|
|
)
|
|
return [ClientProviderBasicDTO.model_validate(c) for c in clients]
|
|
|
|
|
|
@router.get("/providers", response_model=List[ClientProviderBasicDTO])
|
|
async def get_providers_only(
|
|
company_id: int = Query(..., description="Company ID"),
|
|
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 only providers (client_or_provider = 'provider')"""
|
|
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
|
|
|
providers = (
|
|
db.query(ClientProvider)
|
|
.filter(
|
|
ClientProvider.tenant_id == tenant_id,
|
|
ClientProvider.company_id == company_id,
|
|
ClientProvider.client_or_provider == "provider"
|
|
)
|
|
.offset(skip)
|
|
.limit(limit)
|
|
.all()
|
|
)
|
|
return [ClientProviderBasicDTO.model_validate(p) for p in providers]
|
|
|
|
|
|
@router.get("/search/rfc/{rfc}", response_model=List[ClientProviderBasicDTO])
|
|
async def search_by_rfc(
|
|
rfc: str,
|
|
company_id: int = Query(..., description="Company ID"),
|
|
db: Session = Depends(get_core_db),
|
|
current_user: dict = Depends(get_current_user),
|
|
):
|
|
"""Search clients/providers by RFC"""
|
|
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
|
|
|
clients = (
|
|
db.query(ClientProvider)
|
|
.filter(
|
|
ClientProvider.tenant_id == tenant_id,
|
|
ClientProvider.company_id == company_id,
|
|
ClientProvider.rfc.ilike(f"%{rfc}%")
|
|
)
|
|
.all()
|
|
)
|
|
return [ClientProviderBasicDTO.model_validate(c) for c in clients]
|
|
|
|
|
|
@router.patch("/{client_id}/toggle-status", response_model=ClientProviderResponseDTO)
|
|
async def toggle_clients_and_providers_status(
|
|
client_id: int,
|
|
company_id: int = Query(..., description="Company ID"),
|
|
db: Session = Depends(get_core_db),
|
|
current_user: dict = Depends(get_current_user),
|
|
):
|
|
"""Toggle client/provider enabled/disabled status"""
|
|
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")
|
|
|
|
# Toggle status (1 = enabled, 0 = disabled)
|
|
client.enabled_disabled = 1 if client.enabled_disabled == 0 else 0
|
|
|
|
try:
|
|
db.commit()
|
|
db.refresh(client)
|
|
return client
|
|
except Exception as e:
|
|
db.rollback()
|
|
raise HTTPException(status_code=500, detail="Error updating status")
|
|
|
|
|
|
@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)
|