Files
SDSStundenerfassung/routes/user.js
Carsten Graf daf4f9b77c Massdownload
2026-01-23 17:29:46 +01:00

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;