- Enhanced TenantMiddleware to validate tenant information from JWT tokens. - Added LicenseValidationMiddleware to check tenant licenses before processing requests. - Updated security utilities to extract tenant information from tokens and validate company access. - Introduced CompanyStore to manage active company state and handle company switching in the frontend. - Modified API routes to include company_id in requests for better resource management. - Improved logging and error handling throughout the middleware and API layers. - Updated frontend components to reflect changes in company management and selection. - Added new API route for fetching user's companies with proper authentication handling.
326 lines
11 KiB
Python
326 lines
11 KiB
Python
"""
|
|
Endpoints API para gestión de clientes y proveedores
|
|
"""
|
|
|
|
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 ClientProviderService
|
|
from .dto import (
|
|
ClientProviderCreateDTO,
|
|
ClientProviderUpdateDTO,
|
|
ClientProviderResponseDTO,
|
|
ClientProviderBasicDTO,
|
|
ClientProviderListDTO,
|
|
)
|
|
|
|
router = APIRouter(prefix="/clients-providers")
|
|
|
|
|
|
@router.post(
|
|
"/", response_model=ClientProviderResponseDTO, status_code=status.HTTP_201_CREATED
|
|
)
|
|
async def create_client_provider(
|
|
client_data: ClientProviderCreateDTO,
|
|
db: Session = Depends(get_core_db),
|
|
current_user: dict = Depends(get_current_user),
|
|
):
|
|
"""
|
|
Create a new client or provider in the system
|
|
"""
|
|
# 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")
|
|
|
|
# Ensure the client_data is associated with the correct tenant and company
|
|
if client_data.tenant_id != tenant_id or client_data.company_id != company_id:
|
|
raise HTTPException(status_code=400, detail="Mismatch in tenant or company association")
|
|
|
|
service = ClientProviderService(db)
|
|
return service.create_client_provider(client_data)
|
|
|
|
|
|
@router.get("/", response_model=ClientProviderListDTO)
|
|
async def list_clients_providers(
|
|
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"
|
|
),
|
|
search: Optional[str] = Query(None, description="Search text for name, RFC, or ID"),
|
|
client_or_provider: Optional[str] = Query(
|
|
None, regex="^[CP]$", description="Filter by type: C=Client, P=Provider"
|
|
),
|
|
enabled_only: bool = Query(False, description="Show only enabled records"),
|
|
db: Session = Depends(get_core_db),
|
|
current_user: dict = Depends(get_current_user),
|
|
):
|
|
"""
|
|
List clients and providers 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 = ClientProviderService(db)
|
|
return service.list_clients_providers(
|
|
skip, limit, search, client_or_provider, enabled_only
|
|
)
|
|
|
|
|
|
@router.get("/clients", response_model=List[ClientProviderBasicDTO])
|
|
async def get_clients_only(
|
|
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 = 'C')
|
|
"""
|
|
# 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 = ClientProviderService(db)
|
|
return service.get_clients_only(skip, limit)
|
|
|
|
|
|
@router.get("/providers", response_model=List[ClientProviderBasicDTO])
|
|
async def get_providers_only(
|
|
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 = 'P')
|
|
"""
|
|
# 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 = ClientProviderService(db)
|
|
return service.get_providers_only(skip, limit)
|
|
|
|
|
|
@router.get("/search/rfc/{rfc}", response_model=List[ClientProviderBasicDTO])
|
|
async def search_by_rfc(
|
|
rfc: str,
|
|
db: Session = Depends(get_core_db),
|
|
current_user: dict = Depends(get_current_user),
|
|
):
|
|
"""
|
|
Search clients/providers by RFC
|
|
"""
|
|
# 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 = ClientProviderService(db)
|
|
return service.search_by_rfc(rfc)
|
|
|
|
|
|
@router.get("/{client_id}", response_model=ClientProviderResponseDTO)
|
|
async def get_client_provider(
|
|
client_id: str,
|
|
db: Session = Depends(get_core_db),
|
|
current_user: dict = Depends(get_current_user),
|
|
):
|
|
"""
|
|
Get client/provider by ID with all related information
|
|
"""
|
|
# 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 = ClientProviderService(db)
|
|
client = service.get_client_provider(client_id)
|
|
if not client:
|
|
raise HTTPException(
|
|
status_code=404, detail=f"Client/Provider with ID '{client_id}' not found"
|
|
)
|
|
return client
|
|
|
|
|
|
@router.put("/{client_id}", response_model=ClientProviderResponseDTO)
|
|
async def update_client_provider(
|
|
client_id: str,
|
|
client_data: ClientProviderUpdateDTO,
|
|
db: Session = Depends(get_core_db),
|
|
current_user: dict = Depends(get_current_user),
|
|
):
|
|
"""
|
|
Update client/provider information
|
|
"""
|
|
# 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 = ClientProviderService(db)
|
|
client = service.update_client_provider(client_id, client_data)
|
|
if not client:
|
|
raise HTTPException(
|
|
status_code=404, detail=f"Client/Provider with ID '{client_id}' not found"
|
|
)
|
|
return client
|
|
|
|
|
|
@router.delete("/{client_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
async def delete_client_provider(
|
|
client_id: str,
|
|
db: Session = Depends(get_core_db),
|
|
current_user: dict = Depends(get_current_user),
|
|
):
|
|
"""
|
|
Delete client/provider from the system
|
|
|
|
Note: This will completely remove the client/provider and all related data.
|
|
"""
|
|
# 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 = ClientProviderService(db)
|
|
if not service.delete_client_provider(client_id):
|
|
raise HTTPException(
|
|
status_code=404, detail=f"Client/Provider with ID '{client_id}' not found"
|
|
)
|
|
|
|
|
|
@router.patch("/{client_id}/toggle-status", response_model=ClientProviderResponseDTO)
|
|
async def toggle_client_provider_status(
|
|
client_id: str,
|
|
db: Session = Depends(get_core_db),
|
|
current_user: dict = Depends(get_current_user),
|
|
):
|
|
"""
|
|
Toggle client/provider enabled/disabled status
|
|
"""
|
|
# 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 = ClientProviderService(db)
|
|
client = service.toggle_status(client_id)
|
|
if not client:
|
|
raise HTTPException(
|
|
status_code=404, detail=f"Client/Provider with ID '{client_id}' not found"
|
|
)
|
|
return client
|
|
|
|
|
|
# Endpoints específicos para información detallada
|
|
@router.get("/{client_id}/address", response_model=dict)
|
|
async def get_client_provider_address(
|
|
client_id: str,
|
|
db: Session = Depends(get_core_db),
|
|
current_user: dict = Depends(get_current_user),
|
|
):
|
|
"""
|
|
Get only address information for a client/provider
|
|
"""
|
|
# 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 = ClientProviderService(db)
|
|
client = service.get_client_provider(client_id)
|
|
if not client:
|
|
raise HTTPException(
|
|
status_code=404, detail=f"Client/Provider with ID '{client_id}' not found"
|
|
)
|
|
|
|
return {"client_id": client.client_id, "address": client.address}
|
|
|
|
|
|
@router.get("/{client_id}/programs", response_model=dict)
|
|
async def get_client_provider_programs(
|
|
client_id: str,
|
|
db: Session = Depends(get_core_db),
|
|
current_user: dict = Depends(get_current_user),
|
|
):
|
|
"""
|
|
Get only programs information for a client/provider
|
|
"""
|
|
# 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 = ClientProviderService(db)
|
|
client = service.get_client_provider(client_id)
|
|
if not client:
|
|
raise HTTPException(
|
|
status_code=404, detail=f"Client/Provider with ID '{client_id}' not found"
|
|
)
|
|
|
|
return {"client_id": client.client_id, "programs": client.programs}
|
|
|
|
|
|
@router.get("/{client_id}/basic", response_model=ClientProviderBasicDTO)
|
|
async def get_client_provider_basic_info(
|
|
client_id: str,
|
|
db: Session = Depends(get_core_db),
|
|
current_user: dict = Depends(get_current_user),
|
|
):
|
|
"""
|
|
Get basic information for a client/provider (without address and programs)
|
|
"""
|
|
# 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 = ClientProviderService(db)
|
|
client = service.get_client_provider(client_id)
|
|
if not client:
|
|
raise HTTPException(
|
|
status_code=404, detail=f"Client/Provider with ID '{client_id}' not found"
|
|
)
|
|
|
|
return ClientProviderBasicDTO(
|
|
client_id=client.client_id,
|
|
name=client.name,
|
|
short_name=client.short_name,
|
|
rfc=client.rfc,
|
|
client_or_provider=client.client_or_provider,
|
|
enabled_disabled=client.enabled_disabled,
|
|
)
|