major refactoring adding vendor and customer features
This commit is contained in:
@@ -38,12 +38,12 @@ from .marketplace_product import (
|
||||
MarketplaceProductCSVImportException,
|
||||
)
|
||||
|
||||
from .stock import (
|
||||
StockNotFoundException,
|
||||
InsufficientStockException,
|
||||
InvalidStockOperationException,
|
||||
StockValidationException,
|
||||
NegativeStockException,
|
||||
from .inventory import (
|
||||
InventoryNotFoundException,
|
||||
InsufficientInventoryException,
|
||||
InvalidInventoryOperationException,
|
||||
InventoryValidationException,
|
||||
NegativeInventoryException,
|
||||
InvalidQuantityException,
|
||||
LocationNotFoundException
|
||||
)
|
||||
@@ -59,9 +59,50 @@ from .vendor import (
|
||||
VendorValidationException,
|
||||
)
|
||||
|
||||
from .customer import (
|
||||
CustomerNotFoundException,
|
||||
CustomerAlreadyExistsException,
|
||||
DuplicateCustomerEmailException,
|
||||
CustomerNotActiveException,
|
||||
InvalidCustomerCredentialsException,
|
||||
CustomerValidationException,
|
||||
CustomerAuthorizationException,
|
||||
)
|
||||
|
||||
from .team import (
|
||||
TeamMemberNotFoundException,
|
||||
TeamMemberAlreadyExistsException,
|
||||
TeamInvitationNotFoundException,
|
||||
TeamInvitationExpiredException,
|
||||
TeamInvitationAlreadyAcceptedException,
|
||||
UnauthorizedTeamActionException,
|
||||
CannotRemoveOwnerException,
|
||||
CannotModifyOwnRoleException,
|
||||
RoleNotFoundException,
|
||||
InvalidRoleException,
|
||||
InsufficientPermissionsException,
|
||||
MaxTeamMembersReachedException,
|
||||
TeamValidationException,
|
||||
InvalidInvitationDataException,
|
||||
)
|
||||
|
||||
from .product import (
|
||||
ProductNotFoundException,
|
||||
ProductAlreadyExistsException,
|
||||
ProductNotInCatalogException,
|
||||
ProductNotActiveException,
|
||||
InvalidProductDataException,
|
||||
ProductValidationException,
|
||||
CannotDeleteProductWithInventoryException,
|
||||
CannotDeleteProductWithOrdersException,
|
||||
)
|
||||
|
||||
from .order import (
|
||||
OrderNotFoundException,
|
||||
OrderAlreadyExistsException,
|
||||
OrderValidationException,
|
||||
InvalidOrderStatusException,
|
||||
OrderCannotBeCancelledException,
|
||||
)
|
||||
|
||||
from .marketplace_import_job import (
|
||||
@@ -100,6 +141,7 @@ __all__ = [
|
||||
"BusinessLogicException",
|
||||
"ExternalServiceException",
|
||||
"RateLimitException",
|
||||
"ServiceUnavailableException",
|
||||
|
||||
# Auth exceptions
|
||||
"InvalidCredentialsException",
|
||||
@@ -110,20 +152,37 @@ __all__ = [
|
||||
"AdminRequiredException",
|
||||
"UserAlreadyExistsException",
|
||||
|
||||
# MarketplaceProduct exceptions
|
||||
"MarketplaceProductNotFoundException",
|
||||
"MarketplaceProductAlreadyExistsException",
|
||||
"InvalidMarketplaceProductDataException",
|
||||
"MarketplaceProductValidationException",
|
||||
"InvalidGTINException",
|
||||
"MarketplaceProductCSVImportException",
|
||||
# Customer exceptions
|
||||
"CustomerNotFoundException",
|
||||
"CustomerAlreadyExistsException",
|
||||
"DuplicateCustomerEmailException",
|
||||
"CustomerNotActiveException",
|
||||
"InvalidCustomerCredentialsException",
|
||||
"CustomerValidationException",
|
||||
"CustomerAuthorizationException",
|
||||
|
||||
# Stock exceptions
|
||||
"StockNotFoundException",
|
||||
"InsufficientStockException",
|
||||
"InvalidStockOperationException",
|
||||
"StockValidationException",
|
||||
"NegativeStockException",
|
||||
# Team exceptions
|
||||
"TeamMemberNotFoundException",
|
||||
"TeamMemberAlreadyExistsException",
|
||||
"TeamInvitationNotFoundException",
|
||||
"TeamInvitationExpiredException",
|
||||
"TeamInvitationAlreadyAcceptedException",
|
||||
"UnauthorizedTeamActionException",
|
||||
"CannotRemoveOwnerException",
|
||||
"CannotModifyOwnRoleException",
|
||||
"RoleNotFoundException",
|
||||
"InvalidRoleException",
|
||||
"InsufficientPermissionsException",
|
||||
"MaxTeamMembersReachedException",
|
||||
"TeamValidationException",
|
||||
"InvalidInvitationDataException",
|
||||
|
||||
# Inventory exceptions
|
||||
"InventoryNotFoundException",
|
||||
"InsufficientInventoryException",
|
||||
"InvalidInventoryOperationException",
|
||||
"InventoryValidationException",
|
||||
"NegativeInventoryException",
|
||||
"InvalidQuantityException",
|
||||
"LocationNotFoundException",
|
||||
|
||||
@@ -138,8 +197,29 @@ __all__ = [
|
||||
"VendorValidationException",
|
||||
|
||||
# Product exceptions
|
||||
"ProductAlreadyExistsException",
|
||||
"ProductNotFoundException",
|
||||
"ProductAlreadyExistsException",
|
||||
"ProductNotInCatalogException",
|
||||
"ProductNotActiveException",
|
||||
"InvalidProductDataException",
|
||||
"ProductValidationException",
|
||||
"CannotDeleteProductWithInventoryException",
|
||||
"CannotDeleteProductWithOrdersException",
|
||||
|
||||
# Order exceptions
|
||||
"OrderNotFoundException",
|
||||
"OrderAlreadyExistsException",
|
||||
"OrderValidationException",
|
||||
"InvalidOrderStatusException",
|
||||
"OrderCannotBeCancelledException",
|
||||
|
||||
# MarketplaceProduct exceptions
|
||||
"MarketplaceProductNotFoundException",
|
||||
"MarketplaceProductAlreadyExistsException",
|
||||
"InvalidMarketplaceProductDataException",
|
||||
"MarketplaceProductValidationException",
|
||||
"InvalidGTINException",
|
||||
"MarketplaceProductCSVImportException",
|
||||
|
||||
# Marketplace import exceptions
|
||||
"MarketplaceImportException",
|
||||
|
||||
102
app/exceptions/customer.py
Normal file
102
app/exceptions/customer.py
Normal file
@@ -0,0 +1,102 @@
|
||||
# app/exceptions/customer.py
|
||||
"""
|
||||
Customer management specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
from .base import (
|
||||
ResourceNotFoundException,
|
||||
ConflictException,
|
||||
ValidationException,
|
||||
AuthenticationException,
|
||||
BusinessLogicException
|
||||
)
|
||||
|
||||
|
||||
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):
|
||||
"""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 vendor."""
|
||||
|
||||
def __init__(self, email: str, vendor_code: str):
|
||||
super().__init__(
|
||||
message=f"Email '{email}' is already registered for this vendor",
|
||||
error_code="DUPLICATE_CUSTOMER_EMAIL",
|
||||
details={
|
||||
"email": email,
|
||||
"vendor_code": vendor_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: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None
|
||||
):
|
||||
super().__init__(
|
||||
message=message,
|
||||
field=field,
|
||||
details=details
|
||||
)
|
||||
self.error_code = "CUSTOMER_VALIDATION_FAILED"
|
||||
|
||||
|
||||
class CustomerAuthorizationException(BusinessLogicException):
|
||||
"""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
|
||||
}
|
||||
)
|
||||
@@ -1,31 +1,31 @@
|
||||
# app/exceptions/stock.py
|
||||
# app/exceptions/inventory.py
|
||||
"""
|
||||
Stock management specific exceptions.
|
||||
Inventory management specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
from .base import ResourceNotFoundException, ValidationException, BusinessLogicException
|
||||
|
||||
|
||||
class StockNotFoundException(ResourceNotFoundException):
|
||||
"""Raised when stock record is not found."""
|
||||
class InventoryNotFoundException(ResourceNotFoundException):
|
||||
"""Raised when inventory record is not found."""
|
||||
|
||||
def __init__(self, identifier: str, identifier_type: str = "ID"):
|
||||
if identifier_type.lower() == "gtin":
|
||||
message = f"No stock found for GTIN '{identifier}'"
|
||||
message = f"No inventory found for GTIN '{identifier}'"
|
||||
else:
|
||||
message = f"Stock record with {identifier_type} '{identifier}' not found"
|
||||
message = f"Inventory record with {identifier_type} '{identifier}' not found"
|
||||
|
||||
super().__init__(
|
||||
resource_type="Stock",
|
||||
resource_type="Inventory",
|
||||
identifier=identifier,
|
||||
message=message,
|
||||
error_code="STOCK_NOT_FOUND",
|
||||
error_code="INVENTORY_NOT_FOUND",
|
||||
)
|
||||
|
||||
|
||||
class InsufficientStockException(BusinessLogicException):
|
||||
"""Raised when trying to remove more stock than available."""
|
||||
class InsufficientInventoryException(BusinessLogicException):
|
||||
"""Raised when trying to remove more inventory than available."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -34,11 +34,11 @@ class InsufficientStockException(BusinessLogicException):
|
||||
requested: int,
|
||||
available: int,
|
||||
):
|
||||
message = f"Insufficient stock for GTIN '{gtin}' at '{location}'. Requested: {requested}, Available: {available}"
|
||||
message = f"Insufficient inventory for GTIN '{gtin}' at '{location}'. Requested: {requested}, Available: {available}"
|
||||
|
||||
super().__init__(
|
||||
message=message,
|
||||
error_code="INSUFFICIENT_STOCK",
|
||||
error_code="INSUFFICIENT_INVENTORY",
|
||||
details={
|
||||
"gtin": gtin,
|
||||
"location": location,
|
||||
@@ -48,8 +48,8 @@ class InsufficientStockException(BusinessLogicException):
|
||||
)
|
||||
|
||||
|
||||
class InvalidStockOperationException(ValidationException):
|
||||
"""Raised when stock operation is invalid."""
|
||||
class InvalidInventoryOperationException(ValidationException):
|
||||
"""Raised when inventory operation is invalid."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -67,15 +67,15 @@ class InvalidStockOperationException(ValidationException):
|
||||
message=message,
|
||||
details=details,
|
||||
)
|
||||
self.error_code = "INVALID_STOCK_OPERATION"
|
||||
self.error_code = "INVALID_INVENTORY_OPERATION"
|
||||
|
||||
|
||||
class StockValidationException(ValidationException):
|
||||
"""Raised when stock data validation fails."""
|
||||
class InventoryValidationException(ValidationException):
|
||||
"""Raised when inventory data validation fails."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Stock validation failed",
|
||||
message: str = "Inventory validation failed",
|
||||
field: Optional[str] = None,
|
||||
validation_errors: Optional[Dict[str, str]] = None,
|
||||
):
|
||||
@@ -88,18 +88,18 @@ class StockValidationException(ValidationException):
|
||||
field=field,
|
||||
details=details,
|
||||
)
|
||||
self.error_code = "STOCK_VALIDATION_FAILED"
|
||||
self.error_code = "INVENTORY_VALIDATION_FAILED"
|
||||
|
||||
|
||||
class NegativeStockException(BusinessLogicException):
|
||||
"""Raised when stock quantity would become negative."""
|
||||
class NegativeInventoryException(BusinessLogicException):
|
||||
"""Raised when inventory quantity would become negative."""
|
||||
|
||||
def __init__(self, gtin: str, location: str, resulting_quantity: int):
|
||||
message = f"Stock operation would result in negative quantity ({resulting_quantity}) for GTIN '{gtin}' at '{location}'"
|
||||
message = f"Inventory operation would result in negative quantity ({resulting_quantity}) for GTIN '{gtin}' at '{location}'"
|
||||
|
||||
super().__init__(
|
||||
message=message,
|
||||
error_code="NEGATIVE_STOCK_NOT_ALLOWED",
|
||||
error_code="NEGATIVE_INVENTORY_NOT_ALLOWED",
|
||||
details={
|
||||
"gtin": gtin,
|
||||
"location": location,
|
||||
@@ -121,12 +121,12 @@ class InvalidQuantityException(ValidationException):
|
||||
|
||||
|
||||
class LocationNotFoundException(ResourceNotFoundException):
|
||||
"""Raised when stock location is not found."""
|
||||
"""Raised when inventory location is not found."""
|
||||
|
||||
def __init__(self, location: str):
|
||||
super().__init__(
|
||||
resource_type="Location",
|
||||
identifier=location,
|
||||
message=f"Stock location '{location}' not found",
|
||||
message=f"Inventory location '{location}' not found",
|
||||
error_code="LOCATION_NOT_FOUND",
|
||||
)
|
||||
73
app/exceptions/order.py
Normal file
73
app/exceptions/order.py
Normal file
@@ -0,0 +1,73 @@
|
||||
# app/exceptions/order.py
|
||||
"""
|
||||
Order management specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from .base import (
|
||||
ResourceNotFoundException,
|
||||
ValidationException,
|
||||
BusinessLogicException
|
||||
)
|
||||
|
||||
|
||||
class OrderNotFoundException(ResourceNotFoundException):
|
||||
"""Raised when an order is not found."""
|
||||
|
||||
def __init__(self, order_identifier: str):
|
||||
super().__init__(
|
||||
resource_type="Order",
|
||||
identifier=order_identifier,
|
||||
message=f"Order '{order_identifier}' not found",
|
||||
error_code="ORDER_NOT_FOUND"
|
||||
)
|
||||
|
||||
|
||||
class OrderAlreadyExistsException(ValidationException):
|
||||
"""Raised when trying to create a duplicate order."""
|
||||
|
||||
def __init__(self, order_number: str):
|
||||
super().__init__(
|
||||
message=f"Order with number '{order_number}' already exists",
|
||||
error_code="ORDER_ALREADY_EXISTS",
|
||||
details={"order_number": order_number}
|
||||
)
|
||||
|
||||
|
||||
class OrderValidationException(ValidationException):
|
||||
"""Raised when order data validation fails."""
|
||||
|
||||
def __init__(self, message: str, details: Optional[dict] = None):
|
||||
super().__init__(
|
||||
message=message,
|
||||
error_code="ORDER_VALIDATION_FAILED",
|
||||
details=details
|
||||
)
|
||||
|
||||
|
||||
class InvalidOrderStatusException(BusinessLogicException):
|
||||
"""Raised when trying to set an invalid order status."""
|
||||
|
||||
def __init__(self, current_status: str, new_status: str):
|
||||
super().__init__(
|
||||
message=f"Cannot change order status from '{current_status}' to '{new_status}'",
|
||||
error_code="INVALID_ORDER_STATUS_CHANGE",
|
||||
details={
|
||||
"current_status": current_status,
|
||||
"new_status": new_status
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class OrderCannotBeCancelledException(BusinessLogicException):
|
||||
"""Raised when order cannot be cancelled."""
|
||||
|
||||
def __init__(self, order_number: str, reason: str):
|
||||
super().__init__(
|
||||
message=f"Order '{order_number}' cannot be cancelled: {reason}",
|
||||
error_code="ORDER_CANNOT_BE_CANCELLED",
|
||||
details={
|
||||
"order_number": order_number,
|
||||
"reason": reason
|
||||
}
|
||||
)
|
||||
@@ -1,34 +1,142 @@
|
||||
# app/exceptions/vendor.py
|
||||
# app/exceptions/product.py
|
||||
"""
|
||||
Vendor management specific exceptions.
|
||||
Product (vendor catalog) specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from .base import (
|
||||
ResourceNotFoundException,
|
||||
ConflictException
|
||||
ConflictException,
|
||||
ValidationException,
|
||||
BusinessLogicException
|
||||
)
|
||||
|
||||
class ProductAlreadyExistsException(ConflictException):
|
||||
"""Raised when trying to add a product that already exists in vendor."""
|
||||
|
||||
def __init__(self, vendor_code: str, marketplace_product_id: str):
|
||||
class ProductNotFoundException(ResourceNotFoundException):
|
||||
"""Raised when a product is not found in vendor catalog."""
|
||||
|
||||
def __init__(self, product_id: int, vendor_id: Optional[int] = None):
|
||||
details = {"product_id": product_id}
|
||||
if vendor_id:
|
||||
details["vendor_id"] = vendor_id
|
||||
message = f"Product with ID '{product_id}' not found in vendor {vendor_id} catalog"
|
||||
else:
|
||||
message = f"Product with ID '{product_id}' not found"
|
||||
|
||||
super().__init__(
|
||||
message=f"MarketplaceProduct '{marketplace_product_id}' already exists in vendor '{vendor_code}'",
|
||||
resource_type="Product",
|
||||
identifier=str(product_id),
|
||||
message=message,
|
||||
error_code="PRODUCT_NOT_FOUND",
|
||||
details=details,
|
||||
)
|
||||
|
||||
|
||||
class ProductAlreadyExistsException(ConflictException):
|
||||
"""Raised when trying to add a marketplace product that's already in vendor catalog."""
|
||||
|
||||
def __init__(self, vendor_id: int, marketplace_product_id: int):
|
||||
super().__init__(
|
||||
message=f"Marketplace product {marketplace_product_id} already exists in vendor {vendor_id} catalog",
|
||||
error_code="PRODUCT_ALREADY_EXISTS",
|
||||
details={
|
||||
"vendor_code": vendor_code,
|
||||
"vendor_id": vendor_id,
|
||||
"marketplace_product_id": marketplace_product_id,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class ProductNotFoundException(ResourceNotFoundException):
|
||||
"""Raised when a vendor product relationship is not found."""
|
||||
class ProductNotInCatalogException(ResourceNotFoundException):
|
||||
"""Raised when trying to access a product that's not in vendor's catalog."""
|
||||
|
||||
def __init__(self, vendor_code: str, marketplace_product_id: str):
|
||||
def __init__(self, product_id: int, vendor_id: int):
|
||||
super().__init__(
|
||||
resource_type="Product",
|
||||
identifier=f"{vendor_code}/{marketplace_product_id}",
|
||||
message=f"MarketplaceProduct '{marketplace_product_id}' not found in vendor '{vendor_code}'",
|
||||
error_code="PRODUCT_NOT_FOUND",
|
||||
identifier=str(product_id),
|
||||
message=f"Product {product_id} is not in vendor {vendor_id} catalog",
|
||||
error_code="PRODUCT_NOT_IN_CATALOG",
|
||||
details={
|
||||
"product_id": product_id,
|
||||
"vendor_id": vendor_id,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class ProductNotActiveException(BusinessLogicException):
|
||||
"""Raised when trying to perform operations on inactive product."""
|
||||
|
||||
def __init__(self, product_id: int, vendor_id: int):
|
||||
super().__init__(
|
||||
message=f"Product {product_id} in vendor {vendor_id} catalog is not active",
|
||||
error_code="PRODUCT_NOT_ACTIVE",
|
||||
details={
|
||||
"product_id": product_id,
|
||||
"vendor_id": vendor_id,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class InvalidProductDataException(ValidationException):
|
||||
"""Raised when product data is invalid."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Invalid product data",
|
||||
field: Optional[str] = None,
|
||||
details: Optional[dict] = None,
|
||||
):
|
||||
super().__init__(
|
||||
message=message,
|
||||
field=field,
|
||||
details=details,
|
||||
)
|
||||
self.error_code = "INVALID_PRODUCT_DATA"
|
||||
|
||||
|
||||
class ProductValidationException(ValidationException):
|
||||
"""Raised when product validation fails."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Product validation failed",
|
||||
field: Optional[str] = None,
|
||||
validation_errors: Optional[dict] = None,
|
||||
):
|
||||
details = {}
|
||||
if validation_errors:
|
||||
details["validation_errors"] = validation_errors
|
||||
|
||||
super().__init__(
|
||||
message=message,
|
||||
field=field,
|
||||
details=details,
|
||||
)
|
||||
self.error_code = "PRODUCT_VALIDATION_FAILED"
|
||||
|
||||
|
||||
class CannotDeleteProductWithInventoryException(BusinessLogicException):
|
||||
"""Raised when trying to delete a product that has inventory."""
|
||||
|
||||
def __init__(self, product_id: int, inventory_count: int):
|
||||
super().__init__(
|
||||
message=f"Cannot delete product {product_id} - it has {inventory_count} inventory entries",
|
||||
error_code="CANNOT_DELETE_PRODUCT_WITH_INVENTORY",
|
||||
details={
|
||||
"product_id": product_id,
|
||||
"inventory_count": inventory_count,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class CannotDeleteProductWithOrdersException(BusinessLogicException):
|
||||
"""Raised when trying to delete a product that has been ordered."""
|
||||
|
||||
def __init__(self, product_id: int, order_count: int):
|
||||
super().__init__(
|
||||
message=f"Cannot delete product {product_id} - it has {order_count} associated orders",
|
||||
error_code="CANNOT_DELETE_PRODUCT_WITH_ORDERS",
|
||||
details={
|
||||
"product_id": product_id,
|
||||
"order_count": order_count,
|
||||
},
|
||||
)
|
||||
|
||||
236
app/exceptions/team.py
Normal file
236
app/exceptions/team.py
Normal file
@@ -0,0 +1,236 @@
|
||||
# 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"
|
||||
Reference in New Issue
Block a user