from typing import Any, List, Optional from datetime import datetime from fastapi import APIRouter, Depends, HTTPException, Body from sqlalchemy.orm import Session from app import crud, models, schemas from app.api import deps router = APIRouter() @router.get("/", response_model=List[schemas.Inventory]) def read_inventories( db: Session = Depends(deps.get_db), skip: int = 0, limit: int = 100, location: Optional[str] = None, current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Retrieve inventory items with optional filtering by location. """ if location: inventories = crud.inventory.get_by_location(db, location=location, skip=skip, limit=limit) else: inventories = crud.inventory.get_multi(db, skip=skip, limit=limit) return inventories @router.get("/product/{product_id}", response_model=schemas.Inventory) def read_inventory_by_product( *, db: Session = Depends(deps.get_db), product_id: int, current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Get inventory by product ID. """ inventory = crud.inventory.get_by_product_id(db, product_id=product_id) if not inventory: raise HTTPException(status_code=404, detail="Inventory not found for this product") return inventory @router.get("/low-stock", response_model=schemas.InventorySummaryList) def read_low_stock( db: Session = Depends(deps.get_db), skip: int = 0, limit: int = 100, current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Get products with inventory below minimum stock level. """ low_stock_items = crud.inventory.get_low_stock(db, skip=skip, limit=limit) return {"inventories": low_stock_items} @router.get("/summary", response_model=schemas.InventorySummaryList) def read_inventory_summary( db: Session = Depends(deps.get_db), skip: int = 0, limit: int = 100, current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Get inventory summary for all products. """ summary = crud.inventory.get_inventory_summary(db, skip=skip, limit=limit) return {"inventories": summary} @router.put("/adjust/{product_id}", response_model=schemas.Inventory) def adjust_inventory( *, db: Session = Depends(deps.get_db), product_id: int, quantity_change: int = Body(..., description="Amount to add or subtract from inventory"), location: Optional[str] = Body(None, description="Location of the inventory"), notes: Optional[str] = Body(None, description="Notes about the adjustment"), current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Adjust inventory quantity for a product. Positive values increase stock, negative values decrease stock. """ # Verify the product exists product = crud.product.get(db, id=product_id) if not product: raise HTTPException(status_code=404, detail="Product not found") # Update inventory and create transaction record result = crud.inventory.update_stock( db=db, product_id=product_id, quantity_change=quantity_change, user_id=current_user.id, transaction_type="adjustment", notes=notes ) # Update location if provided if location and location != result["inventory"].location: inventory_update = schemas.InventoryUpdate( product_id=product_id, quantity=result["inventory"].quantity, location=location ) inventory = crud.inventory.update(db, db_obj=result["inventory"], obj_in=inventory_update) return inventory return result["inventory"] @router.post("/transactions/", response_model=schemas.InventoryTransaction) def create_inventory_transaction( *, db: Session = Depends(deps.get_db), transaction_in: schemas.InventoryTransactionCreate, current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Create a new inventory transaction and update inventory accordingly. """ # Verify the product exists product = crud.product.get(db, id=transaction_in.product_id) if not product: raise HTTPException(status_code=404, detail="Product not found") # Validate transaction type valid_types = ["purchase", "sale", "adjustment", "return", "transfer"] if transaction_in.transaction_type not in valid_types: raise HTTPException( status_code=400, detail=f"Invalid transaction type. Must be one of: {', '.join(valid_types)}" ) # Update inventory and create transaction record result = crud.inventory.update_stock( db=db, product_id=transaction_in.product_id, quantity_change=transaction_in.quantity, user_id=current_user.id, transaction_type=transaction_in.transaction_type, reference=transaction_in.reference, unit_price=transaction_in.unit_price, notes=transaction_in.notes ) return result["transaction"] @router.get("/transactions/", response_model=List[schemas.InventoryTransaction]) def read_inventory_transactions( db: Session = Depends(deps.get_db), skip: int = 0, limit: int = 100, product_id: Optional[int] = None, transaction_type: Optional[str] = None, start_date: Optional[datetime] = None, end_date: Optional[datetime] = None, current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Retrieve inventory transactions with optional filtering. """ if product_id: transactions = crud.inventory_transaction.get_by_product_id( db, product_id=product_id, skip=skip, limit=limit ) elif transaction_type: transactions = crud.inventory_transaction.get_by_type( db, transaction_type=transaction_type, skip=skip, limit=limit ) elif start_date and end_date: transactions = crud.inventory_transaction.get_by_date_range( db, start_date=start_date, end_date=end_date, skip=skip, limit=limit ) else: transactions = crud.inventory_transaction.get_multi(db, skip=skip, limit=limit) return transactions @router.get("/transactions/{transaction_id}", response_model=schemas.InventoryTransaction) def read_inventory_transaction( *, db: Session = Depends(deps.get_db), transaction_id: int, current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Get inventory transaction by ID. """ transaction = crud.inventory_transaction.get(db, id=transaction_id) if not transaction: raise HTTPException(status_code=404, detail="Transaction not found") return transaction