shop product refactoring

This commit is contained in:
2025-10-04 23:38:53 +02:00
parent 4d2866af5e
commit 0114b6c46e
68 changed files with 2234 additions and 2236 deletions

View File

@@ -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",

View File

@@ -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,
)

View File

@@ -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.

View File

@@ -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}: "

View File

@@ -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,
},
)

View File

@@ -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",
)

View File

@@ -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
View 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"