137 lines
4.2 KiB
Python
137 lines
4.2 KiB
Python
import logging
|
|
from typing import Any
|
|
|
|
from fastapi import HTTPException, status
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.models.product import Product, ProductStatus
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class InventoryService:
|
|
"""
|
|
Service for managing product inventory.
|
|
"""
|
|
|
|
@staticmethod
|
|
def update_stock(
|
|
db: Session,
|
|
product_id: str,
|
|
quantity_change: int,
|
|
operation: str = "add"
|
|
) -> Product:
|
|
"""
|
|
Update the stock quantity of a product.
|
|
|
|
Args:
|
|
db: Database session
|
|
product_id: Product ID
|
|
quantity_change: Amount to change stock by (positive or negative)
|
|
operation: "add" to increase stock, "subtract" to decrease
|
|
|
|
Returns:
|
|
Updated product object
|
|
|
|
"""
|
|
product = db.query(Product).filter(Product.id == product_id).first()
|
|
if not product:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Product not found"
|
|
)
|
|
|
|
if operation == "add":
|
|
product.stock_quantity += quantity_change
|
|
|
|
# If stock was 0 and now it's not, update status
|
|
if product.stock_quantity > 0 and product.status == ProductStatus.OUT_OF_STOCK:
|
|
product.status = ProductStatus.PUBLISHED
|
|
|
|
elif operation == "subtract":
|
|
if product.stock_quantity < quantity_change:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Cannot subtract {quantity_change} units. Only {product.stock_quantity} in stock."
|
|
)
|
|
|
|
product.stock_quantity -= quantity_change
|
|
|
|
# If stock is now 0, update status
|
|
if product.stock_quantity == 0 and product.status == ProductStatus.PUBLISHED:
|
|
product.status = ProductStatus.OUT_OF_STOCK
|
|
else:
|
|
raise ValueError(f"Invalid operation: {operation}. Must be 'add' or 'subtract'.")
|
|
|
|
db.commit()
|
|
db.refresh(product)
|
|
|
|
logger.info(f"Updated stock for product {product.id}. New quantity: {product.stock_quantity}")
|
|
return product
|
|
|
|
@staticmethod
|
|
def get_low_stock_products(
|
|
db: Session,
|
|
threshold: int = 5,
|
|
category_id: str | None = None,
|
|
seller_id: str | None = None
|
|
) -> list[Product]:
|
|
"""
|
|
Get products with low stock.
|
|
|
|
Args:
|
|
db: Database session
|
|
threshold: Stock threshold to consider "low"
|
|
category_id: Optional category ID to filter by
|
|
seller_id: Optional seller ID to filter by
|
|
|
|
Returns:
|
|
List of products with low stock
|
|
|
|
"""
|
|
query = db.query(Product).filter(Product.stock_quantity <= threshold)
|
|
|
|
if category_id:
|
|
query = query.filter(Product.category_id == category_id)
|
|
|
|
if seller_id:
|
|
query = query.filter(Product.seller_id == seller_id)
|
|
|
|
return query.all()
|
|
|
|
@staticmethod
|
|
def bulk_update_stock(
|
|
db: Session,
|
|
updates: list[dict[str, Any]]
|
|
) -> list[Product]:
|
|
"""
|
|
Update stock for multiple products at once.
|
|
|
|
Args:
|
|
db: Database session
|
|
updates: List of dicts with product_id, quantity_change, and operation
|
|
|
|
Returns:
|
|
List of updated products
|
|
|
|
"""
|
|
updated_products = []
|
|
|
|
for update in updates:
|
|
product_id = update.get('product_id')
|
|
quantity_change = update.get('quantity_change', 0)
|
|
operation = update.get('operation', 'add')
|
|
|
|
if not product_id or not isinstance(quantity_change, int):
|
|
logger.warning(f"Skipping invalid update: {update}")
|
|
continue
|
|
|
|
try:
|
|
product = InventoryService.update_stock(
|
|
db, product_id, quantity_change, operation
|
|
)
|
|
updated_products.append(product)
|
|
except Exception as e:
|
|
logger.error(f"Error updating stock for product {product_id}: {str(e)}")
|
|
|
|
return updated_products
|