Files
orion/app/api/v1/vendor/inventory.py
Samir Boulahtit cad862f469 refactor(api): introduce UserContext schema for API dependency injection
Replace direct User database model imports in API endpoints with UserContext
schema, following the architecture principle that API routes should not import
database models directly.

Changes:
- Create UserContext schema in models/schema/auth.py with from_user() factory
- Update app/api/deps.py to return UserContext from all auth dependencies
- Add _get_user_model() helper for functions needing User model access
- Update 58 API endpoint files to use UserContext instead of User
- Add noqa comments for 4 legitimate edge cases (enums, internal helpers)

Architecture validation: 0 errors (down from 61), 11 warnings remain

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 20:47:33 +01:00

258 lines
8.0 KiB
Python

# app/api/v1/vendor/inventory.py
"""
Vendor inventory management endpoints.
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern).
The get_current_vendor_api dependency guarantees token_vendor_id is present.
"""
import logging
from fastapi import APIRouter, Depends, Query
from sqlalchemy.orm import Session
from app.api.deps import get_current_vendor_api
from app.core.database import get_db
from app.services.inventory_service import inventory_service
from app.services.inventory_transaction_service import inventory_transaction_service
from models.schema.auth import UserContext
from app.modules.inventory.schemas import (
InventoryAdjust,
InventoryCreate,
InventoryListResponse,
InventoryMessageResponse,
InventoryReserve,
InventoryResponse,
InventoryTransactionListResponse,
InventoryTransactionWithProduct,
InventoryUpdate,
OrderTransactionHistoryResponse,
ProductInventorySummary,
ProductTransactionHistoryResponse,
)
router = APIRouter()
logger = logging.getLogger(__name__)
@router.post("/inventory/set", response_model=InventoryResponse)
def set_inventory(
inventory: InventoryCreate,
current_user: UserContext = Depends(get_current_vendor_api),
db: Session = Depends(get_db),
):
"""Set exact inventory quantity (replaces existing)."""
result = inventory_service.set_inventory(
db, current_user.token_vendor_id, inventory
)
db.commit()
return result
@router.post("/inventory/adjust", response_model=InventoryResponse)
def adjust_inventory(
adjustment: InventoryAdjust,
current_user: UserContext = Depends(get_current_vendor_api),
db: Session = Depends(get_db),
):
"""Adjust inventory (positive to add, negative to remove)."""
result = inventory_service.adjust_inventory(
db, current_user.token_vendor_id, adjustment
)
db.commit()
return result
@router.post("/inventory/reserve", response_model=InventoryResponse)
def reserve_inventory(
reservation: InventoryReserve,
current_user: UserContext = Depends(get_current_vendor_api),
db: Session = Depends(get_db),
):
"""Reserve inventory for an order."""
result = inventory_service.reserve_inventory(
db, current_user.token_vendor_id, reservation
)
db.commit()
return result
@router.post("/inventory/release", response_model=InventoryResponse)
def release_reservation(
reservation: InventoryReserve,
current_user: UserContext = Depends(get_current_vendor_api),
db: Session = Depends(get_db),
):
"""Release reserved inventory (cancel order)."""
result = inventory_service.release_reservation(
db, current_user.token_vendor_id, reservation
)
db.commit()
return result
@router.post("/inventory/fulfill", response_model=InventoryResponse)
def fulfill_reservation(
reservation: InventoryReserve,
current_user: UserContext = Depends(get_current_vendor_api),
db: Session = Depends(get_db),
):
"""Fulfill reservation (complete order, remove from stock)."""
result = inventory_service.fulfill_reservation(
db, current_user.token_vendor_id, reservation
)
db.commit()
return result
@router.get("/inventory/product/{product_id}", response_model=ProductInventorySummary)
def get_product_inventory(
product_id: int,
current_user: UserContext = Depends(get_current_vendor_api),
db: Session = Depends(get_db),
):
"""Get inventory summary for a product."""
return inventory_service.get_product_inventory(
db, current_user.token_vendor_id, product_id
)
@router.get("/inventory", response_model=InventoryListResponse)
def get_vendor_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_vendor_api),
db: Session = Depends(get_db),
):
"""Get all inventory for vendor."""
inventories = inventory_service.get_vendor_inventory(
db, current_user.token_vendor_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
)
@router.put("/inventory/{inventory_id}", response_model=InventoryResponse)
def update_inventory(
inventory_id: int,
inventory_update: InventoryUpdate,
current_user: UserContext = Depends(get_current_vendor_api),
db: Session = Depends(get_db),
):
"""Update inventory entry."""
result = inventory_service.update_inventory(
db, current_user.token_vendor_id, inventory_id, inventory_update
)
db.commit()
return result
@router.delete("/inventory/{inventory_id}", response_model=InventoryMessageResponse)
def delete_inventory(
inventory_id: int,
current_user: UserContext = Depends(get_current_vendor_api),
db: Session = Depends(get_db),
):
"""Delete inventory entry."""
inventory_service.delete_inventory(db, current_user.token_vendor_id, inventory_id)
db.commit()
return InventoryMessageResponse(message="Inventory deleted successfully")
# ============================================================================
# Inventory Transaction History Endpoints
# ============================================================================
@router.get("/inventory/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_vendor_api),
db: Session = Depends(get_db),
):
"""
Get inventory transaction history for the vendor.
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_vendor_transactions(
db=db,
vendor_id=current_user.token_vendor_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,
)
@router.get(
"/inventory/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_vendor_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,
vendor_id=current_user.token_vendor_id,
product_id=product_id,
limit=limit,
)
return ProductTransactionHistoryResponse(**result)
@router.get(
"/inventory/transactions/order/{order_id}",
response_model=OrderTransactionHistoryResponse,
)
def get_order_transaction_history(
order_id: int,
current_user: UserContext = Depends(get_current_vendor_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,
vendor_id=current_user.token_vendor_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"]
],
)