from typing import Any, Dict, List 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, status from api.v1.common.catalog_validation_errors import CatalogValidationError from sqlalchemy.orm import Session from .dto import DriverCreateDTO, DriverResponseDTO, DriverUpdateDTO from .models import Driver from .services import DriverService from api.v1.modules.a76.transportation.transporters.services import TransporterService from api.v1.modules.a76.transportation.transporters.models import Transporter from api.v1.modules.a76.layouts_csv.drivers.routes import router as imports_router from sqlalchemy import func import logging logger = logging.getLogger(__name__) router = APIRouter(prefix="/drivers") # CSV import (upload -> scan -> status -> commit) router.include_router(imports_router, prefix="/imports", tags=["a76 / drivers / csv_import"]) @router.get("/", response_model=Dict[str, Any]) async def list_drivers( company_id: int = Query(..., description="Company ID for filtering"), page: int = Query(1, ge=1, description="Page number"), page_size: int = Query(50, ge=1, le=100, description="Page size"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): tenant_id = validate_access_to_resource(db, company_id, current_user) drivers = DriverService.list_drivers(db, str(company_id), tenant_id) total = len(drivers) # Aplicar paginación manualmente skip = (page - 1) * page_size paginated_drivers = drivers[skip : skip + page_size] return { "items": [DriverResponseDTO.model_validate(driver) for driver in paginated_drivers], "total": total, "page": page, "page_size": page_size, } @router.get("/{transporter_key}/{line}", response_model=DriverResponseDTO) async def read_driver( transporter_key: str, line: int, company_id: int = Query(..., description="Company ID for filtering"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): tenant_id = validate_access_to_resource(db, company_id, current_user) driver = DriverService.get_driver_by_key_and_line( db, transporter_key, line, str(company_id), tenant_id ) if not driver: raise HTTPException(status_code=404, detail="Driver not found") return driver @router.post("/", response_model=DriverResponseDTO, status_code=status.HTTP_201_CREATED) async def create_driver( driver_data: DriverCreateDTO, db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): # Validar acceso a la empresa del cuerpo tenant_id = validate_access_to_resource( db, driver_data.company_id, current_user ) tk = (driver_data.transporter_key or "").strip() # Buscar transportista: primero exacto, luego ignorando mayúsculas transporter = TransporterService.get_by_id( db, tk, tenant_id, driver_data.company_id ) if not transporter: transporter = TransporterService.get_by_id_ignore_case( db, tk, tenant_id, driver_data.company_id ) if not transporter: # Diagnóstico: ¿existe ese transportista con otra empresa/tenant? any_with_key = ( db.query(Transporter) .filter(func.upper(Transporter.transporter_key) == tk.upper()) .limit(1) .first() ) logger.warning( "Driver create: transporter not found. key=%r tenant_id=%s company_id=%s; " "any_transporter_with_key=%s (other_tenant=%s other_company=%s)", tk, tenant_id, driver_data.company_id, getattr(any_with_key, "transporter_key", None) if any_with_key else None, getattr(any_with_key, "tenant_id", None) if any_with_key else None, getattr(any_with_key, "company_id", None) if any_with_key else None, ) raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"El transportista {tk} no existe en el catálogo de esta empresa. Crea primero el transportista o elige uno existente.", ) # Usar la clave tal como está en BD (mismo caso) driver_data.transporter_key = transporter.transporter_key try: return DriverService.create_driver(db, driver_data) except CatalogValidationError as e: raise HTTPException( status_code=422, detail={"message": str(e), "errors": e.errors}, ) @router.put("/{transporter_key}/{line}", response_model=DriverResponseDTO) async def update_driver( transporter_key: str, line: int, driver_data: DriverUpdateDTO, company_id: int = Query(..., description="Company ID for filtering"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): tenant_id = validate_access_to_resource(db, company_id, current_user) try: driver = DriverService.update_driver( db, transporter_key, line, str(company_id), tenant_id, driver_data, ) except CatalogValidationError as e: raise HTTPException( status_code=422, detail={"message": str(e), "errors": e.errors}, ) if not driver: raise HTTPException(status_code=404, detail="Driver not found") return driver @router.delete("/{transporter_key}/{line}", status_code=status.HTTP_204_NO_CONTENT) async def delete_driver( transporter_key: str, line: int, company_id: int = Query(..., description="Company ID for filtering"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): tenant_id = validate_access_to_resource(db, company_id, current_user) driver = DriverService.delete_driver( db, transporter_key, line, str(company_id), tenant_id ) if not driver: raise HTTPException(status_code=404, detail="Driver not found")