feat(inventory): add spare parts management
This commit is contained in:
parent
abc6e308dd
commit
fb5c2cc26a
43 changed files with 2950 additions and 2 deletions
307
backend/hermes/app/api/inventory.py
Normal file
307
backend/hermes/app/api/inventory.py
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
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
|
||||
Loading…
Add table
Add a link
Reference in a new issue