import { useState } from 'react'; import DashboardLayout from '../components/dashboard/DashboardLayout'; import { Alert, Box, Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, FormControl, IconButton, InputAdornment, InputLabel, MenuItem, Paper, Select, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, } from '@mui/material'; import { Add as AddIcon, ArrowBack, Delete, Edit, PlaylistAdd } from '@mui/icons-material'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useNavigate, useParams } from 'react-router-dom'; import { buchhaltungApi } from '../services/buchhaltung'; import { useNotification } from '../contexts/NotificationContext'; import { usePermissionContext } from '../contexts/PermissionContext'; import type { Konto, Planposition, PlanungStatus } from '../types/buchhaltung.types'; const STATUS_LABELS: Record = { entwurf: 'Entwurf', aktiv: 'Aktiv', abgeschlossen: 'Abgeschlossen', }; const STATUS_COLORS: Record = { entwurf: 'default', aktiv: 'success', abgeschlossen: 'info', }; function fmtEur(val: number) { return new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(val); } export default function HaushaltsplanDetail() { const { id } = useParams<{ id: string }>(); const planungId = Number(id); const navigate = useNavigate(); const qc = useQueryClient(); const { showSuccess, showError } = useNotification(); const { hasPermission } = usePermissionContext(); const canManage = hasPermission('buchhaltung:manage_accounts'); const [posDialog, setPosDialog] = useState<{ open: boolean; existing?: Planposition }>({ open: false }); const [posForm, setPosForm] = useState<{ konto_id: string; bezeichnung: string; budget_gwg: string; budget_anlagen: string; budget_instandhaltung: string; notizen: string }>({ konto_id: '', bezeichnung: '', budget_gwg: '0', budget_anlagen: '0', budget_instandhaltung: '0', notizen: '', }); const { data: planung, isLoading, isError } = useQuery({ queryKey: ['planung', planungId], queryFn: () => buchhaltungApi.getPlanung(planungId), enabled: !isNaN(planungId), }); // Load konten for the linked haushaltsjahr (for Konto dropdown in position dialog) const { data: konten = [] } = useQuery({ queryKey: ['buchhaltung-konten', planung?.haushaltsjahr_id], queryFn: () => buchhaltungApi.getKonten(planung!.haushaltsjahr_id!), enabled: !!planung?.haushaltsjahr_id, }); const createPosMut = useMutation({ mutationFn: (data: Parameters[1]) => buchhaltungApi.createPlanposition(planungId, data), onSuccess: () => { qc.invalidateQueries({ queryKey: ['planung', planungId] }); setPosDialog({ open: false }); showSuccess('Position hinzugefügt'); }, onError: () => showError('Position konnte nicht erstellt werden'), }); const updatePosMut = useMutation({ mutationFn: ({ posId, data }: { posId: number; data: Parameters[1] }) => buchhaltungApi.updatePlanposition(posId, data), onSuccess: () => { qc.invalidateQueries({ queryKey: ['planung', planungId] }); setPosDialog({ open: false }); showSuccess('Position aktualisiert'); }, onError: () => showError('Position konnte nicht aktualisiert werden'), }); const deletePosMut = useMutation({ mutationFn: buchhaltungApi.deletePlanposition, onSuccess: () => { qc.invalidateQueries({ queryKey: ['planung', planungId] }); showSuccess('Position gelöscht'); }, onError: () => showError('Position konnte nicht gelöscht werden'), }); const createHjMut = useMutation({ mutationFn: () => buchhaltungApi.createHaushaltsjahrFromPlan(planungId), onSuccess: () => { qc.invalidateQueries({ queryKey: ['planung', planungId] }); qc.invalidateQueries({ queryKey: ['haushaltsjahre'] }); qc.invalidateQueries({ queryKey: ['planungen'] }); showSuccess('Haushaltsjahr aus Plan erstellt'); }, onError: (err: any) => showError(err?.response?.data?.message || 'Haushaltsjahr konnte nicht erstellt werden'), }); const openAddDialog = () => { setPosForm({ konto_id: '', bezeichnung: '', budget_gwg: '0', budget_anlagen: '0', budget_instandhaltung: '0', notizen: '' }); setPosDialog({ open: true }); }; const openEditDialog = (pos: Planposition) => { setPosForm({ konto_id: pos.konto_id ? String(pos.konto_id) : '', bezeichnung: pos.bezeichnung, budget_gwg: String(pos.budget_gwg), budget_anlagen: String(pos.budget_anlagen), budget_instandhaltung: String(pos.budget_instandhaltung), notizen: pos.notizen || '', }); setPosDialog({ open: true, existing: pos }); }; const handlePosSave = () => { const data = { konto_id: posForm.konto_id ? Number(posForm.konto_id) : null, bezeichnung: posForm.bezeichnung.trim(), budget_gwg: parseFloat(posForm.budget_gwg) || 0, budget_anlagen: parseFloat(posForm.budget_anlagen) || 0, budget_instandhaltung: parseFloat(posForm.budget_instandhaltung) || 0, notizen: posForm.notizen.trim() || undefined, }; if (posDialog.existing) { updatePosMut.mutate({ posId: posDialog.existing.id, data }); } else { createPosMut.mutate(data); } }; if (isLoading) return Laden...; if (isError || !planung) return Haushaltsplan nicht gefunden; const positionen = planung.positionen || []; const totalGwg = positionen.reduce((s, p) => s + Number(p.budget_gwg), 0); const totalAnlagen = positionen.reduce((s, p) => s + Number(p.budget_anlagen), 0); const totalInstandhaltung = positionen.reduce((s, p) => s + Number(p.budget_instandhaltung), 0); return ( navigate('/haushaltsplan')}> {planung.bezeichnung} {planung.haushaltsjahr_bezeichnung && ( Haushaltsjahr: {planung.haushaltsjahr_bezeichnung} )} {!planung.haushaltsjahr_id && canManage && ( } onClick={() => createHjMut.mutate()} disabled={createHjMut.isPending}> Haushaltsjahr erstellen }> Diesem Plan ist noch kein Haushaltsjahr zugeordnet. Sie können aus diesem Plan ein Haushaltsjahr mit Konten erstellen. )} Positionen ({positionen.length}) {canManage && ( )} Bezeichnung Konto GWG Anlagen Instandh. Gesamt {canManage && Aktionen} {positionen.length === 0 && ( Keine Positionen )} {positionen.map((pos: Planposition) => { const rowTotal = Number(pos.budget_gwg) + Number(pos.budget_anlagen) + Number(pos.budget_instandhaltung); return ( {pos.bezeichnung} {pos.konto_bezeichnung ? `${pos.konto_kontonummer} – ${pos.konto_bezeichnung}` : '–'} {fmtEur(Number(pos.budget_gwg))} {fmtEur(Number(pos.budget_anlagen))} {fmtEur(Number(pos.budget_instandhaltung))} {fmtEur(rowTotal)} {canManage && ( openEditDialog(pos)}> deletePosMut.mutate(pos.id)}> )} ); })} {positionen.length > 0 && ( Summe {fmtEur(totalGwg)} {fmtEur(totalAnlagen)} {fmtEur(totalInstandhaltung)} {fmtEur(totalGwg + totalAnlagen + totalInstandhaltung)} {canManage && } )}
{/* Position Add/Edit Dialog */} setPosDialog({ open: false })} maxWidth="sm" fullWidth> {posDialog.existing ? 'Position bearbeiten' : 'Neue Position'} setPosForm(f => ({ ...f, bezeichnung: e.target.value }))} required /> {konten.length > 0 && ( Konto (optional) )} setPosForm(f => ({ ...f, budget_gwg: e.target.value }))} InputProps={{ startAdornment: EUR }} /> setPosForm(f => ({ ...f, budget_anlagen: e.target.value }))} InputProps={{ startAdornment: EUR }} /> setPosForm(f => ({ ...f, budget_instandhaltung: e.target.value }))} InputProps={{ startAdornment: EUR }} /> setPosForm(f => ({ ...f, notizen: e.target.value }))} multiline rows={2} />
); }