# app/modules/inventory/routes/api/store.py """ Store inventory management endpoints. Store Context: Uses token_store_id from JWT token (authenticated store API pattern). The get_current_store_api dependency guarantees token_store_id is present. """ import logging from fastapi import APIRouter, Depends, Query from sqlalchemy.orm import Session from app.api.deps import get_current_store_api, require_module_access from app.core.database import get_db from app.modules.enums import FrontendType from app.modules.inventory.schemas import ( InventoryAdjust, InventoryCreate, InventoryListResponse, InventoryMessageResponse, InventoryReserve, InventoryResponse, InventoryTransactionListResponse, InventoryTransactionWithProduct, InventoryUpdate, OrderTransactionHistoryResponse, ProductInventorySummary, ProductTransactionHistoryResponse, ) from app.modules.inventory.services.inventory_service import inventory_service from app.modules.inventory.services.inventory_transaction_service import ( inventory_transaction_service, ) from app.modules.tenancy.schemas.auth import UserContext store_router = APIRouter( prefix="/inventory", dependencies=[Depends(require_module_access("inventory", FrontendType.STORE))], ) logger = logging.getLogger(__name__) @store_router.post("/set", response_model=InventoryResponse) def set_inventory( inventory: InventoryCreate, current_user: UserContext = Depends(get_current_store_api), db: Session = Depends(get_db), ): """Set exact inventory quantity (replaces existing).""" result = inventory_service.set_inventory( db, current_user.token_store_id, inventory ) db.commit() return result @store_router.post("/adjust", response_model=InventoryResponse) def adjust_inventory( adjustment: InventoryAdjust, current_user: UserContext = Depends(get_current_store_api), db: Session = Depends(get_db), ): """Adjust inventory (positive to add, negative to remove).""" result = inventory_service.adjust_inventory( db, current_user.token_store_id, adjustment ) db.commit() return result @store_router.post("/reserve", response_model=InventoryResponse) def reserve_inventory( reservation: InventoryReserve, current_user: UserContext = Depends(get_current_store_api), db: Session = Depends(get_db), ): """Reserve inventory for an order.""" result = inventory_service.reserve_inventory( db, current_user.token_store_id, reservation ) db.commit() return result @store_router.post("/release", response_model=InventoryResponse) def release_reservation( reservation: InventoryReserve, current_user: UserContext = Depends(get_current_store_api), db: Session = Depends(get_db), ): """Release reserved inventory (cancel order).""" result = inventory_service.release_reservation( db, current_user.token_store_id, reservation ) db.commit() return result @store_router.post("/fulfill", response_model=InventoryResponse) def fulfill_reservation( reservation: InventoryReserve, current_user: UserContext = Depends(get_current_store_api), db: Session = Depends(get_db), ): """Fulfill reservation (complete order, remove from stock).""" result = inventory_service.fulfill_reservation( db, current_user.token_store_id, reservation ) db.commit() return result @store_router.get("/product/{product_id}", response_model=ProductInventorySummary) def get_product_inventory( product_id: int, current_user: UserContext = Depends(get_current_store_api), db: Session = Depends(get_db), ): """Get inventory summary for a product.""" return inventory_service.get_product_inventory( db, current_user.token_store_id, product_id ) @store_router.get("", response_model=InventoryListResponse) def get_store_inventory( skip: int = Query(0, ge=0), limit: int = Query(100, ge=1, le=1000), location: str | None = Query(None), low_stock: int | None = Query(None, ge=0), current_user: UserContext = Depends(get_current_store_api), db: Session = Depends(get_db), ): """Get all inventory for store.""" inventories = inventory_service.get_store_inventory( db, current_user.token_store_id, skip, limit, location, low_stock ) # Get total count total = len(inventories) # You might want a separate count query for large datasets return InventoryListResponse( inventories=inventories, total=total, skip=skip, limit=limit ) @store_router.put("/{inventory_id}", response_model=InventoryResponse) def update_inventory( inventory_id: int, inventory_update: InventoryUpdate, current_user: UserContext = Depends(get_current_store_api), db: Session = Depends(get_db), ): """Update inventory entry.""" result = inventory_service.update_inventory( db, current_user.token_store_id, inventory_id, inventory_update ) db.commit() return result @store_router.delete("/{inventory_id}", response_model=InventoryMessageResponse) def delete_inventory( inventory_id: int, current_user: UserContext = Depends(get_current_store_api), db: Session = Depends(get_db), ): """Delete inventory entry.""" inventory_service.delete_inventory(db, current_user.token_store_id, inventory_id) db.commit() return InventoryMessageResponse(message="Inventory deleted successfully") # ============================================================================ # Inventory Transaction History Endpoints # ============================================================================ @store_router.get("/transactions", response_model=InventoryTransactionListResponse) def get_inventory_transactions( skip: int = Query(0, ge=0), limit: int = Query(50, ge=1, le=200), product_id: int | None = Query(None, description="Filter by product"), transaction_type: str | None = Query(None, description="Filter by type"), current_user: UserContext = Depends(get_current_store_api), db: Session = Depends(get_db), ): """ Get inventory transaction history for the store. Returns a paginated list of all stock movements with product details. Use filters to narrow down by product or transaction type. """ transactions, total = inventory_transaction_service.get_store_transactions( db=db, store_id=current_user.token_store_id, skip=skip, limit=limit, product_id=product_id, transaction_type=transaction_type, ) return InventoryTransactionListResponse( transactions=[InventoryTransactionWithProduct(**tx) for tx in transactions], total=total, skip=skip, limit=limit, ) @store_router.get( "/transactions/product/{product_id}", response_model=ProductTransactionHistoryResponse, ) def get_product_transaction_history( product_id: int, limit: int = Query(50, ge=1, le=200), current_user: UserContext = Depends(get_current_store_api), db: Session = Depends(get_db), ): """ Get transaction history for a specific product. Returns recent stock movements with current inventory status. """ result = inventory_transaction_service.get_product_history( db=db, store_id=current_user.token_store_id, product_id=product_id, limit=limit, ) return ProductTransactionHistoryResponse(**result) @store_router.get( "/transactions/order/{order_id}", response_model=OrderTransactionHistoryResponse, ) def get_order_transaction_history( order_id: int, current_user: UserContext = Depends(get_current_store_api), db: Session = Depends(get_db), ): """ Get all inventory transactions for a specific order. Shows all stock movements (reserve, fulfill, release) related to an order. """ result = inventory_transaction_service.get_order_history( db=db, store_id=current_user.token_store_id, order_id=order_id, ) return OrderTransactionHistoryResponse( order_id=result["order_id"], order_number=result["order_number"], transactions=[ InventoryTransactionWithProduct(**tx) for tx in result["transactions"] ], )