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:
@@ -77,6 +77,12 @@ type BudgetFormState = {
|
||||
colorCode: string;
|
||||
};
|
||||
|
||||
type BudgetReleaseFormState = {
|
||||
workingGroupId: string;
|
||||
budgetId: string;
|
||||
releasedAmount: string;
|
||||
};
|
||||
|
||||
type WorkingGroupFormState = {
|
||||
name: string;
|
||||
};
|
||||
@@ -243,6 +249,11 @@ export function DashboardShell({
|
||||
totalBudget: "1200",
|
||||
colorCode: "#FFB94A"
|
||||
});
|
||||
const [budgetReleaseForm, setBudgetReleaseForm] = useState<BudgetReleaseFormState>({
|
||||
workingGroupId: visibleGroups[0]?.id ?? "",
|
||||
budgetId: visibleGroups[0]?.budgets[0]?.id ?? "",
|
||||
releasedAmount: (visibleGroups[0]?.budgets[0]?.releasedAmount ?? 0).toFixed(2)
|
||||
});
|
||||
const [workingGroupForm, setWorkingGroupForm] = useState<WorkingGroupFormState>({
|
||||
name: ""
|
||||
});
|
||||
@@ -310,6 +321,52 @@ export function DashboardShell({
|
||||
}
|
||||
}, [budgetForm.workingGroupId, visibleGroups]);
|
||||
|
||||
useEffect(() => {
|
||||
if (visibleGroups.length === 0) {
|
||||
setBudgetReleaseForm({
|
||||
workingGroupId: "",
|
||||
budgetId: "",
|
||||
releasedAmount: "0.00"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedGroup = visibleGroups.find((group) => group.id === budgetReleaseForm.workingGroupId) ?? visibleGroups[0];
|
||||
const selectedBudget = selectedGroup?.budgets.find((budget) => budget.id === budgetReleaseForm.budgetId) ?? selectedGroup?.budgets[0];
|
||||
|
||||
if (
|
||||
budgetReleaseForm.workingGroupId !== (selectedGroup?.id ?? "") ||
|
||||
budgetReleaseForm.budgetId !== (selectedBudget?.id ?? "")
|
||||
) {
|
||||
setBudgetReleaseForm({
|
||||
workingGroupId: selectedGroup?.id ?? "",
|
||||
budgetId: selectedBudget?.id ?? "",
|
||||
releasedAmount: (selectedBudget?.releasedAmount ?? 0).toFixed(2)
|
||||
});
|
||||
}
|
||||
}, [budgetReleaseForm.budgetId, budgetReleaseForm.workingGroupId, visibleGroups]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!budgetReleaseForm.budgetId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedGroup = visibleGroups.find((group) => group.id === budgetReleaseForm.workingGroupId) ?? visibleGroups[0];
|
||||
const selectedBudget = selectedGroup?.budgets.find((budget) => budget.id === budgetReleaseForm.budgetId);
|
||||
|
||||
if (!selectedBudget) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextReleasedAmount = selectedBudget.releasedAmount.toFixed(2);
|
||||
if (budgetReleaseForm.releasedAmount !== nextReleasedAmount) {
|
||||
setBudgetReleaseForm((current) => ({
|
||||
...current,
|
||||
releasedAmount: nextReleasedAmount
|
||||
}));
|
||||
}
|
||||
}, [budgetReleaseForm.budgetId, budgetReleaseForm.workingGroupId, visibleGroups]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!message || message.type !== "success") {
|
||||
return;
|
||||
@@ -375,6 +432,18 @@ export function DashboardShell({
|
||||
const mobileSelectedGroup = visibleGroups.find((group) => group.id === selectedMobileGroupId) ?? visibleGroups[0];
|
||||
const selectedBudgetWorkingGroup =
|
||||
visibleGroups.find((group) => group.id === budgetForm.workingGroupId) ?? null;
|
||||
const selectedBudgetReleaseGroup =
|
||||
visibleGroups.find((group) => group.id === budgetReleaseForm.workingGroupId) ?? visibleGroups[0] ?? null;
|
||||
const selectedBudgetReleaseOptions = selectedBudgetReleaseGroup?.budgets ?? [];
|
||||
const selectedBudgetReleaseBudget =
|
||||
selectedBudgetReleaseOptions.find((budget) => budget.id === budgetReleaseForm.budgetId) ??
|
||||
selectedBudgetReleaseOptions[0] ??
|
||||
null;
|
||||
const selectedBudgetReleasePaidAmount =
|
||||
selectedBudgetReleaseBudget?.expenses.reduce(
|
||||
(sum, expense) => sum + (expense.paidAt ? expense.periodAmount : 0),
|
||||
0
|
||||
) ?? 0;
|
||||
const selectedPeriodForManagement =
|
||||
accountingPeriods.find((period) => period.id === selectedCurrentPeriodId) ?? currentPeriod ?? null;
|
||||
|
||||
@@ -645,6 +714,44 @@ export function DashboardShell({
|
||||
}, "Budget wurde aktualisiert.");
|
||||
}
|
||||
|
||||
async function handleSaveBudgetRelease(event: FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
|
||||
if (!selectedBudgetReleaseBudget) {
|
||||
setMessage({ type: "error", text: "Bitte zuerst ein Budget ausw\u00e4hlen." });
|
||||
return;
|
||||
}
|
||||
|
||||
const nextReleasedAmount = Number(budgetReleaseForm.releasedAmount.replace(",", "."));
|
||||
|
||||
if (!Number.isFinite(nextReleasedAmount) || nextReleasedAmount < 0) {
|
||||
setMessage({ type: "error", text: "Bitte einen g\u00fcltigen zus\u00e4tzlichen \u00dcbergabebetrag eingeben." });
|
||||
return;
|
||||
}
|
||||
|
||||
await runAction(async () => {
|
||||
await parseResponse(
|
||||
await fetch(`/api/budgets/${selectedBudgetReleaseBudget.id}`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: selectedBudgetReleaseBudget.name,
|
||||
totalBudget: selectedBudgetReleaseBudget.totalBudget,
|
||||
releasedAmount: nextReleasedAmount,
|
||||
colorCode: selectedBudgetReleaseBudget.colorCode
|
||||
})
|
||||
})
|
||||
);
|
||||
|
||||
setBudgetReleaseForm((current) => ({
|
||||
...current,
|
||||
releasedAmount: nextReleasedAmount.toFixed(2)
|
||||
}));
|
||||
}, `Zus\u00e4tzliche Mittel\u00fcbergabe f\u00fcr ${selectedBudgetReleaseBudget.name} wurde gespeichert.`);
|
||||
}
|
||||
|
||||
async function handleDeleteBudget(budgetId: string) {
|
||||
await runAction(async () => {
|
||||
await parseResponse(
|
||||
@@ -1274,6 +1381,98 @@ export function DashboardShell({
|
||||
</Card>
|
||||
) : null}
|
||||
|
||||
{canManagePeriods && (isCompactLayout || desktopSection === "overview") ? (
|
||||
<Card sx={islandCardSx}>
|
||||
<CardContent sx={{ p: 3 }}>
|
||||
<Stack spacing={2.5}>
|
||||
<Box>
|
||||
<Typography variant="h3" sx={{ fontSize: "1.35rem" }}>
|
||||
{"Bereits an AG übergeben"}
|
||||
</Typography>
|
||||
<Typography color="text.secondary">
|
||||
{"\"Bezahlt setzen\" z\u00e4hlt automatisch mit. Hier erg\u00e4nzt du nur zus\u00e4tzlich bereits \u00fcbergebenes Geld, das nicht \u00fcber eine einzelne Ausgabe l\u00e4uft."}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box component="form" onSubmit={handleSaveBudgetRelease}>
|
||||
<Stack spacing={2}>
|
||||
<TextField
|
||||
select
|
||||
label="Arbeitsgruppe"
|
||||
value={budgetReleaseForm.workingGroupId}
|
||||
onChange={(event) =>
|
||||
setBudgetReleaseForm((current) => ({
|
||||
...current,
|
||||
workingGroupId: event.target.value,
|
||||
budgetId: ""
|
||||
}))
|
||||
}
|
||||
required
|
||||
fullWidth
|
||||
disabled={visibleGroups.length === 0}
|
||||
helperText={visibleGroups.length === 0 ? "Lege zuerst eine AG an." : "Wähle die AG mit dem betroffenen Budget."}
|
||||
>
|
||||
{visibleGroups.map((group) => (
|
||||
<MenuItem key={group.id} value={group.id}>
|
||||
{group.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
<TextField
|
||||
select
|
||||
label="Budget"
|
||||
value={budgetReleaseForm.budgetId}
|
||||
onChange={(event) =>
|
||||
setBudgetReleaseForm((current) => ({
|
||||
...current,
|
||||
budgetId: event.target.value
|
||||
}))
|
||||
}
|
||||
required
|
||||
fullWidth
|
||||
disabled={selectedBudgetReleaseOptions.length === 0}
|
||||
helperText={
|
||||
selectedBudgetReleaseOptions.length === 0
|
||||
? "In dieser AG gibt es noch kein Budget."
|
||||
: "Die gestrichelte Linie im Budget zeigt die gesamte Mittelübergabe inklusive bezahlter Posten."
|
||||
}
|
||||
>
|
||||
{selectedBudgetReleaseOptions.map((budget) => (
|
||||
<MenuItem key={budget.id} value={budget.id}>
|
||||
{budget.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
<TextField
|
||||
label="Zusätzlich bereits übergeben in EUR"
|
||||
type="number"
|
||||
inputProps={{ min: 0, step: 0.01, max: selectedBudgetReleaseBudget?.totalBudget ?? undefined }}
|
||||
value={budgetReleaseForm.releasedAmount}
|
||||
onChange={(event) =>
|
||||
setBudgetReleaseForm((current) => ({
|
||||
...current,
|
||||
releasedAmount: event.target.value
|
||||
}))
|
||||
}
|
||||
required
|
||||
fullWidth
|
||||
disabled={!selectedBudgetReleaseBudget}
|
||||
helperText={
|
||||
selectedBudgetReleaseBudget
|
||||
? `Automatisch \u00fcber Bezahlt: ${currencyFormatter.format(selectedBudgetReleasePaidAmount)} | Zus\u00e4tzlich erfasst: ${currencyFormatter.format(selectedBudgetReleaseBudget.releasedAmount)}`
|
||||
: "Wähle zuerst ein Budget aus."
|
||||
}
|
||||
/>
|
||||
<Button type="submit" variant="outlined" disabled={busy || !selectedBudgetReleaseBudget}>
|
||||
Betrag speichern
|
||||
</Button>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : null}
|
||||
|
||||
{canManageBudgets(viewer.role) && (isCompactLayout || desktopSection === "budgetGroups") ? (
|
||||
<Card sx={islandCardSx}>
|
||||
<CardContent sx={{ p: 3 }}>
|
||||
|
||||
Reference in New Issue
Block a user