# app/modules/customers/exceptions.py """ Customers module exceptions. This module provides exception classes for customer operations including: - Customer management (create, update, authentication) - Address management - Password reset """ from typing import Any from app.exceptions.base import ( AuthenticationException, BusinessLogicException, ConflictException, ResourceNotFoundException, ValidationException, ) __all__ = [ # Customer exceptions "CustomerNotFoundException", "CustomerAlreadyExistsException", "DuplicateCustomerEmailException", "CustomerNotActiveException", "InvalidCustomerCredentialsException", "CustomerValidationException", "CustomerAuthorizationException", "InvalidPasswordResetTokenException", "PasswordTooShortException", # Address exceptions "AddressNotFoundException", "AddressLimitExceededException", "InvalidAddressTypeException", ] # ============================================================================= # Customer Exceptions # ============================================================================= class CustomerNotFoundException(ResourceNotFoundException): """Raised when a customer is not found.""" def __init__(self, customer_identifier: str): super().__init__( resource_type="Customer", identifier=customer_identifier, message=f"Customer '{customer_identifier}' not found", error_code="CUSTOMER_NOT_FOUND", ) class CustomerAlreadyExistsException(ConflictException): # noqa: MOD025 """Raised when trying to create a customer that already exists.""" def __init__(self, email: str): super().__init__( message=f"Customer with email '{email}' already exists", error_code="CUSTOMER_ALREADY_EXISTS", details={"email": email}, ) class DuplicateCustomerEmailException(ConflictException): """Raised when email already exists for store.""" def __init__(self, email: str, store_code: str): super().__init__( message=f"Email '{email}' is already registered for this store", error_code="DUPLICATE_CUSTOMER_EMAIL", details={"email": email, "store_code": store_code}, ) class CustomerNotActiveException(BusinessLogicException): """Raised when trying to perform operations on inactive customer.""" def __init__(self, email: str): super().__init__( message=f"Customer account '{email}' is not active", error_code="CUSTOMER_NOT_ACTIVE", details={"email": email}, ) class InvalidCustomerCredentialsException(AuthenticationException): """Raised when customer credentials are invalid.""" def __init__(self): super().__init__( message="Invalid email or password", error_code="INVALID_CUSTOMER_CREDENTIALS", ) class CustomerValidationException(ValidationException): """Raised when customer data validation fails.""" def __init__( self, message: str = "Customer validation failed", field: str | None = None, details: dict[str, Any] | None = None, ): super().__init__(message=message, field=field, details=details) self.error_code = "CUSTOMER_VALIDATION_FAILED" class CustomerAuthorizationException(BusinessLogicException): # noqa: MOD025 """Raised when customer is not authorized for operation.""" def __init__(self, customer_email: str, operation: str): super().__init__( message=f"Customer '{customer_email}' not authorized for: {operation}", error_code="CUSTOMER_NOT_AUTHORIZED", details={"customer_email": customer_email, "operation": operation}, ) class InvalidPasswordResetTokenException(ValidationException): """Raised when password reset token is invalid or expired.""" def __init__(self): super().__init__( message="Invalid or expired password reset link. Please request a new one.", field="reset_token", ) self.error_code = "INVALID_RESET_TOKEN" class PasswordTooShortException(ValidationException): """Raised when password doesn't meet minimum length requirement.""" def __init__(self, min_length: int = 8): super().__init__( message=f"Password must be at least {min_length} characters long", field="password", details={"min_length": min_length}, ) self.error_code = "PASSWORD_TOO_SHORT" # ============================================================================= # Address Exceptions # ============================================================================= class AddressNotFoundException(ResourceNotFoundException): """Raised when a customer address is not found.""" def __init__(self, address_id: str | int): super().__init__( resource_type="Address", identifier=str(address_id), error_code="ADDRESS_NOT_FOUND", ) class AddressLimitExceededException(BusinessLogicException): """Raised when customer exceeds maximum number of addresses.""" def __init__(self, max_addresses: int = 10): super().__init__( message=f"Maximum number of addresses ({max_addresses}) reached", error_code="ADDRESS_LIMIT_EXCEEDED", details={"max_addresses": max_addresses}, ) class InvalidAddressTypeException(BusinessLogicException): # noqa: MOD025 """Raised when an invalid address type is provided.""" def __init__(self, address_type: str): super().__init__( message=f"Invalid address type '{address_type}'. Must be 'shipping' or 'billing'", error_code="INVALID_ADDRESS_TYPE", details={"address_type": address_type}, )