const express = require('express'); const db = require('../db'); const asyncHandler = require('../middleware/asyncHandler'); const router = express.Router(); function validateAmount(amount) { return amount === null || amount === undefined || (Number.isFinite(parseFloat(amount)) && parseFloat(amount) >= 0); } router.get('/', asyncHandler(async (req, res) => { const { vo_id } = req.query; const params = []; let where = ''; if (vo_id) { where = 'WHERE t.vo_id = ?'; params.push(vo_id); } const rows = await db.all( `SELECT t.*, a.name AS anbieter_name, v.nummer AS vo_nummer, v.bezeichnung AS vo_bezeichnung FROM tarife t JOIN anbieter a ON t.anbieter_id = a.id LEFT JOIN vos v ON t.vo_id = v.id ${where} ORDER BY a.name, t.name`, params ); res.json(rows); })); router.get('/:anbieter_id', asyncHandler(async (req, res) => { const { anbieter_id } = req.params; const rows = await db.all( `SELECT t.*, v.nummer AS vo_nummer, v.bezeichnung AS vo_bezeichnung FROM tarife t LEFT JOIN vos v ON t.vo_id = v.id WHERE t.anbieter_id = ? ORDER BY t.name`, [anbieter_id] ); res.json(rows); })); router.post('/', asyncHandler(async (req, res) => { const { name, anbieter_id, vo_id, provision_euro, beschreibung, tarifoption_ids } = req.body || {}; if (!name || !name.trim()) { return res.status(400).json({ error: 'Tarifname ist erforderlich' }); } if (!anbieter_id) { return res.status(400).json({ error: 'Anbieter ist erforderlich' }); } if (!vo_id) { return res.status(400).json({ error: 'VO ist erforderlich' }); } // Make sure the VO actually belongs to the same provider. const vo = await db.get('SELECT anbieter_id FROM vos WHERE id = ?', [vo_id]); if (!vo) return res.status(400).json({ error: 'VO nicht gefunden' }); if (vo.anbieter_id !== Number(anbieter_id)) { return res.status(400).json({ error: 'VO gehört nicht zum gewählten Anbieter' }); } if (!validateAmount(provision_euro)) { return res.status(400).json({ error: 'Provision muss eine positive Zahl sein' }); } try { const tarifId = await db.transaction(async () => { const result = await db.run( `INSERT INTO tarife (name, anbieter_id, vo_id, provision_euro, beschreibung) VALUES (?, ?, ?, ?, ?)`, [name.trim(), anbieter_id, vo_id, provision_euro || 0, beschreibung || null] ); if (Array.isArray(tarifoption_ids)) { for (const optId of tarifoption_ids) { await db.run( 'INSERT OR IGNORE INTO tarifoptionen_tarife (tarifoption_id, tarif_id) VALUES (?, ?)', [optId, result.lastID] ); } } return result.lastID; }); res.json({ id: tarifId, name: name.trim(), anbieter_id, vo_id, provision_euro: provision_euro || 0, beschreibung: beschreibung || null, }); } catch (err) { if (/UNIQUE/.test(err.message)) { return res.status(400).json({ error: 'Tarif mit diesem Namen existiert bereits für diesen Anbieter' }); } throw err; } })); router.put('/:id', asyncHandler(async (req, res) => { const { id } = req.params; const { name, vo_id, provision_euro, beschreibung, tarifoption_ids } = req.body || {}; if (!name) return res.status(400).json({ error: 'Tarif-Name ist erforderlich' }); if (!validateAmount(provision_euro)) { return res.status(400).json({ error: 'Provision muss eine positive Zahl sein' }); } const current = await db.get('SELECT anbieter_id FROM tarife WHERE id = ?', [id]); if (!current) return res.status(404).json({ error: 'Tarif nicht gefunden' }); if (vo_id) { const vo = await db.get('SELECT anbieter_id FROM vos WHERE id = ?', [vo_id]); if (!vo) return res.status(400).json({ error: 'VO nicht gefunden' }); if (vo.anbieter_id !== current.anbieter_id) { return res.status(400).json({ error: 'VO gehört nicht zum Anbieter des Tarifs' }); } } await db.transaction(async () => { await db.run( `UPDATE tarife SET name = ?, vo_id = ?, provision_euro = ?, beschreibung = ? WHERE id = ?`, [name, vo_id || null, provision_euro || 0, beschreibung || null, id] ); if (Array.isArray(tarifoption_ids)) { await db.run('DELETE FROM tarifoptionen_tarife WHERE tarif_id = ?', [id]); for (const optId of tarifoption_ids) { await db.run( 'INSERT OR IGNORE INTO tarifoptionen_tarife (tarifoption_id, tarif_id) VALUES (?, ?)', [optId, id] ); } } }); res.json({ message: 'Tarif erfolgreich aktualisiert' }); })); // All tarifoptionen of this tariff's provider, each annotated with whether it // is currently assigned to this tariff. Frontend uses this to render the // checkbox grid in the tariff editor. router.get('/:id/tarifoptionen-multi', asyncHandler(async (req, res) => { const { id } = req.params; const tarif = await db.get('SELECT anbieter_id FROM tarife WHERE id = ?', [id]); if (!tarif) return res.status(404).json({ error: 'Tarif nicht gefunden' }); const rows = await db.all( `SELECT tn.id, tn.name, tn.code, tn.provision_euro, tn.beschreibung, CASE WHEN tt.tarifoption_id IS NULL THEN 0 ELSE 1 END AS assigned FROM tarifoptionen_new tn LEFT JOIN tarifoptionen_tarife tt ON tt.tarifoption_id = tn.id AND tt.tarif_id = ? WHERE tn.aktiv = 1 AND EXISTS ( SELECT 1 FROM tarifoptionen_tarife tt2 JOIN tarife t2 ON tt2.tarif_id = t2.id WHERE tt2.tarifoption_id = tn.id AND t2.anbieter_id = ? ) ORDER BY tn.name`, [id, tarif.anbieter_id] ); res.json(rows); })); router.delete('/:id', asyncHandler(async (req, res) => { const { id } = req.params; const used = await db.get( 'SELECT EXISTS(SELECT 1 FROM schaltungen WHERE tarif_id = ?) AS has_schaltungen', [id] ); if (used.has_schaltungen) { return res.status(400).json({ error: 'Tarif kann nicht gelöscht werden: Tarif wird in Schaltungen verwendet' }); } const result = await db.run('DELETE FROM tarife WHERE id = ?', [id]); if (result.changes === 0) return res.status(404).json({ error: 'Tarif nicht gefunden' }); res.json({ message: 'Tarif erfolgreich gelöscht' }); })); router.get('/:tarif_id/tarifoptionen', asyncHandler(async (req, res) => { const { tarif_id } = req.params; const rows = await db.all( 'SELECT * FROM tarifoptionen WHERE tarif_id = ? AND aktiv = 1 ORDER BY name', [tarif_id] ); res.json(rows); })); router.get('/:id/provisionsregeln', asyncHandler(async (req, res) => { const { id } = req.params; const rows = await db.all( `SELECT pr.*, t.name AS tarif_name, a.name AS anbieter_name FROM provisionsregeln pr JOIN tarife t ON pr.tarif_id = t.id JOIN anbieter a ON t.anbieter_id = a.id WHERE pr.tarif_id = ? AND pr.aktiv = 1 ORDER BY pr.gueltig_ab DESC`, [id] ); res.json(rows); })); router.put('/:id/mitarbeiter-provision', asyncHandler(async (req, res) => { const { id } = req.params; const { mitarbeiter_provision } = req.body || {}; const result = await db.run( 'UPDATE tarife SET mitarbeiter_provision = ? WHERE id = ?', [mitarbeiter_provision || 0, id] ); if (result.changes === 0) return res.status(404).json({ error: 'Tarif nicht gefunden' }); res.json({ message: 'Mitarbeiter-Provision erfolgreich aktualisiert' }); })); module.exports = router;