307 lines
11 KiB
Python
307 lines
11 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
|
from sqlalchemy.orm import Session
|
|
from starlette.requests import Request
|
|
|
|
from app.core.rbac import require_permission
|
|
from app.db.database import get_db
|
|
from app.models.inventory import InventoryCategory, InventoryItem, InventoryLocation, InventorySupplier
|
|
from app.models.user import User
|
|
from app.repositories.inventory_repository import InventoryRepository
|
|
from app.schemas.inventory import (
|
|
InventoryCategoryPayload,
|
|
InventoryCategoryResponse,
|
|
InventoryItemCreate,
|
|
InventoryItemListResponse,
|
|
InventoryItemResponse,
|
|
InventoryItemUpdate,
|
|
InventoryLocationPayload,
|
|
InventoryLocationResponse,
|
|
InventoryStockAction,
|
|
InventoryStockMovementResponse,
|
|
InventorySupplierPayload,
|
|
InventorySupplierResponse,
|
|
)
|
|
from app.services.inventory_service import InventoryService
|
|
|
|
router = APIRouter(prefix="/inventory", tags=["Inventory"])
|
|
|
|
|
|
def get_item_or_404(db: Session, item_id: int) -> InventoryItem:
|
|
item = InventoryRepository.get_item(db, item_id)
|
|
if item is None:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Artikel nicht gefunden")
|
|
return item
|
|
|
|
|
|
def get_category_or_404(db: Session, category_id: int) -> InventoryCategory:
|
|
category = InventoryRepository.get_category(db, category_id)
|
|
if category is None:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Kategorie nicht gefunden")
|
|
return category
|
|
|
|
|
|
def get_location_or_404(db: Session, location_id: int) -> InventoryLocation:
|
|
location = InventoryRepository.get_location(db, location_id)
|
|
if location is None:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Lagerort nicht gefunden")
|
|
return location
|
|
|
|
|
|
def get_supplier_or_404(db: Session, supplier_id: int) -> InventorySupplier:
|
|
supplier = InventoryRepository.get_supplier(db, supplier_id)
|
|
if supplier is None:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Lieferant nicht gefunden")
|
|
return supplier
|
|
|
|
|
|
@router.get("/items", response_model=InventoryItemListResponse)
|
|
def list_items(
|
|
q: str | None = None,
|
|
category_id: int | None = None,
|
|
supplier_id: int | None = None,
|
|
location_id: int | None = None,
|
|
low_stock: bool | None = None,
|
|
active: bool | None = True,
|
|
limit: int = Query(default=50, ge=1, le=200),
|
|
offset: int = Query(default=0, ge=0),
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.read")),
|
|
):
|
|
items, total = InventoryRepository.list_items(
|
|
db,
|
|
q=q,
|
|
category_id=category_id,
|
|
supplier_id=supplier_id,
|
|
location_id=location_id,
|
|
low_stock=low_stock,
|
|
active=active,
|
|
limit=limit,
|
|
offset=offset,
|
|
)
|
|
return InventoryItemListResponse(items=items, total=total, limit=limit, offset=offset)
|
|
|
|
|
|
@router.post("/items", response_model=InventoryItemResponse, status_code=status.HTTP_201_CREATED)
|
|
def create_item(
|
|
payload: InventoryItemCreate,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.create")),
|
|
):
|
|
return InventoryService.create_item(db, payload, actor=current_user, request=request)
|
|
|
|
|
|
@router.get("/items/{item_id}", response_model=InventoryItemResponse)
|
|
def get_item(
|
|
item_id: int,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.read")),
|
|
):
|
|
return get_item_or_404(db, item_id)
|
|
|
|
|
|
@router.put("/items/{item_id}", response_model=InventoryItemResponse)
|
|
def update_item(
|
|
item_id: int,
|
|
payload: InventoryItemUpdate,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.update")),
|
|
):
|
|
item = get_item_or_404(db, item_id)
|
|
return InventoryService.update_item(db, item, payload, actor=current_user, request=request)
|
|
|
|
|
|
@router.delete("/items/{item_id}", response_model=InventoryItemResponse | None)
|
|
def delete_item(
|
|
item_id: int,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.delete")),
|
|
):
|
|
item = get_item_or_404(db, item_id)
|
|
return InventoryService.delete_item(db, item, actor=current_user, request=request)
|
|
|
|
|
|
@router.post("/items/{item_id}/stock/adjust", response_model=InventoryItemResponse)
|
|
def adjust_stock(
|
|
item_id: int,
|
|
payload: InventoryStockAction,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.stock.adjust")),
|
|
):
|
|
item = get_item_or_404(db, item_id)
|
|
return InventoryService.adjust_stock(db, item, payload, actor=current_user, request=request)
|
|
|
|
|
|
@router.post("/items/{item_id}/stock/reserve", response_model=InventoryItemResponse)
|
|
def reserve_stock(
|
|
item_id: int,
|
|
payload: InventoryStockAction,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.stock.reserve")),
|
|
):
|
|
item = get_item_or_404(db, item_id)
|
|
return InventoryService.reserve_stock(db, item, payload, actor=current_user, request=request)
|
|
|
|
|
|
@router.post("/items/{item_id}/stock/release", response_model=InventoryItemResponse)
|
|
def release_stock(
|
|
item_id: int,
|
|
payload: InventoryStockAction,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.stock.reserve")),
|
|
):
|
|
item = get_item_or_404(db, item_id)
|
|
return InventoryService.release_stock(db, item, payload, actor=current_user, request=request)
|
|
|
|
|
|
@router.post("/items/{item_id}/stock/consume", response_model=InventoryItemResponse)
|
|
def consume_stock(
|
|
item_id: int,
|
|
payload: InventoryStockAction,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.stock.consume")),
|
|
):
|
|
item = get_item_or_404(db, item_id)
|
|
return InventoryService.consume_stock(db, item, payload, actor=current_user, request=request)
|
|
|
|
|
|
@router.get("/items/{item_id}/movements", response_model=list[InventoryStockMovementResponse])
|
|
def list_movements(
|
|
item_id: int,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.read")),
|
|
):
|
|
get_item_or_404(db, item_id)
|
|
return InventoryRepository.list_movements(db, item_id)
|
|
|
|
|
|
@router.get("/categories", response_model=list[InventoryCategoryResponse])
|
|
def list_categories(
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.read")),
|
|
):
|
|
return InventoryRepository.list_categories(db)
|
|
|
|
|
|
@router.post("/categories", response_model=InventoryCategoryResponse, status_code=status.HTTP_201_CREATED)
|
|
def create_category(
|
|
payload: InventoryCategoryPayload,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.manage.masterdata")),
|
|
):
|
|
return InventoryService.create_category(db, payload, actor=current_user, request=request)
|
|
|
|
|
|
@router.put("/categories/{category_id}", response_model=InventoryCategoryResponse)
|
|
def update_category(
|
|
category_id: int,
|
|
payload: InventoryCategoryPayload,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.manage.masterdata")),
|
|
):
|
|
category = get_category_or_404(db, category_id)
|
|
return InventoryService.update_category(db, category, payload, actor=current_user, request=request)
|
|
|
|
|
|
@router.delete("/categories/{category_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
def delete_category(
|
|
category_id: int,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.manage.masterdata")),
|
|
):
|
|
category = get_category_or_404(db, category_id)
|
|
InventoryService.delete_master(db, category, action="inventory.categories.delete", actor=current_user, request=request)
|
|
return None
|
|
|
|
|
|
@router.get("/locations", response_model=list[InventoryLocationResponse])
|
|
def list_locations(
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.read")),
|
|
):
|
|
return InventoryRepository.list_locations(db)
|
|
|
|
|
|
@router.post("/locations", response_model=InventoryLocationResponse, status_code=status.HTTP_201_CREATED)
|
|
def create_location(
|
|
payload: InventoryLocationPayload,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.manage.masterdata")),
|
|
):
|
|
return InventoryService.create_location(db, payload, actor=current_user, request=request)
|
|
|
|
|
|
@router.put("/locations/{location_id}", response_model=InventoryLocationResponse)
|
|
def update_location(
|
|
location_id: int,
|
|
payload: InventoryLocationPayload,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.manage.masterdata")),
|
|
):
|
|
location = get_location_or_404(db, location_id)
|
|
return InventoryService.update_location(db, location, payload, actor=current_user, request=request)
|
|
|
|
|
|
@router.delete("/locations/{location_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
def delete_location(
|
|
location_id: int,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.manage.masterdata")),
|
|
):
|
|
location = get_location_or_404(db, location_id)
|
|
InventoryService.delete_master(db, location, action="inventory.locations.delete", actor=current_user, request=request)
|
|
return None
|
|
|
|
|
|
@router.get("/suppliers", response_model=list[InventorySupplierResponse])
|
|
def list_suppliers(
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.read")),
|
|
):
|
|
return InventoryRepository.list_suppliers(db)
|
|
|
|
|
|
@router.post("/suppliers", response_model=InventorySupplierResponse, status_code=status.HTTP_201_CREATED)
|
|
def create_supplier(
|
|
payload: InventorySupplierPayload,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.manage.masterdata")),
|
|
):
|
|
return InventoryService.create_supplier(db, payload, actor=current_user, request=request)
|
|
|
|
|
|
@router.put("/suppliers/{supplier_id}", response_model=InventorySupplierResponse)
|
|
def update_supplier(
|
|
supplier_id: int,
|
|
payload: InventorySupplierPayload,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.manage.masterdata")),
|
|
):
|
|
supplier = get_supplier_or_404(db, supplier_id)
|
|
return InventoryService.update_supplier(db, supplier, payload, actor=current_user, request=request)
|
|
|
|
|
|
@router.delete("/suppliers/{supplier_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
def delete_supplier(
|
|
supplier_id: int,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("inventory.manage.masterdata")),
|
|
):
|
|
supplier = get_supplier_or_404(db, supplier_id)
|
|
InventoryService.delete_master(db, supplier, action="inventory.suppliers.delete", actor=current_user, request=request)
|
|
return None
|