feat(vorlage): Vorlage direkt editierbar ohne Versionszwang (Nutzer-Vorgabe)
CI / backend-tests (push) Successful in 2m0s
CI / frontend-build (push) Successful in 18s

Bisher wurde die Sollmenge einer Objektposition live aus der Vorlagenposition
gelesen (objekt.vorlage_id -> vorlagenposition.sollmenge), Bearbeiten der
Vorlage erzeugte deshalb zwingend eine neue Version - sonst hätte sich jede
Änderung rückwirkend auf alle Objekte ausgewirkt, die diese Vorlage nutzen.

- Neue Spalte objektposition.sollmenge_vorlage (Migration 0020): kopiert die
  Sollmenge fest bei Anlage/Duplizieren statt live nachzuschlagen
- sollmenge_effektiv(position) liest jetzt nur noch override/sollmenge_vorlage,
  kein DB-Lookup mehr nötig (sollmenge_map() entfernt)
- Neuer Endpoint PUT /vorlagen/{id}/positionen: ersetzt Positionen direkt,
  ohne neue Version. Bestehende Objekte unbetroffen, nur neu angelegte nutzen
  die aktualisierten Werte (Test: test_aktualisiere_positionen_wirkt_nicht_
  rueckwirkend_auf_bestehendes_objekt)
- POST /vorlagen/{id}/neue-version bleibt zusätzlich bestehen (nicht entfernt)
- Frontend: "Neue Version speichern" -> "Positionen speichern" (PUT statt POST)
- 140 Tests grün

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
2026-09-05 22:33:08 +02:00
co-authored by Claude Sonnet 5
parent cce643fbf8
commit 0249921bd5
9 changed files with 175 additions and 36 deletions
+12 -8
View File
@@ -45,17 +45,20 @@ export function VorlageSection({ vorlagen, objekttypen, materialien, faecher, on
setNeueVersionZeilen((bisherige) => bisherige.map((z, i) => (i === index ? { ...z, ...zeile } : z)));
}
async function neueVersionSpeichern(vorlageId: number) {
async function positionenSpeichern(vorlageId: number) {
const positionen = neueVersionZeilen
.filter((z) => z.materialId !== "")
.map((z) => ({ material_id: z.materialId, fach: z.fach || null, sollmenge: z.sollmenge }));
setWirdVersioniert(true);
try {
await apiRequest(`/vorlagen/${vorlageId}/neue-version`, { method: "POST", body: { positionen } });
// Nutzer-Vorgabe 2026-09-05: Vorlage muss keine Versionen haben - ersetzt
// die Positionen direkt (PUT), keine "neue Version" mehr. Bereits
// angelegte Objekte sind unbetroffen (Sollmenge fest kopiert, Backend).
await apiRequest(`/vorlagen/${vorlageId}/positionen`, { method: "PUT", body: { positionen } });
setAusgeklapptId(null);
await onGeaendert();
} catch {
onFehler("Neue Version konnte nicht angelegt werden.");
onFehler("Positionen konnten nicht gespeichert werden.");
} finally {
setWirdVersioniert(false);
}
@@ -164,14 +167,15 @@ export function VorlageSection({ vorlagen, objekttypen, materialien, faecher, on
{v.name} <span className="badge badge-neutral">v{v.version}</span>
</span>
<button className="btn btn-secondary" onClick={() => versionBearbeiten(v)}>
{ausgeklapptId === v.id ? "Schließen" : "Positionen / neue Version"}
{ausgeklapptId === v.id ? "Schließen" : "Positionen bearbeiten"}
</button>
</div>
{ausgeklapptId === v.id && (
<div className="stack" style={{ marginTop: "0.6rem" }}>
<p className="text-muted" style={{ marginBottom: 0 }}>
Änderungen erzeugen eine neue Version die aktuelle v{v.version} bleibt unverändert
erhalten.
Änderungen wirken direkt auf diese Vorlage, ohne neue Version. Bereits angelegte
Objekte behalten ihre bisherige Sollmenge nur neu angelegte Objekte nutzen die
aktualisierten Werte.
</p>
{neueVersionZeilen.map((zeile, i) => (
<div className="row" key={i}>
@@ -211,10 +215,10 @@ export function VorlageSection({ vorlagen, objekttypen, materialien, faecher, on
</button>
<button
className="btn btn-primary"
onClick={() => neueVersionSpeichern(v.id)}
onClick={() => positionenSpeichern(v.id)}
disabled={wirdVersioniert}
>
Neue Version speichern
Positionen speichern
</button>
</div>
)}