const database = require('../config/database'); const { Op } = require('sequelize'); /** * Service-Klasse für Zeitwünsche (Timewish) * Verwaltet gewünschte Arbeitszeiten pro Wochentag und Zeitraum */ class TimewishService { /** * Holt alle Zeitwünsche eines Users * @param {number} userId - Benutzer-ID * @returns {Promise} Array von Zeitwünschen */ async getAllTimewishes(userId) { const { Timewish } = database.getModels(); const timewishes = await Timewish.findAll({ where: { user_id: userId }, order: [['day', 'ASC'], ['start_date', 'DESC']], raw: true }); // Konvertiere DB-Format zu Frontend-Format const dayNames = { 1: 'Montag', 2: 'Dienstag', 3: 'Mittwoch', 4: 'Donnerstag', 5: 'Freitag', 6: 'Samstag', 7: 'Sonntag' }; const wishtypeNames = { 0: 'Kein Wunsch', 1: 'Frei', 2: 'Arbeit' }; return timewishes.map(tw => ({ id: tw.id, day: tw.day, dayName: dayNames[tw.day] || `Tag ${tw.day}`, wishtype: tw.wishtype, wishtypeName: wishtypeNames[tw.wishtype] || 'Unbekannt', hours: tw.hours, startDate: tw.start_date, endDate: tw.end_date })); } /** * Erstellt einen neuen Zeitwunsch * @param {number} userId - Benutzer-ID * @param {number} day - Wochentag (1=Mo, 7=So) * @param {number} wishtype - Typ (0=Kein Wunsch, 1=Frei, 2=Arbeit) * @param {number} hours - Stunden (nur bei wishtype=2) * @param {string} startDate - Startdatum (YYYY-MM-DD) * @param {string} endDate - Enddatum (YYYY-MM-DD, optional) * @returns {Promise} Erstellter Zeitwunsch */ async createTimewish(userId, day, wishtype, hours, startDate, endDate) { const { Timewish } = database.getModels(); // Validierung if (day < 1 || day > 7) { throw new Error('Wochentag muss zwischen 1 (Montag) und 7 (Sonntag) liegen'); } if (wishtype < 0 || wishtype > 2) { throw new Error('Ungültiger Wunschtyp'); } if (wishtype === 2 && (!hours || hours < 0 || hours > 24)) { throw new Error('Arbeitsstunden müssen zwischen 0 und 24 liegen'); } if (!startDate) { throw new Error('Startdatum ist erforderlich'); } // Prüfe auf Überschneidungen const overlapping = await Timewish.findOne({ where: { user_id: userId, day: day, [Op.or]: [ // Fall 1: Neuer Eintrag hat kein Enddatum (gilt ab start_date) endDate ? {} : { [Op.or]: [ // Bestehender Eintrag hat kein Enddatum UND start_date <= neuer start_date { end_date: null, start_date: { [Op.lte]: startDate } }, // Bestehender Eintrag hat Enddatum UND überlappt { end_date: { [Op.gte]: startDate } } ] }, // Fall 2: Neuer Eintrag hat Enddatum endDate ? { [Op.or]: [ // Bestehender Eintrag hat kein Enddatum UND start_date <= neues end_date { end_date: null, start_date: { [Op.lte]: endDate } }, // Bestehender Eintrag hat Enddatum UND überlappt { start_date: { [Op.lte]: endDate }, end_date: { [Op.gte]: startDate } } ] } : {} ] } }); if (overlapping) { throw new Error(`Überschneidung mit bestehendem Zeitwunsch: ${overlapping.start_date} - ${overlapping.end_date || 'unbegrenzt'}`); } const timewish = await Timewish.create({ user_id: userId, day, wishtype, hours: wishtype === 2 ? hours : null, start_date: startDate, end_date: endDate || null, version: 0 }); // Formatiere für Response const allTimewishes = await this.getAllTimewishes(userId); return allTimewishes.find(tw => tw.id === timewish.id); } /** * Löscht einen Zeitwunsch * @param {number} userId - Benutzer-ID * @param {number} timewishId - Timewish-ID * @returns {Promise} */ async deleteTimewish(userId, timewishId) { const { Timewish } = database.getModels(); const timewish = await Timewish.findByPk(timewishId); if (!timewish) { throw new Error('Zeitwunsch nicht gefunden'); } // Prüfe Berechtigung if (timewish.user_id !== userId) { throw new Error('Keine Berechtigung für diesen Eintrag'); } await timewish.destroy(); } } module.exports = new TimewishService();