feat(buchhaltung): add categories, recurring tx scheduling, sub-pot budget validation, and UX polish

This commit is contained in:
Matthias Hochmeister
2026-03-30 12:56:33 +02:00
parent 86cb175aeb
commit 13aa4be599
9 changed files with 674 additions and 149 deletions

View File

@@ -6,6 +6,55 @@ const param = (req: Request, key: string): string => req.params[key] as string;
class BuchhaltungController {
// ── Kategorien ──────────────────────────────────────────────────────────────
async listKategorien(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 data = await buchhaltungService.getKategorien(haushaltsjahrId);
res.json({ success: true, data });
} catch (error) {
logger.error('BuchhaltungController.listKategorien', { error });
res.status(500).json({ success: false, message: 'Kategorien konnten nicht geladen werden' });
}
}
async createKategorie(req: Request, res: Response): Promise<void> {
try {
const data = await buchhaltungService.createKategorie(req.body);
res.status(201).json({ success: true, data });
} catch (error) {
logger.error('BuchhaltungController.createKategorie', { error });
res.status(500).json({ success: false, message: 'Kategorie konnte nicht erstellt werden' });
}
}
async updateKategorie(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.updateKategorie(id, req.body);
if (!data) { res.status(404).json({ success: false, message: 'Kategorie nicht gefunden' }); return; }
res.json({ success: true, data });
} catch (error) {
logger.error('BuchhaltungController.updateKategorie', { error });
res.status(500).json({ success: false, message: 'Kategorie konnte nicht aktualisiert werden' });
}
}
async deleteKategorie(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.deleteKategorie(id);
res.json({ success: true });
} catch (error) {
logger.error('BuchhaltungController.deleteKategorie', { error });
res.status(500).json({ success: false, message: 'Kategorie konnte nicht gelöscht werden' });
}
}
// ── Haushaltsjahre ──────────────────────────────────────────────────────────
async listHaushaltsjahre(_req: Request, res: Response): Promise<void> {
@@ -181,9 +230,10 @@ class BuchhaltungController {
const data = await buchhaltungService.updateKonto(id, req.body);
if (!data) { res.status(404).json({ success: false, message: 'Konto nicht gefunden' }); return; }
res.json({ success: true, data });
} catch (error) {
} catch (error: any) {
logger.error('BuchhaltungController.updateKonto', { error });
res.status(500).json({ success: false, message: 'Konto konnte nicht aktualisiert werden' });
const status = error.statusCode || 500;
res.status(status).json({ success: false, message: error.message || 'Konto konnte nicht aktualisiert werden' });
}
}