feat(buchhaltung): add edit support for pending transactions

This commit is contained in:
Matthias Hochmeister
2026-04-14 13:46:07 +02:00
parent f403c73334
commit 967cad5922
2 changed files with 50 additions and 16 deletions

View File

@@ -501,12 +501,14 @@ function TransaktionDialog({
haushaltsjahre,
selectedJahrId,
onSave,
existing,
}: {
open: boolean;
onClose: () => void;
haushaltsjahre: Haushaltsjahr[];
selectedJahrId: number | null;
onSave: (data: TransaktionFormData) => void;
existing?: Transaktion;
}) {
const today = new Date().toISOString().slice(0, 10);
const [form, setForm] = useState<TransaktionFormData>({
@@ -537,13 +539,32 @@ function TransaktionDialog({
});
useEffect(() => {
if (open) setForm(f => ({ ...f, haushaltsjahr_id: selectedJahrId || 0, datum: today }));
if (open) {
if (existing) {
setForm({
haushaltsjahr_id: existing.haushaltsjahr_id,
typ: existing.typ as 'einnahme' | 'ausgabe',
betrag: Number(existing.betrag),
datum: existing.datum.slice(0, 10),
konto_id: existing.konto_id,
bankkonto_id: existing.bankkonto_id,
beschreibung: existing.beschreibung || '',
empfaenger_auftraggeber: existing.empfaenger_auftraggeber || '',
verwendungszweck: existing.verwendungszweck || '',
beleg_nr: existing.beleg_nr || '',
bestellung_id: existing.bestellung_id,
ausgaben_typ: existing.ausgaben_typ,
});
} else {
setForm(f => ({ ...f, haushaltsjahr_id: selectedJahrId || 0, datum: today }));
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [open]);
return (
<Dialog open={open} onClose={onClose} maxWidth="sm" fullWidth>
<DialogTitle>Neue Transaktion</DialogTitle>
<DialogTitle>{existing ? 'Transaktion bearbeiten' : 'Neue Transaktion'}</DialogTitle>
<DialogContent>
<Stack spacing={2} sx={{ mt: 1 }}>
<FormControl fullWidth required>
@@ -609,7 +630,7 @@ function TransaktionDialog({
</DialogContent>
<DialogActions>
<Button onClick={onClose}>Abbrechen</Button>
<Button variant="contained" onClick={() => onSave(form)} disabled={!form.haushaltsjahr_id || !form.betrag || !form.datum}>Erstellen</Button>
<Button variant="contained" onClick={() => onSave(form)} disabled={!form.haushaltsjahr_id || !form.betrag || !form.datum}>{existing ? 'Speichern' : 'Erstellen'}</Button>
</DialogActions>
</Dialog>
);
@@ -1119,6 +1140,7 @@ function TransaktionenTab({ haushaltsjahre, selectedJahrId, onJahrChange }: {
const { hasPermission } = usePermissionContext();
const [filters, setFilters] = useState<TransaktionFilters>({ haushaltsjahr_id: selectedJahrId || undefined });
const [createOpen, setCreateOpen] = useState(false);
const [editingTx, setEditingTx] = useState<Transaktion | null>(null);
const [filterAusgabenTyp, setFilterAusgabenTyp] = useState('');
const [txSubTab, setTxSubTab] = useState(0);
@@ -1150,6 +1172,12 @@ function TransaktionenTab({ haushaltsjahre, selectedJahrId, onJahrChange }: {
onError: () => showError('Transaktion konnte nicht erstellt werden'),
});
const updateMut = useMutation({
mutationFn: ({ id, data }: { id: number; data: Partial<TransaktionFormData> }) => buchhaltungApi.updateTransaktion(id, data),
onSuccess: () => { qc.invalidateQueries({ queryKey: ['buchhaltung-transaktionen'] }); setEditingTx(null); showSuccess('Transaktion aktualisiert'); },
onError: () => showError('Aktualisierung fehlgeschlagen'),
});
const buchenMut = useMutation({
mutationFn: (id: number) => buchhaltungApi.buchenTransaktion(id),
onSuccess: () => { qc.invalidateQueries({ queryKey: ['buchhaltung-transaktionen'] }); qc.invalidateQueries({ queryKey: ['buchhaltung-stats'] }); showSuccess('Transaktion gebucht'); },
@@ -1383,6 +1411,13 @@ function TransaktionenTab({ haushaltsjahre, selectedJahrId, onJahrChange }: {
Stornieren
</Button>
)}
{t.status === 'entwurf' && hasPermission('buchhaltung:edit') && (
<Tooltip title="Bearbeiten">
<IconButton size="small" onClick={() => setEditingTx(t)}>
<Edit fontSize="small" />
</IconButton>
</Tooltip>
)}
{t.status === 'entwurf' && hasPermission('buchhaltung:delete') && (
<Tooltip title="Löschen">
<IconButton size="small" color="error" onClick={() => deleteMut.mutate(t.id)}>
@@ -1413,6 +1448,15 @@ function TransaktionenTab({ haushaltsjahre, selectedJahrId, onJahrChange }: {
onSave={data => createMut.mutate(data)}
/>
<TransaktionDialog
open={!!editingTx}
onClose={() => setEditingTx(null)}
haushaltsjahre={haushaltsjahre}
selectedJahrId={selectedJahrId}
existing={editingTx ?? undefined}
onSave={data => updateMut.mutate({ id: editingTx!.id, data })}
/>
<ErstattungDialog
open={erstattungOpen}
onClose={() => setErstattungOpen(false)}

View File

@@ -4,7 +4,7 @@ import { useQuery } from '@tanstack/react-query';
import {
Box, Typography, Button, Grid, Card, CardContent,
Table, TableHead, TableBody, TableRow, TableCell,
LinearProgress, Chip, Alert, Skeleton, TableContainer, Paper,
Chip, Alert, Skeleton, TableContainer, Paper,
IconButton, CircularProgress,
} from '@mui/material';
import { ArrowBack, KeyboardArrowDown, KeyboardArrowUp } from '@mui/icons-material';
@@ -16,23 +16,13 @@ import type { AusgabenTyp, BuchhaltungAudit } from '../types/buchhaltung.types';
const fmtEur = (n: number) => new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(n);
function BudgetCard({ label, budget, spent }: { label: string; budget: number; spent: number }) {
const utilization = budget > 0 ? Math.min((spent / budget) * 100, 100) : 0;
const over = spent > budget && budget > 0;
return (
<Card>
<CardContent>
<Typography variant="subtitle2" color="text.secondary">{label}</Typography>
<Typography variant="h6">{fmtEur(Number(spent))}</Typography>
{budget > 0 && (
<>
<Typography variant="body2" color="text.secondary">Budget: {fmtEur(Number(budget))}</Typography>
<LinearProgress
variant="determinate"
value={utilization}
color={over ? 'error' : utilization > 80 ? 'warning' : 'primary'}
sx={{ mt: 1, height: 6, borderRadius: 3 }}
/>
</>
)}
</CardContent>
</Card>
@@ -204,7 +194,7 @@ export default function BuchhaltungKontoDetail() {
const childIsEinfach = (child.budget_typ || 'detailliert') === 'einfach';
const childTotal = childIsEinfach
? Number(child.budget_gesamt || 0)
: Number(child.budget_gwg) + Number(child.budget_anlagen) + Number(child.budget_instandhaltung);
: Number(child.budget_gwg || 0) + Number(child.budget_anlagen || 0) + Number(child.budget_instandhaltung || 0);
return (
<TableRow
key={child.id}