Mehrere Stichtage pro Zeitraum verwalten
All checks were successful
CI / Build and Deploy (push) Successful in 2m43s

This commit is contained in:
jan
2026-05-12 01:37:28 +02:00
parent 08df13c044
commit 5591d10d96
11 changed files with 790 additions and 88 deletions

View File

@@ -143,11 +143,15 @@ export default async function DashboardPage() {
: [];
const periodCutoffs = await prisma.$queryRaw<
{ id: string; cutoff_name: string; cutoff_date: Date | null }[]
>`SELECT id, cutoff_name, cutoff_date FROM accounting_periods`;
{ id: string; name: string; date: Date | null; period_id: string }[]
>`
SELECT id, name, date, period_id
FROM period_cutoffs
ORDER BY date ASC NULLS LAST, created_at ASC
`;
const expenseCutoffs = await prisma.$queryRaw<
{ id: string; cutoff_phase: "PRE" | "POST" }[]
>`SELECT id, cutoff_phase FROM expenses WHERE period_id = ${currentPeriod.id}`;
{ id: string; cutoff_id: string | null; cutoff_phase: "PRE" | "POST" }[]
>`SELECT id, cutoff_id, cutoff_phase FROM expenses WHERE period_id = ${currentPeriod.id}`;
const donationRows = await prisma.$queryRaw<
{
id: string;
@@ -175,8 +179,14 @@ export default async function DashboardPage() {
WHERE d.period_id = ${currentPeriod.id}
ORDER BY d.donated_at DESC
`;
const periodCutoffById = new Map(periodCutoffs.map((period) => [period.id, period]));
const expenseCutoffById = new Map(expenseCutoffs.map((expense) => [expense.id, expense.cutoff_phase]));
const periodCutoffsByPeriodId = new Map<string, typeof periodCutoffs>();
for (const cutoff of periodCutoffs) {
periodCutoffsByPeriodId.set(cutoff.period_id, [...(periodCutoffsByPeriodId.get(cutoff.period_id) ?? []), cutoff]);
}
const primaryCutoffByPeriodId = new Map(
[...periodCutoffsByPeriodId.entries()].map(([periodId, cutoffs]) => [periodId, cutoffs[0]])
);
const expenseCutoffById = new Map(expenseCutoffs.map((expense) => [expense.id, expense]));
const donationsByExpenseId = new Map<string, number>();
const donationRowsByExpenseId = new Map<string, typeof donationRows>();
for (const donation of donationRows) {
@@ -248,7 +258,8 @@ export default async function DashboardPage() {
approvalStatus: expense.approvalStatus,
recurrence: expense.recurrence,
recurrenceStartAt,
cutoffPhase: expenseCutoffById.get(expense.id) ?? "PRE",
cutoffId: expenseCutoffById.get(expense.id)?.cutoff_id ?? primaryCutoffByPeriodId.get(expense.periodId)?.id ?? null,
cutoffPhase: expenseCutoffById.get(expense.id)?.cutoff_phase ?? "PRE",
donationAmount: donationsByExpenseId.get(expense.id) ?? 0,
donations: (donationRowsByExpenseId.get(expense.id) ?? []).map((donation) => ({
id: donation.id,
@@ -310,13 +321,19 @@ export default async function DashboardPage() {
}));
const serializedPeriods: DashboardAccountingPeriod[] = accountingPeriods.map((period) => ({
cutoffs: (periodCutoffsByPeriodId.get(period.id) ?? []).map((cutoff) => ({
id: cutoff.id,
name: cutoff.name,
date: cutoff.date?.toISOString() ?? null,
periodId: cutoff.period_id
})),
id: period.id,
name: period.name,
startsAt: period.startsAt.toISOString(),
endsAt: period.endsAt.toISOString(),
isCurrent: period.isCurrent,
cutoffName: periodCutoffById.get(period.id)?.cutoff_name ?? "Open Air",
cutoffDate: periodCutoffById.get(period.id)?.cutoff_date?.toISOString() ?? null
cutoffName: primaryCutoffByPeriodId.get(period.id)?.name ?? "Open Air",
cutoffDate: primaryCutoffByPeriodId.get(period.id)?.date?.toISOString() ?? null
}));
const serializedDonations: DashboardDonation[] = donationRows.map((donation) => ({