feat(identity): IDENT-005 Hersteller/Modell-Stammdaten
Neue Tabellen hersteller/modell (Migration 0030), CRUD-Endpunkte (/hersteller, /modelle) nach bestehendem Kategorie-Muster, inkl. 409 bei Duplikaten (IntegrityError abgefangen wie in personal.py). geraet_instanz bekommt optionales modell_id-Feld - verknüpft eine konkrete Geräteinstanz (mit Seriennummer, IDENT-004) mit ihrem Hersteller/Modell. Ergänzt Material.hersteller (bleibt unverändert als Freitext) um eine normalisierte Variante gezielt für Geräte mit Seriennummer - keine Datenmigration bestehender Freitext-Werte (Scope dieser Kachel). Bewusst kein neues Admin-UI-Screen für die Pflege (Scope-Grenze der Kachel) - Hersteller/Modell aktuell nur über die API verwaltbar. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.api.deps import get_current_user, require_roles, require_roles_or_permission
|
||||
from app.db.session import get_db
|
||||
from app.models.auth import RolleTyp
|
||||
from app.models.objekt import Objekt
|
||||
from app.models.stammdaten import Bereich, Fach, Kategorie, Material, Objekttyp, Standort
|
||||
from app.models.stammdaten import Bereich, Fach, Hersteller, Kategorie, Material, Modell, Objekttyp, Standort
|
||||
from app.models.vorlage import Beladungsvorlage, Vorlagenposition
|
||||
from app.services.material_merge import finde_konflikte, fuehre_zusammen, hole_verwendung
|
||||
from app.schemas.stammdaten import (
|
||||
@@ -18,9 +19,13 @@ from app.schemas.stammdaten import (
|
||||
FachRead,
|
||||
FachUpdate,
|
||||
FachVerwendungObjekt,
|
||||
HerstellerCreate,
|
||||
HerstellerRead,
|
||||
KategorieCreate,
|
||||
KategorieRead,
|
||||
KategorieUpdate,
|
||||
ModellCreate,
|
||||
ModellRead,
|
||||
MaterialCreate,
|
||||
MaterialRead,
|
||||
MaterialUpdate,
|
||||
@@ -75,6 +80,61 @@ async def aendere_bereich(
|
||||
return bereich
|
||||
|
||||
|
||||
# -- Hersteller/Modell (IDENT-005) ---------------------------------------------
|
||||
|
||||
@router.get("/hersteller", response_model=list[HerstellerRead])
|
||||
async def liste_hersteller(
|
||||
db: AsyncSession = Depends(get_db), _=Depends(get_current_user)
|
||||
) -> list[Hersteller]:
|
||||
result = await db.execute(select(Hersteller))
|
||||
return list(result.scalars().all())
|
||||
|
||||
|
||||
@router.post("/hersteller", response_model=HerstellerRead, status_code=status.HTTP_201_CREATED)
|
||||
async def erstelle_hersteller(
|
||||
payload: HerstellerCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_=Depends(require_roles(RolleTyp.administration)),
|
||||
) -> Hersteller:
|
||||
hersteller = Hersteller(**payload.model_dump())
|
||||
db.add(hersteller)
|
||||
try:
|
||||
await db.flush()
|
||||
except IntegrityError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Hersteller existiert bereits") from exc
|
||||
return hersteller
|
||||
|
||||
|
||||
@router.get("/modelle", response_model=list[ModellRead])
|
||||
async def liste_modelle(
|
||||
hersteller_id: int | None = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_=Depends(get_current_user),
|
||||
) -> list[Modell]:
|
||||
stmt = select(Modell)
|
||||
if hersteller_id is not None:
|
||||
stmt = stmt.where(Modell.hersteller_id == hersteller_id)
|
||||
result = await db.execute(stmt)
|
||||
return list(result.scalars().all())
|
||||
|
||||
|
||||
@router.post("/modelle", response_model=ModellRead, status_code=status.HTTP_201_CREATED)
|
||||
async def erstelle_modell(
|
||||
payload: ModellCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_=Depends(require_roles(RolleTyp.administration)),
|
||||
) -> Modell:
|
||||
if await db.get(Hersteller, payload.hersteller_id) is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Hersteller nicht gefunden")
|
||||
modell = Modell(**payload.model_dump())
|
||||
db.add(modell)
|
||||
try:
|
||||
await db.flush()
|
||||
except IntegrityError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Modell existiert bereits") from exc
|
||||
return modell
|
||||
|
||||
|
||||
# -- Kategorie ------------------------------------------------------------------
|
||||
|
||||
@router.get("/kategorien", response_model=list[KategorieRead])
|
||||
|
||||
Reference in New Issue
Block a user