Files
orion/app/services/product_service.py
Samir Boulahtit 5778bdaa69 fix: add explicit type annotations to service methods
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>
2025-12-13 14:51:10 +01:00

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