From bd59e50a51246237c168dd44f1e8e4f9afda3215 Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 1 May 2026 17:16:18 +0200 Subject: [PATCH] Belegupload nach Freigabe und Bezahlt-Logik korrigieren --- src/app/api/audit-logs/[id]/restore/route.ts | 3 ++- src/app/api/expenses/[id]/proof/route.ts | 7 ++++-- src/components/dashboard/budget-column.tsx | 25 ++++++++++++++++---- src/components/dashboard/dashboard-shell.tsx | 5 ++++ 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/app/api/audit-logs/[id]/restore/route.ts b/src/app/api/audit-logs/[id]/restore/route.ts index 5ad6128..659d35e 100644 --- a/src/app/api/audit-logs/[id]/restore/route.ts +++ b/src/app/api/audit-logs/[id]/restore/route.ts @@ -576,7 +576,8 @@ export async function POST(_: Request, { params }: Context) { data: { proofUrl: asNullableString(rollback.previousProofUrl), invoiceDate: asDate(rollback.previousInvoiceDate, "Vorheriges Rechnungsdatum"), - documentedAt: asDate(rollback.previousDocumentedAt, "Vorheriger Dokumentationszeitpunkt") + documentedAt: asDate(rollback.previousDocumentedAt, "Vorheriger Dokumentationszeitpunkt"), + paidAt: asDate(rollback.previousPaidAt, "Vorheriger Bezahlt-Zeitpunkt") } }); break; diff --git a/src/app/api/expenses/[id]/proof/route.ts b/src/app/api/expenses/[id]/proof/route.ts index 992d5dd..50b13c0 100644 --- a/src/app/api/expenses/[id]/proof/route.ts +++ b/src/app/api/expenses/[id]/proof/route.ts @@ -81,7 +81,8 @@ export async function POST(request: Request, { params }: Context) { data: { proofUrl, invoiceDate, - documentedAt: expense.documentedAt ?? new Date() + documentedAt: expense.documentedAt ?? new Date(), + paidAt: expense.paidAt ?? new Date() } }); @@ -103,7 +104,9 @@ export async function POST(request: Request, { params }: Context) { previousDocumentedAt: expense.documentedAt?.toISOString() ?? null, nextProofUrl: updatedExpense.proofUrl, nextInvoiceDate: updatedExpense.invoiceDate?.toISOString() ?? null, - nextDocumentedAt: updatedExpense.documentedAt?.toISOString() ?? null + nextDocumentedAt: updatedExpense.documentedAt?.toISOString() ?? null, + previousPaidAt: expense.paidAt?.toISOString() ?? null, + nextPaidAt: updatedExpense.paidAt?.toISOString() ?? null } } }); diff --git a/src/components/dashboard/budget-column.tsx b/src/components/dashboard/budget-column.tsx index d98d2e6..6dac4d6 100644 --- a/src/components/dashboard/budget-column.tsx +++ b/src/components/dashboard/budget-column.tsx @@ -186,6 +186,10 @@ export function BudgetColumn({ () => group.budgets.reduce((sum, budget) => sum + getApprovedSpend(budget.expenses), 0), [group.budgets] ); + const paidSpend = useMemo( + () => group.budgets.reduce((sum, budget) => sum + getPaidSpend(budget.expenses), 0), + [group.budgets] + ); const pendingSpend = useMemo( () => group.budgets.reduce((sum, budget) => sum + getPendingSpend(budget.expenses), 0), [group.budgets] @@ -340,6 +344,12 @@ export function BudgetColumn({ label={`Freigegeben: ${formatCurrency(approvedSpend)}`} sx={{ ...wrappingChipSx, width: "fit-content" }} /> + } + label={`Bezahlt: ${formatCurrency(paidSpend)}`} + color="info" + sx={{ ...wrappingChipSx, width: "fit-content" }} + /> } label={`Geplant: ${formatCurrency(pendingSpend)}`} @@ -391,12 +401,13 @@ export function BudgetColumn({ const draft = getDraft(budget); const isEditing = editingBudgetId === budget.id; const budgetApproved = getApprovedSpend(budget.expenses); + const budgetPaid = getPaidSpend(budget.expenses); const budgetPending = getPendingSpend(budget.expenses); const budgetReleasedByPayments = getPaidSpend(budget.expenses); const budgetReleasedTotal = budget.releasedAmount + budgetReleasedByPayments; const budgetCommitted = budgetApproved + budgetPending; const budgetRemaining = budget.totalBudget - budgetCommitted; - const approvedPercent = budget.totalBudget > 0 ? Math.min((budgetApproved / budget.totalBudget) * 100, 100) : 0; + const paidPercent = budget.totalBudget > 0 ? Math.min((budgetPaid / budget.totalBudget) * 100, 100) : 0; const cumulativePercent = budget.totalBudget > 0 ? Math.min((budgetCommitted / budget.totalBudget) * 100, 100) : 0; const releasedPercent = @@ -485,7 +496,7 @@ export function BudgetColumn({ position: "absolute", insetInline: 0, bottom: 0, - height: `${approvedPercent}%`, + height: `${paidPercent}%`, backgroundColor: budget.colorCode, transition: "height 220ms ease" }} @@ -512,6 +523,12 @@ export function BudgetColumn({ label={`Freigegeben: ${formatCurrency(budgetApproved)}`} sx={{ ...wrappingChipSx, width: "fit-content", bgcolor: alpha(budget.colorCode, 0.14) }} /> + } + label={`Bezahlt: ${formatCurrency(budgetPaid)}`} + color="info" + sx={{ ...wrappingChipSx, width: "fit-content" }} + /> } label={`Geplant: ${formatCurrency(budgetPending)}`} @@ -630,6 +647,7 @@ export function BudgetColumn({ : []; const isRecurringSeries = expense.recurrence === "MONTHLY"; const isRecurringExpanded = expandedRecurringExpenses[expense.id] ?? false; + const canUploadProof = expense.creator.id === viewer.id || canDocumentExpense(viewer.role); return ( Rechnung abgeben und bezahlt setzen diff --git a/src/components/dashboard/dashboard-shell.tsx b/src/components/dashboard/dashboard-shell.tsx index 501987c..e226ac4 100644 --- a/src/components/dashboard/dashboard-shell.tsx +++ b/src/components/dashboard/dashboard-shell.tsx @@ -769,6 +769,11 @@ export function DashboardShell({ }) )) as { proofUrl: string }; + setMessage({ type: "success", text: "Rechnung wurde abgegeben und die Ausgabe ist jetzt bezahlt." }); + startTransition(() => { + router.refresh(); + }); + return result.proofUrl; } catch (error) { const text = error instanceof Error ? error.message : "Beleg konnte nicht hochgeladen werden.";