Add Optional type annotations to nullable parameters in service methods to fix architecture validation warnings. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
248 lines
7.3 KiB
Python
248 lines
7.3 KiB
Python
# 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 UTC, datetime
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.exceptions import (
|
|
ProductAlreadyExistsException,
|
|
ProductNotFoundException,
|
|
ValidationException,
|
|
)
|
|
from models.database.marketplace_product import MarketplaceProduct
|
|
from models.database.product import Product
|
|
from models.schema.product import ProductCreate, ProductUpdate
|
|
|
|
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
|
|
marketplace_product = (
|
|
db.query(MarketplaceProduct)
|
|
.filter(MarketplaceProduct.id == product_data.marketplace_product_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("Product already exists in catalog")
|
|
|
|
# Create product
|
|
product = Product(
|
|
vendor_id=vendor_id,
|
|
marketplace_product_id=product_data.marketplace_product_id,
|
|
vendor_sku=product_data.vendor_sku,
|
|
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.flush()
|
|
db.refresh(product)
|
|
|
|
logger.info(f"Added product {product.id} to vendor {vendor_id} catalog")
|
|
return product
|
|
|
|
except (ProductAlreadyExistsException, ValidationException):
|
|
raise
|
|
except Exception as e:
|
|
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(UTC)
|
|
db.flush()
|
|
db.refresh(product)
|
|
|
|
logger.info(f"Updated product {product_id} in vendor {vendor_id} catalog")
|
|
return product
|
|
|
|
except ProductNotFoundException:
|
|
raise
|
|
except Exception as e:
|
|
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)
|
|
|
|
logger.info(f"Deleted product {product_id} from vendor {vendor_id} catalog")
|
|
return True
|
|
|
|
except ProductNotFoundException:
|
|
raise
|
|
except Exception as e:
|
|
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: bool | None = None,
|
|
is_featured: bool | None = 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()
|