new features

This commit is contained in:
Matthias Hochmeister
2026-03-23 14:01:39 +01:00
parent d2dc64d54a
commit 3326156b15
35 changed files with 1341 additions and 257 deletions

View File

@@ -120,6 +120,7 @@ class AuthController {
});
await userService.updateGroups(user.id, groups);
logger.info('Groups synced for user', { userId: user.id, groupCount: groups.length });
await memberService.ensureProfileExists(user.id);
// Audit: first-ever login (user record creation)
@@ -168,6 +169,7 @@ class AuthController {
await userService.updateLastLogin(user.id);
await userService.updateGroups(user.id, groups);
logger.info('Groups synced for user', { userId: user.id, groupCount: groups.length });
await memberService.ensureProfileExists(user.id);
const { given_name: updatedGivenName, family_name: updatedFamilyName } = extractNames(userInfo);

View File

@@ -113,9 +113,9 @@ class BestellungController {
}
async createOrder(req: Request, res: Response): Promise<void> {
const { titel } = req.body;
if (!titel || typeof titel !== 'string' || titel.trim().length === 0) {
res.status(400).json({ success: false, message: 'Titel ist erforderlich' });
const { bezeichnung } = req.body;
if (!bezeichnung || typeof bezeichnung !== 'string' || bezeichnung.trim().length === 0) {
res.status(400).json({ success: false, message: 'Bezeichnung ist erforderlich' });
return;
}
try {
@@ -203,9 +203,9 @@ class BestellungController {
res.status(400).json({ success: false, message: 'Ungültige Bestellungs-ID' });
return;
}
const { artikel, menge } = req.body;
if (!artikel || typeof artikel !== 'string' || artikel.trim().length === 0) {
res.status(400).json({ success: false, message: 'Artikel ist erforderlich' });
const { bezeichnung, menge } = req.body;
if (!bezeichnung || typeof bezeichnung !== 'string' || bezeichnung.trim().length === 0) {
res.status(400).json({ success: false, message: 'Bezeichnung ist erforderlich' });
return;
}
if (menge === undefined || menge === null || menge <= 0) {
@@ -364,11 +364,7 @@ class BestellungController {
res.status(400).json({ success: false, message: 'Ungültige Bestellungs-ID' });
return;
}
const { titel, faellig_am } = req.body;
if (!titel || typeof titel !== 'string' || titel.trim().length === 0) {
res.status(400).json({ success: false, message: 'Titel ist erforderlich' });
return;
}
const { nachricht, faellig_am } = req.body;
if (!faellig_am) {
res.status(400).json({ success: false, message: 'Fälligkeitsdatum ist erforderlich' });
return;

View File

@@ -158,7 +158,7 @@ class BookingController {
handleZodError(res, parsed.error);
return;
}
const booking = await bookingService.create(parsed.data, req.user!.id);
const booking = await bookingService.create(parsed.data, req.user!.id, req.body.ignoreOutOfService === true);
res.status(201).json({ success: true, data: booking });
} catch (error: any) {
if (handleConflictError(res, error)) return;

View File

@@ -391,6 +391,36 @@ class EquipmentController {
res.status(500).json({ success: false, message: 'Wartungseintrag konnte nicht gespeichert werden' });
}
}
async getStatusHistory(req: Request, res: Response): Promise<void> {
try {
const history = await equipmentService.getStatusHistory(req.params.id);
res.status(200).json({ success: true, data: history });
} catch (error) {
logger.error('getStatusHistory error', { error, id: req.params.id });
res.status(500).json({ success: false, message: 'Status-Historie konnte nicht geladen werden' });
}
}
async uploadWartungFile(req: Request, res: Response): Promise<void> {
const wartungId = parseInt(req.params.wartungId, 10);
if (isNaN(wartungId)) {
res.status(400).json({ success: false, message: 'Ungültige Wartungs-ID' });
return;
}
const file = (req as any).file;
if (!file) {
res.status(400).json({ success: false, message: 'Keine Datei hochgeladen' });
return;
}
try {
const result = await equipmentService.updateWartungslogFile(wartungId, file.path);
res.status(200).json({ success: true, data: result });
} catch (error) {
logger.error('uploadWartungFile error', { error, wartungId });
res.status(500).json({ success: false, message: 'Datei konnte nicht hochgeladen werden' });
}
}
}
export default new EquipmentController();

View File

@@ -304,7 +304,12 @@ class EventsController {
deleteEvent = async (req: Request, res: Response): Promise<void> => {
try {
const { id } = req.params as Record<string, string>;
const deleted = await eventsService.deleteEvent(id);
const mode = (req.body?.mode as string) || 'all';
if (!['all', 'single', 'future'].includes(mode)) {
res.status(400).json({ success: false, message: 'Ungültiger Löschmodus. Erlaubt: all, single, future' });
return;
}
const deleted = await eventsService.deleteEvent(id, mode as 'all' | 'single' | 'future');
if (!deleted) {
res.status(404).json({ success: false, message: 'Veranstaltung nicht gefunden' });
return;

View File

@@ -372,6 +372,36 @@ class VehicleController {
res.status(500).json({ success: false, message: 'Wartungslog konnte nicht geladen werden' });
}
}
async getStatusHistory(req: Request, res: Response): Promise<void> {
try {
const history = await vehicleService.getStatusHistory(req.params.id);
res.status(200).json({ success: true, data: history });
} catch (error) {
logger.error('getStatusHistory error', { error, id: req.params.id });
res.status(500).json({ success: false, message: 'Status-Historie konnte nicht geladen werden' });
}
}
async uploadWartungFile(req: Request, res: Response): Promise<void> {
const wartungId = parseInt(req.params.wartungId, 10);
if (isNaN(wartungId)) {
res.status(400).json({ success: false, message: 'Ungültige Wartungs-ID' });
return;
}
const file = (req as any).file;
if (!file) {
res.status(400).json({ success: false, message: 'Keine Datei hochgeladen' });
return;
}
try {
const result = await vehicleService.updateWartungslogFile(wartungId, file.path);
res.status(200).json({ success: true, data: result });
} catch (error) {
logger.error('uploadWartungFile error', { error, wartungId });
res.status(500).json({ success: false, message: 'Datei konnte nicht hochgeladen werden' });
}
}
}
export default new VehicleController();