fix(objekt): Fahrzeug-Zuordnung als 1:n statt n:m (fachliche Korrektur)
Nutzer-Korrektur: "Ein Objekt gehört immer nur zu genau einem Fahrzeug"
- Spalte gezogen_von_objekt_id -> fahrzeug_id (Rename-Migration 0010)
- Zuvor begonnener n:m-Ansatz (objekt_kopplung Tabelle/Endpoints/Panel)
wieder entfernt, da fachlich falsch
- PATCH /objekte/{id} prüft Existenz + direkten Zyklus (A<->B)
- Frontend: einfache Single-Select "Fahrzeug zuordnen" statt Kopplungs-Panel
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
@@ -6,7 +6,7 @@ export interface Objekt {
|
||||
vorlage_id: number | null;
|
||||
standort_id: number;
|
||||
status: string;
|
||||
gezogen_von_objekt_id: number | null;
|
||||
fahrzeug_id: number | null;
|
||||
}
|
||||
|
||||
export interface Objektposition {
|
||||
|
||||
@@ -33,6 +33,7 @@ export function ObjektSection({
|
||||
const [name, setName] = useState("");
|
||||
const [vorlageId, setVorlageId] = useState<number | "">("");
|
||||
const [standortId, setStandortId] = useState<number | "">("");
|
||||
const [fahrzeugId, setFahrzeugId] = useState<number | "">("");
|
||||
const [wirdAngelegt, setWirdAngelegt] = useState(false);
|
||||
const [ausgeklapptId, setAusgeklapptId] = useState<number | null>(null);
|
||||
const [duplizierId, setDuplizierId] = useState<number | null>(null);
|
||||
@@ -40,11 +41,38 @@ export function ObjektSection({
|
||||
const [neuerCode, setNeuerCode] = useState("");
|
||||
const [neuerName, setNeuerName] = useState("");
|
||||
const [neuerStandortId, setNeuerStandortId] = useState<number | "">("");
|
||||
const [gezogenVonId, setGezogenVonId] = useState<number | "">("");
|
||||
const [kopplungBearbeitenId, setKopplungBearbeitenId] = useState<number | null>(null);
|
||||
const [kopplungAuswahl, setKopplungAuswahl] = useState<number | "">("");
|
||||
const [fahrzeugBearbeitenId, setFahrzeugBearbeitenId] = useState<number | null>(null);
|
||||
const [fahrzeugAuswahl, setFahrzeugAuswahl] = useState<number | "">("");
|
||||
const [suche, setSuche] = useState("");
|
||||
|
||||
function objektName(id: number | null): string {
|
||||
if (id === null) return "";
|
||||
const gefunden = objekte.find((x) => x.id === id);
|
||||
return gefunden ? `${gefunden.name} (${gefunden.code})` : `#${id}`;
|
||||
}
|
||||
|
||||
function fahrzeugBearbeiten(o: Objekt) {
|
||||
if (fahrzeugBearbeitenId === o.id) {
|
||||
setFahrzeugBearbeitenId(null);
|
||||
return;
|
||||
}
|
||||
setFahrzeugBearbeitenId(o.id);
|
||||
setFahrzeugAuswahl(o.fahrzeug_id ?? "");
|
||||
}
|
||||
|
||||
async function fahrzeugSpeichern(objektId: number) {
|
||||
try {
|
||||
await apiRequest(`/objekte/${objektId}`, {
|
||||
method: "PATCH",
|
||||
body: { fahrzeug_id: fahrzeugAuswahl === "" ? null : fahrzeugAuswahl },
|
||||
});
|
||||
setFahrzeugBearbeitenId(null);
|
||||
await onGeaendert();
|
||||
} catch {
|
||||
onFehler("Fahrzeug-Zuordnung konnte nicht gespeichert werden.");
|
||||
}
|
||||
}
|
||||
|
||||
async function codeVorschlagen() {
|
||||
if (!praefix) {
|
||||
onFehler("Präfix eingeben (z. B. RK, FZ).");
|
||||
@@ -91,13 +119,13 @@ export function ObjektSection({
|
||||
objekttyp_id: vorlage?.objekttyp_id,
|
||||
vorlage_id: vorlageId,
|
||||
standort_id: standortId,
|
||||
gezogen_von_objekt_id: gezogenVonId || null,
|
||||
fahrzeug_id: fahrzeugId === "" ? null : fahrzeugId,
|
||||
},
|
||||
});
|
||||
setPraefix("");
|
||||
setCode("");
|
||||
setName("");
|
||||
setGezogenVonId("");
|
||||
setFahrzeugId("");
|
||||
await onGeaendert();
|
||||
} catch {
|
||||
onFehler("Objekt konnte nicht angelegt werden (Code evtl. schon vergeben).");
|
||||
@@ -106,30 +134,6 @@ export function ObjektSection({
|
||||
}
|
||||
}
|
||||
|
||||
function objektName(id: number | null): string {
|
||||
if (id === null) return "";
|
||||
const o = objekte.find((x) => x.id === id);
|
||||
return o ? `${o.name} (${o.code})` : `Objekt ${id}`;
|
||||
}
|
||||
|
||||
function kopplungBearbeiten(o: Objekt) {
|
||||
setKopplungBearbeitenId(kopplungBearbeitenId === o.id ? null : o.id);
|
||||
setKopplungAuswahl(o.gezogen_von_objekt_id ?? "");
|
||||
}
|
||||
|
||||
async function kopplungSpeichern(objektId: number) {
|
||||
try {
|
||||
await apiRequest(`/objekte/${objektId}`, {
|
||||
method: "PATCH",
|
||||
body: { gezogen_von_objekt_id: kopplungAuswahl || null },
|
||||
});
|
||||
setKopplungBearbeitenId(null);
|
||||
await onGeaendert();
|
||||
} catch {
|
||||
onFehler("Kopplung konnte nicht geändert werden.");
|
||||
}
|
||||
}
|
||||
|
||||
function duplizierenStarten(o: Objekt) {
|
||||
setDuplizierId(duplizierId === o.id ? null : o.id);
|
||||
setNeuerPraefix("");
|
||||
@@ -222,16 +226,16 @@ export function ObjektSection({
|
||||
<input className="input" value={name} onChange={(e) => setName(e.target.value)} />
|
||||
</div>
|
||||
<div className="field">
|
||||
<label>Gezogen von (optional, Anhänger-Kopplung)</label>
|
||||
<label>Fahrzeug (optional)</label>
|
||||
<select
|
||||
className="input"
|
||||
value={gezogenVonId}
|
||||
onChange={(e) => setGezogenVonId(Number(e.target.value) || "")}
|
||||
value={fahrzeugId}
|
||||
onChange={(e) => setFahrzeugId(Number(e.target.value) || "")}
|
||||
>
|
||||
<option value="">Kein Zugfahrzeug</option>
|
||||
{objekte.map((o) => (
|
||||
<option key={o.id} value={o.id}>
|
||||
{o.name} ({o.code})
|
||||
<option value="">Kein Fahrzeug</option>
|
||||
{objekte.map((x) => (
|
||||
<option key={x.id} value={x.id}>
|
||||
{x.name} ({x.code})
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
@@ -260,8 +264,8 @@ export function ObjektSection({
|
||||
<div className="row-between" style={{ padding: "0.4rem 0" }}>
|
||||
<span>
|
||||
{o.name} <span className="text-muted">({o.code})</span>
|
||||
{o.gezogen_von_objekt_id !== null && (
|
||||
<span className="text-muted"> – gezogen von {objektName(o.gezogen_von_objekt_id)}</span>
|
||||
{o.fahrzeug_id !== null && (
|
||||
<span className="text-muted"> – Fahrzeug: {objektName(o.fahrzeug_id)}</span>
|
||||
)}
|
||||
</span>
|
||||
<div className="row">
|
||||
@@ -272,8 +276,8 @@ export function ObjektSection({
|
||||
{ausgeklapptId === o.id ? "Positionen ausblenden" : "Ablauf/Charge/SN pflegen"}
|
||||
</button>
|
||||
{!nurPflegen && (
|
||||
<button className="btn btn-secondary" onClick={() => kopplungBearbeiten(o)}>
|
||||
{kopplungBearbeitenId === o.id ? "Schließen" : "Kopplung ändern"}
|
||||
<button className="btn btn-secondary" onClick={() => fahrzeugBearbeiten(o)}>
|
||||
{fahrzeugBearbeitenId === o.id ? "Schließen" : "Fahrzeug zuordnen"}
|
||||
</button>
|
||||
)}
|
||||
{!nurPflegen && (
|
||||
@@ -283,14 +287,14 @@ export function ObjektSection({
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{kopplungBearbeitenId === o.id && (
|
||||
{fahrzeugBearbeitenId === o.id && (
|
||||
<div className="row" style={{ marginTop: "0.5rem", flexWrap: "wrap" }}>
|
||||
<select
|
||||
className="input"
|
||||
value={kopplungAuswahl}
|
||||
onChange={(e) => setKopplungAuswahl(Number(e.target.value) || "")}
|
||||
value={fahrzeugAuswahl}
|
||||
onChange={(e) => setFahrzeugAuswahl(Number(e.target.value) || "")}
|
||||
>
|
||||
<option value="">Kein Zugfahrzeug</option>
|
||||
<option value="">Kein Fahrzeug</option>
|
||||
{objekte
|
||||
.filter((x) => x.id !== o.id)
|
||||
.map((x) => (
|
||||
@@ -299,7 +303,7 @@ export function ObjektSection({
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<button className="btn btn-primary" onClick={() => kopplungSpeichern(o.id)}>
|
||||
<button className="btn btn-primary" onClick={() => fahrzeugSpeichern(o.id)}>
|
||||
Speichern
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user