import { Request, Response } from 'express'; import bestellungService from '../services/bestellung.service'; import logger from '../utils/logger'; import fs from 'fs'; // Helper to safely extract a route param as string const param = (req: Request, key: string): string => req.params[key] as string; class BestellungController { // --------------------------------------------------------------------------- // Vendors // --------------------------------------------------------------------------- async listVendors(_req: Request, res: Response): Promise { try { const vendors = await bestellungService.getVendors(); res.status(200).json({ success: true, data: vendors }); } catch (error) { logger.error('BestellungController.listVendors error', { error }); res.status(500).json({ success: false, message: 'Lieferanten konnten nicht geladen werden' }); } } async getVendor(req: Request, res: Response): Promise { const id = parseInt(param(req, 'id'), 10); if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; } try { const vendor = await bestellungService.getVendorById(id); if (!vendor) { res.status(404).json({ success: false, message: 'Lieferant nicht gefunden' }); return; } res.status(200).json({ success: true, data: vendor }); } catch (error) { logger.error('BestellungController.getVendor error', { error }); res.status(500).json({ success: false, message: 'Lieferant konnte nicht geladen werden' }); } } async getVendorOrders(req: Request, res: Response): Promise { const id = parseInt(param(req, 'id'), 10); if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; } try { const orders = await bestellungService.getOrders({ lieferant_id: id }); res.status(200).json({ success: true, data: orders }); } catch (error) { logger.error('BestellungController.getVendorOrders error', { error }); res.status(500).json({ success: false, message: 'Bestellungen konnten nicht geladen werden' }); } } async createVendor(req: Request, res: Response): Promise { const { name } = req.body; if (!name || typeof name !== 'string' || name.trim().length === 0) { res.status(400).json({ success: false, message: 'Name ist erforderlich' }); return; } try { const vendor = await bestellungService.createVendor(req.body, req.user!.id); res.status(201).json({ success: true, data: vendor }); } catch (error) { logger.error('BestellungController.createVendor error', { error }); res.status(500).json({ success: false, message: 'Lieferant konnte nicht erstellt werden' }); } } async updateVendor(req: Request, res: Response): Promise { const id = parseInt(param(req, 'id'), 10); if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; } try { const vendor = await bestellungService.updateVendor(id, req.body, req.user!.id); if (!vendor) { res.status(404).json({ success: false, message: 'Lieferant nicht gefunden' }); return; } res.status(200).json({ success: true, data: vendor }); } catch (error) { logger.error('BestellungController.updateVendor error', { error }); res.status(500).json({ success: false, message: 'Lieferant konnte nicht aktualisiert werden' }); } } async deleteVendor(req: Request, res: Response): Promise { const id = parseInt(param(req, 'id'), 10); if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; } try { const deleted = await bestellungService.deleteVendor(id); if (!deleted) { res.status(404).json({ success: false, message: 'Lieferant nicht gefunden' }); return; } res.status(200).json({ success: true, message: 'Lieferant gelöscht' }); } catch (error) { logger.error('BestellungController.deleteVendor error', { error }); res.status(500).json({ success: false, message: 'Lieferant konnte nicht gelöscht werden' }); } } // --------------------------------------------------------------------------- // Orders // --------------------------------------------------------------------------- async listOrders(req: Request, res: Response): Promise { try { const filters: { status?: string; lieferant_id?: number; besteller_id?: string } = {}; if (req.query.status) filters.status = req.query.status as string; if (req.query.lieferant_id) filters.lieferant_id = parseInt(req.query.lieferant_id as string, 10); if (req.query.besteller_id) filters.besteller_id = req.query.besteller_id as string; const orders = await bestellungService.getOrders(filters); res.status(200).json({ success: true, data: orders }); } catch (error) { logger.error('BestellungController.listOrders error', { error }); res.status(500).json({ success: false, message: 'Bestellungen konnten nicht geladen werden' }); } } async getOrder(req: Request, res: Response): Promise { const id = parseInt(param(req, 'id'), 10); if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; } try { const order = await bestellungService.getOrderById(id); if (!order) { res.status(404).json({ success: false, message: 'Bestellung nicht gefunden' }); return; } res.status(200).json({ success: true, data: order }); } catch (error) { logger.error('BestellungController.getOrder error', { error }); res.status(500).json({ success: false, message: 'Bestellung konnte nicht geladen werden' }); } } async createOrder(req: Request, res: Response): Promise { const { bezeichnung, lieferant_id, budget, besteller_id, positionen } = req.body; if (!bezeichnung || typeof bezeichnung !== 'string' || bezeichnung.trim().length === 0) { res.status(400).json({ success: false, message: 'Bezeichnung ist erforderlich' }); return; } if (lieferant_id != null && (!Number.isInteger(lieferant_id) || lieferant_id <= 0)) { res.status(400).json({ success: false, message: 'Ungültige Lieferanten-ID' }); return; } if (budget != null && (typeof budget !== 'number' || budget < 0)) { res.status(400).json({ success: false, message: 'Budget muss eine positive Zahl sein' }); return; } if (besteller_id != null && besteller_id !== '' && (typeof besteller_id !== 'string' || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(besteller_id))) { res.status(400).json({ success: false, message: 'Ungültige Besteller-ID' }); return; } if (positionen != null && !Array.isArray(positionen)) { res.status(400).json({ success: false, message: 'Positionen muss ein Array sein' }); return; } try { const order = await bestellungService.createOrder(req.body, req.user!.id); res.status(201).json({ success: true, data: order }); } catch (error) { logger.error('BestellungController.createOrder error', { error }); res.status(500).json({ success: false, message: error instanceof Error ? error.message : 'Bestellung konnte nicht erstellt werden' }); } } async updateOrder(req: Request, res: Response): Promise { const id = parseInt(param(req, 'id'), 10); if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; } try { const order = await bestellungService.updateOrder(id, req.body, req.user!.id); if (!order) { res.status(404).json({ success: false, message: 'Bestellung nicht gefunden' }); return; } res.status(200).json({ success: true, data: order }); } catch (error) { logger.error('BestellungController.updateOrder error', { error }); res.status(500).json({ success: false, message: 'Bestellung konnte nicht aktualisiert werden' }); } } async deleteOrder(req: Request, res: Response): Promise { const id = parseInt(param(req, 'id'), 10); if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; } try { const deleted = await bestellungService.deleteOrder(id, req.user!.id); if (!deleted) { res.status(404).json({ success: false, message: 'Bestellung nicht gefunden' }); return; } res.status(200).json({ success: true, message: 'Bestellung gelöscht' }); } catch (error) { logger.error('BestellungController.deleteOrder error', { error }); res.status(500).json({ success: false, message: 'Bestellung konnte nicht gelöscht werden' }); } } async updateStatus(req: Request, res: Response): Promise { const id = parseInt(param(req, 'id'), 10); if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; } const { status, force } = req.body; if (!status || typeof status !== 'string') { res.status(400).json({ success: false, message: 'Status ist erforderlich' }); return; } try { const order = await bestellungService.updateOrderStatus(id, status, req.user!.id, !!force); if (!order) { res.status(404).json({ success: false, message: 'Bestellung nicht gefunden' }); return; } res.status(200).json({ success: true, data: order }); } catch (error: any) { if (error.message?.includes('Ungültiger Statusübergang')) { res.status(400).json({ success: false, message: error.message }); return; } logger.error('BestellungController.updateStatus error', { error }); res.status(500).json({ success: false, message: 'Status konnte nicht aktualisiert werden' }); } } // --------------------------------------------------------------------------- // Line Items // --------------------------------------------------------------------------- async addLineItem(req: Request, res: Response): Promise { const bestellungId = parseInt(param(req, 'id'), 10); if (isNaN(bestellungId)) { res.status(400).json({ success: false, message: 'Ungültige Bestellungs-ID' }); return; } 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) { res.status(400).json({ success: false, message: 'Menge muss größer als 0 sein' }); return; } try { const item = await bestellungService.addLineItem(bestellungId, req.body, req.user!.id); res.status(201).json({ success: true, data: item }); } catch (error) { logger.error('BestellungController.addLineItem error', { error }); res.status(500).json({ success: false, message: 'Position konnte nicht hinzugefügt werden' }); } } async updateLineItem(req: Request, res: Response): Promise { const itemId = parseInt(param(req, 'itemId'), 10); if (isNaN(itemId)) { res.status(400).json({ success: false, message: 'Ungültige Position-ID' }); return; } try { const item = await bestellungService.updateLineItem(itemId, req.body, req.user!.id); if (!item) { res.status(404).json({ success: false, message: 'Position nicht gefunden' }); return; } res.status(200).json({ success: true, data: item }); } catch (error) { logger.error('BestellungController.updateLineItem error', { error }); res.status(500).json({ success: false, message: 'Position konnte nicht aktualisiert werden' }); } } async deleteLineItem(req: Request, res: Response): Promise { const itemId = parseInt(param(req, 'itemId'), 10); if (isNaN(itemId)) { res.status(400).json({ success: false, message: 'Ungültige Position-ID' }); return; } try { const deleted = await bestellungService.deleteLineItem(itemId, req.user!.id); if (!deleted) { res.status(404).json({ success: false, message: 'Position nicht gefunden' }); return; } res.status(200).json({ success: true, message: 'Position gelöscht' }); } catch (error) { logger.error('BestellungController.deleteLineItem error', { error }); res.status(500).json({ success: false, message: 'Position konnte nicht gelöscht werden' }); } } async updateReceivedQuantity(req: Request, res: Response): Promise { const itemId = parseInt(param(req, 'itemId'), 10); if (isNaN(itemId)) { res.status(400).json({ success: false, message: 'Ungültige Position-ID' }); return; } const { menge } = req.body; if (menge === undefined || menge === null || menge < 0) { res.status(400).json({ success: false, message: 'Erhaltene Menge muss >= 0 sein' }); return; } try { const item = await bestellungService.updateReceivedQuantity(itemId, menge, req.user!.id); if (!item) { res.status(404).json({ success: false, message: 'Position nicht gefunden' }); return; } res.status(200).json({ success: true, data: item }); } catch (error) { logger.error('BestellungController.updateReceivedQuantity error', { error }); res.status(500).json({ success: false, message: 'Liefermenge konnte nicht aktualisiert werden' }); } } // --------------------------------------------------------------------------- // Files // --------------------------------------------------------------------------- async uploadFile(req: Request, res: Response): Promise { const bestellungId = parseInt(param(req, 'id'), 10); if (isNaN(bestellungId)) { res.status(400).json({ success: false, message: 'Ungültige Bestellungs-ID' }); return; } const file = (req as any).file; if (!file) { res.status(400).json({ success: false, message: 'Keine Datei hochgeladen' }); return; } try { const fileRecord = await bestellungService.addFile(bestellungId, { dateiname: file.originalname, dateipfad: file.path, dateityp: file.mimetype, dateigroesse: file.size, }, req.user!.id); res.status(201).json({ success: true, data: fileRecord }); } catch (error) { logger.error('BestellungController.uploadFile error', { error }); res.status(500).json({ success: false, message: 'Datei konnte nicht hochgeladen werden' }); } } async deleteFile(req: Request, res: Response): Promise { const fileId = parseInt(param(req, 'fileId'), 10); if (isNaN(fileId)) { res.status(400).json({ success: false, message: 'Ungültige Datei-ID' }); return; } try { const result = await bestellungService.deleteFile(fileId, req.user!.id); if (!result) { res.status(404).json({ success: false, message: 'Datei nicht gefunden' }); return; } // Remove from disk try { if (result.dateipfad && fs.existsSync(result.dateipfad)) { fs.unlinkSync(result.dateipfad); } } catch (err) { logger.warn('Failed to delete file from disk', { path: result.dateipfad, error: err }); } res.status(200).json({ success: true, message: 'Datei gelöscht' }); } catch (error) { logger.error('BestellungController.deleteFile error', { error }); res.status(500).json({ success: false, message: 'Datei konnte nicht gelöscht werden' }); } } async listFiles(req: Request, res: Response): Promise { const bestellungId = parseInt(param(req, 'id'), 10); if (isNaN(bestellungId)) { res.status(400).json({ success: false, message: 'Ungültige Bestellungs-ID' }); return; } try { const files = await bestellungService.getFilesByOrder(bestellungId); res.status(200).json({ success: true, data: files }); } catch (error) { logger.error('BestellungController.listFiles error', { error }); res.status(500).json({ success: false, message: 'Dateien konnten nicht geladen werden' }); } } // --------------------------------------------------------------------------- // Reminders // --------------------------------------------------------------------------- async addReminder(req: Request, res: Response): Promise { const bestellungId = parseInt(param(req, 'id'), 10); if (isNaN(bestellungId)) { res.status(400).json({ success: false, message: 'Ungültige Bestellungs-ID' }); return; } const { faellig_am } = req.body; if (!faellig_am) { res.status(400).json({ success: false, message: 'Fälligkeitsdatum ist erforderlich' }); return; } try { const reminder = await bestellungService.addReminder(bestellungId, req.body, req.user!.id); res.status(201).json({ success: true, data: reminder }); } catch (error) { logger.error('BestellungController.addReminder error', { error }); res.status(500).json({ success: false, message: 'Erinnerung konnte nicht erstellt werden' }); } } async markReminderDone(req: Request, res: Response): Promise { const remId = parseInt(param(req, 'remId'), 10); if (isNaN(remId)) { res.status(400).json({ success: false, message: 'Ungültige Erinnerungs-ID' }); return; } try { const reminder = await bestellungService.markReminderDone(remId, req.user!.id); if (!reminder) { res.status(404).json({ success: false, message: 'Erinnerung nicht gefunden' }); return; } res.status(200).json({ success: true, data: reminder }); } catch (error) { logger.error('BestellungController.markReminderDone error', { error }); res.status(500).json({ success: false, message: 'Erinnerung konnte nicht als erledigt markiert werden' }); } } async deleteReminder(req: Request, res: Response): Promise { const remId = parseInt(param(req, 'remId'), 10); if (isNaN(remId)) { res.status(400).json({ success: false, message: 'Ungültige Erinnerungs-ID' }); return; } try { const deleted = await bestellungService.deleteReminder(remId); if (!deleted) { res.status(404).json({ success: false, message: 'Erinnerung nicht gefunden' }); return; } res.status(200).json({ success: true, message: 'Erinnerung gelöscht' }); } catch (error) { logger.error('BestellungController.deleteReminder error', { error }); res.status(500).json({ success: false, message: 'Erinnerung konnte nicht gelöscht werden' }); } } // --------------------------------------------------------------------------- // History // --------------------------------------------------------------------------- async getHistory(req: Request, res: Response): Promise { const bestellungId = parseInt(param(req, 'id'), 10); if (isNaN(bestellungId)) { res.status(400).json({ success: false, message: 'Ungültige Bestellungs-ID' }); return; } try { const history = await bestellungService.getHistory(bestellungId); res.status(200).json({ success: true, data: history }); } catch (error) { logger.error('BestellungController.getHistory error', { error }); res.status(500).json({ success: false, message: 'Historie konnte nicht geladen werden' }); } } // --------------------------------------------------------------------------- // Export (placeholder — returns order detail as JSON for now) // --------------------------------------------------------------------------- async exportOrder(req: Request, res: Response): Promise { const id = parseInt(param(req, 'id'), 10); if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; } try { const order = await bestellungService.getOrderById(id); if (!order) { res.status(404).json({ success: false, message: 'Bestellung nicht gefunden' }); return; } res.status(200).json({ success: true, data: order }); } catch (error) { logger.error('BestellungController.exportOrder error', { error }); res.status(500).json({ success: false, message: 'Export fehlgeschlagen' }); } } } export default new BestellungController();