Finanzuebersicht Stichtage und Spenden ergaenzen
All checks were successful
CI / Build and Deploy (push) Successful in 3m11s

This commit is contained in:
jan
2026-05-11 23:41:07 +02:00
parent c93616f09e
commit c738b35d06
14 changed files with 884 additions and 62 deletions

View File

@@ -8,13 +8,18 @@ export function snapshotWorkingGroup(workingGroup: Pick<WorkingGroup, "id" | "na
};
}
export function snapshotPeriod(period: Pick<AccountingPeriod, "id" | "name" | "startsAt" | "endsAt" | "isCurrent" | "createdAt">) {
export function snapshotPeriod(period: Pick<AccountingPeriod, "id" | "name" | "startsAt" | "endsAt" | "isCurrent" | "createdAt"> & {
cutoffName?: string;
cutoffDate?: Date | null;
}) {
return {
id: period.id,
name: period.name,
startsAt: period.startsAt.toISOString(),
endsAt: period.endsAt.toISOString(),
isCurrent: period.isCurrent,
cutoffName: period.cutoffName,
cutoffDate: period.cutoffDate?.toISOString() ?? null,
createdAt: period.createdAt.toISOString()
};
}
@@ -63,7 +68,9 @@ export function snapshotExpense(
| "createdAt"
| "paidAt"
| "documentedAt"
>
> & {
cutoffPhase?: "PRE" | "POST";
}
) {
return {
id: expense.id,
@@ -77,12 +84,39 @@ export function snapshotExpense(
approvalStatus: expense.approvalStatus,
recurrence: expense.recurrence,
recurrenceStartAt: expense.recurrenceStartAt?.toISOString() ?? null,
cutoffPhase: expense.cutoffPhase ?? "PRE",
createdAt: expense.createdAt.toISOString(),
paidAt: expense.paidAt?.toISOString() ?? null,
documentedAt: expense.documentedAt?.toISOString() ?? null
};
}
export function snapshotDonation(
donation: {
id: string;
title: string;
description: string | null;
amount: unknown;
donatedAt: Date;
periodId: string;
expenseId: string | null;
creatorId: string;
createdAt: Date;
}
) {
return {
id: donation.id,
title: donation.title,
description: donation.description,
amount: Number(donation.amount),
donatedAt: donation.donatedAt.toISOString(),
periodId: donation.periodId,
expenseId: donation.expenseId,
creatorId: donation.creatorId,
createdAt: donation.createdAt.toISOString()
};
}
export function snapshotApproval(approval: Pick<Approval, "id" | "expenseId" | "userId" | "approvalType" | "timestamp">) {
return {
id: approval.id,

View File

@@ -3,6 +3,7 @@ import type {
ApprovalStatusValue,
ApprovalTypeValue,
BudgetReleaseNotifyTargetValue,
CutoffPhaseValue,
ExpenseRecurrenceValue
} from "@/lib/domain";
@@ -12,6 +13,8 @@ export type DashboardAccountingPeriod = {
startsAt: string;
endsAt: string;
isCurrent: boolean;
cutoffName: string;
cutoffDate: string | null;
};
export type DashboardViewer = {
@@ -68,6 +71,9 @@ export type DashboardExpense = {
approvalStatus: ApprovalStatusValue;
recurrence: ExpenseRecurrenceValue;
recurrenceStartAt: string | null;
cutoffPhase: CutoffPhaseValue;
donationAmount: number;
netPeriodAmount: number;
paidAt: string | null;
documentedAt: string | null;
documents: DashboardExpenseDocument[];
@@ -79,6 +85,21 @@ export type DashboardExpense = {
approvals: DashboardApproval[];
};
export type DashboardDonation = {
id: string;
title: string;
description: string | null;
amount: number;
donatedAt: string;
periodId: string;
expenseId: string | null;
createdAt: string;
creator: {
id: string;
name: string;
};
};
export type DashboardSettings = {
approvalThreshold: number;
requiredApprovalTypes: ApprovalTypeValue[];

View File

@@ -18,6 +18,7 @@ export type AppRole = "BOARD" | "ORGA" | "FINANCE" | "MEMBER";
export type ApprovalTypeValue = (typeof APPROVAL_FLOW)[number];
export type ApprovalStatusValue = "PENDING" | "APPROVED";
export type ExpenseRecurrenceValue = "NONE" | "MONTHLY";
export type CutoffPhaseValue = "PRE" | "POST";
export type BudgetReleaseNotifyTargetValue = "ALL_GROUP_USERS" | "GROUP_MEMBERS_ONLY";
export function requiresManualApproval(amount: number, approvalThreshold = DEFAULT_APPROVAL_THRESHOLD) {
@@ -57,6 +58,10 @@ export function recurrenceLabel(recurrence: ExpenseRecurrenceValue) {
}
}
export function cutoffPhaseLabel(phase: CutoffPhaseValue, cutoffName = "Open Air") {
return phase === "PRE" ? `Pre ${cutoffName}` : `Post ${cutoffName}`;
}
export function hasAdministrativeAccess(role: AppRole) {
return role === "BOARD" || role === "ORGA" || role === "FINANCE";
}