major refactoring adding vendor and customer features
This commit is contained in:
247
app/services/product_service.py
Normal file
247
app/services/product_service.py
Normal file
@@ -0,0 +1,247 @@
|
||||
# app/services/product_service.py
|
||||
"""
|
||||
Product service for vendor catalog management.
|
||||
|
||||
This module provides:
|
||||
- Product catalog CRUD operations
|
||||
- Product publishing from marketplace staging
|
||||
- Product search and filtering
|
||||
"""
|
||||
|
||||
import logging
|
||||
from datetime import datetime, timezone
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.exceptions import (
|
||||
ProductNotFoundException,
|
||||
ProductAlreadyExistsException,
|
||||
ValidationException,
|
||||
)
|
||||
from models.schemas.product import ProductCreate, ProductUpdate
|
||||
from models.database.product import Product
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ProductService:
|
||||
"""Service for vendor catalog product operations."""
|
||||
|
||||
def get_product(self, db: Session, vendor_id: int, product_id: int) -> Product:
|
||||
"""
|
||||
Get a product from vendor catalog.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
vendor_id: Vendor ID
|
||||
product_id: Product ID
|
||||
|
||||
Returns:
|
||||
Product object
|
||||
|
||||
Raises:
|
||||
ProductNotFoundException: If product not found
|
||||
"""
|
||||
try:
|
||||
product = db.query(Product).filter(
|
||||
Product.id == product_id,
|
||||
Product.vendor_id == vendor_id
|
||||
).first()
|
||||
|
||||
if not product:
|
||||
raise ProductNotFoundException(f"Product {product_id} not found")
|
||||
|
||||
return product
|
||||
|
||||
except ProductNotFoundException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting product: {str(e)}")
|
||||
raise ValidationException("Failed to retrieve product")
|
||||
|
||||
def create_product(
|
||||
self, db: Session, vendor_id: int, product_data: ProductCreate
|
||||
) -> Product:
|
||||
"""
|
||||
Add a product from marketplace to vendor catalog.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
vendor_id: Vendor ID
|
||||
product_data: Product creation data
|
||||
|
||||
Returns:
|
||||
Created Product object
|
||||
|
||||
Raises:
|
||||
ProductAlreadyExistsException: If product already in catalog
|
||||
ValidationException: If marketplace product not found
|
||||
"""
|
||||
try:
|
||||
# Verify marketplace product exists and belongs to vendor
|
||||
marketplace_product = db.query(MarketplaceProduct).filter(
|
||||
MarketplaceProduct.id == product_data.marketplace_product_id,
|
||||
MarketplaceProduct.vendor_id == vendor_id
|
||||
).first()
|
||||
|
||||
if not marketplace_product:
|
||||
raise ValidationException(
|
||||
f"Marketplace product {product_data.marketplace_product_id} not found"
|
||||
)
|
||||
|
||||
# Check if already in catalog
|
||||
existing = db.query(Product).filter(
|
||||
Product.vendor_id == vendor_id,
|
||||
Product.marketplace_product_id == product_data.marketplace_product_id
|
||||
).first()
|
||||
|
||||
if existing:
|
||||
raise ProductAlreadyExistsException(
|
||||
f"Product already exists in catalog"
|
||||
)
|
||||
|
||||
# Create product
|
||||
product = Product(
|
||||
vendor_id=vendor_id,
|
||||
marketplace_product_id=product_data.marketplace_product_id,
|
||||
product_id=product_data.product_id,
|
||||
price=product_data.price,
|
||||
sale_price=product_data.sale_price,
|
||||
currency=product_data.currency,
|
||||
availability=product_data.availability,
|
||||
condition=product_data.condition,
|
||||
is_featured=product_data.is_featured,
|
||||
is_active=True,
|
||||
min_quantity=product_data.min_quantity,
|
||||
max_quantity=product_data.max_quantity,
|
||||
)
|
||||
|
||||
db.add(product)
|
||||
db.commit()
|
||||
db.refresh(product)
|
||||
|
||||
logger.info(
|
||||
f"Added product {product.id} to vendor {vendor_id} catalog"
|
||||
)
|
||||
return product
|
||||
|
||||
except (ProductAlreadyExistsException, ValidationException):
|
||||
db.rollback()
|
||||
raise
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error creating product: {str(e)}")
|
||||
raise ValidationException("Failed to create product")
|
||||
|
||||
def update_product(
|
||||
self, db: Session, vendor_id: int, product_id: int, product_update: ProductUpdate
|
||||
) -> Product:
|
||||
"""
|
||||
Update product in vendor catalog.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
vendor_id: Vendor ID
|
||||
product_id: Product ID
|
||||
product_update: Update data
|
||||
|
||||
Returns:
|
||||
Updated Product object
|
||||
"""
|
||||
try:
|
||||
product = self.get_product(db, vendor_id, product_id)
|
||||
|
||||
# Update fields
|
||||
update_data = product_update.model_dump(exclude_unset=True)
|
||||
for key, value in update_data.items():
|
||||
setattr(product, key, value)
|
||||
|
||||
product.updated_at = datetime.now(timezone.utc)
|
||||
db.commit()
|
||||
db.refresh(product)
|
||||
|
||||
logger.info(f"Updated product {product_id} in vendor {vendor_id} catalog")
|
||||
return product
|
||||
|
||||
except ProductNotFoundException:
|
||||
db.rollback()
|
||||
raise
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error updating product: {str(e)}")
|
||||
raise ValidationException("Failed to update product")
|
||||
|
||||
def delete_product(self, db: Session, vendor_id: int, product_id: int) -> bool:
|
||||
"""
|
||||
Remove product from vendor catalog.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
vendor_id: Vendor ID
|
||||
product_id: Product ID
|
||||
|
||||
Returns:
|
||||
True if deleted
|
||||
"""
|
||||
try:
|
||||
product = self.get_product(db, vendor_id, product_id)
|
||||
|
||||
db.delete(product)
|
||||
db.commit()
|
||||
|
||||
logger.info(f"Deleted product {product_id} from vendor {vendor_id} catalog")
|
||||
return True
|
||||
|
||||
except ProductNotFoundException:
|
||||
raise
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error deleting product: {str(e)}")
|
||||
raise ValidationException("Failed to delete product")
|
||||
|
||||
def get_vendor_products(
|
||||
self,
|
||||
db: Session,
|
||||
vendor_id: int,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
is_active: Optional[bool] = None,
|
||||
is_featured: Optional[bool] = None,
|
||||
) -> Tuple[List[Product], int]:
|
||||
"""
|
||||
Get products in vendor catalog with filtering.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
vendor_id: Vendor ID
|
||||
skip: Pagination offset
|
||||
limit: Pagination limit
|
||||
is_active: Filter by active status
|
||||
is_featured: Filter by featured status
|
||||
|
||||
Returns:
|
||||
Tuple of (products, total_count)
|
||||
"""
|
||||
try:
|
||||
query = db.query(Product).filter(Product.vendor_id == vendor_id)
|
||||
|
||||
if is_active is not None:
|
||||
query = query.filter(Product.is_active == is_active)
|
||||
|
||||
if is_featured is not None:
|
||||
query = query.filter(Product.is_featured == is_featured)
|
||||
|
||||
total = query.count()
|
||||
products = query.offset(skip).limit(limit).all()
|
||||
|
||||
return products, total
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting vendor products: {str(e)}")
|
||||
raise ValidationException("Failed to retrieve products")
|
||||
|
||||
|
||||
# Create service instance
|
||||
product_service = ProductService()
|
||||
Reference in New Issue
Block a user