shop product refactoring
This commit is contained in:
@@ -16,8 +16,8 @@ from sqlalchemy.orm import Session
|
||||
from app.api.deps import get_current_user, get_user_shop
|
||||
from app.core.database import get_db
|
||||
from app.services.shop_service import shop_service
|
||||
from models.schemas.shop import (ShopCreate, ShopListResponse, ShopProductCreate,
|
||||
ShopProductResponse, ShopResponse)
|
||||
from models.schemas.shop import (ShopCreate, ShopListResponse,ShopResponse)
|
||||
from models.schemas.product import (ProductCreate,ProductResponse)
|
||||
from models.database.user import User
|
||||
|
||||
router = APIRouter()
|
||||
@@ -72,10 +72,10 @@ def get_shop(
|
||||
return ShopResponse.model_validate(shop)
|
||||
|
||||
|
||||
@router.post("/shop/{shop_code}/products", response_model=ShopProductResponse)
|
||||
@router.post("/shop/{shop_code}/products", response_model=ProductResponse)
|
||||
def add_product_to_shop(
|
||||
shop_code: str,
|
||||
shop_product: ShopProductCreate,
|
||||
product: ProductCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
@@ -84,18 +84,18 @@ def add_product_to_shop(
|
||||
shop = get_user_shop(shop_code, current_user, db)
|
||||
|
||||
# Add product to shop
|
||||
new_shop_product = shop_service.add_product_to_shop(
|
||||
db=db, shop=shop, shop_product=shop_product
|
||||
new_product = shop_service.add_product_to_shop(
|
||||
db=db, shop=shop, product=product
|
||||
)
|
||||
|
||||
# Return with product details
|
||||
response = ShopProductResponse.model_validate(new_shop_product)
|
||||
response.product = new_shop_product.product
|
||||
response = ProductResponse.model_validate(new_product)
|
||||
response.marketplace_product = new_product.marketplace_product
|
||||
return response
|
||||
|
||||
|
||||
@router.get("/shop/{shop_code}/products")
|
||||
def get_shop_products(
|
||||
def get_products(
|
||||
shop_code: str,
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(100, ge=1, le=1000),
|
||||
@@ -111,7 +111,7 @@ def get_shop_products(
|
||||
)
|
||||
|
||||
# Get shop products
|
||||
shop_products, total = shop_service.get_shop_products(
|
||||
vendor_products, total = shop_service.get_products(
|
||||
db=db,
|
||||
shop=shop,
|
||||
current_user=current_user,
|
||||
@@ -123,9 +123,9 @@ def get_shop_products(
|
||||
|
||||
# Format response
|
||||
products = []
|
||||
for sp in shop_products:
|
||||
product_response = ShopProductResponse.model_validate(sp)
|
||||
product_response.product = sp.product
|
||||
for vp in vendor_products:
|
||||
product_response = ProductResponse.model_validate(vp)
|
||||
product_response.marketplace_product = vp.marketplace_product
|
||||
products.append(product_response)
|
||||
|
||||
return {
|
||||
|
||||
@@ -55,12 +55,15 @@ from .shop import (
|
||||
ShopNotVerifiedException,
|
||||
UnauthorizedShopAccessException,
|
||||
InvalidShopDataException,
|
||||
ShopProductAlreadyExistsException,
|
||||
ShopProductNotFoundException,
|
||||
MaxShopsReachedException,
|
||||
ShopValidationException,
|
||||
)
|
||||
|
||||
from .product import (
|
||||
ProductNotFoundException,
|
||||
ProductAlreadyExistsException,
|
||||
)
|
||||
|
||||
from .marketplace_import_job import (
|
||||
MarketplaceImportException,
|
||||
ImportJobNotFoundException,
|
||||
@@ -131,11 +134,13 @@ __all__ = [
|
||||
"ShopNotVerifiedException",
|
||||
"UnauthorizedShopAccessException",
|
||||
"InvalidShopDataException",
|
||||
"ShopProductAlreadyExistsException",
|
||||
"ShopProductNotFoundException",
|
||||
"MaxShopsReachedException",
|
||||
"ShopValidationException",
|
||||
|
||||
# Product exceptions
|
||||
"ProductAlreadyExistsException",
|
||||
"ProductNotFoundException",
|
||||
|
||||
# Marketplace import exceptions
|
||||
"MarketplaceImportException",
|
||||
"ImportJobNotFoundException",
|
||||
|
||||
34
app/exceptions/product.py
Normal file
34
app/exceptions/product.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# app/exceptions/shop.py
|
||||
"""
|
||||
Shop management specific exceptions.
|
||||
"""
|
||||
|
||||
from .base import (
|
||||
ResourceNotFoundException,
|
||||
ConflictException
|
||||
)
|
||||
|
||||
class ProductAlreadyExistsException(ConflictException):
|
||||
"""Raised when trying to add a product that already exists in shop."""
|
||||
|
||||
def __init__(self, shop_code: str, marketplace_product_id: str):
|
||||
super().__init__(
|
||||
message=f"MarketplaceProduct '{marketplace_product_id}' already exists in shop '{shop_code}'",
|
||||
error_code="PRODUCT_ALREADY_EXISTS",
|
||||
details={
|
||||
"shop_code": shop_code,
|
||||
"marketplace_product_id": marketplace_product_id,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class ProductNotFoundException(ResourceNotFoundException):
|
||||
"""Raised when a shop product relationship is not found."""
|
||||
|
||||
def __init__(self, shop_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}'",
|
||||
error_code="PRODUCT_NOT_FOUND",
|
||||
)
|
||||
@@ -95,32 +95,6 @@ class InvalidShopDataException(ValidationException):
|
||||
self.error_code = "INVALID_SHOP_DATA"
|
||||
|
||||
|
||||
class ShopProductAlreadyExistsException(ConflictException):
|
||||
"""Raised when trying to add a product that already exists in shop."""
|
||||
|
||||
def __init__(self, shop_code: str, marketplace_product_id: str):
|
||||
super().__init__(
|
||||
message=f"MarketplaceProduct '{marketplace_product_id}' already exists in shop '{shop_code}'",
|
||||
error_code="SHOP_PRODUCT_ALREADY_EXISTS",
|
||||
details={
|
||||
"shop_code": shop_code,
|
||||
"marketplace_product_id": marketplace_product_id,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class ShopProductNotFoundException(ResourceNotFoundException):
|
||||
"""Raised when a shop product relationship is not found."""
|
||||
|
||||
def __init__(self, shop_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}'",
|
||||
error_code="SHOP_PRODUCT_NOT_FOUND",
|
||||
)
|
||||
|
||||
|
||||
class MaxShopsReachedException(BusinessLogicException):
|
||||
"""Raised when user tries to create more shops than allowed."""
|
||||
|
||||
|
||||
@@ -21,13 +21,15 @@ from app.exceptions import (
|
||||
UnauthorizedShopAccessException,
|
||||
InvalidShopDataException,
|
||||
MarketplaceProductNotFoundException,
|
||||
ShopProductAlreadyExistsException,
|
||||
ProductAlreadyExistsException,
|
||||
MaxShopsReachedException,
|
||||
ValidationException,
|
||||
)
|
||||
from models.schemas.shop import ShopCreate, ShopProductCreate
|
||||
from models.schemas.shop import ShopCreate
|
||||
from models.schemas.product import ProductCreate
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
from models.database.shop import Shop, ShopProduct
|
||||
from models.database.shop import Shop
|
||||
from models.database.product import Product
|
||||
from models.database.user import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -184,49 +186,49 @@ class ShopService:
|
||||
raise ValidationException("Failed to retrieve shop")
|
||||
|
||||
def add_product_to_shop(
|
||||
self, db: Session, shop: Shop, shop_product: ShopProductCreate
|
||||
) -> ShopProduct:
|
||||
self, db: Session, shop: Shop, product: ProductCreate
|
||||
) -> Product:
|
||||
"""
|
||||
Add existing product to shop catalog with shop-specific settings.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
shop: Shop to add product to
|
||||
shop_product: Shop product data
|
||||
product: Shop product data
|
||||
|
||||
Returns:
|
||||
Created ShopProduct object
|
||||
|
||||
Raises:
|
||||
MarketplaceProductNotFoundException: If product not found
|
||||
ShopProductAlreadyExistsException: If product already in shop
|
||||
ProductAlreadyExistsException: If product already in shop
|
||||
"""
|
||||
try:
|
||||
# Check if product exists
|
||||
marketplace_product = self._get_product_by_id_or_raise(db, shop_product.marketplace_product_id)
|
||||
marketplace_product = self._get_product_by_id_or_raise(db, product.marketplace_product_id)
|
||||
|
||||
# Check if product already in shop
|
||||
if self._product_in_shop(db, shop.id, marketplace_product.id):
|
||||
raise ShopProductAlreadyExistsException(shop.shop_code, shop_product.marketplace_product_id)
|
||||
raise ProductAlreadyExistsException(shop.shop_code, product.marketplace_product_id)
|
||||
|
||||
# Create shop-product association
|
||||
new_shop_product = ShopProduct(
|
||||
new_product = Product(
|
||||
shop_id=shop.id,
|
||||
marketplace_product_id=marketplace_product.id,
|
||||
**shop_product.model_dump(exclude={"marketplace_product_id"}),
|
||||
**product.model_dump(exclude={"marketplace_product_id"}),
|
||||
)
|
||||
|
||||
db.add(new_shop_product)
|
||||
db.add(new_product)
|
||||
db.commit()
|
||||
db.refresh(new_shop_product)
|
||||
db.refresh(new_product)
|
||||
|
||||
# Load the product relationship
|
||||
db.refresh(new_shop_product)
|
||||
db.refresh(new_product)
|
||||
|
||||
logger.info(f"MarketplaceProduct {shop_product.marketplace_product_id} added to shop {shop.shop_code}")
|
||||
return new_shop_product
|
||||
logger.info(f"MarketplaceProduct {product.marketplace_product_id} added to shop {shop.shop_code}")
|
||||
return new_product
|
||||
|
||||
except (MarketplaceProductNotFoundException, ShopProductAlreadyExistsException):
|
||||
except (MarketplaceProductNotFoundException, ProductAlreadyExistsException):
|
||||
db.rollback()
|
||||
raise # Re-raise custom exceptions
|
||||
except Exception as e:
|
||||
@@ -234,7 +236,7 @@ class ShopService:
|
||||
logger.error(f"Error adding product to shop: {str(e)}")
|
||||
raise ValidationException("Failed to add product to shop")
|
||||
|
||||
def get_shop_products(
|
||||
def get_products(
|
||||
self,
|
||||
db: Session,
|
||||
shop: Shop,
|
||||
@@ -243,7 +245,7 @@ class ShopService:
|
||||
limit: int = 100,
|
||||
active_only: bool = True,
|
||||
featured_only: bool = False,
|
||||
) -> Tuple[List[ShopProduct], int]:
|
||||
) -> Tuple[List[Product], int]:
|
||||
"""
|
||||
Get products in shop catalog with filtering.
|
||||
|
||||
@@ -257,7 +259,7 @@ class ShopService:
|
||||
featured_only: Filter for featured products only
|
||||
|
||||
Returns:
|
||||
Tuple of (shop_products_list, total_count)
|
||||
Tuple of (products_list, total_count)
|
||||
|
||||
Raises:
|
||||
UnauthorizedShopAccessException: If shop access denied
|
||||
@@ -268,17 +270,17 @@ class ShopService:
|
||||
raise UnauthorizedShopAccessException(shop.shop_code, current_user.id)
|
||||
|
||||
# Query shop products
|
||||
query = db.query(ShopProduct).filter(ShopProduct.shop_id == shop.id)
|
||||
query = db.query(Product).filter(Product.shop_id == shop.id)
|
||||
|
||||
if active_only:
|
||||
query = query.filter(ShopProduct.is_active == True)
|
||||
query = query.filter(Product.is_active == True)
|
||||
if featured_only:
|
||||
query = query.filter(ShopProduct.is_featured == True)
|
||||
query = query.filter(Product.is_featured == True)
|
||||
|
||||
total = query.count()
|
||||
shop_products = query.offset(skip).limit(limit).all()
|
||||
products = query.offset(skip).limit(limit).all()
|
||||
|
||||
return shop_products, total
|
||||
return products, total
|
||||
|
||||
except UnauthorizedShopAccessException:
|
||||
raise # Re-raise custom exceptions
|
||||
@@ -332,10 +334,10 @@ class ShopService:
|
||||
def _product_in_shop(self, db: Session, shop_id: int, marketplace_product_id: int) -> bool:
|
||||
"""Check if product is already in shop."""
|
||||
return (
|
||||
db.query(ShopProduct)
|
||||
db.query(Product)
|
||||
.filter(
|
||||
ShopProduct.shop_id == shop_id,
|
||||
ShopProduct.marketplace_product_id == marketplace_product_id
|
||||
Product.shop_id == shop_id,
|
||||
Product.marketplace_product_id == marketplace_product_id
|
||||
)
|
||||
.first() is not None
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user