feat(buchhaltung): add edit support for pending transactions
This commit is contained in:
@@ -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)}
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user