feat: add Buchhaltung dashboard widget, CSV export, Bestellungen linking, recurring bookings, and approval workflow

This commit is contained in:
Matthias Hochmeister
2026-03-28 20:34:53 +01:00
parent c1fca5cc67
commit bc39963746
10 changed files with 750 additions and 5 deletions

View File

@@ -335,6 +335,109 @@ class BuchhaltungController {
res.status(500).json({ success: false, message: 'Einstellungen konnten nicht gespeichert werden' });
}
}
// ── Wiederkehrend ────────────────────────────────────────────────────────────
async listWiederkehrend(_req: Request, res: Response): Promise<void> {
try {
const data = await buchhaltungService.getAllWiederkehrend();
res.json({ success: true, data });
} catch (error) {
logger.error('BuchhaltungController.listWiederkehrend', { error });
res.status(500).json({ success: false, message: 'Wiederkehrende Buchungen konnten nicht geladen werden' });
}
}
async createWiederkehrend(req: Request, res: Response): Promise<void> {
try {
const data = await buchhaltungService.createWiederkehrend(req.body, req.user!.id);
res.status(201).json({ success: true, data });
} catch (error) {
logger.error('BuchhaltungController.createWiederkehrend', { error });
res.status(500).json({ success: false, message: 'Wiederkehrende Buchung konnte nicht erstellt werden' });
}
}
async updateWiederkehrend(req: Request, res: Response): Promise<void> {
const id = parseInt(param(req, 'id'), 10);
if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; }
try {
const data = await buchhaltungService.updateWiederkehrend(id, req.body);
if (!data) { res.status(404).json({ success: false, message: 'Wiederkehrende Buchung nicht gefunden' }); return; }
res.json({ success: true, data });
} catch (error) {
logger.error('BuchhaltungController.updateWiederkehrend', { error });
res.status(500).json({ success: false, message: 'Wiederkehrende Buchung konnte nicht aktualisiert werden' });
}
}
async deleteWiederkehrend(req: Request, res: Response): Promise<void> {
const id = parseInt(param(req, 'id'), 10);
if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; }
try {
await buchhaltungService.deleteWiederkehrend(id);
res.json({ success: true });
} catch (error) {
logger.error('BuchhaltungController.deleteWiederkehrend', { error });
res.status(500).json({ success: false, message: 'Wiederkehrende Buchung konnte nicht gelöscht werden' });
}
}
// ── CSV Export ───────────────────────────────────────────────────────────────
async exportCsv(req: Request, res: Response): Promise<void> {
const haushaltsjahrId = parseInt(req.query.haushaltsjahr_id as string, 10);
if (isNaN(haushaltsjahrId)) { res.status(400).json({ success: false, message: 'haushaltsjahr_id erforderlich' }); return; }
try {
const csv = await buchhaltungService.exportTransaktionenCsv(haushaltsjahrId);
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
res.setHeader('Content-Disposition', `attachment; filename="transaktionen_${haushaltsjahrId}.csv"`);
res.send(csv);
} catch (error) {
logger.error('BuchhaltungController.exportCsv', { error });
res.status(500).json({ success: false, message: 'CSV-Export fehlgeschlagen' });
}
}
// ── Freigaben ────────────────────────────────────────────────────────────────
async requestFreigabe(req: Request, res: Response): Promise<void> {
const id = parseInt(param(req, 'id'), 10);
if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; }
try {
const data = await buchhaltungService.createFreigabe(id, req.user!.id);
res.status(201).json({ success: true, data });
} catch (error) {
logger.error('BuchhaltungController.requestFreigabe', { error });
res.status(500).json({ success: false, message: 'Freigabe konnte nicht angefragt werden' });
}
}
async approveFreigabe(req: Request, res: Response): Promise<void> {
const id = parseInt(param(req, 'id'), 10);
if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; }
try {
const data = await buchhaltungService.approveFreigabe(id, req.body.kommentar, req.user!.id);
if (!data) { res.status(404).json({ success: false, message: 'Freigabe nicht gefunden' }); return; }
res.json({ success: true, data });
} catch (error) {
logger.error('BuchhaltungController.approveFreigabe', { error });
res.status(500).json({ success: false, message: 'Freigabe konnte nicht genehmigt werden' });
}
}
async rejectFreigabe(req: Request, res: Response): Promise<void> {
const id = parseInt(param(req, 'id'), 10);
if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; }
try {
const data = await buchhaltungService.rejectFreigabe(id, req.body.kommentar, req.user!.id);
if (!data) { res.status(404).json({ success: false, message: 'Freigabe nicht gefunden' }); return; }
res.json({ success: true, data });
} catch (error) {
logger.error('BuchhaltungController.rejectFreigabe', { error });
res.status(500).json({ success: false, message: 'Freigabe konnte nicht abgelehnt werden' });
}
}
}
export default new BuchhaltungController();