shop product refactoring
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# app/exceptions/__init__.py
|
||||
"""
|
||||
Custom exception classes for the LetzShop API.
|
||||
Custom exception classes for the LetzVendor API.
|
||||
|
||||
This module provides frontend-friendly exceptions with consistent error codes,
|
||||
messages, and HTTP status mappings.
|
||||
@@ -48,15 +48,15 @@ from .stock import (
|
||||
LocationNotFoundException
|
||||
)
|
||||
|
||||
from .shop import (
|
||||
ShopNotFoundException,
|
||||
ShopAlreadyExistsException,
|
||||
ShopNotActiveException,
|
||||
ShopNotVerifiedException,
|
||||
UnauthorizedShopAccessException,
|
||||
InvalidShopDataException,
|
||||
MaxShopsReachedException,
|
||||
ShopValidationException,
|
||||
from .vendor import (
|
||||
VendorNotFoundException,
|
||||
VendorAlreadyExistsException,
|
||||
VendorNotActiveException,
|
||||
VendorNotVerifiedException,
|
||||
UnauthorizedVendorAccessException,
|
||||
InvalidVendorDataException,
|
||||
MaxVendorsReachedException,
|
||||
VendorValidationException,
|
||||
)
|
||||
|
||||
from .product import (
|
||||
@@ -81,7 +81,7 @@ from .marketplace_import_job import (
|
||||
from .admin import (
|
||||
UserNotFoundException,
|
||||
UserStatusChangeException,
|
||||
ShopVerificationException,
|
||||
VendorVerificationException,
|
||||
AdminOperationException,
|
||||
CannotModifyAdminException,
|
||||
CannotModifySelfException,
|
||||
@@ -127,15 +127,15 @@ __all__ = [
|
||||
"InvalidQuantityException",
|
||||
"LocationNotFoundException",
|
||||
|
||||
# Shop exceptions
|
||||
"ShopNotFoundException",
|
||||
"ShopAlreadyExistsException",
|
||||
"ShopNotActiveException",
|
||||
"ShopNotVerifiedException",
|
||||
"UnauthorizedShopAccessException",
|
||||
"InvalidShopDataException",
|
||||
"MaxShopsReachedException",
|
||||
"ShopValidationException",
|
||||
# Vendor exceptions
|
||||
"VendorNotFoundException",
|
||||
"VendorAlreadyExistsException",
|
||||
"VendorNotActiveException",
|
||||
"VendorNotVerifiedException",
|
||||
"UnauthorizedVendorAccessException",
|
||||
"InvalidVendorDataException",
|
||||
"MaxVendorsReachedException",
|
||||
"VendorValidationException",
|
||||
|
||||
# Product exceptions
|
||||
"ProductAlreadyExistsException",
|
||||
@@ -157,7 +157,7 @@ __all__ = [
|
||||
# Admin exceptions
|
||||
"UserNotFoundException",
|
||||
"UserStatusChangeException",
|
||||
"ShopVerificationException",
|
||||
"VendorVerificationException",
|
||||
"AdminOperationException",
|
||||
"CannotModifyAdminException",
|
||||
"CannotModifySelfException",
|
||||
|
||||
@@ -57,17 +57,17 @@ class UserStatusChangeException(BusinessLogicException):
|
||||
)
|
||||
|
||||
|
||||
class ShopVerificationException(BusinessLogicException):
|
||||
"""Raised when shop verification fails."""
|
||||
class VendorVerificationException(BusinessLogicException):
|
||||
"""Raised when vendor verification fails."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
shop_id: int,
|
||||
vendor_id: int,
|
||||
reason: str,
|
||||
current_verification_status: Optional[bool] = None,
|
||||
):
|
||||
details = {
|
||||
"shop_id": shop_id,
|
||||
"vendor_id": vendor_id,
|
||||
"reason": reason,
|
||||
}
|
||||
|
||||
@@ -75,8 +75,8 @@ class ShopVerificationException(BusinessLogicException):
|
||||
details["current_verification_status"] = current_verification_status
|
||||
|
||||
super().__init__(
|
||||
message=f"Shop verification failed for shop {shop_id}: {reason}",
|
||||
error_code="SHOP_VERIFICATION_FAILED",
|
||||
message=f"Vendor verification failed for vendor {vendor_id}: {reason}",
|
||||
error_code="VENDOR_VERIFICATION_FAILED",
|
||||
details=details,
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# app/exceptions/base.py
|
||||
"""
|
||||
Base exception classes for the LetzShop application.
|
||||
Base exception classes for the LetzVendor application.
|
||||
|
||||
This module provides classes and functions for:
|
||||
- Base exception class with consistent error formatting
|
||||
@@ -12,7 +12,7 @@ from typing import Any, Dict, Optional
|
||||
|
||||
|
||||
class LetzShopException(Exception):
|
||||
"""Base exception class for all LetzShop custom exceptions."""
|
||||
"""Base exception class for all LetzVendor custom exceptions."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -206,6 +206,6 @@ class ServiceUnavailableException(LetzShopException):
|
||||
status_code=503,
|
||||
)
|
||||
|
||||
# Note: Domain-specific exceptions like ShopNotFoundException, UserNotFoundException, etc.
|
||||
# are defined in their respective domain modules (shop.py, admin.py, etc.)
|
||||
# Note: Domain-specific exceptions like VendorNotFoundException, UserNotFoundException, etc.
|
||||
# are defined in their respective domain modules (vendor.py, admin.py, etc.)
|
||||
# to keep domain-specific logic separate from base exceptions.
|
||||
|
||||
@@ -26,7 +26,7 @@ def setup_exception_handlers(app):
|
||||
|
||||
@app.exception_handler(LetzShopException)
|
||||
async def custom_exception_handler(request: Request, exc: LetzShopException):
|
||||
"""Handle custom LetzShop exceptions."""
|
||||
"""Handle custom LetzVendor exceptions."""
|
||||
|
||||
logger.error(
|
||||
f"Custom exception in {request.method} {request.url}: "
|
||||
|
||||
@@ -189,12 +189,12 @@ class InvalidMarketplaceException(ValidationException):
|
||||
class ImportJobAlreadyProcessingException(BusinessLogicException):
|
||||
"""Raised when trying to start import while another is already processing."""
|
||||
|
||||
def __init__(self, shop_code: str, existing_job_id: int):
|
||||
def __init__(self, vendor_code: str, existing_job_id: int):
|
||||
super().__init__(
|
||||
message=f"Import already in progress for shop '{shop_code}'",
|
||||
message=f"Import already in progress for vendor '{vendor_code}'",
|
||||
error_code="IMPORT_JOB_ALREADY_PROCESSING",
|
||||
details={
|
||||
"shop_code": shop_code,
|
||||
"vendor_code": vendor_code,
|
||||
"existing_job_id": existing_job_id,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# app/exceptions/shop.py
|
||||
# app/exceptions/vendor.py
|
||||
"""
|
||||
Shop management specific exceptions.
|
||||
Vendor management specific exceptions.
|
||||
"""
|
||||
|
||||
from .base import (
|
||||
@@ -9,26 +9,26 @@ from .base import (
|
||||
)
|
||||
|
||||
class ProductAlreadyExistsException(ConflictException):
|
||||
"""Raised when trying to add a product that already exists in shop."""
|
||||
"""Raised when trying to add a product that already exists in vendor."""
|
||||
|
||||
def __init__(self, shop_code: str, marketplace_product_id: str):
|
||||
def __init__(self, vendor_code: str, marketplace_product_id: str):
|
||||
super().__init__(
|
||||
message=f"MarketplaceProduct '{marketplace_product_id}' already exists in shop '{shop_code}'",
|
||||
message=f"MarketplaceProduct '{marketplace_product_id}' already exists in vendor '{vendor_code}'",
|
||||
error_code="PRODUCT_ALREADY_EXISTS",
|
||||
details={
|
||||
"shop_code": shop_code,
|
||||
"vendor_code": vendor_code,
|
||||
"marketplace_product_id": marketplace_product_id,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class ProductNotFoundException(ResourceNotFoundException):
|
||||
"""Raised when a shop product relationship is not found."""
|
||||
"""Raised when a vendor product relationship is not found."""
|
||||
|
||||
def __init__(self, shop_code: str, marketplace_product_id: str):
|
||||
def __init__(self, vendor_code: str, marketplace_product_id: str):
|
||||
super().__init__(
|
||||
resource_type="ShopProduct",
|
||||
identifier=f"{shop_code}/{marketplace_product_id}",
|
||||
message=f"MarketplaceProduct '{marketplace_product_id}' not found in shop '{shop_code}'",
|
||||
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",
|
||||
)
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
# app/exceptions/shop.py
|
||||
"""
|
||||
Shop management specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
from .base import (
|
||||
ResourceNotFoundException,
|
||||
ConflictException,
|
||||
ValidationException,
|
||||
AuthorizationException,
|
||||
BusinessLogicException
|
||||
)
|
||||
|
||||
|
||||
class ShopNotFoundException(ResourceNotFoundException):
|
||||
"""Raised when a shop is not found."""
|
||||
|
||||
def __init__(self, shop_identifier: str, identifier_type: str = "code"):
|
||||
if identifier_type.lower() == "id":
|
||||
message = f"Shop with ID '{shop_identifier}' not found"
|
||||
else:
|
||||
message = f"Shop with code '{shop_identifier}' not found"
|
||||
|
||||
super().__init__(
|
||||
resource_type="Shop",
|
||||
identifier=shop_identifier,
|
||||
message=message,
|
||||
error_code="SHOP_NOT_FOUND",
|
||||
)
|
||||
|
||||
|
||||
class ShopAlreadyExistsException(ConflictException):
|
||||
"""Raised when trying to create a shop that already exists."""
|
||||
|
||||
def __init__(self, shop_code: str):
|
||||
super().__init__(
|
||||
message=f"Shop with code '{shop_code}' already exists",
|
||||
error_code="SHOP_ALREADY_EXISTS",
|
||||
details={"shop_code": shop_code},
|
||||
)
|
||||
|
||||
|
||||
class ShopNotActiveException(BusinessLogicException):
|
||||
"""Raised when trying to perform operations on inactive shop."""
|
||||
|
||||
def __init__(self, shop_code: str):
|
||||
super().__init__(
|
||||
message=f"Shop '{shop_code}' is not active",
|
||||
error_code="SHOP_NOT_ACTIVE",
|
||||
details={"shop_code": shop_code},
|
||||
)
|
||||
|
||||
|
||||
class ShopNotVerifiedException(BusinessLogicException):
|
||||
"""Raised when trying to perform operations requiring verified shop."""
|
||||
|
||||
def __init__(self, shop_code: str):
|
||||
super().__init__(
|
||||
message=f"Shop '{shop_code}' is not verified",
|
||||
error_code="SHOP_NOT_VERIFIED",
|
||||
details={"shop_code": shop_code},
|
||||
)
|
||||
|
||||
|
||||
class UnauthorizedShopAccessException(AuthorizationException):
|
||||
"""Raised when user tries to access shop they don't own."""
|
||||
|
||||
def __init__(self, shop_code: str, user_id: Optional[int] = None):
|
||||
details = {"shop_code": shop_code}
|
||||
if user_id:
|
||||
details["user_id"] = user_id
|
||||
|
||||
super().__init__(
|
||||
message=f"Unauthorized access to shop '{shop_code}'",
|
||||
error_code="UNAUTHORIZED_SHOP_ACCESS",
|
||||
details=details,
|
||||
)
|
||||
|
||||
|
||||
class InvalidShopDataException(ValidationException):
|
||||
"""Raised when shop data is invalid."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Invalid shop data",
|
||||
field: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
):
|
||||
super().__init__(
|
||||
message=message,
|
||||
field=field,
|
||||
details=details,
|
||||
)
|
||||
self.error_code = "INVALID_SHOP_DATA"
|
||||
|
||||
|
||||
class MaxShopsReachedException(BusinessLogicException):
|
||||
"""Raised when user tries to create more shops than allowed."""
|
||||
|
||||
def __init__(self, max_shops: int, user_id: Optional[int] = None):
|
||||
details = {"max_shops": max_shops}
|
||||
if user_id:
|
||||
details["user_id"] = user_id
|
||||
|
||||
super().__init__(
|
||||
message=f"Maximum number of shops reached ({max_shops})",
|
||||
error_code="MAX_SHOPS_REACHED",
|
||||
details=details,
|
||||
)
|
||||
|
||||
|
||||
class ShopValidationException(ValidationException):
|
||||
"""Raised when shop validation fails."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Shop 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 = "SHOP_VALIDATION_FAILED"
|
||||
131
app/exceptions/vendor.py
Normal file
131
app/exceptions/vendor.py
Normal file
@@ -0,0 +1,131 @@
|
||||
# app/exceptions/vendor.py
|
||||
"""
|
||||
Vendor management specific exceptions.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
from .base import (
|
||||
ResourceNotFoundException,
|
||||
ConflictException,
|
||||
ValidationException,
|
||||
AuthorizationException,
|
||||
BusinessLogicException
|
||||
)
|
||||
|
||||
|
||||
class VendorNotFoundException(ResourceNotFoundException):
|
||||
"""Raised when a vendor is not found."""
|
||||
|
||||
def __init__(self, vendor_identifier: str, identifier_type: str = "code"):
|
||||
if identifier_type.lower() == "id":
|
||||
message = f"Vendor with ID '{vendor_identifier}' not found"
|
||||
else:
|
||||
message = f"Vendor with code '{vendor_identifier}' not found"
|
||||
|
||||
super().__init__(
|
||||
resource_type="Vendor",
|
||||
identifier=vendor_identifier,
|
||||
message=message,
|
||||
error_code="VENDOR_NOT_FOUND",
|
||||
)
|
||||
|
||||
|
||||
class VendorAlreadyExistsException(ConflictException):
|
||||
"""Raised when trying to create a vendor that already exists."""
|
||||
|
||||
def __init__(self, vendor_code: str):
|
||||
super().__init__(
|
||||
message=f"Vendor with code '{vendor_code}' already exists",
|
||||
error_code="VENDOR_ALREADY_EXISTS",
|
||||
details={"vendor_code": vendor_code},
|
||||
)
|
||||
|
||||
|
||||
class VendorNotActiveException(BusinessLogicException):
|
||||
"""Raised when trying to perform operations on inactive vendor."""
|
||||
|
||||
def __init__(self, vendor_code: str):
|
||||
super().__init__(
|
||||
message=f"Vendor '{vendor_code}' is not active",
|
||||
error_code="VENDOR_NOT_ACTIVE",
|
||||
details={"vendor_code": vendor_code},
|
||||
)
|
||||
|
||||
|
||||
class VendorNotVerifiedException(BusinessLogicException):
|
||||
"""Raised when trying to perform operations requiring verified vendor."""
|
||||
|
||||
def __init__(self, vendor_code: str):
|
||||
super().__init__(
|
||||
message=f"Vendor '{vendor_code}' is not verified",
|
||||
error_code="VENDOR_NOT_VERIFIED",
|
||||
details={"vendor_code": vendor_code},
|
||||
)
|
||||
|
||||
|
||||
class UnauthorizedVendorAccessException(AuthorizationException):
|
||||
"""Raised when user tries to access vendor they don't own."""
|
||||
|
||||
def __init__(self, vendor_code: str, user_id: Optional[int] = None):
|
||||
details = {"vendor_code": vendor_code}
|
||||
if user_id:
|
||||
details["user_id"] = user_id
|
||||
|
||||
super().__init__(
|
||||
message=f"Unauthorized access to vendor '{vendor_code}'",
|
||||
error_code="UNAUTHORIZED_VENDOR_ACCESS",
|
||||
details=details,
|
||||
)
|
||||
|
||||
|
||||
class InvalidVendorDataException(ValidationException):
|
||||
"""Raised when vendor data is invalid."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Invalid vendor data",
|
||||
field: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
):
|
||||
super().__init__(
|
||||
message=message,
|
||||
field=field,
|
||||
details=details,
|
||||
)
|
||||
self.error_code = "INVALID_VENDOR_DATA"
|
||||
|
||||
|
||||
class MaxVendorsReachedException(BusinessLogicException):
|
||||
"""Raised when user tries to create more vendors than allowed."""
|
||||
|
||||
def __init__(self, max_vendors: int, user_id: Optional[int] = None):
|
||||
details = {"max_vendors": max_vendors}
|
||||
if user_id:
|
||||
details["user_id"] = user_id
|
||||
|
||||
super().__init__(
|
||||
message=f"Maximum number of vendors reached ({max_vendors})",
|
||||
error_code="MAX_VENDORS_REACHED",
|
||||
details=details,
|
||||
)
|
||||
|
||||
|
||||
class VendorValidationException(ValidationException):
|
||||
"""Raised when vendor validation fails."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Vendor 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 = "VENDOR_VALIDATION_FAILED"
|
||||
Reference in New Issue
Block a user