from typing import Any, Dict, List, Optional, Union from fastapi.encoders import jsonable_encoder from sqlalchemy.orm import Session from app.models.product import Product from app.schemas.product import ProductCreate, ProductUpdate from app.utils.uuid import generate_uuid def get(db: Session, product_id: str) -> Optional[Product]: """ Get a product by ID. """ return db.query(Product).filter(Product.id == product_id).first() def get_by_sku(db: Session, sku: str) -> Optional[Product]: """ Get a product by SKU. """ return db.query(Product).filter(Product.sku == sku).first() def get_by_barcode(db: Session, barcode: str) -> Optional[Product]: """ Get a product by barcode. """ return db.query(Product).filter(Product.barcode == barcode).first() def get_multi( db: Session, *, skip: int = 0, limit: int = 100, filters: Optional[Dict[str, Any]] = None ) -> List[Product]: """ Get multiple products with optional filtering. """ query = db.query(Product) # Apply filters if provided if filters: if "name" in filters and filters["name"]: query = query.filter(Product.name.ilike(f"%{filters['name']}%")) if "category_id" in filters and filters["category_id"]: query = query.filter(Product.category_id == filters["category_id"]) if "supplier_id" in filters and filters["supplier_id"]: query = query.filter(Product.supplier_id == filters["supplier_id"]) if "is_active" in filters and filters["is_active"] is not None: query = query.filter(Product.is_active == filters["is_active"]) if "low_stock" in filters and filters["low_stock"] is True: query = query.filter(Product.current_stock <= Product.min_stock_level) return query.offset(skip).limit(limit).all() def create(db: Session, *, obj_in: ProductCreate) -> Product: """ Create a new product. """ obj_in_data = jsonable_encoder(obj_in) db_obj = Product(**obj_in_data, id=generate_uuid()) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def update( db: Session, *, db_obj: Product, obj_in: Union[ProductUpdate, Dict[str, Any]] ) -> Product: """ Update a product. """ obj_data = jsonable_encoder(db_obj) if isinstance(obj_in, dict): update_data = obj_in else: update_data = obj_in.model_dump(exclude_unset=True) for field in obj_data: if field in update_data: setattr(db_obj, field, update_data[field]) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def remove(db: Session, *, product_id: str) -> Optional[Product]: """ Delete a product. """ obj = db.query(Product).get(product_id) if obj: db.delete(obj) db.commit() return obj def update_stock(db: Session, *, db_obj: Product, quantity: int) -> Product: """ Update product stock quantity. """ db_obj.current_stock += quantity db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj