315 lines
13 KiB
JavaScript
315 lines
13 KiB
JavaScript
// User API Routes
|
|
|
|
const { db } = require('../database');
|
|
const { hasRole, getCurrentDate } = require('../helpers/utils');
|
|
const { requireAuth } = require('../middleware/auth');
|
|
|
|
// Routes registrieren
|
|
function registerUserRoutes(app) {
|
|
// API: Letzte bearbeitete Woche abrufen
|
|
app.get('/api/user/last-week', requireAuth, (req, res) => {
|
|
const userId = req.session.userId;
|
|
|
|
db.get('SELECT last_week_start FROM users WHERE id = ?', [userId], (err, user) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: 'Fehler beim Abrufen der letzten Woche' });
|
|
}
|
|
|
|
res.json({ last_week_start: user?.last_week_start || null });
|
|
});
|
|
});
|
|
|
|
// API: Letzte bearbeitete Woche speichern
|
|
app.post('/api/user/last-week', requireAuth, (req, res) => {
|
|
const userId = req.session.userId;
|
|
const { week_start } = req.body;
|
|
|
|
if (!week_start) {
|
|
return res.status(400).json({ error: 'week_start ist erforderlich' });
|
|
}
|
|
|
|
db.run('UPDATE users SET last_week_start = ? WHERE id = ?',
|
|
[week_start, userId],
|
|
(err) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: 'Fehler beim Speichern der letzten Woche' });
|
|
}
|
|
res.json({ success: true });
|
|
});
|
|
});
|
|
|
|
// API: User-Daten abrufen (Wochenstunden)
|
|
app.get('/api/user/data', requireAuth, (req, res) => {
|
|
const userId = req.session.userId;
|
|
|
|
db.get('SELECT wochenstunden FROM users WHERE id = ?', [userId], (err, user) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: 'Fehler beim Abrufen der User-Daten' });
|
|
}
|
|
|
|
res.json({ wochenstunden: user?.wochenstunden || 0 });
|
|
});
|
|
});
|
|
|
|
// API: Ping-IP abrufen
|
|
app.get('/api/user/ping-ip', requireAuth, (req, res) => {
|
|
const userId = req.session.userId;
|
|
|
|
db.get('SELECT ping_ip FROM users WHERE id = ?', [userId], (err, user) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: 'Fehler beim Abrufen der IP-Adresse' });
|
|
}
|
|
|
|
res.json({ ping_ip: user?.ping_ip || null });
|
|
});
|
|
});
|
|
|
|
// API: Ping-IP speichern
|
|
app.post('/api/user/ping-ip', requireAuth, (req, res) => {
|
|
const userId = req.session.userId;
|
|
const { ping_ip } = req.body;
|
|
|
|
// Validierung: IPv4 Format (einfache Prüfung)
|
|
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
|
|
if (ping_ip && ping_ip.trim() !== '' && !ipv4Regex.test(ping_ip.trim())) {
|
|
return res.status(400).json({ error: 'Ungültige IP-Adresse. Bitte geben Sie eine gültige IPv4-Adresse ein.' });
|
|
}
|
|
|
|
// Normalisiere: Leere Strings werden zu null
|
|
const normalizedPingIp = (ping_ip && ping_ip.trim() !== '') ? ping_ip.trim() : null;
|
|
|
|
db.run('UPDATE users SET ping_ip = ? WHERE id = ?', [normalizedPingIp, userId], (err) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: 'Fehler beim Speichern der IP-Adresse' });
|
|
}
|
|
|
|
// Wenn IP entfernt wurde, lösche auch den Ping-Status für heute
|
|
if (!normalizedPingIp) {
|
|
const currentDate = getCurrentDate();
|
|
db.run('DELETE FROM ping_status WHERE user_id = ? AND date = ?', [userId, currentDate], (err) => {
|
|
// Fehler ignorieren
|
|
});
|
|
}
|
|
|
|
res.json({ success: true, ping_ip: normalizedPingIp });
|
|
});
|
|
});
|
|
|
|
// API: Rollenwechsel
|
|
app.post('/api/user/switch-role', requireAuth, (req, res) => {
|
|
const { role } = req.body;
|
|
|
|
if (!role) {
|
|
return res.status(400).json({ error: 'Rolle ist erforderlich' });
|
|
}
|
|
|
|
// Prüfe ob User diese Rolle hat
|
|
if (!hasRole(req, role)) {
|
|
return res.status(403).json({ error: 'Sie haben keine Berechtigung für diese Rolle' });
|
|
}
|
|
|
|
// Validiere dass die Rolle eine gültige Rolle ist
|
|
const validRoles = ['mitarbeiter', 'verwaltung', 'admin'];
|
|
if (!validRoles.includes(role)) {
|
|
return res.status(400).json({ error: 'Ungültige Rolle' });
|
|
}
|
|
|
|
// Setze aktuelle Rolle
|
|
req.session.currentRole = role;
|
|
|
|
res.json({ success: true, currentRole: role });
|
|
});
|
|
|
|
// API: Gesamtstatistiken für Mitarbeiter (Überstunden und Urlaubstage)
|
|
app.get('/api/user/stats', requireAuth, (req, res) => {
|
|
const userId = req.session.userId;
|
|
|
|
// User-Daten abrufen
|
|
db.get('SELECT wochenstunden, urlaubstage, overtime_offset_hours FROM users WHERE id = ?', [userId], (err, user) => {
|
|
if (err || !user) {
|
|
return res.status(500).json({ error: 'Fehler beim Abrufen der User-Daten' });
|
|
}
|
|
|
|
const wochenstunden = user.wochenstunden || 0;
|
|
const urlaubstage = user.urlaubstage || 0;
|
|
const overtimeOffsetHours = user.overtime_offset_hours ? parseFloat(user.overtime_offset_hours) : 0;
|
|
|
|
// Alle eingereichten Wochen abrufen
|
|
db.all(`SELECT DISTINCT week_start, week_end
|
|
FROM weekly_timesheets
|
|
WHERE user_id = ? AND status = 'eingereicht'
|
|
ORDER BY week_start`,
|
|
[userId],
|
|
(err, weeks) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: 'Fehler beim Abrufen der Wochen' });
|
|
}
|
|
|
|
// Wenn keine Wochen vorhanden
|
|
if (!weeks || weeks.length === 0) {
|
|
return res.json({
|
|
currentOvertime: overtimeOffsetHours,
|
|
remainingVacation: urlaubstage,
|
|
totalOvertimeHours: 0,
|
|
totalOvertimeTaken: 0,
|
|
totalVacationDays: 0,
|
|
urlaubstage: urlaubstage,
|
|
overtimeOffsetHours: overtimeOffsetHours
|
|
});
|
|
}
|
|
|
|
let totalOvertimeHours = 0;
|
|
let totalOvertimeTaken = 0;
|
|
let totalVacationDays = 0;
|
|
let processedWeeks = 0;
|
|
let hasError = false;
|
|
|
|
// Für jede Woche die Statistiken berechnen
|
|
weeks.forEach((week) => {
|
|
// Einträge für diese Woche abrufen (nur neueste pro Tag)
|
|
db.all(`SELECT id, date, total_hours, overtime_taken_hours, vacation_type, sick_status, start_time, end_time, updated_at
|
|
FROM timesheet_entries
|
|
WHERE user_id = ? AND date >= ? AND date <= ?
|
|
ORDER BY date, updated_at DESC, id DESC`,
|
|
[userId, week.week_start, week.week_end],
|
|
(err, allEntries) => {
|
|
if (hasError) return; // Wenn bereits ein Fehler aufgetreten ist, ignoriere weitere Ergebnisse
|
|
|
|
if (err) {
|
|
hasError = true;
|
|
return res.status(500).json({ error: 'Fehler beim Abrufen der Einträge' });
|
|
}
|
|
|
|
// Filtere auf neuesten Eintrag pro Tag
|
|
const entriesByDate = {};
|
|
(allEntries || []).forEach(entry => {
|
|
const existing = entriesByDate[entry.date];
|
|
if (!existing) {
|
|
entriesByDate[entry.date] = entry;
|
|
} else {
|
|
// Vergleiche updated_at (falls vorhanden) oder id (höhere ID = neuer)
|
|
const existingTime = existing.updated_at ? new Date(existing.updated_at).getTime() : 0;
|
|
const currentTime = entry.updated_at ? new Date(entry.updated_at).getTime() : 0;
|
|
if (currentTime > existingTime || (currentTime === existingTime && entry.id > existing.id)) {
|
|
entriesByDate[entry.date] = entry;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Konvertiere zurück zu Array
|
|
const entries = Object.values(entriesByDate);
|
|
|
|
// Prüfe ob Woche vollständig ausgefüllt ist (alle 5 Werktage)
|
|
|
|
// Prüfe alle 5 Werktage (Montag-Freitag)
|
|
const startDate = new Date(week.week_start);
|
|
const endDate = new Date(week.week_end);
|
|
let workdays = 0;
|
|
let filledWorkdays = 0;
|
|
|
|
for (let d = new Date(startDate); d <= endDate; d.setDate(d.getDate() + 1)) {
|
|
const day = d.getDay();
|
|
if (day >= 1 && day <= 5) { // Montag bis Freitag
|
|
workdays++;
|
|
const dateStr = d.toISOString().split('T')[0];
|
|
const entry = entriesByDate[dateStr];
|
|
|
|
// Tag gilt als ausgefüllt wenn:
|
|
// - Ganzer Tag Urlaub (vacation_type = 'full')
|
|
// - Krank (sick_status = 1)
|
|
// - ODER Start- und End-Zeit vorhanden sind
|
|
if (entry) {
|
|
const isFullDayVacation = entry.vacation_type === 'full';
|
|
const isSick = entry.sick_status === 1 || entry.sick_status === true;
|
|
const hasStartAndEnd = entry.start_time && entry.end_time &&
|
|
entry.start_time.toString().trim() !== '' &&
|
|
entry.end_time.toString().trim() !== '';
|
|
|
|
if (isFullDayVacation || isSick || hasStartAndEnd) {
|
|
filledWorkdays++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Nur berechnen wenn alle Werktage ausgefüllt sind
|
|
if (filledWorkdays < workdays) {
|
|
// Woche nicht vollständig - überspringe diese Woche
|
|
processedWeeks++;
|
|
if (processedWeeks === weeks.length && !hasError) {
|
|
const currentOvertime = (totalOvertimeHours - totalOvertimeTaken) + overtimeOffsetHours;
|
|
const remainingVacation = urlaubstage - totalVacationDays;
|
|
|
|
res.json({
|
|
currentOvertime: currentOvertime,
|
|
remainingVacation: remainingVacation,
|
|
totalOvertimeHours: totalOvertimeHours,
|
|
totalOvertimeTaken: totalOvertimeTaken,
|
|
totalVacationDays: totalVacationDays,
|
|
urlaubstage: urlaubstage,
|
|
overtimeOffsetHours: overtimeOffsetHours
|
|
});
|
|
}
|
|
return; // Überspringe diese Woche
|
|
}
|
|
|
|
// Berechnungen für diese Woche (nur wenn vollständig ausgefüllt)
|
|
let weekTotalHours = 0;
|
|
let weekOvertimeTaken = 0;
|
|
let weekVacationDays = 0;
|
|
let weekVacationHours = 0;
|
|
|
|
entries.forEach(entry => {
|
|
if (entry.total_hours) {
|
|
weekTotalHours += entry.total_hours;
|
|
}
|
|
if (entry.overtime_taken_hours) {
|
|
weekOvertimeTaken += entry.overtime_taken_hours;
|
|
}
|
|
if (entry.vacation_type === 'full') {
|
|
weekVacationDays += 1;
|
|
weekVacationHours += 8;
|
|
} else if (entry.vacation_type === 'half') {
|
|
weekVacationDays += 0.5;
|
|
weekVacationHours += 4;
|
|
}
|
|
});
|
|
|
|
// Sollstunden berechnen
|
|
const sollStunden = (wochenstunden / 5) * workdays;
|
|
|
|
// Überstunden für diese Woche: Urlaub zählt als normale Arbeitszeit
|
|
const weekTotalHoursWithVacation = weekTotalHours + weekVacationHours;
|
|
const weekOvertimeHours = weekTotalHoursWithVacation - sollStunden;
|
|
|
|
// Kumulativ addieren
|
|
totalOvertimeHours += weekOvertimeHours;
|
|
totalOvertimeTaken += weekOvertimeTaken;
|
|
totalVacationDays += weekVacationDays;
|
|
|
|
processedWeeks++;
|
|
|
|
// Wenn alle Wochen verarbeitet wurden, Antwort senden
|
|
if (processedWeeks === weeks.length && !hasError) {
|
|
const currentOvertime = (totalOvertimeHours - totalOvertimeTaken) + overtimeOffsetHours;
|
|
const remainingVacation = urlaubstage - totalVacationDays;
|
|
|
|
res.json({
|
|
currentOvertime: currentOvertime,
|
|
remainingVacation: remainingVacation,
|
|
totalOvertimeHours: totalOvertimeHours,
|
|
totalOvertimeTaken: totalOvertimeTaken,
|
|
totalVacationDays: totalVacationDays,
|
|
urlaubstage: urlaubstage,
|
|
overtimeOffsetHours: overtimeOffsetHours
|
|
});
|
|
}
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
module.exports = registerUserRoutes;
|