from typing import List, Optional, Dict, Any from sqlalchemy import func from sqlalchemy.orm import Session, joinedload from app.crud.base import CRUDBase from app.models.product import Product from app.models.inventory import Inventory from app.schemas.product import ProductCreate, ProductUpdate class CRUDProduct(CRUDBase[Product, ProductCreate, ProductUpdate]): def get_with_inventory(self, db: Session, id: int) -> Optional[Dict[str, Any]]: """Get product with inventory quantities""" product = db.query(Product).filter(Product.id == id).first() if not product: return None # Get total inventory for this product inventory = db.query( func.sum(Inventory.quantity).label("total_quantity") ).filter( Inventory.product_id == id ).scalar() or 0 result = { **product.__dict__, "total_quantity": inventory, "available_quantity": inventory # For now they're the same since we don't track reserved items } return result def get_multi_with_inventory( self, db: Session, *, skip: int = 0, limit: int = 100 ) -> List[Dict[str, Any]]: """Get multiple products with their inventory quantities""" products = db.query(Product).offset(skip).limit(limit).all() # Get inventory counts for all products in one query inventory_counts = dict( db.query( Inventory.product_id, func.sum(Inventory.quantity).label("total") ).group_by( Inventory.product_id ).all() ) result = [] for product in products: total_quantity = inventory_counts.get(product.id, 0) result.append({ **product.__dict__, "total_quantity": total_quantity, "available_quantity": total_quantity # For now they're the same }) return result def get_by_sku(self, db: Session, *, sku: str) -> Optional[Product]: return db.query(Product).filter(Product.sku == sku).first() def get_by_barcode(self, db: Session, *, barcode: str) -> Optional[Product]: return db.query(Product).filter(Product.barcode == barcode).first() product = CRUDProduct(Product)