new features
This commit is contained in:
@@ -451,6 +451,86 @@ class EquipmentController {
|
||||
}
|
||||
}
|
||||
|
||||
async createCategory(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { name, kurzname, sortierung, motorisiert } = req.body;
|
||||
if (!name || typeof name !== 'string' || !name.trim()) {
|
||||
res.status(400).json({ success: false, message: 'Name ist erforderlich' });
|
||||
return;
|
||||
}
|
||||
if (!kurzname || typeof kurzname !== 'string' || !kurzname.trim()) {
|
||||
res.status(400).json({ success: false, message: 'Kurzname ist erforderlich' });
|
||||
return;
|
||||
}
|
||||
const category = await equipmentService.createCategory({
|
||||
name: name.trim(),
|
||||
kurzname: kurzname.trim(),
|
||||
sortierung: sortierung != null ? Number(sortierung) : undefined,
|
||||
motorisiert: motorisiert != null ? Boolean(motorisiert) : undefined,
|
||||
});
|
||||
res.status(201).json({ success: true, data: category });
|
||||
} catch (error) {
|
||||
logger.error('createCategory error', { error });
|
||||
res.status(500).json({ success: false, message: 'Kategorie konnte nicht erstellt werden' });
|
||||
}
|
||||
}
|
||||
|
||||
async updateCategory(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { id } = req.params as Record<string, string>;
|
||||
if (!isValidUUID(id)) {
|
||||
res.status(400).json({ success: false, message: 'Ungültige Kategorie-ID' });
|
||||
return;
|
||||
}
|
||||
const { name, kurzname, sortierung, motorisiert } = req.body;
|
||||
const data: Record<string, unknown> = {};
|
||||
if (name !== undefined) data.name = String(name).trim();
|
||||
if (kurzname !== undefined) data.kurzname = String(kurzname).trim();
|
||||
if (sortierung !== undefined) data.sortierung = Number(sortierung);
|
||||
if (motorisiert !== undefined) data.motorisiert = Boolean(motorisiert);
|
||||
|
||||
if (Object.keys(data).length === 0) {
|
||||
res.status(400).json({ success: false, message: 'Kein Feld zum Aktualisieren angegeben' });
|
||||
return;
|
||||
}
|
||||
const category = await equipmentService.updateCategory(id, data as any);
|
||||
if (!category) {
|
||||
res.status(404).json({ success: false, message: 'Kategorie nicht gefunden' });
|
||||
return;
|
||||
}
|
||||
res.status(200).json({ success: true, data: category });
|
||||
} catch (error: any) {
|
||||
if (error?.message === 'No fields to update') {
|
||||
res.status(400).json({ success: false, message: 'Kein Feld zum Aktualisieren angegeben' });
|
||||
return;
|
||||
}
|
||||
logger.error('updateCategory error', { error, id: req.params.id });
|
||||
res.status(500).json({ success: false, message: 'Kategorie konnte nicht aktualisiert werden' });
|
||||
}
|
||||
}
|
||||
|
||||
async deleteCategory(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { id } = req.params as Record<string, string>;
|
||||
if (!isValidUUID(id)) {
|
||||
res.status(400).json({ success: false, message: 'Ungültige Kategorie-ID' });
|
||||
return;
|
||||
}
|
||||
const result = await equipmentService.deleteCategory(id);
|
||||
if (!result.deleted) {
|
||||
res.status(result.error === 'Kategorie nicht gefunden' ? 404 : 409).json({
|
||||
success: false,
|
||||
message: result.error,
|
||||
});
|
||||
return;
|
||||
}
|
||||
res.status(200).json({ success: true, message: 'Kategorie gelöscht' });
|
||||
} catch (error) {
|
||||
logger.error('deleteCategory error', { error, id: req.params.id });
|
||||
res.status(500).json({ success: false, message: 'Kategorie konnte nicht gelöscht werden' });
|
||||
}
|
||||
}
|
||||
|
||||
async uploadWartungFile(req: Request, res: Response): Promise<void> {
|
||||
const { wartungId } = req.params as Record<string, string>;
|
||||
const id = parseInt(wartungId, 10);
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Add manage_categories permission for equipment
|
||||
INSERT INTO permissions (feature_group, action, beschreibung)
|
||||
VALUES ('ausruestung', 'manage_categories', 'Kategorien verwalten')
|
||||
ON CONFLICT DO NOTHING;
|
||||
@@ -12,6 +12,9 @@ router.get('/', authenticate, requirePermission('ausruestung:
|
||||
router.get('/stats', authenticate, requirePermission('ausruestung:view'), equipmentController.getStats.bind(equipmentController));
|
||||
router.get('/alerts', authenticate, requirePermission('ausruestung:view'), equipmentController.getAlerts.bind(equipmentController));
|
||||
router.get('/categories', authenticate, requirePermission('ausruestung:view'), equipmentController.getCategories.bind(equipmentController));
|
||||
router.post('/categories', authenticate, requirePermission('ausruestung:manage_categories'), equipmentController.createCategory.bind(equipmentController));
|
||||
router.patch('/categories/:id', authenticate, requirePermission('ausruestung:manage_categories'), equipmentController.updateCategory.bind(equipmentController));
|
||||
router.delete('/categories/:id', authenticate, requirePermission('ausruestung:manage_categories'), equipmentController.deleteCategory.bind(equipmentController));
|
||||
router.get('/vehicle-warnings', authenticate, equipmentController.getVehicleWarnings.bind(equipmentController));
|
||||
router.get('/vehicle/:fahrzeugId', authenticate, equipmentController.getByVehicle.bind(equipmentController));
|
||||
router.get('/:id', authenticate, equipmentController.getEquipment.bind(equipmentController));
|
||||
|
||||
@@ -132,6 +132,75 @@ class EquipmentService {
|
||||
}
|
||||
}
|
||||
|
||||
async createCategory(data: { name: string; kurzname: string; sortierung?: number; motorisiert?: boolean }): Promise<AusruestungKategorie> {
|
||||
try {
|
||||
const result = await pool.query(
|
||||
`INSERT INTO ausruestung_kategorien (id, name, kurzname, sortierung, motorisiert)
|
||||
VALUES (uuid_generate_v4(), $1, $2, COALESCE($3, (SELECT COALESCE(MAX(sortierung),0)+1 FROM ausruestung_kategorien)), COALESCE($4, false))
|
||||
RETURNING *`,
|
||||
[data.name, data.kurzname, data.sortierung ?? null, data.motorisiert ?? null]
|
||||
);
|
||||
logger.info('Equipment category created', { id: result.rows[0].id, name: data.name });
|
||||
return result.rows[0] as AusruestungKategorie;
|
||||
} catch (error) {
|
||||
logger.error('EquipmentService.createCategory failed', { error });
|
||||
throw new Error('Failed to create equipment category');
|
||||
}
|
||||
}
|
||||
|
||||
async updateCategory(id: string, data: { name?: string; kurzname?: string; sortierung?: number; motorisiert?: boolean }): Promise<AusruestungKategorie | null> {
|
||||
try {
|
||||
const fields: string[] = [];
|
||||
const values: unknown[] = [];
|
||||
let p = 1;
|
||||
|
||||
if (data.name !== undefined) { fields.push(`name = $${p++}`); values.push(data.name); }
|
||||
if (data.kurzname !== undefined) { fields.push(`kurzname = $${p++}`); values.push(data.kurzname); }
|
||||
if (data.sortierung !== undefined) { fields.push(`sortierung = $${p++}`); values.push(data.sortierung); }
|
||||
if (data.motorisiert !== undefined) { fields.push(`motorisiert = $${p++}`); values.push(data.motorisiert); }
|
||||
|
||||
if (fields.length === 0) throw new Error('No fields to update');
|
||||
|
||||
values.push(id);
|
||||
const result = await pool.query(
|
||||
`UPDATE ausruestung_kategorien SET ${fields.join(', ')} WHERE id = $${p} RETURNING *`,
|
||||
values
|
||||
);
|
||||
if (result.rows.length === 0) return null;
|
||||
logger.info('Equipment category updated', { id });
|
||||
return result.rows[0] as AusruestungKategorie;
|
||||
} catch (error) {
|
||||
logger.error('EquipmentService.updateCategory failed', { error, id });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async deleteCategory(id: string): Promise<{ deleted: boolean; error?: string }> {
|
||||
try {
|
||||
// Check if any equipment items reference this category
|
||||
const usage = await pool.query(
|
||||
`SELECT COUNT(*) AS cnt FROM ausruestung WHERE kategorie_id = $1 AND deleted_at IS NULL`,
|
||||
[id]
|
||||
);
|
||||
const count = parseInt(usage.rows[0].cnt, 10);
|
||||
if (count > 0) {
|
||||
return { deleted: false, error: `Kategorie wird von ${count} Ausrüstungsgegenständen verwendet und kann nicht gelöscht werden.` };
|
||||
}
|
||||
const result = await pool.query(
|
||||
`DELETE FROM ausruestung_kategorien WHERE id = $1 RETURNING id`,
|
||||
[id]
|
||||
);
|
||||
if (result.rows.length === 0) {
|
||||
return { deleted: false, error: 'Kategorie nicht gefunden' };
|
||||
}
|
||||
logger.info('Equipment category deleted', { id });
|
||||
return { deleted: true };
|
||||
} catch (error) {
|
||||
logger.error('EquipmentService.deleteCategory failed', { error, id });
|
||||
throw new Error('Failed to delete equipment category');
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// CRUD
|
||||
// =========================================================================
|
||||
|
||||
Reference in New Issue
Block a user