Effektive Werte: Immobilien-Bugfix, Bearbeiten; Sidebar-Ebenen, Ring-Klick
Deploy App / deploy (push) Successful in 1m4s
Deploy App / deploy (push) Successful in 1m4s
Immobilien-Bugfix (gravierend): - Der Ist-Wizard belegte den Immobilienwert mit dem EIGENKAPITAL vor (ElementYearPoint.value), waehrend Erfassung und Rechenkern den VERKEHRSWERT erwarten (propertyValue). Der Kern setzte die Zahl als Verkehrswert ein und liess die Hypothek stehen -> das Eigenkapital brach im Ist-Jahr um genau die Hypothek ein, meist ins Negative. - Sichtbar als negative Gesamt-Abweichung trotz reiner Lohnerhoehung und als "wegbrechendes" Wohneigentum in der Vermoegensaufteilung. - Feld ist neu als "Verkehrswert + Restschuld" beschriftet; 3 Regressionstests. Ist-Datensaetze bearbeitbar: - Klick auf die Zeile (oder "Bearbeiten") oeffnet den Satz erneut - neuer Endpunkt PUT /api/plans/<id>/actuals/<setId> - beim Bearbeiten ueberschreiben die Planwerte die erfassten Zahlen nicht Ring-Klick in der Vermoegensaufteilung repariert: - Recharts 3 reicht kein activePayload mehr durch (nur activeIndex) -- der Handler feuerte nie, der Ring zeigte immer das Planende Seitenleiste sauber dreistufig: - Ebene 1 Plaene, Ebene 2 die vier Bereiche mit buendigen Symbolen, Ebene 3 nur die Szenarien (verschachtelt nach Herkunft) Szenario-Liste zeigt neben der Version deren Kommentar. SPEZIFIKATION 0.32 (neue Kapitel 3.9.6, 3.9.7). 275 -> 278 Tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,56 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
import { prisma } from "@/lib/db";
|
||||
import { getCurrentUserId } from "@/lib/session";
|
||||
|
||||
const valueSchema = z.object({
|
||||
value: z.number().min(-1_000_000_000).max(1_000_000_000).optional(),
|
||||
mortgage: z.number().min(0).max(1_000_000_000).optional(),
|
||||
});
|
||||
|
||||
const updateSchema = z.object({
|
||||
recordedOn: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Datum im Format JJJJ-MM-TT"),
|
||||
comment: z.string().max(500).optional(),
|
||||
cash: z.number().min(-1_000_000_000).max(1_000_000_000).nullable().optional(),
|
||||
values: z.record(z.string(), valueSchema),
|
||||
});
|
||||
|
||||
// Ändert einen bestehenden Ist-Satz. Wie beim Anlegen gilt: Ein Ist-Satz ist eine Beobachtung,
|
||||
// keine Planänderung -- er erzeugt also KEINE Szenario-Version. Der ursprüngliche Erfasser
|
||||
// (`createdById`) bleibt stehen; korrigiert wird der Datensatz selbst.
|
||||
export async function PUT(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ planId: string; setId: string }> }
|
||||
) {
|
||||
const userId = await getCurrentUserId();
|
||||
if (!userId) return NextResponse.json({ error: "Nicht authentifiziert." }, { status: 401 });
|
||||
const { planId, setId } = await params;
|
||||
|
||||
const existing = await prisma.actualsSet.findFirst({ where: { id: setId, planId, plan: { userId } } });
|
||||
if (!existing) return NextResponse.json({ error: "Datensatz nicht gefunden." }, { status: 404 });
|
||||
|
||||
const parsed = updateSchema.safeParse(await request.json());
|
||||
if (!parsed.success) {
|
||||
const first = parsed.error.issues[0];
|
||||
return NextResponse.json({ error: first?.message ?? "Ungültige Eingabe." }, { status: 400 });
|
||||
}
|
||||
|
||||
const { recordedOn, comment, cash, values } = parsed.data;
|
||||
const updated = await prisma.actualsSet.update({
|
||||
where: { id: setId },
|
||||
data: {
|
||||
recordedOn: new Date(`${recordedOn}T00:00:00.000Z`),
|
||||
// Für die Berechnung zählt nur die Jahreszahl (siehe POST).
|
||||
year: Number(recordedOn.slice(0, 4)),
|
||||
comment: comment?.trim() || null,
|
||||
cash: typeof cash === "number" ? cash : null,
|
||||
values,
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json({ set: { id: updated.id, year: updated.year } });
|
||||
}
|
||||
|
||||
// Löscht einen Ist-Satz. Ein Ist-Satz ist eine Beobachtung, keine Planänderung -- deshalb
|
||||
// gibt es hier weder Versionierung noch Wiederherstellung.
|
||||
export async function DELETE(
|
||||
|
||||
@@ -24,7 +24,11 @@ export async function GET(_request: NextRequest, { params }: { params: Promise<{
|
||||
_count: { select: { elements: true, versions: true } },
|
||||
// Höchste Version je Szenario -- die Liste zeigt die echte Nummer (z. B. «0.17»),
|
||||
// nicht nur die Hauptversion.
|
||||
versions: { orderBy: [{ major: "desc" }, { minor: "desc" }], take: 1, select: { major: true, minor: true } },
|
||||
versions: {
|
||||
orderBy: [{ major: "desc" }, { minor: "desc" }],
|
||||
take: 1,
|
||||
select: { major: true, minor: true, comment: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
actuals: { orderBy: [{ year: "asc" }, { recordedOn: "asc" }] },
|
||||
@@ -85,6 +89,9 @@ export async function GET(_request: NextRequest, { params }: { params: Promise<{
|
||||
parentScenarioId: s.parentScenarioId,
|
||||
// Die tatsächliche aktuelle Version, nicht nur die Hauptversion. Ohne Historie noch keine.
|
||||
version: top ? `${top.major}.${top.minor}` : null,
|
||||
// Kommentar der aktuellen Version -- gesetzt wird er bei Hauptversionen und beim
|
||||
// Wiederherstellen; Nebenversionen haben in der Regel keinen.
|
||||
versionComment: top?.comment ?? null,
|
||||
elementCount: s._count.elements,
|
||||
phaseCount: computed.phases.length,
|
||||
versionCount: s._count.versions,
|
||||
|
||||
Reference in New Issue
Block a user