""" 库存管理路由模块 提供库存信息的查询功能,包括: - 库存列表查询(支持分页、仓库筛选、产品筛选、低库存筛选) - 显示产品库存数量、锁定数量、可用数量等信息 路由前缀: /api/inventory """ from fastapi import APIRouter, Depends, Query, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from typing import Optional, List from database.database import get_db_session from services.auth_service import get_current_active_user from models.database import User, Product, Warehouse, Inventory from .schemas import InventoryResponse, InventoryCreate, InventoryUpdate router = APIRouter(prefix="/inventory", tags=["库存管理"]) @router.get("", response_model=List[InventoryResponse]) async def list_inventory( warehouse_id: Optional[int] = None, product_id: Optional[int] = None, low_stock: bool = False, skip: int = Query(0, ge=0), limit: int = Query(20, ge=1, le=100), db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): query = ( select(Inventory, Product, Warehouse) .join(Product, Inventory.product_id == Product.id) .join(Warehouse, Inventory.warehouse_id == Warehouse.id) .where(Product.is_active == True) .where(Product.item_type == "material") .where(Warehouse.is_active == True) ) if warehouse_id: query = query.where(Inventory.warehouse_id == warehouse_id) if product_id: query = query.where(Inventory.product_id == product_id) if low_stock: query = query.where(Inventory.quantity <= Product.min_stock) query = query.offset(skip).limit(limit) result = await db_session.execute(query) inventory_list = [] for inv, product, warehouse in result.all(): inventory_list.append(InventoryResponse( id=inv.id, product_id=inv.product_id, product_name=product.name, product_sku=product.sku, warehouse_id=inv.warehouse_id, warehouse_name=warehouse.name, quantity=inv.quantity, locked_quantity=inv.locked_quantity, available_quantity=inv.available_quantity )) return inventory_list @router.post("", response_model=InventoryResponse, status_code=201) async def create_inventory( payload: InventoryCreate, db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): if payload.quantity < 0 or payload.locked_quantity < 0: raise HTTPException(status_code=400, detail="库存数量不能为负数") if payload.locked_quantity > payload.quantity: raise HTTPException(status_code=400, detail="锁定数量不能大于库存数量") product_result = await db_session.execute( select(Product).where(Product.id == payload.product_id, Product.is_active == True) ) product = product_result.scalar_one_or_none() if not product: raise HTTPException(status_code=404, detail="物料不存在") if product.item_type != "material": raise HTTPException(status_code=400, detail="库存仅支持物料") warehouse_result = await db_session.execute( select(Warehouse).where(Warehouse.id == payload.warehouse_id, Warehouse.is_active == True) ) warehouse = warehouse_result.scalar_one_or_none() if not warehouse: raise HTTPException(status_code=404, detail="仓库不存在") exists_result = await db_session.execute( select(Inventory).where( Inventory.product_id == payload.product_id, Inventory.warehouse_id == payload.warehouse_id ) ) if exists_result.scalar_one_or_none(): raise HTTPException(status_code=400, detail="该仓库已存在该物料库存记录") inventory = Inventory( product_id=payload.product_id, warehouse_id=payload.warehouse_id, quantity=payload.quantity, locked_quantity=payload.locked_quantity, batch_number=payload.batch_number, location=payload.location ) db_session.add(inventory) await db_session.commit() await db_session.refresh(inventory) return InventoryResponse( id=inventory.id, product_id=product.id, product_name=product.name, product_sku=product.sku, warehouse_id=warehouse.id, warehouse_name=warehouse.name, quantity=inventory.quantity, locked_quantity=inventory.locked_quantity, available_quantity=inventory.available_quantity ) @router.put("/{inventory_id}", response_model=InventoryResponse) async def update_inventory( inventory_id: int, payload: InventoryUpdate, db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): result = await db_session.execute( select(Inventory, Product, Warehouse) .join(Product, Inventory.product_id == Product.id) .join(Warehouse, Inventory.warehouse_id == Warehouse.id) .where(Inventory.id == inventory_id) .where(Product.item_type == "material") ) row = result.first() if not row: raise HTTPException(status_code=404, detail="库存记录不存在") inventory, product, warehouse = row if payload.quantity is not None: if payload.quantity < 0: raise HTTPException(status_code=400, detail="库存数量不能为负数") inventory.quantity = payload.quantity if payload.locked_quantity is not None: if payload.locked_quantity < 0: raise HTTPException(status_code=400, detail="锁定数量不能为负数") inventory.locked_quantity = payload.locked_quantity if inventory.locked_quantity > inventory.quantity: raise HTTPException(status_code=400, detail="锁定数量不能大于库存数量") if payload.batch_number is not None: inventory.batch_number = payload.batch_number if payload.location is not None: inventory.location = payload.location await db_session.commit() await db_session.refresh(inventory) return InventoryResponse( id=inventory.id, product_id=product.id, product_name=product.name, product_sku=product.sku, warehouse_id=warehouse.id, warehouse_name=warehouse.name, quantity=inventory.quantity, locked_quantity=inventory.locked_quantity, available_quantity=inventory.available_quantity ) @router.delete("/{inventory_id}") async def delete_inventory( inventory_id: int, db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): result = await db_session.execute(select(Inventory).where(Inventory.id == inventory_id)) inventory = result.scalar_one_or_none() if not inventory: raise HTTPException(status_code=404, detail="库存记录不存在") await db_session.delete(inventory) await db_session.commit() return {"message": "库存记录已删除"}