feat(maint): MAINT-008 Lieferanten-/Dienstleister-Verzeichnis
ExterneStelle (ASSET-011) um Kategorie erweitert statt zweites Adressbuch anzulegen; Wartungsauftrag optional mit dienstleister_id verknuepfbar, Freitext werkstatt_name bleibt als Fallback bestehen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
1 parent
3d596ab456
commit
c18e4481b6
15 files changed
+257
-20
No files matched your search
@@ -207,7 +207,7 @@ vergeben statt geraten.
|
||||
| MAINT-005 | Maintenance | Ersatzteile/Kosten | P3 | M | MEDIUM | MAINT-003 | ✅ (`WartungsauftragTeil`, Migration 0033, `/wartungsauftraege/{id}/teile`-CRUD, UI in `WartungSection.tsx`) |
|
||||
| MAINT-006 | Maintenance | Wartungsdokument-Anbindung | P2 | XS | LOW | MAINT-003, DOC-001 | ✅ (Dokument polymorph, deckt Wartungsauftrag ab) |
|
||||
| MAINT-007 | Maintenance | Tankbuch/Kraftstoffverbrauch (neu, Referenz: FleetMS) | P3 | S | LOW | FLEET-002, ASSET-009 | ✅ (`tankbuch_eintrag`, `app/services/tankbuch.py`, Verbrauchsberechnung) |
|
||||
| MAINT-008 | Maintenance | Lieferanten-/Dienstleister-Verzeichnis (neu, Referenz: HiOrg-Server) | P3 | S | LOW | MAINT-003 | ⬜ geplant 2026-09-10 |
|
||||
| MAINT-008 | Maintenance | Lieferanten-/Dienstleister-Verzeichnis (neu, Referenz: HiOrg-Server) | P3 | S | LOW | MAINT-003 | ✅ erledigt 2026-09-10 |
|
||||
| DEFECT-001 | Defects | Mangelmeldung + Foto | P1 | M | LOW | ASSET-003, DOC-001 | ✅ (`mangel.py`, `MangelListePage.tsx`, Dokument-Anhang möglich) |
|
||||
| DEFECT-002 | Defects | Priorität & Status-Workflow | P1 | S | LOW | DEFECT-001 | ✅ (`MangelStatus`, `MangelPrioritaet` Enums) |
|
||||
| DEFECT-003 | Defects | Verantwortlicher & Reparaturzuordnung | P1 | S | LOW | DEFECT-002, PERS-002 | ✅ (Zustaendigkeit/Kontrollverantwortung koppelt an Mangel-Objekt) |
|
||||
|
||||
@@ -219,7 +219,18 @@ ebenfalls P3/offen.
|
||||
zuordnen, bestehende Freitext-Einträge bleiben unverändert lesbar.
|
||||
- **Tests:** CRUD-Test, Test dass ein Wartungsauftrag ohne `dienstleister_id`
|
||||
(nur Freitext) weiterhin funktioniert.
|
||||
- **DoD:** offen.
|
||||
- **DoD:** ✅ erledigt (2026-09-10). DECISION: `dienstleister` mit
|
||||
ASSET-011s `externe_stelle` zusammengelegt (kein zweites Adressbuch) —
|
||||
`externe_stelle.kategorie` (werkstatt/lieferant/verleiher/sonstiges, neu)
|
||||
statt eigener Tabelle. Migration `0050_dienstleister.py`.
|
||||
`Wartungsauftrag.dienstleister_id` (nullable FK auf `externe_stelle`,
|
||||
Freitext `werkstatt_name` bleibt als Fallback bestehen, keine
|
||||
Zwangsmigration). Backend: `erstelle_externe_stelle`/
|
||||
`erstelle_wartungsauftrag` um `kategorie`/`dienstleister_id` erweitert,
|
||||
404 bei unbekanntem `dienstleister_id`. Frontend: `WartungSection.tsx`
|
||||
lädt `externe_stellen` (kategorie=werkstatt) nur bei "externe Werkstatt",
|
||||
Dropdown mit Freitext-Fallback, Liste zeigt verknüpften Namen. Tests:
|
||||
`test_maint008_dienstleister.py` (Verknüpfung, 404, Freitext-Regression).
|
||||
- **Referenz (HiOrg-Server-Vergleich 2026-09-10):** HiOrg-Server führt ein
|
||||
Adressbuch für Lieferanten/Dienstleister, verknüpfbar mit Material-/
|
||||
Wartungsvorgängen — MABEA hat dafür aktuell nur Freitext, kleine, aber
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
"""MAINT-008: externe_stelle als gemeinsames Adressbuch für Verleih (ASSET-011)
|
||||
UND Wartungsdienstleister/Lieferanten - kategorie-Feld ergänzt,
|
||||
wartungsauftrag.dienstleister_id (optionale Verknüpfung, werkstatt_name
|
||||
bleibt als Freitext-Fallback bestehen).
|
||||
|
||||
Revision ID: 0050_dienstleister
|
||||
Revises: 0049_ressourcenbuchung
|
||||
Create Date: 2026-09-10
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "0050_dienstleister"
|
||||
down_revision: Union[str, None] = "0049_ressourcenbuchung"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.execute(
|
||||
"CREATE TYPE externe_stelle_kategorie AS ENUM "
|
||||
"('werkstatt', 'lieferant', 'verleiher', 'sonstiges')"
|
||||
)
|
||||
op.execute(
|
||||
"ALTER TABLE externe_stelle ADD COLUMN kategorie externe_stelle_kategorie "
|
||||
"NOT NULL DEFAULT 'sonstiges'"
|
||||
)
|
||||
op.execute(
|
||||
"ALTER TABLE wartungsauftrag ADD COLUMN dienstleister_id INTEGER REFERENCES externe_stelle(id)"
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.execute("ALTER TABLE wartungsauftrag DROP COLUMN dienstleister_id")
|
||||
op.execute("ALTER TABLE externe_stelle DROP COLUMN kategorie")
|
||||
op.execute("DROP TYPE IF EXISTS externe_stelle_kategorie")
|
||||
@@ -49,7 +49,9 @@ async def hole_externe_stellen(
|
||||
async def erstelle_externe_stelle_endpunkt(
|
||||
payload: ExterneStelleCreate, db: AsyncSession = Depends(get_db), _=Depends(_materialverantwortliche)
|
||||
) -> ExterneStelle:
|
||||
return await erstelle_externe_stelle(db, name=payload.name, kontakt=payload.kontakt, adresse=payload.adresse)
|
||||
return await erstelle_externe_stelle(
|
||||
db, name=payload.name, kontakt=payload.kontakt, adresse=payload.adresse, kategorie=payload.kategorie
|
||||
)
|
||||
|
||||
|
||||
@router.get("/verleihe", response_model=list[VerleihRead])
|
||||
|
||||
@@ -8,6 +8,7 @@ from app.api.deps import get_current_user, require_roles
|
||||
from app.db.session import get_db
|
||||
from app.models.auth import Benutzer, RolleTyp
|
||||
from app.models.objekt import Objekt
|
||||
from app.models.verleih import ExterneStelle
|
||||
from app.models.wartung import Wartungsauftrag, WartungsauftragTeil, Wartungsplan, WartungsauftragStatus
|
||||
from app.schemas.wartung import (
|
||||
FaelligeWartungRead,
|
||||
@@ -111,6 +112,8 @@ async def erstelle_auftrag(
|
||||
objekt = await db.get(Objekt, payload.objekt_id)
|
||||
if objekt is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Objekt nicht gefunden")
|
||||
if payload.dienstleister_id is not None and await db.get(ExterneStelle, payload.dienstleister_id) is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Dienstleister nicht gefunden")
|
||||
return await erstelle_wartungsauftrag(
|
||||
db,
|
||||
objekt_id=payload.objekt_id,
|
||||
@@ -120,6 +123,7 @@ async def erstelle_auftrag(
|
||||
beschreibung=payload.beschreibung,
|
||||
werkstatt_intern=payload.werkstatt_intern,
|
||||
werkstatt_name=payload.werkstatt_name,
|
||||
dienstleister_id=payload.dienstleister_id,
|
||||
faelligkeit_am=payload.faelligkeit_am,
|
||||
)
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ from app.models.personal import (
|
||||
)
|
||||
from app.models.stammdaten import Bereich, Kategorie, Material, MaterialTyp, Objekttyp, Standort
|
||||
from app.models.tankbuch import TankbuchEintrag
|
||||
from app.models.verleih import ExterneStelle, Verleih
|
||||
from app.models.verleih import ExterneStelle, ExterneStelleKategorie, Verleih
|
||||
from app.models.vorlage import Beladungsvorlage, VorlageStatus, Vorlagenposition
|
||||
from app.models.wartung_benachrichtigung import WartungBenachrichtigung
|
||||
from app.models.wartung import (
|
||||
@@ -114,6 +114,7 @@ __all__ = [
|
||||
"Standort",
|
||||
"TankbuchEintrag",
|
||||
"ExterneStelle",
|
||||
"ExterneStelleKategorie",
|
||||
"Verleih",
|
||||
"Beladungsvorlage",
|
||||
"VorlageStatus",
|
||||
|
||||
@@ -1,17 +1,33 @@
|
||||
import enum
|
||||
import uuid
|
||||
from datetime import date, datetime
|
||||
|
||||
from sqlalchemy import Date, ForeignKey, String, Text
|
||||
from sqlalchemy.dialects.postgresql import TIMESTAMP, UUID
|
||||
from sqlalchemy.dialects.postgresql import ENUM as PgEnum, TIMESTAMP, UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.db.base import Base
|
||||
|
||||
|
||||
class ExterneStelleKategorie(str, enum.Enum):
|
||||
werkstatt = "werkstatt"
|
||||
lieferant = "lieferant"
|
||||
verleiher = "verleiher"
|
||||
sonstiges = "sonstiges"
|
||||
|
||||
|
||||
externe_stelle_kategorie_pg = PgEnum(
|
||||
ExterneStelleKategorie, name="externe_stelle_kategorie", create_type=False
|
||||
)
|
||||
|
||||
|
||||
class ExterneStelle(Base):
|
||||
"""ASSET-011: externe Organisation/Stelle OHNE eigenen MABEA-Zugang -
|
||||
bewusst getrennt von `Person` (PERS-001, i.d.R. eigenes Personal mit
|
||||
optionalem Benutzerkonto)."""
|
||||
"""ASSET-011/MAINT-008: EIN Adressbuch für externe Organisationen -
|
||||
Verleih-Empfänger (ASSET-011) UND Wartungsdienstleister/Lieferanten
|
||||
(MAINT-008) sind fachlich dasselbe Konzept (externe Stelle mit
|
||||
Kontaktdaten), Entscheidung 2026-09-10 bewusst gegen zwei getrennte
|
||||
Tabellen. Bewusst getrennt von `Person` (PERS-001, i.d.R. eigenes
|
||||
Personal mit optionalem Benutzerkonto)."""
|
||||
|
||||
__tablename__ = "externe_stelle"
|
||||
|
||||
@@ -19,6 +35,9 @@ class ExterneStelle(Base):
|
||||
name: Mapped[str] = mapped_column(String, nullable=False)
|
||||
kontakt: Mapped[str | None] = mapped_column(String)
|
||||
adresse: Mapped[str | None] = mapped_column(Text)
|
||||
kategorie: Mapped[ExterneStelleKategorie] = mapped_column(
|
||||
externe_stelle_kategorie_pg, nullable=False, default=ExterneStelleKategorie.sonstiges
|
||||
)
|
||||
|
||||
|
||||
class Verleih(Base):
|
||||
|
||||
@@ -71,6 +71,11 @@ class Wartungsauftrag(Base):
|
||||
)
|
||||
werkstatt_intern: Mapped[bool] = mapped_column(nullable=False, default=True)
|
||||
werkstatt_name: Mapped[str | None] = mapped_column(String)
|
||||
# MAINT-008: optionale Verknüpfung zu strukturierten Stammdaten (dieselbe
|
||||
# `externe_stelle`-Tabelle wie ASSET-011 - ein Adressbuch statt zwei,
|
||||
# Entscheidung 2026-09-10). werkstatt_name bleibt als Freitext-Fallback
|
||||
# bestehen, kein Zwang zur Migration alter Einträge.
|
||||
dienstleister_id: Mapped[int | None] = mapped_column(ForeignKey("externe_stelle.id"))
|
||||
faelligkeit_am: Mapped[date | None] = mapped_column()
|
||||
erstellt_am: Mapped[datetime] = mapped_column(TIMESTAMP(timezone=True), nullable=False)
|
||||
erledigt_am: Mapped[datetime | None] = mapped_column(TIMESTAMP(timezone=True))
|
||||
|
||||
@@ -3,11 +3,14 @@ from datetime import date, datetime
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from app.models.verleih import ExterneStelleKategorie
|
||||
|
||||
|
||||
class ExterneStelleCreate(BaseModel):
|
||||
name: str
|
||||
kontakt: str | None = None
|
||||
adresse: str | None = None
|
||||
kategorie: ExterneStelleKategorie = ExterneStelleKategorie.sonstiges
|
||||
|
||||
|
||||
class ExterneStelleRead(BaseModel):
|
||||
@@ -16,6 +19,7 @@ class ExterneStelleRead(BaseModel):
|
||||
name: str
|
||||
kontakt: str | None
|
||||
adresse: str | None
|
||||
kategorie: ExterneStelleKategorie
|
||||
|
||||
|
||||
class VerleihCreate(BaseModel):
|
||||
|
||||
@@ -57,6 +57,7 @@ class WartungsauftragCreate(BaseModel):
|
||||
beschreibung: str | None = None
|
||||
werkstatt_intern: bool = True
|
||||
werkstatt_name: str | None = None
|
||||
dienstleister_id: int | None = None
|
||||
faelligkeit_am: date | None = None
|
||||
|
||||
|
||||
@@ -82,6 +83,7 @@ class WartungsauftragRead(BaseModel):
|
||||
status: WartungsauftragStatus
|
||||
werkstatt_intern: bool
|
||||
werkstatt_name: str | None
|
||||
dienstleister_id: int | None
|
||||
faelligkeit_am: date | None
|
||||
erstellt_am: datetime
|
||||
erledigt_am: datetime | None
|
||||
|
||||
@@ -4,7 +4,7 @@ from sqlalchemy import select
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.verleih import ExterneStelle, Verleih
|
||||
from app.models.verleih import ExterneStelle, ExterneStelleKategorie, Verleih
|
||||
|
||||
|
||||
class ObjektBereitsVerliehenError(Exception):
|
||||
@@ -16,9 +16,14 @@ class KeinOffenerVerleihError(Exception):
|
||||
|
||||
|
||||
async def erstelle_externe_stelle(
|
||||
db: AsyncSession, *, name: str, kontakt: str | None, adresse: str | None
|
||||
db: AsyncSession,
|
||||
*,
|
||||
name: str,
|
||||
kontakt: str | None,
|
||||
adresse: str | None,
|
||||
kategorie: ExterneStelleKategorie = ExterneStelleKategorie.sonstiges,
|
||||
) -> ExterneStelle:
|
||||
stelle = ExterneStelle(name=name, kontakt=kontakt, adresse=adresse)
|
||||
stelle = ExterneStelle(name=name, kontakt=kontakt, adresse=adresse, kategorie=kategorie)
|
||||
db.add(stelle)
|
||||
await db.flush()
|
||||
return stelle
|
||||
|
||||
@@ -67,6 +67,7 @@ async def erstelle_wartungsauftrag(
|
||||
werkstatt_intern: bool,
|
||||
werkstatt_name: str | None,
|
||||
faelligkeit_am: date | None,
|
||||
dienstleister_id: int | None = None,
|
||||
) -> Wartungsauftrag:
|
||||
auftrag = Wartungsauftrag(
|
||||
objekt_id=objekt_id,
|
||||
@@ -77,6 +78,7 @@ async def erstelle_wartungsauftrag(
|
||||
status=WartungsauftragStatus.offen,
|
||||
werkstatt_intern=werkstatt_intern,
|
||||
werkstatt_name=werkstatt_name,
|
||||
dienstleister_id=dienstleister_id,
|
||||
faelligkeit_am=faelligkeit_am,
|
||||
erstellt_am=datetime.now(timezone.utc),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import pytest
|
||||
|
||||
from app.models.objekt import Objekt
|
||||
from tests.conftest import auth_header, login
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_wartungsauftrag_mit_dienstleister_verknuepft(
|
||||
client, db_session, hauptserver_id, admin_user, objekttyp_factory, standort_factory
|
||||
):
|
||||
token = await login(client, "admin1")
|
||||
objekttyp = await objekttyp_factory("RTW-MAINT008")
|
||||
standort = await standort_factory("Standort-MAINT008")
|
||||
objekt = Objekt(
|
||||
code="RTW-MAINT008",
|
||||
name="RTW MAINT008",
|
||||
objekttyp_id=objekttyp.id,
|
||||
standort_id=standort.id,
|
||||
zustaendiger_server_id=hauptserver_id,
|
||||
)
|
||||
db_session.add(objekt)
|
||||
await db_session.flush()
|
||||
await db_session.commit()
|
||||
|
||||
dienstleister = await client.post(
|
||||
"/api/v1/externe-stellen",
|
||||
json={"name": "Werkstatt Muster GmbH", "kategorie": "werkstatt"},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
assert dienstleister.status_code == 201
|
||||
assert dienstleister.json()["kategorie"] == "werkstatt"
|
||||
dienstleister_id = dienstleister.json()["id"]
|
||||
|
||||
auftrag = await client.post(
|
||||
"/api/v1/wartungsauftraege",
|
||||
json={
|
||||
"objekt_id": objekt.id,
|
||||
"wartungsart": "Bremsen prüfen",
|
||||
"werkstatt_intern": False,
|
||||
"dienstleister_id": dienstleister_id,
|
||||
},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
assert auftrag.status_code == 201
|
||||
assert auftrag.json()["dienstleister_id"] == dienstleister_id
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_wartungsauftrag_mit_unbekanntem_dienstleister_404(
|
||||
client, db_session, hauptserver_id, admin_user, objekttyp_factory, standort_factory
|
||||
):
|
||||
token = await login(client, "admin1")
|
||||
objekttyp = await objekttyp_factory("RTW-MAINT008-2")
|
||||
standort = await standort_factory("Standort-MAINT008-2")
|
||||
objekt = Objekt(
|
||||
code="RTW-MAINT008-2",
|
||||
name="RTW MAINT008-2",
|
||||
objekttyp_id=objekttyp.id,
|
||||
standort_id=standort.id,
|
||||
zustaendiger_server_id=hauptserver_id,
|
||||
)
|
||||
db_session.add(objekt)
|
||||
await db_session.flush()
|
||||
await db_session.commit()
|
||||
|
||||
response = await client.post(
|
||||
"/api/v1/wartungsauftraege",
|
||||
json={"objekt_id": objekt.id, "wartungsart": "X", "dienstleister_id": 999999},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_wartungsauftrag_freitext_weiterhin_moeglich(
|
||||
client, db_session, hauptserver_id, admin_user, objekttyp_factory, standort_factory
|
||||
):
|
||||
"""Regressionstest: bestehendes Freitext-Feld funktioniert unverändert,
|
||||
kein Zwang zur dienstleister_id."""
|
||||
token = await login(client, "admin1")
|
||||
objekttyp = await objekttyp_factory("RTW-MAINT008-3")
|
||||
standort = await standort_factory("Standort-MAINT008-3")
|
||||
objekt = Objekt(
|
||||
code="RTW-MAINT008-3",
|
||||
name="RTW MAINT008-3",
|
||||
objekttyp_id=objekttyp.id,
|
||||
standort_id=standort.id,
|
||||
zustaendiger_server_id=hauptserver_id,
|
||||
)
|
||||
db_session.add(objekt)
|
||||
await db_session.flush()
|
||||
await db_session.commit()
|
||||
|
||||
response = await client.post(
|
||||
"/api/v1/wartungsauftraege",
|
||||
json={"objekt_id": objekt.id, "wartungsart": "X", "werkstatt_intern": False, "werkstatt_name": "Freitext GmbH"},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
assert response.status_code == 201
|
||||
assert response.json()["werkstatt_name"] == "Freitext GmbH"
|
||||
assert response.json()["dienstleister_id"] is None
|
||||
@@ -98,6 +98,7 @@ export interface Wartungsauftrag {
|
||||
status: "offen" | "in_arbeit" | "erledigt";
|
||||
werkstatt_intern: boolean;
|
||||
werkstatt_name: string | null;
|
||||
dienstleister_id: number | null;
|
||||
faelligkeit_am: string | null;
|
||||
erstellt_am: string;
|
||||
erledigt_am: string | null;
|
||||
@@ -489,9 +490,12 @@ export interface Ressourcenbuchung {
|
||||
erstellt_am: string;
|
||||
}
|
||||
|
||||
export type ExterneStelleKategorie = "werkstatt" | "lieferant" | "verleiher" | "sonstiges";
|
||||
|
||||
export interface ExterneStelle {
|
||||
id: number;
|
||||
name: string;
|
||||
kategorie: ExterneStelleKategorie;
|
||||
kontakt: string | null;
|
||||
adresse: string | null;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
|
||||
|
||||
import { apiRequest } from "../api/client";
|
||||
import { useAuth } from "../auth/AuthContext";
|
||||
import type { FaelligeWartung, Wartungsauftrag, WartungsauftragTeil } from "../api/types";
|
||||
import type { ExterneStelle, FaelligeWartung, Wartungsauftrag, WartungsauftragTeil } from "../api/types";
|
||||
import { DokumentePanel } from "./DokumentePanel";
|
||||
|
||||
interface Props {
|
||||
@@ -36,6 +36,8 @@ export function WartungSection({ objektId, onFehler }: Props) {
|
||||
const [neuArt, setNeuArt] = useState("");
|
||||
const [neuExtern, setNeuExtern] = useState(false);
|
||||
const [neuWerkstattName, setNeuWerkstattName] = useState("");
|
||||
const [neuDienstleisterId, setNeuDienstleisterId] = useState<number | "">("");
|
||||
const [werkstaetten, setWerkstaetten] = useState<ExterneStelle[]>([]);
|
||||
|
||||
async function laden() {
|
||||
try {
|
||||
@@ -55,6 +57,19 @@ export function WartungSection({ objektId, onFehler }: Props) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [objektId]);
|
||||
|
||||
useEffect(() => {
|
||||
// MAINT-008: strukturierte Dienstleister (kategorie=werkstatt) als
|
||||
// Alternative zum Freitext-Feld - nur wenn "externe Werkstatt" gewählt
|
||||
// ist, wird geladen (kein unnötiger Request im Normalfall).
|
||||
if (!neuExtern || werkstaetten.length > 0) return;
|
||||
apiRequest<ExterneStelle[]>("/externe-stellen")
|
||||
.then((alle) => setWerkstaetten(alle.filter((s) => s.kategorie === "werkstatt")))
|
||||
.catch(() => {
|
||||
/* optional - Freitext bleibt nutzbar */
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [neuExtern]);
|
||||
|
||||
async function auftragAnlegen() {
|
||||
if (!neuArt) {
|
||||
onFehler("Wartungsart angeben.");
|
||||
@@ -67,12 +82,14 @@ export function WartungSection({ objektId, onFehler }: Props) {
|
||||
objekt_id: objektId,
|
||||
wartungsart: neuArt,
|
||||
werkstatt_intern: !neuExtern,
|
||||
werkstatt_name: neuExtern ? neuWerkstattName || null : null,
|
||||
werkstatt_name: neuExtern && neuDienstleisterId === "" ? neuWerkstattName || null : null,
|
||||
dienstleister_id: neuExtern && neuDienstleisterId !== "" ? neuDienstleisterId : null,
|
||||
},
|
||||
});
|
||||
setNeuArt("");
|
||||
setNeuExtern(false);
|
||||
setNeuWerkstattName("");
|
||||
setNeuDienstleisterId("");
|
||||
await laden();
|
||||
} catch {
|
||||
onFehler("Wartungsauftrag konnte nicht angelegt werden.");
|
||||
@@ -174,12 +191,28 @@ export function WartungSection({ objektId, onFehler }: Props) {
|
||||
externe Werkstatt
|
||||
</label>
|
||||
{neuExtern && (
|
||||
<input
|
||||
className="input"
|
||||
placeholder="Werkstatt-Name"
|
||||
value={neuWerkstattName}
|
||||
onChange={(e) => setNeuWerkstattName(e.target.value)}
|
||||
/>
|
||||
<>
|
||||
<select
|
||||
className="input"
|
||||
value={neuDienstleisterId}
|
||||
onChange={(e) => setNeuDienstleisterId(e.target.value ? Number(e.target.value) : "")}
|
||||
>
|
||||
<option value="">Werkstatt wählen (optional)…</option>
|
||||
{werkstaetten.map((w) => (
|
||||
<option key={w.id} value={w.id}>
|
||||
{w.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{neuDienstleisterId === "" && (
|
||||
<input
|
||||
className="input"
|
||||
placeholder="oder Werkstatt-Name (Freitext)"
|
||||
value={neuWerkstattName}
|
||||
onChange={(e) => setNeuWerkstattName(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<button className="btn btn-primary" onClick={auftragAnlegen}>
|
||||
Auftrag anlegen
|
||||
@@ -199,7 +232,13 @@ export function WartungSection({ objektId, onFehler }: Props) {
|
||||
<span>
|
||||
{a.wartungsart}{" "}
|
||||
<span className="text-muted">
|
||||
({a.werkstatt_intern ? "intern" : a.werkstatt_name || "extern"})
|
||||
(
|
||||
{a.werkstatt_intern
|
||||
? "intern"
|
||||
: werkstaetten.find((w) => w.id === a.dienstleister_id)?.name ||
|
||||
a.werkstatt_name ||
|
||||
"extern"}
|
||||
)
|
||||
</span>
|
||||
</span>
|
||||
<span className={`badge ${STATUS_BADGE[a.status]}`}>{STATUS_LABEL[a.status]}</span>
|
||||
|
||||
Reference in new issue
Block a user