refactor(buchhaltung): simplify transaction workflow to two states, reorder tabs, guard booking, add overview divider
This commit is contained in:
@@ -48,13 +48,10 @@ import {
|
||||
Edit,
|
||||
ExpandLess as ExpandLessIcon,
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
HowToReg,
|
||||
Lock,
|
||||
Save,
|
||||
SwapHoriz,
|
||||
PictureAsPdf as PdfIcon,
|
||||
ThumbDown,
|
||||
ThumbUp,
|
||||
} from '@mui/icons-material';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
@@ -96,6 +93,7 @@ function fmtEur(val: number) {
|
||||
}
|
||||
|
||||
const dividerLeft = { borderLeft: '2px solid', borderColor: 'divider' } as const;
|
||||
const dividerRight = { borderRight: '2px solid', borderColor: 'divider' } as const;
|
||||
|
||||
function fmtDate(val: string) {
|
||||
return new Date(val).toLocaleDateString('de-DE');
|
||||
@@ -656,7 +654,7 @@ function KontoRow({ konto, depth = 0, onNavigate }: { konto: KontoTreeNode; dept
|
||||
<TableCell align="right">{isEinfach ? '' : fmtEur(konto.spent_anlagen)}</TableCell>
|
||||
<TableCell align="right">{isEinfach ? '' : fmtEur(konto.spent_instandhaltung)}</TableCell>
|
||||
<TableCell align="right"><strong>{fmtEur(totalSpent)}</strong></TableCell>
|
||||
<TableCell align="right" sx={dividerLeft}>{fmtEur(konto.einnahmen_betrag)}</TableCell>
|
||||
<TableCell align="right" sx={{ ...dividerLeft, ...dividerRight }}>{fmtEur(konto.einnahmen_betrag)}</TableCell>
|
||||
<TableCell sx={{ width: 40, px: 0.5 }}>
|
||||
{konto.children.length > 0 && (
|
||||
<IconButton size="small" onClick={e => { e.stopPropagation(); setOpen(!open); }}>
|
||||
@@ -816,7 +814,7 @@ function UebersichtTab({ haushaltsjahre, selectedJahrId, onJahrChange }: {
|
||||
<TableCell align="right">Ausgaben Anlagen</TableCell>
|
||||
<TableCell align="right">Ausgaben Instandh.</TableCell>
|
||||
<TableCell align="right">Ausgaben Gesamt</TableCell>
|
||||
<TableCell align="right" sx={dividerLeft}>Einnahmen</TableCell>
|
||||
<TableCell align="right" sx={{ ...dividerLeft, ...dividerRight }}>Einnahmen</TableCell>
|
||||
<TableCell sx={{ width: 40 }} />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
@@ -861,7 +859,7 @@ function UebersichtTab({ haushaltsjahre, selectedJahrId, onJahrChange }: {
|
||||
<TableCell align="right">{fmtEur(catSpentAnl)}</TableCell>
|
||||
<TableCell align="right">{fmtEur(catSpentInst)}</TableCell>
|
||||
<TableCell align="right">{fmtEur(catSpentGwg + catSpentAnl + catSpentInst)}</TableCell>
|
||||
<TableCell align="right" sx={dividerLeft}>{fmtEur(catEinnahmen)}</TableCell>
|
||||
<TableCell align="right" sx={{ ...dividerLeft, ...dividerRight }}>{fmtEur(catEinnahmen)}</TableCell>
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
);
|
||||
@@ -883,7 +881,7 @@ function UebersichtTab({ haushaltsjahre, selectedJahrId, onJahrChange }: {
|
||||
<TableCell align="right">{fmtEur(sumSpentAnlagen)}</TableCell>
|
||||
<TableCell align="right">{fmtEur(sumSpentInst)}</TableCell>
|
||||
<TableCell align="right">{fmtEur(sumSpentGesamt)}</TableCell>
|
||||
<TableCell align="right" sx={dividerLeft}>{fmtEur(sumEinnahmen)}</TableCell>
|
||||
<TableCell align="right" sx={{ ...dividerLeft, ...dividerRight }}>{fmtEur(sumEinnahmen)}</TableCell>
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
)}
|
||||
@@ -1171,24 +1169,6 @@ function TransaktionenTab({ haushaltsjahre, selectedJahrId, onJahrChange }: {
|
||||
onError: () => showError('Löschen fehlgeschlagen'),
|
||||
});
|
||||
|
||||
const freigabeMut = useMutation({
|
||||
mutationFn: (id: number) => buchhaltungApi.requestFreigabe(id),
|
||||
onSuccess: () => { qc.invalidateQueries({ queryKey: ['buchhaltung-transaktionen'] }); showSuccess('Freigabe angefragt'); },
|
||||
onError: () => showError('Freigabe konnte nicht angefragt werden'),
|
||||
});
|
||||
|
||||
const approveMut = useMutation({
|
||||
mutationFn: (id: number) => buchhaltungApi.approveFreigabe(id),
|
||||
onSuccess: () => { qc.invalidateQueries({ queryKey: ['buchhaltung-transaktionen'] }); qc.invalidateQueries({ queryKey: ['buchhaltung-stats'] }); showSuccess('Freigabe genehmigt'); },
|
||||
onError: () => showError('Genehmigung fehlgeschlagen'),
|
||||
});
|
||||
|
||||
const rejectMut = useMutation({
|
||||
mutationFn: (id: number) => buchhaltungApi.rejectFreigabe(id),
|
||||
onSuccess: () => { qc.invalidateQueries({ queryKey: ['buchhaltung-transaktionen'] }); showSuccess('Freigabe abgelehnt'); },
|
||||
onError: () => showError('Ablehnung fehlgeschlagen'),
|
||||
});
|
||||
|
||||
// ── Wiederkehrend mutations ──
|
||||
const canManage = hasPermission('buchhaltung:manage_accounts');
|
||||
|
||||
@@ -1263,7 +1243,7 @@ function TransaktionenTab({ haushaltsjahre, selectedJahrId, onJahrChange }: {
|
||||
|
||||
const subTabTransaktionen = sortedTransaktionen.filter(t => {
|
||||
if (txSubTab === 0) return t.status === 'entwurf';
|
||||
if (txSubTab === 1) return t.status === 'gebucht' || t.status === 'freigegeben';
|
||||
if (txSubTab === 1) return t.status === 'gebucht';
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -1391,30 +1371,15 @@ function TransaktionenTab({ haushaltsjahre, selectedJahrId, onJahrChange }: {
|
||||
<TableCell>
|
||||
<Stack direction="row" spacing={0.5} flexWrap="wrap">
|
||||
{t.status === 'entwurf' && hasPermission('buchhaltung:edit') && (
|
||||
<Tooltip title={!t.konto_id ? 'Kein Konto ausgewählt' : ''}>
|
||||
<Tooltip title={kontenFlat.length === 0 ? 'Keine Konten konfiguriert' : bankkonten.length === 0 ? 'Keine Bankkonten konfiguriert' : !t.konto_id ? 'Kein Konto ausgewählt' : ''}>
|
||||
<span>
|
||||
<Button size="small" variant="outlined" startIcon={<BookmarkAdd fontSize="small" />} disabled={!t.konto_id} onClick={() => buchenMut.mutate(t.id)}>
|
||||
<Button size="small" variant="outlined" startIcon={<BookmarkAdd fontSize="small" />} disabled={kontenFlat.length === 0 || bankkonten.length === 0 || !t.konto_id} onClick={() => buchenMut.mutate(t.id)}>
|
||||
Buchen
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
{t.status === 'gebucht' && hasPermission('buchhaltung:edit') && (
|
||||
<Button size="small" variant="outlined" color="info" startIcon={<HowToReg fontSize="small" />} onClick={() => freigabeMut.mutate(t.id)}>
|
||||
Freigabe
|
||||
</Button>
|
||||
)}
|
||||
{t.status === 'freigegeben' && hasPermission('buchhaltung:manage_accounts') && (
|
||||
<>
|
||||
<Button size="small" variant="outlined" color="success" startIcon={<ThumbUp fontSize="small" />} onClick={() => approveMut.mutate(t.id)}>
|
||||
Genehmigen
|
||||
</Button>
|
||||
<Button size="small" variant="outlined" color="error" startIcon={<ThumbDown fontSize="small" />} onClick={() => rejectMut.mutate(t.id)}>
|
||||
Ablehnen
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{(t.status === 'gebucht' || t.status === 'freigegeben') && hasPermission('buchhaltung:edit') && (
|
||||
<Button size="small" variant="outlined" color="warning" startIcon={<Cancel fontSize="small" />} onClick={() => stornoMut.mutate(t.id)}>
|
||||
Stornieren
|
||||
</Button>
|
||||
@@ -2140,20 +2105,20 @@ export default function Buchhaltung() {
|
||||
</Box>
|
||||
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||||
<Tabs value={tabValue} onChange={handleTabChange} variant="scrollable" scrollButtons="auto">
|
||||
<Tab label={<Badge badgeContent={pendingCount || 0} color="warning" invisible={!pendingCount}><span>Transaktionen Übersicht</span></Badge>} />
|
||||
<Tab label="Übersicht" />
|
||||
<Tab label={<Badge badgeContent={pendingCount || 0} color="warning" invisible={!pendingCount}><span>Transaktionen</span></Badge>} />
|
||||
<Tab label="Konten" />
|
||||
</Tabs>
|
||||
</Box>
|
||||
<TabPanel value={tabValue} index={0}>
|
||||
<UebersichtTab
|
||||
<TransaktionenTab
|
||||
haushaltsjahre={haushaltsjahre}
|
||||
selectedJahrId={selectedJahrId}
|
||||
onJahrChange={setSelectedJahrId}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel value={tabValue} index={1}>
|
||||
<TransaktionenTab
|
||||
<UebersichtTab
|
||||
haushaltsjahre={haushaltsjahre}
|
||||
selectedJahrId={selectedJahrId}
|
||||
onJahrChange={setSelectedJahrId}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Lookup types
|
||||
export type KontoArt = 'einnahme' | 'ausgabe' | 'vermoegen' | 'verbindlichkeit';
|
||||
export type TransaktionTyp = 'einnahme' | 'ausgabe' | 'transfer';
|
||||
export type TransaktionStatus = 'entwurf' | 'gebucht' | 'freigegeben' | 'storniert';
|
||||
export type TransaktionStatus = 'entwurf' | 'gebucht' | 'storniert';
|
||||
export type FreigabeStatus = 'ausstehend' | 'genehmigt' | 'abgelehnt';
|
||||
export type WiederkehrendIntervall = 'monatlich' | 'quartalsweise' | 'halbjaehrlich' | 'jaehrlich';
|
||||
export type PlanungStatus = 'entwurf' | 'aktiv' | 'abgeschlossen';
|
||||
@@ -18,14 +18,12 @@ export const AUSGABEN_TYP_LABELS: Record<AusgabenTyp, string> = {
|
||||
export const TRANSAKTION_STATUS_LABELS: Record<TransaktionStatus, string> = {
|
||||
entwurf: 'Entwurf',
|
||||
gebucht: 'Gebucht',
|
||||
freigegeben: 'Freigegeben',
|
||||
storniert: 'Storniert',
|
||||
};
|
||||
|
||||
export const TRANSAKTION_STATUS_COLORS: Record<TransaktionStatus, 'default' | 'warning' | 'success' | 'error'> = {
|
||||
entwurf: 'default',
|
||||
gebucht: 'warning',
|
||||
freigegeben: 'success',
|
||||
storniert: 'error',
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user