from typing import Any, Dict, List, Optional, Tuple, Union from sqlalchemy.orm import Session from app.models.inventory import Inventory from app.models.product import Product from app.models.warehouse import Warehouse from app.schemas.inventory import InventoryCreate, InventoryUpdate def get(db: Session, inventory_id: int) -> Optional[Inventory]: return db.query(Inventory).filter(Inventory.id == inventory_id).first() def get_by_product_and_warehouse( db: Session, *, product_id: int, warehouse_id: int ) -> Optional[Inventory]: return ( db.query(Inventory) .filter(Inventory.product_id == product_id, Inventory.warehouse_id == warehouse_id) .first() ) def get_multi( db: Session, *, skip: int = 0, limit: int = 100, warehouse_id: Optional[int] = None ) -> List[Inventory]: query = db.query(Inventory) if warehouse_id: query = query.filter(Inventory.warehouse_id == warehouse_id) return query.offset(skip).limit(limit).all() def create(db: Session, *, obj_in: InventoryCreate) -> Inventory: # Check if inventory already exists for this product/warehouse combination existing = get_by_product_and_warehouse( db, product_id=obj_in.product_id, warehouse_id=obj_in.warehouse_id ) if existing: # Update quantity instead of creating new existing.quantity += obj_in.quantity db.add(existing) db.commit() db.refresh(existing) return existing # Create new inventory record db_obj = Inventory( product_id=obj_in.product_id, warehouse_id=obj_in.warehouse_id, quantity=obj_in.quantity, location=obj_in.location, min_stock_level=obj_in.min_stock_level, max_stock_level=obj_in.max_stock_level, ) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def update( db: Session, *, db_obj: Inventory, obj_in: Union[InventoryUpdate, Dict[str, Any]] ) -> Inventory: if isinstance(obj_in, dict): update_data = obj_in else: update_data = obj_in.dict(exclude_unset=True) for 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, *, inventory_id: int) -> Inventory: obj = db.query(Inventory).get(inventory_id) db.delete(obj) db.commit() return obj def get_with_details(db: Session, inventory_id: int) -> Optional[Tuple[Inventory, str, str]]: """Get inventory with product and warehouse names""" result = ( db.query(Inventory, Product.name, Warehouse.name) .join(Product) .join(Warehouse) .filter(Inventory.id == inventory_id) .first() ) if result: inventory, product_name, warehouse_name = result return inventory, product_name, warehouse_name return None def get_multi_with_details( db: Session, *, skip: int = 0, limit: int = 100, warehouse_id: Optional[int] = None ) -> List[Tuple[Inventory, str, str]]: """Get multiple inventory items with product and warehouse names""" query = ( db.query(Inventory, Product.name, Warehouse.name) .join(Product) .join(Warehouse) ) if warehouse_id: query = query.filter(Inventory.warehouse_id == warehouse_id) return query.offset(skip).limit(limit).all() def update_quantity( db: Session, *, inventory_id: int, quantity_change: int ) -> Optional[Inventory]: """Update inventory quantity by adding the quantity_change (can be negative)""" inventory = get(db, inventory_id) if not inventory: return None inventory.quantity += quantity_change if inventory.quantity < 0: inventory.quantity = 0 db.add(inventory) db.commit() db.refresh(inventory) return inventory def get_low_stock_items(db: Session, *, limit: int = 100) -> List[Inventory]: """Get inventory items where quantity is below min_stock_level""" return ( db.query(Inventory) .filter(Inventory.quantity < Inventory.min_stock_level) .limit(limit) .all() )