Add timefix and vacation routes to backend; update frontend for new routes and page titles
This commit is contained in:
9
backend/add-vacation-index.sql
Normal file
9
backend/add-vacation-index.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
-- Füge Index für vacation Tabelle hinzu
|
||||
-- Optimiert die Abfrage: WHERE user_id = ? AND first_day >= ? ORDER BY first_day DESC
|
||||
|
||||
CREATE INDEX idx_vacation_user_first_day
|
||||
ON vacation (user_id, first_day DESC);
|
||||
|
||||
-- Prüfe ob der Index erstellt wurde
|
||||
SHOW INDEX FROM vacation WHERE Key_name = 'idx_vacation_user_first_day';
|
||||
|
||||
94
backend/src/controllers/TimefixController.js
Normal file
94
backend/src/controllers/TimefixController.js
Normal file
@@ -0,0 +1,94 @@
|
||||
const timefixService = require('../services/TimefixService');
|
||||
|
||||
class TimefixController {
|
||||
/**
|
||||
* Holt alle Worklog-Einträge für ein bestimmtes Datum
|
||||
*/
|
||||
async getWorklogEntriesForDate(req, res) {
|
||||
try {
|
||||
const userId = req.user.userId;
|
||||
const { date } = req.query;
|
||||
|
||||
if (!date) {
|
||||
return res.status(400).json({ message: 'Datum ist erforderlich' });
|
||||
}
|
||||
|
||||
const entries = await timefixService.getWorklogEntriesForDate(userId, date);
|
||||
res.json(entries);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Abrufen der Worklog-Einträge:', error);
|
||||
res.status(500).json({
|
||||
message: 'Fehler beim Abrufen der Worklog-Einträge',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holt alle Zeitkorrekturen für den heutigen Tag
|
||||
*/
|
||||
async getTodayTimefixes(req, res) {
|
||||
try {
|
||||
const userId = req.user.userId;
|
||||
const timefixes = await timefixService.getTodayTimefixes(userId);
|
||||
res.json(timefixes);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Abrufen der Timefixes:', error);
|
||||
res.status(500).json({
|
||||
message: 'Fehler beim Abrufen der Zeitkorrekturen',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt eine neue Zeitkorrektur
|
||||
*/
|
||||
async createTimefix(req, res) {
|
||||
try {
|
||||
const userId = req.user.userId;
|
||||
const { worklogId, newDate, newTime, newAction } = req.body;
|
||||
|
||||
if (!worklogId || !newDate || !newTime || !newAction) {
|
||||
return res.status(400).json({
|
||||
message: 'Fehlende Pflichtfelder: worklogId, newDate, newTime, newAction'
|
||||
});
|
||||
}
|
||||
|
||||
const timefix = await timefixService.createTimefix(userId, worklogId, newDate, newTime, newAction);
|
||||
|
||||
res.status(201).json({
|
||||
message: 'Zeitkorrektur erfolgreich erstellt',
|
||||
timefix
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Erstellen der Timefix:', error);
|
||||
res.status(500).json({
|
||||
message: 'Fehler beim Erstellen der Zeitkorrektur',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Löscht eine Zeitkorrektur
|
||||
*/
|
||||
async deleteTimefix(req, res) {
|
||||
try {
|
||||
const userId = req.user.userId;
|
||||
const timefixId = req.params.id;
|
||||
|
||||
await timefixService.deleteTimefix(userId, timefixId);
|
||||
|
||||
res.json({ message: 'Zeitkorrektur erfolgreich gelöscht' });
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Löschen der Timefix:', error);
|
||||
res.status(500).json({
|
||||
message: 'Fehler beim Löschen der Zeitkorrektur',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new TimefixController();
|
||||
73
backend/src/controllers/VacationController.js
Normal file
73
backend/src/controllers/VacationController.js
Normal file
@@ -0,0 +1,73 @@
|
||||
const vacationService = require('../services/VacationService');
|
||||
|
||||
class VacationController {
|
||||
/**
|
||||
* Holt alle Urlaubseinträge für einen User
|
||||
*/
|
||||
async getAllVacations(req, res) {
|
||||
try {
|
||||
const userId = req.user.userId;
|
||||
const vacations = await vacationService.getAllVacations(userId);
|
||||
res.json(vacations);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Abrufen der Urlaubseinträge:', error);
|
||||
res.status(500).json({
|
||||
message: 'Fehler beim Abrufen der Urlaubseinträge',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt einen neuen Urlaubseintrag
|
||||
*/
|
||||
async createVacation(req, res) {
|
||||
try {
|
||||
const userId = req.user.userId;
|
||||
const { vacationType, startDate, endDate } = req.body;
|
||||
|
||||
if (vacationType === undefined || !startDate) {
|
||||
return res.status(400).json({
|
||||
message: 'Fehlende Pflichtfelder: vacationType, startDate'
|
||||
});
|
||||
}
|
||||
|
||||
const vacation = await vacationService.createVacation(userId, vacationType, startDate, endDate);
|
||||
|
||||
res.status(201).json({
|
||||
message: 'Urlaubseintrag erfolgreich erstellt',
|
||||
vacation
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Erstellen des Urlaubseintrags:', error);
|
||||
res.status(500).json({
|
||||
message: 'Fehler beim Erstellen des Urlaubseintrags',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Löscht einen Urlaubseintrag
|
||||
*/
|
||||
async deleteVacation(req, res) {
|
||||
try {
|
||||
const userId = req.user.userId;
|
||||
const vacationId = req.params.id;
|
||||
|
||||
console.log('DEBUG deleteVacation: vacationId =', vacationId, 'type =', typeof vacationId);
|
||||
|
||||
await vacationService.deleteVacation(userId, vacationId);
|
||||
|
||||
res.json({ message: 'Urlaubseintrag erfolgreich gelöscht' });
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Löschen des Urlaubseintrags:', error);
|
||||
res.status(500).json({
|
||||
message: 'Fehler beim Löschen des Urlaubseintrags',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new VacationController();
|
||||
@@ -66,6 +66,14 @@ app.use('/api/time-entries', authenticateToken, timeEntriesRouter);
|
||||
const weekOverviewRouter = require('./routes/weekOverview');
|
||||
app.use('/api/week-overview', authenticateToken, weekOverviewRouter);
|
||||
|
||||
// Timefix routes (geschützt) - MIT ID-Hashing
|
||||
const timefixRouter = require('./routes/timefix');
|
||||
app.use('/api/timefix', authenticateToken, timefixRouter);
|
||||
|
||||
// Vacation routes (geschützt) - MIT ID-Hashing
|
||||
const vacationRouter = require('./routes/vacation');
|
||||
app.use('/api/vacation', authenticateToken, vacationRouter);
|
||||
|
||||
// Error handling middleware
|
||||
app.use((err, req, res, next) => {
|
||||
console.error(err.stack);
|
||||
|
||||
@@ -16,7 +16,9 @@ const unhashRequestIds = (req, res, next) => {
|
||||
|
||||
// Route-Parameter verarbeiten
|
||||
if (req.params && typeof req.params === 'object') {
|
||||
console.log('DEBUG unhashRequest: req.params before =', req.params);
|
||||
req.params = unhashRequestData(req.params);
|
||||
console.log('DEBUG unhashRequest: req.params after =', req.params);
|
||||
}
|
||||
|
||||
next();
|
||||
|
||||
21
backend/src/routes/timefix.js
Normal file
21
backend/src/routes/timefix.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const TimefixController = require('../controllers/TimefixController');
|
||||
const unhashRequestIds = require('../middleware/unhashRequest');
|
||||
|
||||
// Hinweis: Authentifizierung wird bereits in index.js durch authenticateToken Middleware gehandhabt
|
||||
|
||||
// GET /api/timefix/worklog-entries - Worklog-Einträge für ein Datum
|
||||
router.get('/worklog-entries', TimefixController.getWorklogEntriesForDate);
|
||||
|
||||
// GET /api/timefix - Liste aller Timefixes für heute
|
||||
router.get('/', TimefixController.getTodayTimefixes);
|
||||
|
||||
// POST /api/timefix - Neue Zeitkorrektur erstellen
|
||||
router.post('/', TimefixController.createTimefix);
|
||||
|
||||
// DELETE /api/timefix/:id - Zeitkorrektur löschen
|
||||
router.delete('/:id', unhashRequestIds, TimefixController.deleteTimefix);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
18
backend/src/routes/vacation.js
Normal file
18
backend/src/routes/vacation.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const VacationController = require('../controllers/VacationController');
|
||||
const unhashRequestIds = require('../middleware/unhashRequest');
|
||||
|
||||
// Hinweis: Authentifizierung wird bereits in index.js durch authenticateToken Middleware gehandhabt
|
||||
|
||||
// GET /api/vacation - Liste aller Urlaubseinträge
|
||||
router.get('/', VacationController.getAllVacations);
|
||||
|
||||
// POST /api/vacation - Neuen Urlaubseintrag erstellen
|
||||
router.post('/', VacationController.createVacation);
|
||||
|
||||
// DELETE /api/vacation/:id - Urlaubseintrag löschen
|
||||
router.delete('/:id', unhashRequestIds, VacationController.deleteVacation);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
330
backend/src/services/TimefixService.js
Normal file
330
backend/src/services/TimefixService.js
Normal file
@@ -0,0 +1,330 @@
|
||||
const database = require('../config/database');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
/**
|
||||
* Service-Klasse für Zeitkorrekturen
|
||||
* Enthält die gesamte Business-Logik für Timefixes
|
||||
*/
|
||||
class TimefixService {
|
||||
constructor() {
|
||||
this.defaultUserId = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holt alle Worklog-Einträge für ein bestimmtes Datum
|
||||
* @param {number} userId - Benutzer-ID
|
||||
* @param {string} date - Datum im Format YYYY-MM-DD
|
||||
* @returns {Promise<Array>} Array von Worklog-Einträgen mit korrigierten Zeiten
|
||||
*/
|
||||
async getWorklogEntriesForDate(userId, date) {
|
||||
const { Worklog } = database.getModels();
|
||||
const sequelize = database.sequelize;
|
||||
|
||||
// Hole alle Worklog-Einträge für das Datum
|
||||
const entries = await Worklog.findAll({
|
||||
where: {
|
||||
user_id: userId,
|
||||
tstamp: {
|
||||
[Op.like]: `${date}%`
|
||||
}
|
||||
},
|
||||
order: [['tstamp', 'ASC']],
|
||||
raw: true
|
||||
});
|
||||
|
||||
// Hole auch die Timefixes für diese Einträge
|
||||
const entryIds = entries.map(e => e.id);
|
||||
const timefixesForEntries = await sequelize.query(
|
||||
`SELECT worklog_id, fix_type, fix_date_time FROM timefix WHERE worklog_id IN (?)`,
|
||||
{
|
||||
replacements: [entryIds],
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
}
|
||||
);
|
||||
|
||||
// Erstelle eine Map: worklog_id -> Timefix
|
||||
const timefixMap = new Map();
|
||||
timefixesForEntries.forEach(tf => {
|
||||
if (!timefixMap.has(tf.worklog_id)) {
|
||||
timefixMap.set(tf.worklog_id, []);
|
||||
}
|
||||
timefixMap.get(tf.worklog_id).push(tf);
|
||||
});
|
||||
|
||||
// Formatiere die Einträge für das Frontend
|
||||
const formattedEntries = entries.map(entry => {
|
||||
// Prüfe ob es eine Korrektur gibt
|
||||
const timefixForEntry = timefixMap.get(entry.id);
|
||||
|
||||
let hours, minutes, action;
|
||||
|
||||
if (timefixForEntry && timefixForEntry.length > 0) {
|
||||
// Verwende korrigierte Werte aus Timefix
|
||||
const tf = timefixForEntry[0];
|
||||
|
||||
if (typeof tf.fix_date_time === 'string') {
|
||||
const [datePart, timePart] = tf.fix_date_time.split(' ');
|
||||
[hours, minutes] = timePart.split(':').map(Number);
|
||||
} else if (tf.fix_date_time instanceof Date) {
|
||||
hours = tf.fix_date_time.getHours();
|
||||
minutes = tf.fix_date_time.getMinutes();
|
||||
}
|
||||
|
||||
action = tf.fix_type;
|
||||
} else {
|
||||
// Keine Korrektur - verwende Original-Werte aus Worklog
|
||||
if (typeof entry.tstamp === 'string') {
|
||||
const [datePart, timePart] = entry.tstamp.split(' ');
|
||||
[hours, minutes] = timePart.split(':').map(Number);
|
||||
} else if (entry.tstamp instanceof Date) {
|
||||
hours = entry.tstamp.getUTCHours();
|
||||
minutes = entry.tstamp.getUTCMinutes();
|
||||
}
|
||||
|
||||
// Parse state
|
||||
action = entry.state;
|
||||
if (typeof action === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(action);
|
||||
action = parsed.action || action;
|
||||
} catch (e) {
|
||||
// action bleibt als String
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: entry.id,
|
||||
time: `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`,
|
||||
action: action,
|
||||
tstamp: entry.tstamp
|
||||
};
|
||||
}).filter(e => e !== null);
|
||||
|
||||
return formattedEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holt alle Zeitkorrekturen für den heutigen Tag
|
||||
* @param {number} userId - Benutzer-ID
|
||||
* @returns {Promise<Array>} Array von Zeitkorrekturen
|
||||
*/
|
||||
async getTodayTimefixes(userId) {
|
||||
const sequelize = database.sequelize;
|
||||
|
||||
// Berechne Start und Ende des heutigen Tages (lokale Zeit)
|
||||
const now = new Date();
|
||||
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0);
|
||||
const todayEnd = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59);
|
||||
|
||||
// Hole alle Timefixes für heute mit Raw SQL
|
||||
const todayStartStr = `${todayStart.getFullYear()}-${String(todayStart.getMonth() + 1).padStart(2, '0')}-${String(todayStart.getDate()).padStart(2, '0')} 00:00:00`;
|
||||
const todayEndStr = `${todayEnd.getFullYear()}-${String(todayEnd.getMonth() + 1).padStart(2, '0')}-${String(todayEnd.getDate()).padStart(2, '0')} 23:59:59`;
|
||||
|
||||
const timefixes = await sequelize.query(
|
||||
`SELECT id, user_id, worklog_id, fix_type, fix_date_time
|
||||
FROM timefix
|
||||
WHERE user_id = ? AND fix_date_time BETWEEN ? AND ?
|
||||
ORDER BY fix_date_time DESC`,
|
||||
{
|
||||
replacements: [userId, todayStartStr, todayEndStr],
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
}
|
||||
);
|
||||
|
||||
// Hole die zugehörigen Worklog-Einträge separat mit Raw SQL
|
||||
const worklogIds = timefixes.map(tf => tf.worklog_id);
|
||||
|
||||
if (worklogIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const worklogs = await sequelize.query(
|
||||
`SELECT id, tstamp, state, relatedTo_id FROM worklog WHERE id IN (?)`,
|
||||
{
|
||||
replacements: [worklogIds],
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
}
|
||||
);
|
||||
|
||||
// Erstelle eine Map für schnellen Zugriff
|
||||
const worklogMap = new Map();
|
||||
worklogs.forEach(wl => {
|
||||
worklogMap.set(wl.id, wl);
|
||||
});
|
||||
|
||||
// Formatiere die Daten für das Frontend
|
||||
const formattedTimefixes = timefixes.map(tf => {
|
||||
const worklog = worklogMap.get(tf.worklog_id);
|
||||
|
||||
if (!worklog || !worklog.tstamp) {
|
||||
console.error('Worklog oder tstamp fehlt für Timefix:', tf.id);
|
||||
return null;
|
||||
}
|
||||
|
||||
// KORRIGIERTE Werte aus timefix.fix_date_time
|
||||
let newDate, newHours, newMinutes;
|
||||
|
||||
if (typeof tf.fix_date_time === 'string') {
|
||||
const [datePart, timePart] = tf.fix_date_time.split(' ');
|
||||
newDate = datePart;
|
||||
[newHours, newMinutes] = timePart.split(':').map(Number);
|
||||
} else if (tf.fix_date_time instanceof Date) {
|
||||
const d = tf.fix_date_time;
|
||||
newDate = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
||||
newHours = d.getHours();
|
||||
newMinutes = d.getMinutes();
|
||||
} else {
|
||||
console.error('Unbekannter fix_date_time-Typ:', typeof tf.fix_date_time);
|
||||
return null;
|
||||
}
|
||||
|
||||
// ORIGINAL Werte aus worklog
|
||||
let originalDate, originalHours, originalMinutes;
|
||||
|
||||
if (typeof worklog.tstamp === 'string') {
|
||||
const [datePart, timePart] = worklog.tstamp.split(' ');
|
||||
originalDate = datePart;
|
||||
[originalHours, originalMinutes] = timePart.split(':').map(Number);
|
||||
} else if (worklog.tstamp instanceof Date) {
|
||||
const d = worklog.tstamp;
|
||||
originalDate = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
||||
originalHours = d.getHours();
|
||||
originalMinutes = d.getMinutes();
|
||||
} else {
|
||||
console.error('Unbekannter tstamp-Typ:', typeof worklog.tstamp);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Parse original action aus worklog.state
|
||||
let originalAction = worklog.state;
|
||||
if (typeof originalAction === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(originalAction);
|
||||
originalAction = parsed.action || originalAction;
|
||||
} catch (e) {
|
||||
// action bleibt als String
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: tf.id,
|
||||
worklogId: worklog.id,
|
||||
fixType: tf.fix_type,
|
||||
fixDateTime: tf.fix_date_time,
|
||||
// Neue Werte (KORRIGIERT - aus timefix.fix_date_time)
|
||||
newDate: newDate,
|
||||
newTime: `${newHours.toString().padStart(2, '0')}:${newMinutes.toString().padStart(2, '0')}`,
|
||||
newAction: tf.fix_type,
|
||||
// Original-Werte (aus worklog)
|
||||
originalDate: originalDate,
|
||||
originalTime: `${originalHours.toString().padStart(2, '0')}:${originalMinutes.toString().padStart(2, '0')}`,
|
||||
originalAction: originalAction
|
||||
};
|
||||
}).filter(tf => tf !== null);
|
||||
|
||||
return formattedTimefixes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt eine neue Zeitkorrektur
|
||||
* @param {number} userId - Benutzer-ID
|
||||
* @param {number} worklogId - Worklog-Eintrag-ID
|
||||
* @param {string} newDate - Neues Datum (YYYY-MM-DD)
|
||||
* @param {string} newTime - Neue Uhrzeit (HH:MM)
|
||||
* @param {string} newAction - Neue Aktion
|
||||
* @returns {Promise<Object>} Erstellter Timefix
|
||||
*/
|
||||
async createTimefix(userId, worklogId, newDate, newTime, newAction) {
|
||||
const { Timefix } = database.getModels();
|
||||
const sequelize = database.sequelize;
|
||||
|
||||
// Hole den Original-Worklog-Eintrag mit Raw SQL
|
||||
const worklogResult = await sequelize.query(
|
||||
`SELECT id, user_id, tstamp, state FROM worklog WHERE id = ?`,
|
||||
{
|
||||
replacements: [worklogId],
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
}
|
||||
);
|
||||
|
||||
if (!worklogResult || worklogResult.length === 0) {
|
||||
throw new Error('Worklog-Eintrag nicht gefunden');
|
||||
}
|
||||
|
||||
const worklog = worklogResult[0];
|
||||
|
||||
// Prüfe Berechtigung
|
||||
if (worklog.user_id !== userId) {
|
||||
throw new Error('Keine Berechtigung für diesen Eintrag');
|
||||
}
|
||||
|
||||
// Erstelle die korrigierte Zeit als Date-Objekt
|
||||
const [newHours, newMinutes] = newTime.split(':').map(Number);
|
||||
const [newYear, newMonth, newDay] = newDate.split('-').map(Number);
|
||||
const correctedDateTime = new Date(newYear, newMonth - 1, newDay, newHours, newMinutes, 0);
|
||||
|
||||
// Prüfe ob bereits ein Timefix existiert
|
||||
const existingTimefix = await Timefix.findOne({
|
||||
where: {
|
||||
worklog_id: worklogId,
|
||||
fix_type: newAction
|
||||
}
|
||||
});
|
||||
|
||||
if (existingTimefix) {
|
||||
// Update existierender Timefix
|
||||
await existingTimefix.update({
|
||||
fix_date_time: correctedDateTime
|
||||
});
|
||||
|
||||
return {
|
||||
id: existingTimefix.id,
|
||||
worklogId: existingTimefix.worklog_id,
|
||||
fixType: existingTimefix.fix_type,
|
||||
fixDateTime: existingTimefix.fix_date_time
|
||||
};
|
||||
}
|
||||
|
||||
// Erstelle neuen Timefix
|
||||
const timefix = await Timefix.create({
|
||||
user_id: userId,
|
||||
worklog_id: worklogId,
|
||||
fix_type: newAction,
|
||||
fix_date_time: correctedDateTime
|
||||
});
|
||||
|
||||
return {
|
||||
id: timefix.id,
|
||||
worklogId: timefix.worklog_id,
|
||||
fixType: timefix.fix_type,
|
||||
fixDateTime: timefix.fix_date_time
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Löscht eine Zeitkorrektur
|
||||
* @param {number} userId - Benutzer-ID
|
||||
* @param {number} timefixId - Timefix-ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async deleteTimefix(userId, timefixId) {
|
||||
const { Timefix } = database.getModels();
|
||||
|
||||
const timefix = await Timefix.findByPk(timefixId);
|
||||
|
||||
if (!timefix) {
|
||||
throw new Error('Zeitkorrektur nicht gefunden');
|
||||
}
|
||||
|
||||
// Prüfe Berechtigung
|
||||
if (timefix.user_id !== userId) {
|
||||
throw new Error('Keine Berechtigung für diese Zeitkorrektur');
|
||||
}
|
||||
|
||||
await timefix.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new TimefixService();
|
||||
|
||||
120
backend/src/services/VacationService.js
Normal file
120
backend/src/services/VacationService.js
Normal file
@@ -0,0 +1,120 @@
|
||||
const database = require('../config/database');
|
||||
|
||||
/**
|
||||
* Service-Klasse für Urlaubseinträge
|
||||
* Enthält die gesamte Business-Logik für Vacation
|
||||
*/
|
||||
class VacationService {
|
||||
constructor() {
|
||||
this.defaultUserId = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holt alle Urlaubseinträge für einen User
|
||||
* Zeigt nur aktuelles Jahr oder letzte 3 Monate (falls wir in den ersten 3 Monaten sind)
|
||||
* @param {number} userId - Benutzer-ID
|
||||
* @returns {Promise<Array>} Array von Urlaubseinträgen
|
||||
*/
|
||||
async getAllVacations(userId) {
|
||||
const { Vacation } = database.getModels();
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
const now = new Date();
|
||||
const currentYear = now.getFullYear();
|
||||
const currentMonth = now.getMonth(); // 0-11
|
||||
|
||||
let startDate;
|
||||
|
||||
if (currentMonth < 3) {
|
||||
// Erste 3 Monate des Jahres: Zeige letzte 3 Monate
|
||||
const threeMonthsAgo = new Date(now);
|
||||
threeMonthsAgo.setMonth(now.getMonth() - 3);
|
||||
startDate = `${threeMonthsAgo.getFullYear()}-${String(threeMonthsAgo.getMonth() + 1).padStart(2, '0')}-01`;
|
||||
} else {
|
||||
// Rest des Jahres: Zeige nur aktuelles Jahr
|
||||
startDate = `${currentYear}-01-01`;
|
||||
}
|
||||
|
||||
const vacations = await Vacation.findAll({
|
||||
where: {
|
||||
user_id: userId,
|
||||
first_day: {
|
||||
[Op.gte]: startDate
|
||||
}
|
||||
},
|
||||
order: [['first_day', 'DESC']],
|
||||
raw: true
|
||||
});
|
||||
|
||||
// Formatiere die Daten
|
||||
return vacations.map(vac => ({
|
||||
id: vac.id,
|
||||
type: vac.vacation_type === 1 ? 'Halber Tag' : 'Zeitraum',
|
||||
typeValue: vac.vacation_type,
|
||||
startDate: vac.first_day,
|
||||
endDate: vac.last_day
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt einen neuen Urlaubseintrag
|
||||
* @param {number} userId - Benutzer-ID
|
||||
* @param {number} vacationType - Typ (0 = Zeitraum, 1 = Halber Tag)
|
||||
* @param {string} startDate - Startdatum (YYYY-MM-DD)
|
||||
* @param {string} endDate - Enddatum (YYYY-MM-DD)
|
||||
* @returns {Promise<Object>} Erstellter Urlaubseintrag
|
||||
*/
|
||||
async createVacation(userId, vacationType, startDate, endDate) {
|
||||
const { Vacation } = database.getModels();
|
||||
|
||||
// Bei "Halber Tag" muss startDate = endDate sein
|
||||
let finalEndDate = endDate;
|
||||
if (vacationType === 1) {
|
||||
finalEndDate = startDate;
|
||||
} else if (!endDate) {
|
||||
throw new Error('Urlaubsende ist erforderlich für Zeitraum');
|
||||
}
|
||||
|
||||
const vacation = await Vacation.create({
|
||||
user_id: userId,
|
||||
first_day: startDate,
|
||||
last_day: finalEndDate,
|
||||
vacation_type: vacationType,
|
||||
version: 0
|
||||
});
|
||||
|
||||
return {
|
||||
id: vacation.id,
|
||||
type: vacationType === 1 ? 'Halber Tag' : 'Zeitraum',
|
||||
typeValue: vacation.vacation_type,
|
||||
startDate: vacation.first_day,
|
||||
endDate: vacation.last_day
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Löscht einen Urlaubseintrag
|
||||
* @param {number} userId - Benutzer-ID
|
||||
* @param {number} vacationId - Vacation-ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async deleteVacation(userId, vacationId) {
|
||||
const { Vacation } = database.getModels();
|
||||
|
||||
const vacation = await Vacation.findByPk(vacationId);
|
||||
|
||||
if (!vacation) {
|
||||
throw new Error('Urlaubseintrag nicht gefunden');
|
||||
}
|
||||
|
||||
// Prüfe Berechtigung
|
||||
if (vacation.user_id !== userId) {
|
||||
throw new Error('Keine Berechtigung für diesen Eintrag');
|
||||
}
|
||||
|
||||
await vacation.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new VacationService();
|
||||
|
||||
Reference in New Issue
Block a user