from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from app.db.session import get_db from app.models.product import Product from app.models.stock_movement import StockMovement, MovementType from app.schemas.product import Product as ProductSchema, ProductCreate, ProductUpdate router = APIRouter(prefix="/products", tags=["products"]) @router.get("/", response_model=List[ProductSchema]) def get_products( skip: int = Query(0, ge=0), limit: int = Query(100, ge=1, le=1000), category_id: Optional[int] = None, supplier_id: Optional[int] = None, low_stock: bool = False, db: Session = Depends(get_db), ): query = db.query(Product) if category_id: query = query.filter(Product.category_id == category_id) if supplier_id: query = query.filter(Product.supplier_id == supplier_id) if low_stock: query = query.filter(Product.quantity_in_stock <= Product.minimum_stock_level) return query.offset(skip).limit(limit).all() @router.get("/{product_id}", response_model=ProductSchema) def get_product(product_id: int, db: Session = Depends(get_db)): product = db.query(Product).filter(Product.id == product_id).first() if not product: raise HTTPException(status_code=404, detail="Product not found") return product @router.post("/", response_model=ProductSchema) def create_product(product: ProductCreate, db: Session = Depends(get_db)): existing_product = db.query(Product).filter(Product.sku == product.sku).first() if existing_product: raise HTTPException(status_code=400, detail="SKU already exists") db_product = Product(**product.dict()) db.add(db_product) db.commit() db.refresh(db_product) return db_product @router.put("/{product_id}", response_model=ProductSchema) def update_product( product_id: int, product: ProductUpdate, db: Session = Depends(get_db) ): db_product = db.query(Product).filter(Product.id == product_id).first() if not db_product: raise HTTPException(status_code=404, detail="Product not found") update_data = product.dict(exclude_unset=True) if "sku" in update_data: existing_product = ( db.query(Product) .filter(Product.sku == update_data["sku"], Product.id != product_id) .first() ) if existing_product: raise HTTPException(status_code=400, detail="SKU already exists") for field, value in update_data.items(): setattr(db_product, field, value) db.commit() db.refresh(db_product) return db_product @router.delete("/{product_id}") def delete_product(product_id: int, db: Session = Depends(get_db)): db_product = db.query(Product).filter(Product.id == product_id).first() if not db_product: raise HTTPException(status_code=404, detail="Product not found") db.delete(db_product) db.commit() return {"message": "Product deleted successfully"} @router.post("/{product_id}/stock/add") def add_stock( product_id: int, quantity: int, reference: Optional[str] = None, notes: Optional[str] = None, db: Session = Depends(get_db), ): db_product = db.query(Product).filter(Product.id == product_id).first() if not db_product: raise HTTPException(status_code=404, detail="Product not found") db_product.quantity_in_stock += quantity movement = StockMovement( product_id=product_id, movement_type=MovementType.IN, quantity=quantity, reference=reference, notes=notes, ) db.add(movement) db.commit() db.refresh(db_product) return { "message": f"Added {quantity} units to stock", "new_stock_level": db_product.quantity_in_stock, } @router.post("/{product_id}/stock/remove") def remove_stock( product_id: int, quantity: int, reference: Optional[str] = None, notes: Optional[str] = None, db: Session = Depends(get_db), ): db_product = db.query(Product).filter(Product.id == product_id).first() if not db_product: raise HTTPException(status_code=404, detail="Product not found") if db_product.quantity_in_stock < quantity: raise HTTPException(status_code=400, detail="Insufficient stock") db_product.quantity_in_stock -= quantity movement = StockMovement( product_id=product_id, movement_type=MovementType.OUT, quantity=quantity, reference=reference, notes=notes, ) db.add(movement) db.commit() db.refresh(db_product) return { "message": f"Removed {quantity} units from stock", "new_stock_level": db_product.quantity_in_stock, }