Feste Fächer-Liste je Objekttyp (Vorlagen-Positionen: Dropdown + Sonstiges)
Nutzer-Vorgabe: Rucksack-Fächer sollen aus einer festen, je Objekttyp gepflegten Liste gewählt werden können statt jedes Mal Freitext einzutippen. Neue Tabelle fach (Migration 0004), CRUD-Endpoints /faecher, Admin-Tab Struktur bekommt Fach-Verwaltung, Vorlagen-Editor nutzt Dropdown mit "Sonstiges"-Freitext-Fallback. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L85hmKbvX7Cqkq47KnQhFt
This commit is contained in:
@@ -5,11 +5,14 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.api.deps import get_current_user, require_roles
|
||||
from app.db.session import get_db
|
||||
from app.models.auth import RolleTyp
|
||||
from app.models.stammdaten import Bereich, Kategorie, Material, Objekttyp, Standort
|
||||
from app.models.stammdaten import Bereich, Fach, Kategorie, Material, Objekttyp, Standort
|
||||
from app.schemas.stammdaten import (
|
||||
BereichCreate,
|
||||
BereichRead,
|
||||
BereichUpdate,
|
||||
FachCreate,
|
||||
FachRead,
|
||||
FachUpdate,
|
||||
KategorieCreate,
|
||||
KategorieRead,
|
||||
KategorieUpdate,
|
||||
@@ -179,6 +182,57 @@ async def aendere_objekttyp(
|
||||
return objekttyp
|
||||
|
||||
|
||||
# -- Fach (feste Fächer-Liste je Objekttyp) ----------------------------------
|
||||
|
||||
@router.get("/faecher", response_model=list[FachRead])
|
||||
async def liste_faecher(
|
||||
db: AsyncSession = Depends(get_db), _=Depends(get_current_user)
|
||||
) -> list[Fach]:
|
||||
result = await db.execute(select(Fach).order_by(Fach.objekttyp_id, Fach.sortierung, Fach.name))
|
||||
return list(result.scalars().all())
|
||||
|
||||
|
||||
@router.post("/faecher", response_model=FachRead, status_code=status.HTTP_201_CREATED)
|
||||
async def erstelle_fach(
|
||||
payload: FachCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_=Depends(require_roles(RolleTyp.administration)),
|
||||
) -> Fach:
|
||||
fach = Fach(**payload.model_dump())
|
||||
db.add(fach)
|
||||
await db.flush()
|
||||
return fach
|
||||
|
||||
|
||||
@router.patch("/faecher/{fach_id}", response_model=FachRead)
|
||||
async def aendere_fach(
|
||||
fach_id: int,
|
||||
payload: FachUpdate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_=Depends(require_roles(RolleTyp.administration)),
|
||||
) -> Fach:
|
||||
fach = await db.get(Fach, fach_id)
|
||||
if fach is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Fach nicht gefunden")
|
||||
for feld, wert in payload.model_dump(exclude_unset=True).items():
|
||||
setattr(fach, feld, wert)
|
||||
await db.flush()
|
||||
return fach
|
||||
|
||||
|
||||
@router.delete("/faecher/{fach_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def loesche_fach(
|
||||
fach_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_=Depends(require_roles(RolleTyp.administration)),
|
||||
) -> None:
|
||||
fach = await db.get(Fach, fach_id)
|
||||
if fach is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Fach nicht gefunden")
|
||||
await db.delete(fach)
|
||||
await db.flush()
|
||||
|
||||
|
||||
# -- Material (Prompt 07) --------------------------------------------------
|
||||
|
||||
@router.get("/materialien", response_model=list[MaterialRead])
|
||||
|
||||
@@ -50,6 +50,19 @@ class Objekttyp(Base):
|
||||
name: Mapped[str] = mapped_column(String, nullable=False)
|
||||
|
||||
|
||||
class Fach(Base):
|
||||
"""Nutzer-Vorgabe: feste Fächer-Liste je Objekttyp, damit beim Anlegen einer
|
||||
Vorlagenposition nicht jedes Mal Freitext getippt werden muss. `sortierung`
|
||||
steuert die Anzeigereihenfolge (z. B. von oben nach unten im Rucksack)."""
|
||||
|
||||
__tablename__ = "fach"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
objekttyp_id: Mapped[int] = mapped_column(ForeignKey("objekttyp.id"), nullable=False)
|
||||
name: Mapped[str] = mapped_column(String, nullable=False)
|
||||
sortierung: Mapped[int] = mapped_column(default=0)
|
||||
|
||||
|
||||
class Material(Base):
|
||||
__tablename__ = "material"
|
||||
|
||||
|
||||
@@ -67,6 +67,22 @@ class ObjekttypUpdate(BaseModel):
|
||||
name: str | None = None
|
||||
|
||||
|
||||
class FachCreate(BaseModel):
|
||||
objekttyp_id: int
|
||||
name: str
|
||||
sortierung: int = 0
|
||||
|
||||
|
||||
class FachRead(FachCreate):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
id: int
|
||||
|
||||
|
||||
class FachUpdate(BaseModel):
|
||||
name: str | None = None
|
||||
sortierung: int | None = None
|
||||
|
||||
|
||||
class MaterialCreate(BaseModel):
|
||||
name: str
|
||||
artikelnummer: str | None = None
|
||||
|
||||
Reference in New Issue
Block a user