# app/exceptions/team.py """ Team management specific exceptions. """ from typing import Any, Dict, Optional from .base import ( ResourceNotFoundException, ConflictException, ValidationException, AuthorizationException, BusinessLogicException ) class TeamMemberNotFoundException(ResourceNotFoundException): """Raised when a team member is not found.""" def __init__(self, user_id: int, vendor_id: Optional[int] = None): details = {"user_id": user_id} if vendor_id: details["vendor_id"] = vendor_id message = f"Team member with user ID '{user_id}' not found in vendor {vendor_id}" else: message = f"Team member with user ID '{user_id}' not found" super().__init__( resource_type="TeamMember", identifier=str(user_id), message=message, error_code="TEAM_MEMBER_NOT_FOUND", details=details, ) class TeamMemberAlreadyExistsException(ConflictException): """Raised when trying to add a user who is already a team member.""" def __init__(self, user_id: int, vendor_id: int): super().__init__( message=f"User {user_id} is already a team member of vendor {vendor_id}", error_code="TEAM_MEMBER_ALREADY_EXISTS", details={ "user_id": user_id, "vendor_id": vendor_id, }, ) class TeamInvitationNotFoundException(ResourceNotFoundException): """Raised when a team invitation is not found.""" def __init__(self, invitation_token: str): super().__init__( resource_type="TeamInvitation", identifier=invitation_token, message=f"Team invitation with token '{invitation_token}' not found or expired", error_code="TEAM_INVITATION_NOT_FOUND", ) class TeamInvitationExpiredException(BusinessLogicException): """Raised when trying to accept an expired invitation.""" def __init__(self, invitation_token: str): super().__init__( message=f"Team invitation has expired", error_code="TEAM_INVITATION_EXPIRED", details={"invitation_token": invitation_token}, ) class TeamInvitationAlreadyAcceptedException(ConflictException): """Raised when trying to accept an already accepted invitation.""" def __init__(self, invitation_token: str): super().__init__( message="Team invitation has already been accepted", error_code="TEAM_INVITATION_ALREADY_ACCEPTED", details={"invitation_token": invitation_token}, ) class UnauthorizedTeamActionException(AuthorizationException): """Raised when user tries to perform team action without permission.""" def __init__(self, action: str, user_id: Optional[int] = None, required_permission: Optional[str] = None): details = {"action": action} if user_id: details["user_id"] = user_id if required_permission: details["required_permission"] = required_permission super().__init__( message=f"Unauthorized to perform action: {action}", error_code="UNAUTHORIZED_TEAM_ACTION", details=details, ) class CannotRemoveOwnerException(BusinessLogicException): """Raised when trying to remove the vendor owner from team.""" def __init__(self, user_id: int, vendor_id: int): super().__init__( message="Cannot remove vendor owner from team", error_code="CANNOT_REMOVE_OWNER", details={ "user_id": user_id, "vendor_id": vendor_id, }, ) class CannotModifyOwnRoleException(BusinessLogicException): """Raised when user tries to modify their own role.""" def __init__(self, user_id: int): super().__init__( message="Cannot modify your own role", error_code="CANNOT_MODIFY_OWN_ROLE", details={"user_id": user_id}, ) class RoleNotFoundException(ResourceNotFoundException): """Raised when a role is not found.""" def __init__(self, role_id: int, vendor_id: Optional[int] = None): details = {"role_id": role_id} if vendor_id: details["vendor_id"] = vendor_id message = f"Role with ID '{role_id}' not found in vendor {vendor_id}" else: message = f"Role with ID '{role_id}' not found" super().__init__( resource_type="Role", identifier=str(role_id), message=message, error_code="ROLE_NOT_FOUND", details=details, ) class InvalidRoleException(ValidationException): """Raised when role data is invalid.""" def __init__( self, message: str = "Invalid role data", field: Optional[str] = None, details: Optional[Dict[str, Any]] = None, ): super().__init__( message=message, field=field, details=details, ) self.error_code = "INVALID_ROLE_DATA" class InsufficientPermissionsException(AuthorizationException): """Raised when user lacks required permissions for an action.""" def __init__( self, required_permission: str, user_id: Optional[int] = None, action: Optional[str] = None, ): details = {"required_permission": required_permission} if user_id: details["user_id"] = user_id if action: details["action"] = action message = f"Insufficient permissions. Required: {required_permission}" super().__init__( message=message, error_code="INSUFFICIENT_PERMISSIONS", details=details, ) class MaxTeamMembersReachedException(BusinessLogicException): """Raised when vendor has reached maximum team members limit.""" def __init__(self, max_members: int, vendor_id: int): super().__init__( message=f"Maximum number of team members reached ({max_members})", error_code="MAX_TEAM_MEMBERS_REACHED", details={ "max_members": max_members, "vendor_id": vendor_id, }, ) class TeamValidationException(ValidationException): """Raised when team operation validation fails.""" def __init__( self, message: str = "Team operation validation failed", field: Optional[str] = None, validation_errors: Optional[Dict[str, str]] = None, ): details = {} if validation_errors: details["validation_errors"] = validation_errors super().__init__( message=message, field=field, details=details, ) self.error_code = "TEAM_VALIDATION_FAILED" class InvalidInvitationDataException(ValidationException): """Raised when team invitation data is invalid.""" def __init__( self, message: str = "Invalid invitation data", field: Optional[str] = None, details: Optional[Dict[str, Any]] = None, ): super().__init__( message=message, field=field, details=details, ) self.error_code = "INVALID_INVITATION_DATA" # ============================================================================ # NEW: Add InvalidInvitationTokenException # ============================================================================ class InvalidInvitationTokenException(ValidationException): """Raised when invitation token is invalid, expired, or already used. This is a general exception for any invitation token validation failure. Use this when checking invitation tokens during the acceptance flow. """ def __init__( self, message: str = "Invalid or expired invitation token", invitation_token: Optional[str] = None ): details = {} if invitation_token: details["invitation_token"] = invitation_token super().__init__( message=message, field="invitation_token", details=details, ) self.error_code = "INVALID_INVITATION_TOKEN"