Bereits an AG übergeben läuft jetzt so, wie du es beschrieben hast:
Some checks failed
CI / Build (push) Failing after 1m6s
CI / Deploy (push) Has been skipped

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:
Jan
2026-04-13 21:22:02 +02:00
parent 051de0eea3
commit 34e5f96db7
13 changed files with 317 additions and 21 deletions

View File

@@ -128,6 +128,10 @@ function getPendingSpend(expenses: DashboardExpense[]) {
return expenses.reduce((sum, expense) => sum + (expense.approvalStatus === "PENDING" ? expense.periodAmount : 0), 0);
}
function getPaidSpend(expenses: DashboardExpense[]) {
return expenses.reduce((sum, expense) => sum + (expense.paidAt ? expense.periodAmount : 0), 0);
}
export function BudgetColumn({
group,
viewer,
@@ -182,6 +186,10 @@ export function BudgetColumn({
() => group.budgets.reduce((sum, budget) => sum + getPendingSpend(budget.expenses), 0),
[group.budgets]
);
const releasedSpend = useMemo(
() => group.budgets.reduce((sum, budget) => sum + budget.releasedAmount + getPaidSpend(budget.expenses), 0),
[group.budgets]
);
const totalCommitted = approvedSpend + pendingSpend;
const remainingBudget = group.totalBudget - totalCommitted;
@@ -340,6 +348,12 @@ export function BudgetColumn({
color={remainingBudget < 0 ? "error" : "default"}
sx={{ ...wrappingChipSx, width: "fit-content" }}
/>
<Chip
label={`An AG übergeben: ${formatCurrency(releasedSpend)}`}
color={releasedSpend > group.totalBudget ? "error" : "default"}
variant="outlined"
sx={{ ...wrappingChipSx, width: "fit-content" }}
/>
</Stack>
{group.budgets.length === 0 ? (
@@ -372,11 +386,15 @@ export function BudgetColumn({
const isEditing = editingBudgetId === budget.id;
const budgetApproved = getApprovedSpend(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 cumulativePercent =
budget.totalBudget > 0 ? Math.min((budgetCommitted / budget.totalBudget) * 100, 100) : 0;
const releasedPercent =
budget.totalBudget > 0 ? Math.min((budgetReleasedTotal / budget.totalBudget) * 100, 100) : 0;
return (
<Box
@@ -466,6 +484,19 @@ export function BudgetColumn({
transition: "height 220ms ease"
}}
/>
{budgetReleasedTotal > 0 ? (
<Box
sx={{
position: "absolute",
left: 8,
right: 8,
bottom: `calc(${releasedPercent}% - 1px)`,
borderTop: `2px dashed ${alpha(isDark ? "#FFFFFF" : budget.colorCode, isDark ? 0.82 : 0.9)}`,
zIndex: 2,
pointerEvents: "none"
}}
/>
) : null}
</Box>
</Box>
@@ -487,6 +518,15 @@ export function BudgetColumn({
color={budgetRemaining < 0 ? "error" : "default"}
sx={{ ...wrappingChipSx, width: "fit-content" }}
/>
<Chip
label={`An AG übergeben: ${formatCurrency(budgetReleasedTotal)}`}
color={budgetReleasedTotal > budget.totalBudget ? "error" : "default"}
variant="outlined"
sx={{ ...wrappingChipSx, width: "fit-content" }}
/>
<Typography variant="body2" color="text.secondary">
{`Davon zusätzlich ohne Einzelposten erfasst: ${formatCurrency(budget.releasedAmount)}. "Bezahlt setzen" zählt automatisch mit.`}
</Typography>
<Typography color="text.secondary">
{`Unter ${formatCurrency(approvalThreshold)} werden sofort freigegeben. Größere Ausgaben bleiben blass, bis alle drei Signaturen vorliegen.`}
</Typography>