fix(assets): ASSET-009 Zählerstand-Panel als Opt-in je Objekttyp
Nutzer-Fund: Panel war ungeachtet des Objekttyps in jeder Akte sichtbar, auch bei einem Rettungsrucksack ohne Kilometerstand/Betriebsstunden. Neues Feld Objekttyp.zaehler_typ (keiner/kilometer/betriebsstunden/beide, Migration 0058, bestehende Fahrzeug-Objekttypen automatisch auf "beide" migriert) steuert jetzt Sichtbarkeit UND welche Felder das Panel zeigt - gleiches Opt-in-Muster wie ressourcenbuchung_aktiv (ASSET-012). Admin-UI in ObjekttypSection.tsx ergänzt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VDMvneg6wKfXdXvTYNd3dN
This commit is contained in:
1 parent
e5bb213cca
commit
29b334578f
12 files changed
+251
-51
No files matched your search
@@ -15677,3 +15677,48 @@ Keine Commits in dieser Session.
|
||||
- arbeitskacheln/10_maintenance.md | 6 +-----
|
||||
|
||||
---
|
||||
## 2026-09-13 23:05 – 23:08 (3m)
|
||||
**Beschreibung:** Claude Code Session
|
||||
**Projekt:** asb-material
|
||||
|
||||
### Commits
|
||||
- e5bb213 fix(akte): Grid-Overlap bei Nicht-Fahrzeug-Objekten auf Desktop-Breite
|
||||
|
||||
### Geänderte Dateien
|
||||
- DEVLOG.md | 36 ++++++++++++++++++++++++++++++++++++
|
||||
- frontend/src/components/ZaehlerstandPanel.tsx | 63 +++++++++++++++++++++++++++++++++++++++------------------------
|
||||
- frontend/src/pages/AktePage.tsx | 12 +++++++-----
|
||||
- frontend/src/styles/global/akte-grid.css | 4 ++++
|
||||
- frontend/src/styles/global/responsive.css | 12 ++++++++++++
|
||||
|
||||
---
|
||||
## 2026-09-13 23:10 – 23:11 (0m)
|
||||
**Beschreibung:** Claude Code Session
|
||||
**Projekt:** asb-material
|
||||
|
||||
### Commits
|
||||
Keine Commits in dieser Session.
|
||||
|
||||
### Geänderte Dateien
|
||||
- DEVLOG.md | 36 ++++++++++++++++++++++++++++++++++++
|
||||
- frontend/src/components/ZaehlerstandPanel.tsx | 63 +++++++++++++++++++++++++++++++++++++++------------------------
|
||||
- frontend/src/pages/AktePage.tsx | 12 +++++++-----
|
||||
- frontend/src/styles/global/akte-grid.css | 4 ++++
|
||||
- frontend/src/styles/global/responsive.css | 12 ++++++++++++
|
||||
|
||||
---
|
||||
## 2026-09-13 23:11 – 23:11 (0m)
|
||||
**Beschreibung:** Claude Code Session
|
||||
**Projekt:** asb-material
|
||||
|
||||
### Commits
|
||||
Keine Commits in dieser Session.
|
||||
|
||||
### Geänderte Dateien
|
||||
- DEVLOG.md | 36 ++++++++++++++++++++++++++++++++++++
|
||||
- frontend/src/components/ZaehlerstandPanel.tsx | 63 +++++++++++++++++++++++++++++++++++++++------------------------
|
||||
- frontend/src/pages/AktePage.tsx | 12 +++++++-----
|
||||
- frontend/src/styles/global/akte-grid.css | 4 ++++
|
||||
- frontend/src/styles/global/responsive.css | 12 ++++++++++++
|
||||
|
||||
---
|
||||
@@ -386,12 +386,26 @@ Details zu ASSET-001 … ASSET-010. ASSET-010 ergänzt am 2026-09-09 aus Open-So
|
||||
Rückwärtslauf mit 409 ab außer bei `RolleTyp.administration`, legt
|
||||
`Fahrzeugdetails`-Zeile bei Bedarf automatisch an (auch für
|
||||
Nicht-Fahrzeuge). Audit-Log-Eintrag `zaehlerstand_aktualisiert`. Frontend:
|
||||
neue eigenständige Komponente `ZaehlerstandPanel.tsx` in `AktePage.tsx`,
|
||||
bewusst OHNE `istFahrzeug`-Gate (für jedes Objekt sichtbar). Tests:
|
||||
Monoton-Validierung, Admin-Override, funktioniert nachweislich mit
|
||||
neue eigenständige Komponente `ZaehlerstandPanel.tsx` in `AktePage.tsx`.
|
||||
Tests: Monoton-Validierung, Admin-Override, funktioniert nachweislich mit
|
||||
`objekt_mit_position` (KEIN Fahrzeugtyp) — erfüllt exakt das
|
||||
Akzeptanzkriterium "Fahrzeug UND Nicht-Fahrzeug nutzen denselben
|
||||
Mechanismus". `backend/tests/test_fahrzeugdetails.py`.
|
||||
**Bugfix 2026-09-13 (Nutzer-Fund per Screenshot):** "bewusst OHNE
|
||||
istFahrzeug-Gate" oben war ein Fehler in der Praxis — das Panel war
|
||||
dadurch in JEDER Akte sichtbar, auch bei einem Rettungsrucksack ohne
|
||||
Kilometerstand/Betriebsstunden. Auf Opt-in je Objekttyp umgestellt
|
||||
(gleiches Muster wie `ressourcenbuchung_aktiv`, ASSET-012): neues Feld
|
||||
`Objekttyp.zaehler_typ` (Enum `keiner`/`kilometer`/`betriebsstunden`/
|
||||
`beide`, Migration `0058_objekttyp_zaehler_typ.py`, bestehende
|
||||
Fahrzeug-Objekttypen automatisch auf `beide` migriert). `AkteRead.
|
||||
zaehler_typ` steuert die bedingte Anzeige in `AktePage.tsx` UND welche(s)
|
||||
Feld(er) `ZaehlerstandPanel.tsx` überhaupt zeigt (nicht mehr immer beide).
|
||||
Admin-UI: Auswahlfeld „Zählerstand in der Akte" in `ObjekttypSection.tsx`.
|
||||
Der `PATCH /objekte/{id}/zaehlerstand`-Endpunkt selbst bleibt ungegatet
|
||||
(reine Anzeige-Einschränkung, kein Zugriffsschutz nötig). Test:
|
||||
`test_akte_zaehler_typ_folgt_objekttyp_und_ist_default_keiner`
|
||||
(`backend/tests/test_akte.py`).
|
||||
|
||||
## ASSET-011 — Externer Verleih mit Rückgabe-Tracking (P2, neu, Referenz: HiOrg-Server)
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
"""ASSET-009-Bugfix: Zählerstand-Panel war ungeachtet des Objekttyps in
|
||||
jeder Akte sichtbar (auch bei Rettungsrucksäcken ohne km/Betriebsstunden).
|
||||
Opt-in je Objekttyp, gleiches Muster wie ressourcenbuchung_aktiv.
|
||||
|
||||
Revision ID: 0058_objekttyp_zaehler_typ
|
||||
Revises: 0057_benutzer_standort_zugriff
|
||||
Create Date: 2026-09-13
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "0058_objekttyp_zaehler_typ"
|
||||
down_revision: Union[str, None] = "0057_benutzer_standort_zugriff"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.execute("CREATE TYPE zaehler_typ AS ENUM ('keiner', 'kilometer', 'betriebsstunden', 'beide')")
|
||||
op.execute(
|
||||
"ALTER TABLE objekttyp ADD COLUMN zaehler_typ zaehler_typ NOT NULL DEFAULT 'keiner'"
|
||||
)
|
||||
# Bestehende Fahrzeug-Objekttypen hatten das Zähler-Panel faktisch schon
|
||||
# genutzt (Fahrzeugdetails.kilometerstand/betriebsstunden existieren seit
|
||||
# Modul Fahrzeuge) - Migration setzt sie automatisch auf "beide", damit
|
||||
# sich für diese produktiv genutzten Fälle nichts sichtbar ändert.
|
||||
op.execute("UPDATE objekttyp SET zaehler_typ = 'beide' WHERE ist_zugfahrzeug = true")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.execute("ALTER TABLE objekttyp DROP COLUMN zaehler_typ")
|
||||
op.execute("DROP TYPE zaehler_typ")
|
||||
@@ -7,6 +7,16 @@ from sqlalchemy.orm import Mapped, mapped_column
|
||||
from app.db.base import Base
|
||||
|
||||
|
||||
class ZaehlerTyp(str, enum.Enum):
|
||||
keiner = "keiner"
|
||||
kilometer = "kilometer"
|
||||
betriebsstunden = "betriebsstunden"
|
||||
beide = "beide"
|
||||
|
||||
|
||||
zaehler_typ_pg = PgEnum(ZaehlerTyp, name="zaehler_typ", create_type=False)
|
||||
|
||||
|
||||
class MaterialTyp(str, enum.Enum):
|
||||
standard = "standard"
|
||||
ablauf_charge = "ablauf_charge"
|
||||
@@ -57,6 +67,12 @@ class Objekttyp(Base):
|
||||
# (Verbrauchsmaterial-Rucksäcke etc.) ergibt eine Buchungskalender-Ansicht
|
||||
# keinen Sinn. Jetzt Opt-in je Objekttyp statt global erzwungen.
|
||||
ressourcenbuchung_aktiv: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||
# ASSET-009 Bugfix (Nutzer-Fund 2026-09-13): Zählerstand-Panel war in
|
||||
# JEDER Akte sichtbar, unabhängig vom Objekttyp - ein Rettungsrucksack
|
||||
# hat weder Kilometerstand noch Betriebsstunden. Gleiches Opt-in-Muster
|
||||
# wie ressourcenbuchung_aktiv, hier als Enum statt bool, da die Kachel
|
||||
# selbst zwischen km/Betriebsstunden/beidem/keinem unterscheidet.
|
||||
zaehler_typ: Mapped[ZaehlerTyp] = mapped_column(zaehler_typ_pg, nullable=False, default=ZaehlerTyp.keiner)
|
||||
|
||||
|
||||
class Nummernschema(Base):
|
||||
|
||||
@@ -41,6 +41,7 @@ class AkteRead(BaseModel):
|
||||
einsatzbereitschaft_gruende: list[str]
|
||||
objekttyp_name: str
|
||||
ressourcenbuchung_aktiv: bool
|
||||
zaehler_typ: str
|
||||
standort_name: str
|
||||
fahrzeugdetails: FahrzeugdetailsRead | None
|
||||
zustaendige_benutzer: list[ZustaendigerBenutzer]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from app.models.stammdaten import MaterialTyp
|
||||
from app.models.stammdaten import MaterialTyp, ZaehlerTyp
|
||||
|
||||
|
||||
class BereichCreate(BaseModel):
|
||||
@@ -91,6 +91,7 @@ class ObjekttypCreate(BaseModel):
|
||||
name: str
|
||||
ist_zugfahrzeug: bool = False
|
||||
ressourcenbuchung_aktiv: bool = False
|
||||
zaehler_typ: ZaehlerTyp = ZaehlerTyp.keiner
|
||||
|
||||
|
||||
class ObjekttypRead(ObjekttypCreate):
|
||||
@@ -104,6 +105,7 @@ class ObjekttypUpdate(BaseModel):
|
||||
name: str | None = None
|
||||
ist_zugfahrzeug: bool | None = None
|
||||
ressourcenbuchung_aktiv: bool | None = None
|
||||
zaehler_typ: ZaehlerTyp | None = None
|
||||
|
||||
|
||||
class FachCreate(BaseModel):
|
||||
|
||||
@@ -140,6 +140,7 @@ async def akte_fuer_objekt(db: AsyncSession, objekt_id: int) -> AkteRead | None:
|
||||
einsatzbereitschaft_gruende=readiness["gruende"],
|
||||
objekttyp_name=objekttyp.name if objekttyp else "",
|
||||
ressourcenbuchung_aktiv=objekttyp.ressourcenbuchung_aktiv if objekttyp else False,
|
||||
zaehler_typ=objekttyp.zaehler_typ.value if objekttyp else "keiner",
|
||||
standort_name=standort.name if standort else "",
|
||||
fahrzeugdetails=FahrzeugdetailsRead.model_validate(fahrzeugdetails) if fahrzeugdetails else None,
|
||||
zustaendige_benutzer=zustaendige,
|
||||
|
||||
@@ -79,6 +79,30 @@ async def test_akte_ressourcenbuchung_aktiv_folgt_objekttyp_flag(client, objekt_
|
||||
assert antwort.json()["ressourcenbuchung_aktiv"] is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_akte_zaehler_typ_folgt_objekttyp_und_ist_default_keiner(client, objekt_mit_position, admin_user):
|
||||
"""ASSET-009-Bugfix (Nutzer-Fund 2026-09-13): Zählerstand-Panel war
|
||||
ungeachtet des Objekttyps immer sichtbar (auch bei einem
|
||||
Rettungsrucksack) - jetzt Opt-in je Objekttyp wie ressourcenbuchung_aktiv."""
|
||||
objekt, _material = objekt_mit_position
|
||||
token = await login(client, "admin1")
|
||||
|
||||
ohne_aenderung = await client.get(f"/api/v1/akte/objekt/{objekt.id}", headers=auth_header(token))
|
||||
assert ohne_aenderung.json()["zaehler_typ"] == "keiner"
|
||||
|
||||
objekttyp_id = objekt.objekttyp_id
|
||||
aendern = await client.patch(
|
||||
f"/api/v1/objekttypen/{objekttyp_id}",
|
||||
json={"zaehler_typ": "betriebsstunden"},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
assert aendern.status_code == 200
|
||||
assert aendern.json()["zaehler_typ"] == "betriebsstunden"
|
||||
|
||||
antwort = await client.get(f"/api/v1/akte/objekt/{objekt.id}", headers=auth_header(token))
|
||||
assert antwort.json()["zaehler_typ"] == "betriebsstunden"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_akte_readiness_unbekannt_bei_nie_kontrolliert(client, objekt_mit_position, admin_user):
|
||||
"""UI-005: ein Objekt ohne jede Auffälligkeit, aber noch nie kontrolliert,
|
||||
|
||||
@@ -413,6 +413,8 @@ export interface Modell {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type ZaehlerTyp = "keiner" | "kilometer" | "betriebsstunden" | "beide";
|
||||
|
||||
export interface Objekttyp {
|
||||
id: number;
|
||||
bereich_id: number;
|
||||
@@ -420,6 +422,7 @@ export interface Objekttyp {
|
||||
name: string;
|
||||
ist_zugfahrzeug: boolean;
|
||||
ressourcenbuchung_aktiv: boolean;
|
||||
zaehler_typ: ZaehlerTyp;
|
||||
}
|
||||
|
||||
export interface Nummernschema {
|
||||
@@ -478,6 +481,7 @@ export interface Akte {
|
||||
einsatzbereitschaft_gruende: string[];
|
||||
objekttyp_name: string;
|
||||
ressourcenbuchung_aktiv: boolean;
|
||||
zaehler_typ: ZaehlerTyp;
|
||||
standort_name: string;
|
||||
fahrzeugdetails: Fahrzeugdetails | null;
|
||||
zustaendige_benutzer: ZustaendigerBenutzer[];
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import { apiRequest, ApiError } from "../api/client";
|
||||
import type { Fahrzeugdetails } from "../api/types";
|
||||
import type { Fahrzeugdetails, ZaehlerTyp } from "../api/types";
|
||||
|
||||
interface Props {
|
||||
objektId: number;
|
||||
zaehlerTyp: ZaehlerTyp;
|
||||
fahrzeugdetails: Fahrzeugdetails | null;
|
||||
onAktualisiert: () => Promise<void>;
|
||||
}
|
||||
|
||||
// ASSET-009: generischer Nutzungszähler - bewusst NICHT auf Fahrzeuge
|
||||
// beschränkt (siehe Kachel-Nachtrag), deshalb hier ohne istFahrzeug-Prüfung
|
||||
// immer angezeigt. Kilometerstand bleibt bei einem Nicht-Fahrzeug (z.B.
|
||||
// Stromerzeuger) einfach ungenutzt/leer.
|
||||
export function ZaehlerstandPanel({ objektId, fahrzeugdetails, onAktualisiert }: Props) {
|
||||
// ASSET-009-Bugfix (Nutzer-Fund 2026-09-13): war ungeachtet des Objekttyps
|
||||
// IMMER sichtbar, z.B. auch bei einem Rettungsrucksack ohne Kilometerstand/
|
||||
// Betriebsstunden - jetzt Opt-in über Objekttyp.zaehler_typ (gleiches
|
||||
// Muster wie ressourcenbuchung_aktiv), AktePage rendert diese Komponente
|
||||
// nur noch bei zaehler_typ !== "keiner". Zeigt zusätzlich nur das/die
|
||||
// tatsächlich konfigurierte(n) Feld(er) statt immer beide.
|
||||
export function ZaehlerstandPanel({ objektId, zaehlerTyp, fahrzeugdetails, onAktualisiert }: Props) {
|
||||
const zeigtKilometer = zaehlerTyp === "kilometer" || zaehlerTyp === "beide";
|
||||
const zeigtBetriebsstunden = zaehlerTyp === "betriebsstunden" || zaehlerTyp === "beide";
|
||||
const [kilometerstand, setKilometerstand] = useState("");
|
||||
const [betriebsstunden, setBetriebsstunden] = useState("");
|
||||
const [wirdGespeichert, setWirdGespeichert] = useState(false);
|
||||
@@ -49,44 +54,52 @@ export function ZaehlerstandPanel({ objektId, fahrzeugdetails, onAktualisiert }:
|
||||
<div className="card">
|
||||
<h3 style={{ marginTop: 0 }}>Zählerstand</h3>
|
||||
<div style={{ marginBottom: "0.75rem" }}>
|
||||
<div>
|
||||
<span className="text-muted">Kilometerstand: </span>
|
||||
{fahrzeugdetails?.kilometerstand ?? "–"} km
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-muted">Betriebsstunden: </span>
|
||||
{fahrzeugdetails?.betriebsstunden ?? "–"} h
|
||||
</div>
|
||||
{zeigtKilometer && (
|
||||
<div>
|
||||
<span className="text-muted">Kilometerstand: </span>
|
||||
{fahrzeugdetails?.kilometerstand ?? "–"} km
|
||||
</div>
|
||||
)}
|
||||
{zeigtBetriebsstunden && (
|
||||
<div>
|
||||
<span className="text-muted">Betriebsstunden: </span>
|
||||
{fahrzeugdetails?.betriebsstunden ?? "–"} h
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Card kann schmal sein (Sidebar-Layout) - Felder untereinander statt
|
||||
in einer Reihe, sonst schneidet der Platzhaltertext ab bzw.
|
||||
überlappt der Zahlen-Spinner den Text (Bug-Fund per Screenshot). */}
|
||||
<div className="stack" style={{ gap: "0.5rem" }}>
|
||||
<div>
|
||||
<label className="text-muted" style={{ fontSize: "0.85rem", display: "block", marginBottom: "0.2rem" }}>
|
||||
Neuer Kilometerstand
|
||||
</label>
|
||||
<input
|
||||
className="input"
|
||||
style={{ width: "100%", boxSizing: "border-box" }}
|
||||
type="number"
|
||||
value={kilometerstand}
|
||||
onChange={(e) => setKilometerstand(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-muted" style={{ fontSize: "0.85rem", display: "block", marginBottom: "0.2rem" }}>
|
||||
Neue Betriebsstunden
|
||||
</label>
|
||||
<input
|
||||
className="input"
|
||||
style={{ width: "100%", boxSizing: "border-box" }}
|
||||
type="number"
|
||||
step="0.1"
|
||||
value={betriebsstunden}
|
||||
onChange={(e) => setBetriebsstunden(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
{zeigtKilometer && (
|
||||
<div>
|
||||
<label className="text-muted" style={{ fontSize: "0.85rem", display: "block", marginBottom: "0.2rem" }}>
|
||||
Neuer Kilometerstand
|
||||
</label>
|
||||
<input
|
||||
className="input"
|
||||
style={{ width: "100%", boxSizing: "border-box" }}
|
||||
type="number"
|
||||
value={kilometerstand}
|
||||
onChange={(e) => setKilometerstand(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{zeigtBetriebsstunden && (
|
||||
<div>
|
||||
<label className="text-muted" style={{ fontSize: "0.85rem", display: "block", marginBottom: "0.2rem" }}>
|
||||
Neue Betriebsstunden
|
||||
</label>
|
||||
<input
|
||||
className="input"
|
||||
style={{ width: "100%", boxSizing: "border-box" }}
|
||||
type="number"
|
||||
step="0.1"
|
||||
value={betriebsstunden}
|
||||
onChange={(e) => setBetriebsstunden(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={melden}
|
||||
|
||||
@@ -268,13 +268,16 @@ export function AktePage() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="akte-zaehlerstand">
|
||||
<ZaehlerstandPanel
|
||||
objektId={objekt.id}
|
||||
fahrzeugdetails={akte.fahrzeugdetails}
|
||||
onAktualisiert={async () => laden()}
|
||||
/>
|
||||
</div>
|
||||
{akte.zaehler_typ !== "keiner" && (
|
||||
<div className="akte-zaehlerstand">
|
||||
<ZaehlerstandPanel
|
||||
objektId={objekt.id}
|
||||
zaehlerTyp={akte.zaehler_typ}
|
||||
fahrzeugdetails={akte.fahrzeugdetails}
|
||||
onAktualisiert={async () => laden()}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="akte-wartung">
|
||||
<WartungSection objektId={objekt.id} onFehler={setFehler} />
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import { apiRequest } from "../../api/client";
|
||||
import type { Bereich, Kategorie, Nummernschema, Objekttyp } from "../../api/types";
|
||||
import type { Bereich, Kategorie, Nummernschema, Objekttyp, ZaehlerTyp } from "../../api/types";
|
||||
import { FormField } from "../../components/ui/form-field";
|
||||
|
||||
const ZAEHLER_TYP_LABEL: Record<ZaehlerTyp, string> = {
|
||||
keiner: "Kein Zähler",
|
||||
kilometer: "Kilometerstand",
|
||||
betriebsstunden: "Betriebsstunden",
|
||||
beide: "Kilometerstand + Betriebsstunden",
|
||||
};
|
||||
|
||||
interface Props {
|
||||
bereiche: Bereich[];
|
||||
kategorien: Kategorie[];
|
||||
@@ -31,6 +38,8 @@ export function ObjekttypSection({
|
||||
const [bearbeitungsZugfahrzeug, setBearbeitungsZugfahrzeug] = useState(false);
|
||||
const [ressourcenbuchungAktiv, setRessourcenbuchungAktiv] = useState(false);
|
||||
const [bearbeitungsRessourcenbuchung, setBearbeitungsRessourcenbuchung] = useState(false);
|
||||
const [zaehlerTyp, setZaehlerTyp] = useState<ZaehlerTyp>("keiner");
|
||||
const [bearbeitungsZaehlerTyp, setBearbeitungsZaehlerTyp] = useState<ZaehlerTyp>("keiner");
|
||||
const [schemaBearbeitenId, setSchemaBearbeitenId] = useState<number | null>(null);
|
||||
const [schemaPraefix, setSchemaPraefix] = useState("");
|
||||
const [schemaStellenzahl, setSchemaStellenzahl] = useState(5);
|
||||
@@ -45,6 +54,7 @@ export function ObjekttypSection({
|
||||
setBearbeitungsName(o.name);
|
||||
setBearbeitungsZugfahrzeug(o.ist_zugfahrzeug);
|
||||
setBearbeitungsRessourcenbuchung(o.ressourcenbuchung_aktiv);
|
||||
setBearbeitungsZaehlerTyp(o.zaehler_typ);
|
||||
}
|
||||
|
||||
async function speichern(id: number) {
|
||||
@@ -55,6 +65,7 @@ export function ObjekttypSection({
|
||||
name: bearbeitungsName,
|
||||
ist_zugfahrzeug: bearbeitungsZugfahrzeug,
|
||||
ressourcenbuchung_aktiv: bearbeitungsRessourcenbuchung,
|
||||
zaehler_typ: bearbeitungsZaehlerTyp,
|
||||
},
|
||||
});
|
||||
setBearbeiteteId(null);
|
||||
@@ -80,11 +91,13 @@ export function ObjekttypSection({
|
||||
kategorie_id: kategorieId || null,
|
||||
ist_zugfahrzeug: istZugfahrzeug,
|
||||
ressourcenbuchung_aktiv: ressourcenbuchungAktiv,
|
||||
zaehler_typ: zaehlerTyp,
|
||||
},
|
||||
});
|
||||
setName("");
|
||||
setIstZugfahrzeug(false);
|
||||
setRessourcenbuchungAktiv(false);
|
||||
setZaehlerTyp("keiner");
|
||||
await onGeaendert();
|
||||
} catch {
|
||||
onFehler("Objekttyp konnte nicht angelegt werden.");
|
||||
@@ -189,6 +202,22 @@ export function ObjekttypSection({
|
||||
/>
|
||||
Ressourcenbuchung in der Akte anzeigen
|
||||
</label>
|
||||
<FormField label="Zählerstand in der Akte">
|
||||
{(feldProps) => (
|
||||
<select
|
||||
{...feldProps}
|
||||
className="input"
|
||||
value={zaehlerTyp}
|
||||
onChange={(e) => setZaehlerTyp(e.target.value as ZaehlerTyp)}
|
||||
>
|
||||
{Object.entries(ZAEHLER_TYP_LABEL).map(([wert, label]) => (
|
||||
<option key={wert} value={wert}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
</FormField>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
style={{ marginTop: "1.5rem" }}
|
||||
@@ -230,6 +259,17 @@ export function ObjekttypSection({
|
||||
/>
|
||||
Ressourcenbuchung
|
||||
</label>
|
||||
<select
|
||||
className="input"
|
||||
value={bearbeitungsZaehlerTyp}
|
||||
onChange={(e) => setBearbeitungsZaehlerTyp(e.target.value as ZaehlerTyp)}
|
||||
>
|
||||
{Object.entries(ZAEHLER_TYP_LABEL).map(([wert, label]) => (
|
||||
<option key={wert} value={wert}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<button className="btn btn-primary" onClick={() => speichern(o.id)}>
|
||||
Speichern
|
||||
</button>
|
||||
@@ -243,6 +283,9 @@ export function ObjekttypSection({
|
||||
{o.name}
|
||||
{o.ist_zugfahrzeug && <span className="text-muted"> (Zugfahrzeug)</span>}
|
||||
{o.ressourcenbuchung_aktiv && <span className="text-muted"> (Buchungen)</span>}
|
||||
{o.zaehler_typ !== "keiner" && (
|
||||
<span className="text-muted"> ({ZAEHLER_TYP_LABEL[o.zaehler_typ]})</span>
|
||||
)}
|
||||
</span>
|
||||
<button className="btn btn-secondary" onClick={() => bearbeiten(o)}>
|
||||
Bearbeiten
|
||||
|
||||
Reference in new issue
Block a user