Umstellung auf Arbeitstage

This commit is contained in:
2026-02-03 22:32:49 +01:00
parent 4be9a365b3
commit a3efbb43ae
11 changed files with 304 additions and 157 deletions

View File

@@ -8,7 +8,7 @@ const { requireAdmin } = require('../middleware/auth');
function registerAdminRoutes(app) {
// Admin-Bereich
app.get('/admin', requireAdmin, (req, res) => {
db.all('SELECT id, username, firstname, lastname, role, personalnummer, wochenstunden, urlaubstage, created_at FROM users ORDER BY created_at DESC',
db.all('SELECT id, username, firstname, lastname, role, personalnummer, wochenstunden, urlaubstage, arbeitstage, created_at FROM users ORDER BY created_at DESC',
(err, users) => {
// LDAP-Konfiguration, Sync-Log und Optionen abrufen
db.get('SELECT * FROM ldap_config WHERE id = 1', (err, ldapConfig) => {
@@ -48,13 +48,14 @@ function registerAdminRoutes(app) {
// Benutzer erstellen
app.post('/admin/users', requireAdmin, (req, res) => {
const { username, password, firstname, lastname, roles, personalnummer, wochenstunden, urlaubstage } = req.body;
const { username, password, firstname, lastname, roles, personalnummer, wochenstunden, urlaubstage, arbeitstage } = req.body;
const hashedPassword = bcrypt.hashSync(password, 10);
// Normalisiere die optionalen Felder
const normalizedPersonalnummer = personalnummer && personalnummer.trim() !== '' ? personalnummer.trim() : null;
const normalizedWochenstunden = wochenstunden && wochenstunden !== '' ? parseFloat(wochenstunden) : null;
const normalizedUrlaubstage = urlaubstage && urlaubstage !== '' ? parseFloat(urlaubstage) : null;
const normalizedArbeitstage = arbeitstage && arbeitstage !== '' ? parseInt(arbeitstage) : 5;
// Rollen verarbeiten: Erwarte Array, konvertiere zu JSON-String
let rolesArray = [];
@@ -72,8 +73,8 @@ function registerAdminRoutes(app) {
const rolesJson = JSON.stringify(rolesArray);
db.run('INSERT INTO users (username, password, firstname, lastname, role, personalnummer, wochenstunden, urlaubstage) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
[username, hashedPassword, firstname, lastname, rolesJson, normalizedPersonalnummer, normalizedWochenstunden, normalizedUrlaubstage],
db.run('INSERT INTO users (username, password, firstname, lastname, role, personalnummer, wochenstunden, urlaubstage, arbeitstage) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
[username, hashedPassword, firstname, lastname, rolesJson, normalizedPersonalnummer, normalizedWochenstunden, normalizedUrlaubstage, normalizedArbeitstage],
(err) => {
if (err) {
return res.status(400).json({ error: 'Benutzername existiert bereits' });
@@ -102,7 +103,7 @@ function registerAdminRoutes(app) {
// Benutzer aktualisieren (Personalnummer, Wochenstunden, Urlaubstage, Rollen)
app.put('/admin/users/:id', requireAdmin, (req, res) => {
const userId = req.params.id;
const { personalnummer, wochenstunden, urlaubstage, roles } = req.body;
const { personalnummer, wochenstunden, urlaubstage, arbeitstage, roles } = req.body;
// Rollen verarbeiten falls vorhanden
let rolesJson = null;
@@ -121,11 +122,12 @@ function registerAdminRoutes(app) {
// SQL-Query dynamisch zusammenstellen
if (rolesJson !== null) {
// Aktualisiere auch Rollen
db.run('UPDATE users SET personalnummer = ?, wochenstunden = ?, urlaubstage = ?, role = ? WHERE id = ?',
db.run('UPDATE users SET personalnummer = ?, wochenstunden = ?, urlaubstage = ?, arbeitstage = ?, role = ? WHERE id = ?',
[
personalnummer || null,
wochenstunden ? parseFloat(wochenstunden) : null,
urlaubstage ? parseFloat(urlaubstage) : null,
arbeitstage ? parseInt(arbeitstage) : 5,
rolesJson,
userId
],
@@ -137,11 +139,12 @@ function registerAdminRoutes(app) {
});
} else {
// Nur andere Felder aktualisieren
db.run('UPDATE users SET personalnummer = ?, wochenstunden = ?, urlaubstage = ? WHERE id = ?',
db.run('UPDATE users SET personalnummer = ?, wochenstunden = ?, urlaubstage = ?, arbeitstage = ? WHERE id = ?',
[
personalnummer || null,
wochenstunden ? parseFloat(wochenstunden) : null,
urlaubstage ? parseFloat(urlaubstage) : null,
arbeitstage ? parseInt(arbeitstage) : 5,
userId
],
(err) => {

View File

@@ -55,15 +55,16 @@ function registerTimesheetRoutes(app) {
}
// User-Daten laden (für Überstunden-Berechnung)
db.get('SELECT wochenstunden FROM users WHERE id = ?', [userId], (err, user) => {
db.get('SELECT wochenstunden, arbeitstage FROM users WHERE id = ?', [userId], (err, user) => {
if (err) {
console.error('Fehler beim Laden der User-Daten:', err);
return res.status(500).json({ error: 'Fehler beim Laden der User-Daten' });
}
const wochenstunden = user?.wochenstunden || 0;
const arbeitstage = user?.arbeitstage || 5;
const overtimeValue = overtime_taken_hours ? parseFloat(overtime_taken_hours) : 0;
const fullDayHours = wochenstunden > 0 ? wochenstunden / 5 : 0;
const fullDayHours = wochenstunden > 0 && arbeitstage > 0 ? wochenstunden / arbeitstage : 0;
// Überstunden-Logik: Prüfe ob ganzer Tag oder weniger
let isFullDayOvertime = false;
@@ -96,11 +97,11 @@ function registerTimesheetRoutes(app) {
finalEndTime = null;
// Keine Tätigkeit setzen - Überstunden werden über overtime_taken_hours in der PDF angezeigt
} else if (vacation_type === 'full') {
total_hours = 8; // Ganzer Tag Urlaub = 8 Stunden normale Arbeitszeit
total_hours = fullDayHours; // Ganzer Tag Urlaub = (Wochenarbeitszeit / Arbeitstage) Stunden normale Arbeitszeit
} else if (isSick) {
total_hours = 8; // Krank = 8 Stunden normale Arbeitszeit
total_hours = fullDayHours; // Krank = (Wochenarbeitszeit / Arbeitstage) Stunden normale Arbeitszeit
finalActivity1Desc = 'Krank';
finalActivity1Hours = 8;
finalActivity1Hours = fullDayHours;
} else if (normalizedStartTime && normalizedEndTime) {
const start = new Date(`2000-01-01T${normalizedStartTime}`);
const end = new Date(`2000-01-01T${normalizedEndTime}`);
@@ -314,13 +315,14 @@ function registerTimesheetRoutes(app) {
const startDay = parseInt(startDateParts[2]);
// User-Daten laden für Überstunden-Berechnung
db.get('SELECT wochenstunden FROM users WHERE id = ?', [userId], (err, user) => {
db.get('SELECT wochenstunden, arbeitstage FROM users WHERE id = ?', [userId], (err, user) => {
if (err) {
return res.status(500).json({ error: 'Fehler beim Laden der User-Daten' });
}
const wochenstunden = user?.wochenstunden || 0;
const fullDayHours = wochenstunden > 0 ? wochenstunden / 5 : 8;
const arbeitstage = user?.arbeitstage || 5;
const fullDayHours = wochenstunden > 0 && arbeitstage > 0 ? wochenstunden / arbeitstage : 8;
// Feiertage laden: Feiertag zählt als ausgefüllt (kein Start/Ende nötig)
getHolidaysForDateRange(week_start, week_end)
@@ -348,12 +350,23 @@ function registerTimesheetRoutes(app) {
continue; // Tag ist ausgefüllt
}
// Prüfe ob 8 Überstunden (ganzer Tag) eingetragen sind
// Prüfe ob Überstunden (ganzer Tag) eingetragen sind
const overtimeValue = entry && entry.overtime_taken_hours ? parseFloat(entry.overtime_taken_hours) : 0;
const isFullDayOvertime = overtimeValue > 0 && Math.abs(overtimeValue - fullDayHours) < 0.01;
if (isFullDayOvertime) {
continue; // Tag ist ausgefüllt (8 Überstunden = ganzer Tag)
continue; // Tag ist ausgefüllt (Überstunden = ganzer Tag)
}
// Wenn Überstunden > fullDayHours, dann müssen Start/Ende vorhanden sein
if (overtimeValue > fullDayHours) {
const hasStartTime = entry && entry.start_time && entry.start_time.toString().trim() !== '';
const hasEndTime = entry && entry.end_time && entry.end_time.toString().trim() !== '';
if (!entry || !hasStartTime || !hasEndTime) {
missingDays.push(dateStr);
continue; // Weiter zum nächsten Tag
}
}
// Bei halbem Tag Urlaub oder keinem Urlaub müssen Start- und Endzeit vorhanden sein

View File

@@ -57,12 +57,15 @@ function registerUserRoutes(app) {
app.get('/api/user/data', requireAuth, (req, res) => {
const userId = req.session.userId;
db.get('SELECT wochenstunden FROM users WHERE id = ?', [userId], (err, user) => {
db.get('SELECT wochenstunden, arbeitstage 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 });
res.json({
wochenstunden: user?.wochenstunden || 0,
arbeitstage: user?.arbeitstage || 5
});
});
});
@@ -223,12 +226,13 @@ function registerUserRoutes(app) {
}
// User-Daten abrufen
db.get('SELECT wochenstunden, urlaubstage, overtime_offset_hours, vacation_offset_days FROM users WHERE id = ?', [userId], (err, user) => {
db.get('SELECT wochenstunden, urlaubstage, overtime_offset_hours, vacation_offset_days, arbeitstage 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 arbeitstage = user.arbeitstage || 5;
const urlaubstage = user.urlaubstage || 0;
const overtimeOffsetHours = user.overtime_offset_hours ? parseFloat(user.overtime_offset_hours) : 0;
const vacationOffsetDays = user.vacation_offset_days ? parseFloat(user.vacation_offset_days) : 0;
@@ -407,7 +411,7 @@ function registerUserRoutes(app) {
let weekVacationDays = 0;
let weekVacationHours = 0;
const fullDayHours = wochenstunden > 0 ? wochenstunden / 5 : 8;
const fullDayHours = wochenstunden > 0 && arbeitstage > 0 ? wochenstunden / arbeitstage : 8;
let fullDayOvertimeDays = 0; // Anzahl Tage mit 8 Überstunden
entries.forEach(entry => {
@@ -428,11 +432,11 @@ function registerUserRoutes(app) {
// Urlaub hat Priorität - wenn Urlaub, zähle nur Urlaubsstunden, nicht zusätzlich Arbeitsstunden
if (entry.vacation_type === 'full') {
weekVacationDays += 1;
weekVacationHours += 8; // Ganzer Tag = 8 Stunden
weekVacationHours += fullDayHours; // Ganzer Tag = (Wochenarbeitszeit / Arbeitstage) Stunden
// Bei vollem Tag Urlaub werden keine Arbeitsstunden gezählt
} else if (entry.vacation_type === 'half') {
weekVacationDays += 0.5;
weekVacationHours += 4; // Halber Tag = 4 Stunden
weekVacationHours += fullDayHours / 2; // Halber Tag = (Wochenarbeitszeit / Arbeitstage) / 2 Stunden
// Bei halbem Tag Urlaub können noch Arbeitsstunden vorhanden sein
// WICHTIG: total_hours enthält bereits Wochenend-Prozentsätze (aus timesheet.js)
if (entry.total_hours && !isFullDayOvertime) {
@@ -447,18 +451,18 @@ function registerUserRoutes(app) {
}
});
// Feiertagsstunden: 8h pro Werktag der ein Feiertag ist
// Feiertagsstunden: (Wochenarbeitszeit / Arbeitstage) pro Werktag der ein Feiertag ist
let holidayHours = 0;
for (let d = new Date(startDate); d <= endDate; d.setDate(d.getDate() + 1)) {
const day = d.getDay();
if (day >= 1 && day <= 5) {
const dateStr = d.toISOString().split('T')[0];
if (holidaySet.has(dateStr)) holidayHours += 8;
if (holidaySet.has(dateStr)) holidayHours += fullDayHours;
}
}
// Sollstunden berechnen
const sollStunden = (wochenstunden / 5) * workdays;
const sollStunden = (wochenstunden / arbeitstage) * workdays;
// Überstunden für diese Woche: (totalHours + vacationHours + holidayHours) - adjustedSollStunden
const weekTotalHoursWithVacation = weekTotalHours + weekVacationHours + holidayHours;
@@ -532,12 +536,13 @@ function registerUserRoutes(app) {
}
// User-Daten abrufen
db.get('SELECT wochenstunden, overtime_offset_hours FROM users WHERE id = ?', [userId], (err, user) => {
db.get('SELECT wochenstunden, overtime_offset_hours, arbeitstage 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 arbeitstage = user.arbeitstage || 5;
const overtimeOffsetHours = user.overtime_offset_hours ? parseFloat(user.overtime_offset_hours) : 0;
// Alle eingereichten Wochen abrufen
@@ -645,7 +650,7 @@ function registerUserRoutes(app) {
let weekVacationDays = 0;
let weekVacationHours = 0;
const fullDayHours = wochenstunden > 0 ? wochenstunden / 5 : 8;
const fullDayHours = wochenstunden > 0 && arbeitstage > 0 ? wochenstunden / arbeitstage : 8;
let fullDayOvertimeDays = 0;
entries.forEach(entry => {
@@ -662,10 +667,10 @@ function registerUserRoutes(app) {
if (entry.vacation_type === 'full') {
weekVacationDays += 1;
weekVacationHours += 8;
weekVacationHours += fullDayHours;
} else if (entry.vacation_type === 'half') {
weekVacationDays += 0.5;
weekVacationHours += 4;
weekVacationHours += fullDayHours / 2;
// WICHTIG: total_hours enthält bereits Wochenend-Prozentsätze (aus timesheet.js)
if (entry.total_hours && !isFullDayOvertime) {
weekTotalHours += parseFloat(entry.total_hours) || 0;
@@ -678,18 +683,18 @@ function registerUserRoutes(app) {
}
});
// Feiertagsstunden
// Feiertagsstunden: (Wochenarbeitszeit / Arbeitstage) pro Werktag der ein Feiertag ist
let holidayHours = 0;
for (let d = new Date(startDate); d <= endDate; d.setDate(d.getDate() + 1)) {
const day = d.getDay();
if (day >= 1 && day <= 5) {
const dateStr = d.toISOString().split('T')[0];
if (holidaySet.has(dateStr)) holidayHours += 8;
if (holidaySet.has(dateStr)) holidayHours += fullDayHours;
}
}
// Sollstunden berechnen
const sollStunden = (wochenstunden / 5) * workdays;
const sollStunden = (wochenstunden / arbeitstage) * workdays;
const weekTotalHoursWithVacation = weekTotalHours + weekVacationHours + holidayHours;
const adjustedSollStunden = sollStunden - (fullDayOvertimeDays * fullDayHours);
const weekOvertimeHours = weekTotalHoursWithVacation - adjustedSollStunden;

View File

@@ -12,7 +12,7 @@ function registerVerwaltungRoutes(app) {
// Verwaltungs-Bereich
app.get('/verwaltung', requireVerwaltung, (req, res) => {
db.all(`
SELECT wt.*, u.firstname, u.lastname, u.username, u.personalnummer, u.wochenstunden, u.urlaubstage, u.overtime_offset_hours, u.vacation_offset_days,
SELECT wt.*, u.firstname, u.lastname, u.username, u.personalnummer, u.wochenstunden, u.urlaubstage, u.overtime_offset_hours, u.vacation_offset_days, u.arbeitstage,
dl.firstname as downloaded_by_firstname,
dl.lastname as downloaded_by_lastname,
(SELECT COUNT(*) FROM weekly_timesheets wt2
@@ -217,115 +217,191 @@ function registerVerwaltungRoutes(app) {
}
// User-Daten abrufen
db.get('SELECT wochenstunden, urlaubstage, overtime_offset_hours, vacation_offset_days FROM users WHERE id = ?', [userId], (err, user) => {
db.get('SELECT wochenstunden, urlaubstage, overtime_offset_hours, vacation_offset_days, arbeitstage 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 arbeitstage = user.arbeitstage || 5;
const fullDayHours = wochenstunden > 0 && arbeitstage > 0 ? wochenstunden / arbeitstage : 8;
const urlaubstage = user.urlaubstage || 0;
const overtimeOffsetHours = user.overtime_offset_hours ? parseFloat(user.overtime_offset_hours) : 0;
const vacationOffsetDays = user.vacation_offset_days ? parseFloat(user.vacation_offset_days) : 0;
// Einträge für die Woche abrufen
db.all(`SELECT date, total_hours, overtime_taken_hours, vacation_type, sick_status
FROM timesheet_entries
WHERE user_id = ? AND date >= ? AND date <= ?
ORDER BY date`,
[userId, week_start, week_end],
(err, entries) => {
// Alle bereits genommenen Urlaubstage aus eingereichten Wochen berechnen
// Zuerst: 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 Einträge' });
return res.status(500).json({ error: 'Fehler beim Abrufen der Wochen' });
}
// Berechnungen
let totalHours = 0;
let overtimeTaken = 0;
let vacationDays = 0;
let vacationHours = 0;
let sickDays = 0;
// Für jede Woche die neuesten Einträge abrufen
let processedWeeks = 0;
let totalVacationDays = 0;
const vacationByDate = {};
entries.forEach(entry => {
if (entry.overtime_taken_hours) {
overtimeTaken += entry.overtime_taken_hours;
}
// Krankheitstage zählen
if (entry.sick_status === 1 || entry.sick_status === true) {
sickDays += 1;
}
// Urlaub hat Priorität - wenn Urlaub, zähle nur Urlaubsstunden, nicht zusätzlich Arbeitsstunden
if (entry.vacation_type === 'full') {
vacationDays += 1;
vacationHours += 8; // Ganzer Tag = 8 Stunden
// Bei vollem Tag Urlaub werden keine Arbeitsstunden gezählt
} else if (entry.vacation_type === 'half') {
vacationDays += 0.5;
vacationHours += 4; // Halber Tag = 4 Stunden
// Bei halbem Tag Urlaub können noch Arbeitsstunden vorhanden sein
// WICHTIG: total_hours enthält bereits Wochenend-Prozentsätze (aus timesheet.js)
if (entry.total_hours) {
totalHours += parseFloat(entry.total_hours) || 0;
}
} else {
// Kein Urlaub - zähle nur Arbeitsstunden
// WICHTIG: total_hours enthält bereits Wochenend-Prozentsätze (aus timesheet.js)
if (entry.total_hours) {
totalHours += parseFloat(entry.total_hours) || 0;
}
}
});
// Feiertage für die Woche laden (8h pro Feiertag; Arbeit an Feiertag = Überstunden)
getHolidaysForDateRange(week_start, week_end)
.catch(() => new Set())
.then((holidaySet) => {
// Anzahl Werktage berechnen (Montag-Freitag)
const startDate = new Date(week_start);
const endDate = new Date(week_end);
let workdays = 0;
let holidayHours = 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];
if (holidaySet.has(dateStr)) holidayHours += 8;
}
}
// Sollstunden berechnen
const sollStunden = (wochenstunden / 5) * workdays;
// Überstunden: (Tatsächliche Stunden + Urlaubsstunden + Feiertagsstunden) - Sollstunden
const totalHoursWithVacation = totalHours + vacationHours + holidayHours;
const overtimeHours = totalHoursWithVacation - sollStunden;
const remainingOvertime = overtimeHours - overtimeTaken;
const remainingOvertimeWithOffset = remainingOvertime + overtimeOffsetHours;
// Verbleibende Urlaubstage
const remainingVacation = urlaubstage - vacationDays + vacationOffsetDays;
res.json({
wochenstunden,
urlaubstage,
totalHours,
sollStunden,
overtimeHours,
overtimeTaken,
remainingOvertime,
overtimeOffsetHours,
remainingOvertimeWithOffset,
vacationDays,
vacationOffsetDays,
remainingVacation,
sickDays,
workdays
});
if (!weeks || weeks.length === 0) {
// Keine eingereichten Wochen - setze totalVacationDays auf 0
totalVacationDays = 0;
// Weiter mit der normalen Verarbeitung der aktuellen Woche
processCurrentWeek(0);
} else {
weeks.forEach((week) => {
// Einträge für diese Woche abrufen (nur neueste pro Tag)
db.all(`SELECT date, vacation_type, updated_at, id
FROM timesheet_entries
WHERE user_id = ? AND date >= ? AND date <= ?
AND vacation_type IS NOT NULL
AND vacation_type != ''
ORDER BY date, updated_at DESC, id DESC`,
[userId, week.week_start, week.week_end],
(err, weekEntries) => {
if (err) {
return res.status(500).json({ error: 'Fehler beim Abrufen der Einträge' });
}
// Filtere auf neuesten Eintrag pro Tag
(weekEntries || []).forEach(entry => {
const existing = vacationByDate[entry.date];
if (!existing) {
vacationByDate[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)) {
vacationByDate[entry.date] = entry;
}
}
});
processedWeeks++;
if (processedWeeks === weeks.length) {
// Alle Wochen verarbeitet - summiere Urlaubstage
Object.values(vacationByDate).forEach(entry => {
if (entry.vacation_type === 'full') {
totalVacationDays += 1;
} else if (entry.vacation_type === 'half') {
totalVacationDays += 0.5;
}
});
// Weiter mit der normalen Verarbeitung der aktuellen Woche
processCurrentWeek(totalVacationDays);
}
});
});
}
function processCurrentWeek(totalVacationDays) {
// Einträge für die Woche abrufen
db.all(`SELECT date, total_hours, overtime_taken_hours, vacation_type, sick_status
FROM timesheet_entries
WHERE user_id = ? AND date >= ? AND date <= ?
ORDER BY date`,
[userId, week_start, week_end],
(err, entries) => {
if (err) {
return res.status(500).json({ error: 'Fehler beim Abrufen der Einträge' });
}
// Berechnungen
let totalHours = 0;
let overtimeTaken = 0;
let vacationDays = 0;
let vacationHours = 0;
let sickDays = 0;
entries.forEach(entry => {
if (entry.overtime_taken_hours) {
overtimeTaken += entry.overtime_taken_hours;
}
// Krankheitstage zählen
if (entry.sick_status === 1 || entry.sick_status === true) {
sickDays += 1;
}
// Urlaub hat Priorität - wenn Urlaub, zähle nur Urlaubsstunden, nicht zusätzlich Arbeitsstunden
if (entry.vacation_type === 'full') {
vacationDays += 1;
vacationHours += fullDayHours; // Ganzer Tag = (Wochenarbeitszeit / Arbeitstage) Stunden
// Bei vollem Tag Urlaub werden keine Arbeitsstunden gezählt
} else if (entry.vacation_type === 'half') {
vacationDays += 0.5;
vacationHours += fullDayHours / 2; // Halber Tag = (Wochenarbeitszeit / Arbeitstage) / 2 Stunden
// Bei halbem Tag Urlaub können noch Arbeitsstunden vorhanden sein
// WICHTIG: total_hours enthält bereits Wochenend-Prozentsätze (aus timesheet.js)
if (entry.total_hours) {
totalHours += parseFloat(entry.total_hours) || 0;
}
} else {
// Kein Urlaub - zähle nur Arbeitsstunden
// WICHTIG: total_hours enthält bereits Wochenend-Prozentsätze (aus timesheet.js)
if (entry.total_hours) {
totalHours += parseFloat(entry.total_hours) || 0;
}
}
});
// Feiertage für die Woche laden ((Wochenarbeitszeit / Arbeitstage) pro Feiertag; Arbeit an Feiertag = Überstunden)
getHolidaysForDateRange(week_start, week_end)
.catch(() => new Set())
.then((holidaySet) => {
// Anzahl Werktage berechnen (Montag-Freitag)
const startDate = new Date(week_start);
const endDate = new Date(week_end);
let workdays = 0;
let holidayHours = 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];
if (holidaySet.has(dateStr)) holidayHours += fullDayHours;
}
}
// Sollstunden berechnen
const sollStunden = (wochenstunden / arbeitstage) * workdays;
// Überstunden: (Tatsächliche Stunden + Urlaubsstunden + Feiertagsstunden) - Sollstunden
const totalHoursWithVacation = totalHours + vacationHours + holidayHours;
const overtimeHours = totalHoursWithVacation - sollStunden;
const remainingOvertime = overtimeHours - overtimeTaken;
const remainingOvertimeWithOffset = remainingOvertime + overtimeOffsetHours;
// Verbleibende Urlaubstage (berücksichtigt alle eingereichten Wochen, nicht nur die aktuelle)
const remainingVacation = urlaubstage - totalVacationDays + vacationOffsetDays;
res.json({
wochenstunden,
urlaubstage,
totalHours,
sollStunden,
overtimeHours,
overtimeTaken,
remainingOvertime,
overtimeOffsetHours,
remainingOvertimeWithOffset,
vacationDays,
vacationOffsetDays,
remainingVacation,
totalVacationDays,
sickDays,
workdays
});
});
});
}
});
});
});
});
});