# 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()