Bereits an AG übergeben läuft jetzt so, wie du es beschrieben hast:
Bezahlt setzen zählt in der Budgetanzeige automatisch mit. Zusätzlich gibt es unter Neue Ausgabe eine eigene Insel für zusätzlich bereits übergebenes Geld, falls das nicht über einzelne Ausgaben läuft. In den Budgetkarten wird das als gestrichelte Querlinie plus eigenem Chip dargestellt
This commit is contained in:
@@ -159,6 +159,7 @@ export async function POST(_: Request, { params }: Context) {
|
||||
data: {
|
||||
name: asString(previous.name, "Budgetname"),
|
||||
totalBudget: asNumber(previous.totalBudget, "Budgetbetrag"),
|
||||
releasedAmount: asNumber(previous.releasedAmount ?? 0, "Zus\u00e4tzliche Mittel\u00fcbergabe"),
|
||||
colorCode: asString(previous.colorCode, "Budgetfarbe")
|
||||
}
|
||||
});
|
||||
@@ -173,6 +174,7 @@ export async function POST(_: Request, { params }: Context) {
|
||||
id: asString(deleted.id, "Budget-ID"),
|
||||
name: asString(deleted.name, "Budgetname"),
|
||||
totalBudget: asNumber(deleted.totalBudget, "Budgetbetrag"),
|
||||
releasedAmount: asNumber(deleted.releasedAmount ?? 0, "Zus\u00e4tzliche Mittel\u00fcbergabe"),
|
||||
colorCode: asString(deleted.colorCode, "Budgetfarbe"),
|
||||
workingGroupId: asString(deleted.workingGroupId, "AG-ID"),
|
||||
periodId: asString(deleted.periodId, "Zeitraum-ID"),
|
||||
|
||||
@@ -8,11 +8,22 @@ import { canManageBudgets } from "@/lib/domain";
|
||||
import prisma from "@/lib/prisma";
|
||||
import { getCurrentViewer } from "@/lib/session";
|
||||
|
||||
const updateBudgetSchema = z.object({
|
||||
name: z.string().trim().min(2).max(80),
|
||||
totalBudget: z.coerce.number().min(0),
|
||||
colorCode: z.string().regex(/^#([0-9a-fA-F]{6})$/)
|
||||
});
|
||||
const updateBudgetSchema = z
|
||||
.object({
|
||||
name: z.string().trim().min(2).max(80),
|
||||
totalBudget: z.coerce.number().min(0),
|
||||
releasedAmount: z.coerce.number().min(0).optional(),
|
||||
colorCode: z.string().regex(/^#([0-9a-fA-F]{6})$/)
|
||||
})
|
||||
.superRefine((value, ctx) => {
|
||||
if (value.releasedAmount !== undefined && value.releasedAmount > value.totalBudget) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Der bereits an die AG uebergebene Betrag darf das Budget nicht uebersteigen.",
|
||||
path: ["releasedAmount"]
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
type Context = {
|
||||
params: {
|
||||
@@ -43,16 +54,18 @@ export async function PATCH(request: Request, { params }: Context) {
|
||||
const parsed = updateBudgetSchema.safeParse(body);
|
||||
|
||||
if (!parsed.success) {
|
||||
return NextResponse.json({ error: "Budgetname, Betrag oder Farbe sind ungueltig." }, { status: 400 });
|
||||
return NextResponse.json({ error: "Budgetname, Betrag, Mitteluebergabe oder Farbe sind ungueltig." }, { status: 400 });
|
||||
}
|
||||
|
||||
try {
|
||||
const previousBudget = budget;
|
||||
const nextReleasedAmount = parsed.data.releasedAmount ?? Number(previousBudget.releasedAmount);
|
||||
const updatedBudget = await prisma.budget.update({
|
||||
where: { id: params.id },
|
||||
data: {
|
||||
name: parsed.data.name,
|
||||
totalBudget: parsed.data.totalBudget,
|
||||
releasedAmount: nextReleasedAmount,
|
||||
colorCode: parsed.data.colorCode
|
||||
}
|
||||
});
|
||||
@@ -66,6 +79,7 @@ export async function PATCH(request: Request, { params }: Context) {
|
||||
summary: `Budget ${updatedBudget.name} wurde aktualisiert.`,
|
||||
metadata: {
|
||||
totalBudget: parsed.data.totalBudget,
|
||||
releasedAmount: nextReleasedAmount,
|
||||
colorCode: parsed.data.colorCode,
|
||||
rollback: {
|
||||
kind: "budget.update",
|
||||
@@ -99,7 +113,7 @@ export async function DELETE(_: Request, { params }: Context) {
|
||||
}
|
||||
|
||||
if (!canManageBudgets(viewer.role)) {
|
||||
return NextResponse.json({ error: "Nur Vorstand oder Finanz-AG dürfen Budgets löschen." }, { status: 403 });
|
||||
return NextResponse.json({ error: "Nur Vorstand oder Finanz-AG duerfen Budgets loeschen." }, { status: 403 });
|
||||
}
|
||||
|
||||
const budget = await prisma.budget.findUnique({
|
||||
|
||||
@@ -7,13 +7,24 @@ import { canManageBudgets } from "@/lib/domain";
|
||||
import prisma from "@/lib/prisma";
|
||||
import { getCurrentViewer } from "@/lib/session";
|
||||
|
||||
const budgetSchema = z.object({
|
||||
workingGroupId: z.string().trim().min(1),
|
||||
periodId: z.string().trim().min(1),
|
||||
name: z.string().trim().min(2).max(80),
|
||||
totalBudget: z.coerce.number().min(0),
|
||||
colorCode: z.string().regex(/^#([0-9a-fA-F]{6})$/)
|
||||
});
|
||||
const budgetSchema = z
|
||||
.object({
|
||||
workingGroupId: z.string().trim().min(1),
|
||||
periodId: z.string().trim().min(1),
|
||||
name: z.string().trim().min(2).max(80),
|
||||
totalBudget: z.coerce.number().min(0),
|
||||
releasedAmount: z.coerce.number().min(0).optional(),
|
||||
colorCode: z.string().regex(/^#([0-9a-fA-F]{6})$/)
|
||||
})
|
||||
.superRefine((value, ctx) => {
|
||||
if (value.releasedAmount !== undefined && value.releasedAmount > value.totalBudget) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Der bereits an die AG uebergebene Betrag darf das Budget nicht uebersteigen.",
|
||||
path: ["releasedAmount"]
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const viewer = await getCurrentViewer();
|
||||
@@ -30,7 +41,10 @@ export async function POST(request: Request) {
|
||||
const parsed = budgetSchema.safeParse(body);
|
||||
|
||||
if (!parsed.success) {
|
||||
return NextResponse.json({ error: "Bitte AG, Budgetname, Betrag und Farbe korrekt angeben." }, { status: 400 });
|
||||
return NextResponse.json(
|
||||
{ error: "Bitte AG, Budgetname, Betrag, Mitteluebergabe und Farbe korrekt angeben." },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const workingGroup = await prisma.workingGroup.findUnique({
|
||||
@@ -63,6 +77,8 @@ export async function POST(request: Request) {
|
||||
}
|
||||
});
|
||||
|
||||
const nextReleasedAmount = parsed.data.releasedAmount ?? Number(existingBudget?.releasedAmount ?? 0);
|
||||
|
||||
const budget = existingBudget
|
||||
? await prisma.budget.update({
|
||||
where: {
|
||||
@@ -70,6 +86,7 @@ export async function POST(request: Request) {
|
||||
},
|
||||
data: {
|
||||
totalBudget: parsed.data.totalBudget,
|
||||
releasedAmount: nextReleasedAmount,
|
||||
colorCode: parsed.data.colorCode
|
||||
}
|
||||
})
|
||||
@@ -79,6 +96,7 @@ export async function POST(request: Request) {
|
||||
periodId: accountingPeriod.id,
|
||||
name: parsed.data.name,
|
||||
totalBudget: parsed.data.totalBudget,
|
||||
releasedAmount: nextReleasedAmount,
|
||||
colorCode: parsed.data.colorCode
|
||||
}
|
||||
});
|
||||
@@ -98,6 +116,7 @@ export async function POST(request: Request) {
|
||||
periodId: accountingPeriod.id,
|
||||
periodName: accountingPeriod.name,
|
||||
totalBudget: parsed.data.totalBudget,
|
||||
releasedAmount: nextReleasedAmount,
|
||||
rollback: existingBudget
|
||||
? {
|
||||
kind: "budget.update",
|
||||
|
||||
@@ -33,6 +33,7 @@ const CSV_HEADERS = [
|
||||
"description",
|
||||
"amount",
|
||||
"totalBudget",
|
||||
"releasedAmount",
|
||||
"colorCode",
|
||||
"approvalStatus",
|
||||
"approvalType",
|
||||
@@ -198,6 +199,7 @@ export async function GET() {
|
||||
description: "",
|
||||
amount: "",
|
||||
totalBudget: "",
|
||||
releasedAmount: "",
|
||||
colorCode: "",
|
||||
approvalStatus: "",
|
||||
approvalType: "",
|
||||
@@ -250,6 +252,7 @@ export async function GET() {
|
||||
description: "",
|
||||
amount: "",
|
||||
totalBudget: "",
|
||||
releasedAmount: "",
|
||||
colorCode: "",
|
||||
approvalStatus: "",
|
||||
approvalType: "",
|
||||
@@ -302,6 +305,7 @@ export async function GET() {
|
||||
description: "",
|
||||
amount: "",
|
||||
totalBudget: group.budgets.reduce((sum, budget) => sum + Number(budget.totalBudget), 0).toFixed(2),
|
||||
releasedAmount: group.budgets.reduce((sum, budget) => sum + Number(budget.releasedAmount), 0).toFixed(2),
|
||||
colorCode: "",
|
||||
approvalStatus: "",
|
||||
approvalType: "",
|
||||
@@ -353,6 +357,7 @@ export async function GET() {
|
||||
description: "",
|
||||
amount: "",
|
||||
totalBudget: Number(budget.totalBudget).toFixed(2),
|
||||
releasedAmount: Number(budget.releasedAmount).toFixed(2),
|
||||
colorCode: budget.colorCode,
|
||||
approvalStatus: "",
|
||||
approvalType: "",
|
||||
@@ -510,6 +515,7 @@ export async function GET() {
|
||||
description: "",
|
||||
amount: "",
|
||||
totalBudget: "",
|
||||
releasedAmount: "",
|
||||
colorCode: "",
|
||||
approvalStatus: "",
|
||||
approvalType: "",
|
||||
|
||||
@@ -167,9 +167,14 @@ export async function POST(request: Request) {
|
||||
|
||||
for (const row of budgetRows) {
|
||||
const totalBudget = toNumber(row.totalBudget);
|
||||
const releasedAmount = toNumber(row.releasedAmount) ?? 0;
|
||||
|
||||
if (totalBudget === null) {
|
||||
throw new Error(`Budget ${row.budgetName || row.id} enthält keinen gültigen Betrag.`);
|
||||
throw new Error(`Budget ${row.budgetName || row.id} enth\u00e4lt keinen g\u00fcltigen Betrag.`);
|
||||
}
|
||||
|
||||
if (releasedAmount > totalBudget) {
|
||||
throw new Error(`Budget ${row.budgetName || row.id} enth\u00e4lt eine zu hohe zus\u00e4tzliche Mittel\u00fcbergabe.`);
|
||||
}
|
||||
|
||||
await tx.budget.create({
|
||||
@@ -177,6 +182,7 @@ export async function POST(request: Request) {
|
||||
id: row.id,
|
||||
name: row.budgetName,
|
||||
totalBudget,
|
||||
releasedAmount,
|
||||
colorCode: row.colorCode,
|
||||
workingGroupId: row.workingGroupId,
|
||||
periodId: row.periodId,
|
||||
|
||||
@@ -150,6 +150,7 @@ export default async function DashboardPage() {
|
||||
id: budget.id,
|
||||
name: budget.name,
|
||||
totalBudget: Number(budget.totalBudget),
|
||||
releasedAmount: Number(budget.releasedAmount),
|
||||
colorCode: budget.colorCode,
|
||||
periodId: budget.periodId,
|
||||
expenses: budget.expenses.map((expense) => {
|
||||
|
||||
Reference in New Issue
Block a user