Anpassung in der Verwaltung mit der Berechnung
This commit is contained in:
5
.cursor/worktrees.json
Normal file
5
.cursor/worktrees.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"setup-worktree": [
|
||||||
|
"npm install"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -243,12 +243,19 @@ function registerVerwaltungRoutes(app) {
|
|||||||
|
|
||||||
// Nur Wochen bis Ende der angezeigten Kalenderwoche (Stand Urlaub = Ende dieser KW)
|
// Nur Wochen bis Ende der angezeigten Kalenderwoche (Stand Urlaub = Ende dieser KW)
|
||||||
const weeksUpToDisplayed = (weeks || []).filter((w) => w.week_end <= week_end);
|
const weeksUpToDisplayed = (weeks || []).filter((w) => w.week_end <= week_end);
|
||||||
|
// Wochen VOR der aktuellen Woche für kumulative Überstunden-Berechnung
|
||||||
|
const weeksBeforeCurrent = (weeks || []).filter((w) => w.week_end < week_end);
|
||||||
let processedWeeks = 0;
|
let processedWeeks = 0;
|
||||||
let totalVacationDays = 0;
|
let totalVacationDays = 0;
|
||||||
const vacationByDate = {};
|
const vacationByDate = {};
|
||||||
|
|
||||||
|
// Kumulative Überstunden über alle Wochen VOR der aktuellen Woche
|
||||||
|
let cumulativeOvertimeHours = 0;
|
||||||
|
let cumulativeOvertimeTaken = 0;
|
||||||
|
|
||||||
|
// Urlaubstage für alle Wochen bis zur aktuellen Woche (inklusive)
|
||||||
if (weeksUpToDisplayed.length === 0) {
|
if (weeksUpToDisplayed.length === 0) {
|
||||||
processCurrentWeek(0);
|
processCurrentWeek(0, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
weeksUpToDisplayed.forEach((week) => {
|
weeksUpToDisplayed.forEach((week) => {
|
||||||
db.all(`SELECT date, vacation_type, updated_at, id
|
db.all(`SELECT date, vacation_type, updated_at, id
|
||||||
@@ -285,13 +292,111 @@ function registerVerwaltungRoutes(app) {
|
|||||||
totalVacationDays += 0.5;
|
totalVacationDays += 0.5;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
processCurrentWeek(totalVacationDays);
|
|
||||||
|
// Berechne Überstunden für alle Wochen VOR der aktuellen Woche
|
||||||
|
if (weeksBeforeCurrent.length === 0) {
|
||||||
|
processCurrentWeek(totalVacationDays, 0, 0);
|
||||||
|
} else {
|
||||||
|
let processedOvertimeWeeks = 0;
|
||||||
|
weeksBeforeCurrent.forEach((week) => {
|
||||||
|
calculateWeekOvertime(week.week_start, week.week_end, (weekOvertime, weekOvertimeTaken) => {
|
||||||
|
cumulativeOvertimeHours += weekOvertime;
|
||||||
|
cumulativeOvertimeTaken += weekOvertimeTaken;
|
||||||
|
|
||||||
|
processedOvertimeWeeks++;
|
||||||
|
if (processedOvertimeWeeks === weeksBeforeCurrent.length) {
|
||||||
|
processCurrentWeek(totalVacationDays, cumulativeOvertimeHours, cumulativeOvertimeTaken);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function processCurrentWeek(totalVacationDays) {
|
function calculateWeekOvertime(weekStart, weekEnd, callback) {
|
||||||
|
db.all(`SELECT id, date, updated_at, total_hours, overtime_taken_hours, vacation_type, sick_status
|
||||||
|
FROM timesheet_entries
|
||||||
|
WHERE user_id = ? AND date >= ? AND date <= ?
|
||||||
|
ORDER BY date, updated_at DESC, id DESC`,
|
||||||
|
[userId, weekStart, weekEnd],
|
||||||
|
(err, allEntries) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nur neuesten Eintrag pro Tag zählen
|
||||||
|
const entriesByDate = {};
|
||||||
|
(allEntries || []).forEach(entry => {
|
||||||
|
const existing = entriesByDate[entry.date];
|
||||||
|
if (!existing) {
|
||||||
|
entriesByDate[entry.date] = entry;
|
||||||
|
} else {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const entries = Object.values(entriesByDate);
|
||||||
|
|
||||||
|
let weekTotalHours = 0;
|
||||||
|
let weekOvertimeTaken = 0;
|
||||||
|
let weekVacationHours = 0;
|
||||||
|
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.overtime_taken_hours) {
|
||||||
|
weekOvertimeTaken += parseFloat(entry.overtime_taken_hours) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.vacation_type === 'full') {
|
||||||
|
weekVacationHours += fullDayHours;
|
||||||
|
} else if (entry.vacation_type === 'half') {
|
||||||
|
weekVacationHours += fullDayHours / 2;
|
||||||
|
if (entry.total_hours) {
|
||||||
|
weekTotalHours += parseFloat(entry.total_hours) || 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (entry.total_hours) {
|
||||||
|
weekTotalHours += parseFloat(entry.total_hours) || 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
getHolidaysForDateRange(weekStart, weekEnd)
|
||||||
|
.catch(() => new Set())
|
||||||
|
.then((holidaySet) => {
|
||||||
|
const startDate = new Date(weekStart);
|
||||||
|
const endDate = new Date(weekEnd);
|
||||||
|
let workdays = 0;
|
||||||
|
let holidayDays = 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) {
|
||||||
|
workdays++;
|
||||||
|
const dateStr = d.toISOString().split('T')[0];
|
||||||
|
if (holidaySet.has(dateStr)) {
|
||||||
|
holidayDays++;
|
||||||
|
// Feiertagsstunden für alle Feiertage hinzufügen (wie im PDF)
|
||||||
|
holidayHours += fullDayHours;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sollstunden = Wochenstunden (Feiertage reduzieren Soll nicht, da bezahlt)
|
||||||
|
const sollStunden = wochenstunden;
|
||||||
|
const totalHoursWithVacation = weekTotalHours + weekVacationHours + holidayHours;
|
||||||
|
const weekOvertimeHours = totalHoursWithVacation - sollStunden;
|
||||||
|
|
||||||
|
callback(weekOvertimeHours, weekOvertimeTaken);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function processCurrentWeek(totalVacationDays, cumulativeOvertimeHours, cumulativeOvertimeTaken) {
|
||||||
// Einträge für die Woche abrufen (id/updated_at für neuesten pro Tag)
|
// Einträge für die Woche abrufen (id/updated_at für neuesten pro Tag)
|
||||||
db.all(`SELECT id, date, updated_at, total_hours, overtime_taken_hours, vacation_type, sick_status
|
db.all(`SELECT id, date, updated_at, total_hours, overtime_taken_hours, vacation_type, sick_status
|
||||||
FROM timesheet_entries
|
FROM timesheet_entries
|
||||||
@@ -366,23 +471,35 @@ function registerVerwaltungRoutes(app) {
|
|||||||
const startDate = new Date(week_start);
|
const startDate = new Date(week_start);
|
||||||
const endDate = new Date(week_end);
|
const endDate = new Date(week_end);
|
||||||
let workdays = 0;
|
let workdays = 0;
|
||||||
|
let holidayDays = 0;
|
||||||
let holidayHours = 0;
|
let holidayHours = 0;
|
||||||
for (let d = new Date(startDate); d <= endDate; d.setDate(d.getDate() + 1)) {
|
for (let d = new Date(startDate); d <= endDate; d.setDate(d.getDate() + 1)) {
|
||||||
const day = d.getDay();
|
const day = d.getDay();
|
||||||
if (day >= 1 && day <= 5) { // Montag bis Freitag
|
if (day >= 1 && day <= 5) { // Montag bis Freitag
|
||||||
workdays++;
|
workdays++;
|
||||||
const dateStr = d.toISOString().split('T')[0];
|
const dateStr = d.toISOString().split('T')[0];
|
||||||
if (holidaySet.has(dateStr)) holidayHours += fullDayHours;
|
if (holidaySet.has(dateStr)) {
|
||||||
|
holidayDays++;
|
||||||
|
// Feiertagsstunden für alle Feiertage hinzufügen (wie im PDF)
|
||||||
|
holidayHours += fullDayHours;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sollstunden berechnen
|
// Sollstunden = Wochenstunden (Feiertage reduzieren Soll nicht, da bezahlt)
|
||||||
const sollStunden = (wochenstunden / arbeitstage) * workdays;
|
const sollStunden = wochenstunden;
|
||||||
|
|
||||||
// Überstunden: (Tatsächliche Stunden + Urlaubsstunden + Feiertagsstunden) - Sollstunden
|
// Überstunden für die aktuelle Woche: (Tatsächliche Stunden + Urlaubsstunden + Feiertagsstunden) - Sollstunden
|
||||||
const totalHoursWithVacation = totalHours + vacationHours + holidayHours;
|
const totalHoursWithVacation = totalHours + vacationHours + holidayHours;
|
||||||
const overtimeHours = totalHoursWithVacation - sollStunden;
|
const weekOvertimeHours = totalHoursWithVacation - sollStunden;
|
||||||
const remainingOvertime = overtimeHours - overtimeTaken;
|
|
||||||
|
// Kumulative Überstunden: Summe aller Wochen bis zur aktuellen Woche
|
||||||
|
// cumulativeOvertimeHours enthält bereits alle vorherigen Wochen
|
||||||
|
const totalCumulativeOvertimeHours = cumulativeOvertimeHours + weekOvertimeHours;
|
||||||
|
const totalCumulativeOvertimeTaken = cumulativeOvertimeTaken + overtimeTaken;
|
||||||
|
|
||||||
|
// Verbleibende Überstunden = kumulative Überstunden - kumulative genommene Überstunden
|
||||||
|
const remainingOvertime = totalCumulativeOvertimeHours - totalCumulativeOvertimeTaken;
|
||||||
const remainingOvertimeWithOffset = remainingOvertime + overtimeOffsetHours;
|
const remainingOvertimeWithOffset = remainingOvertime + overtimeOffsetHours;
|
||||||
|
|
||||||
// Verbleibende Urlaubstage (berücksichtigt alle eingereichten Wochen, nicht nur die aktuelle)
|
// Verbleibende Urlaubstage (berücksichtigt alle eingereichten Wochen, nicht nur die aktuelle)
|
||||||
@@ -393,8 +510,9 @@ function registerVerwaltungRoutes(app) {
|
|||||||
urlaubstage,
|
urlaubstage,
|
||||||
totalHours,
|
totalHours,
|
||||||
sollStunden,
|
sollStunden,
|
||||||
overtimeHours,
|
weekOvertimeHours, // Überstunden nur für diese Woche
|
||||||
overtimeTaken,
|
overtimeHours: totalCumulativeOvertimeHours, // Kumulative Überstunden
|
||||||
|
overtimeTaken: totalCumulativeOvertimeTaken, // Kumulative genommene Überstunden
|
||||||
remainingOvertime,
|
remainingOvertime,
|
||||||
overtimeOffsetHours,
|
overtimeOffsetHours,
|
||||||
remainingOvertimeWithOffset,
|
remainingOvertimeWithOffset,
|
||||||
|
|||||||
@@ -321,11 +321,11 @@
|
|||||||
|
|
||||||
// Statistiken anzeigen
|
// Statistiken anzeigen
|
||||||
let statsHTML = '';
|
let statsHTML = '';
|
||||||
if (data.overtimeHours !== undefined) {
|
if (data.weekOvertimeHours !== undefined) {
|
||||||
|
const weekOvertimeColor = data.weekOvertimeHours < 0 ? '#dc3545' : (data.weekOvertimeHours > 0 ? '#28a745' : '#666');
|
||||||
|
const sign = data.weekOvertimeHours >= 0 ? '+' : '';
|
||||||
statsHTML += `<div class="stats-inline" style="display: inline-block; margin-right: 20px;">
|
statsHTML += `<div class="stats-inline" style="display: inline-block; margin-right: 20px;">
|
||||||
<strong>Überstunden:</strong> <span>${data.overtimeHours.toFixed(2)} h</span>
|
<strong>Überstunden:</strong> <span style="color: ${weekOvertimeColor};">${sign}${data.weekOvertimeHours.toFixed(2)} h</span>
|
||||||
${data.overtimeTaken > 0 ? `<span style="color: #666;">(davon genommen: ${data.overtimeTaken.toFixed(2)} h)</span>` : ''}
|
|
||||||
${data.remainingOvertime !== data.overtimeHours ? `<span style="color: #28a745;">(verbleibend: ${data.remainingOvertime.toFixed(2)} h)</span>` : ''}
|
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
if (data.overtimeOffsetHours !== undefined && data.overtimeOffsetHours !== 0) {
|
if (data.overtimeOffsetHours !== undefined && data.overtimeOffsetHours !== 0) {
|
||||||
@@ -338,9 +338,9 @@
|
|||||||
const totalTaken = data.totalVacationDays !== undefined ? data.totalVacationDays : 0;
|
const totalTaken = data.totalVacationDays !== undefined ? data.totalVacationDays : 0;
|
||||||
const inWeek = data.vacationDays !== undefined ? data.vacationDays : 0;
|
const inWeek = data.vacationDays !== undefined ? data.vacationDays : 0;
|
||||||
statsHTML += `<div class="stats-inline" style="display: inline-block; margin-right: 20px;">
|
statsHTML += `<div class="stats-inline" style="display: inline-block; margin-right: 20px;">
|
||||||
<strong>Urlaub genommen (kumuliert bis Ende KW):</strong> <span>${Number(totalTaken).toFixed(1)} Tag${totalTaken !== 1 ? 'e' : ''}</span>
|
<strong>Urlaub genommen (dieses Jahr):</strong> <span>${Number(totalTaken).toFixed(1)} Tag${totalTaken !== 1 ? 'e' : ''}</span>
|
||||||
${inWeek > 0 ? ` <span style="color: #666;">(davon in dieser Woche: ${inWeek.toFixed(1)})</span>` : ''}
|
${inWeek > 0 ? ` <span style="color: #666;">(diese Woche: ${inWeek.toFixed(1)})</span>` : ''}
|
||||||
${data.remainingVacation !== undefined ? ` <span style="color: #28a745;">(verbleibend Stand Ende KW: ${Number(data.remainingVacation).toFixed(1)} Tage)</span>` : ''}
|
${data.remainingVacation !== undefined ? ` <span style="color: #28a745;">(verbleibend: ${Number(data.remainingVacation).toFixed(1)} Tage)</span>` : ''}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
if (data.vacationOffsetDays !== undefined && data.vacationOffsetDays !== 0) {
|
if (data.vacationOffsetDays !== undefined && data.vacationOffsetDays !== 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user