To-DO abgearbeitet

This commit is contained in:
2026-02-04 19:03:42 +01:00
parent 76f63ed4ec
commit 063fb68b12
5 changed files with 158 additions and 91 deletions

View File

@@ -11,7 +11,7 @@
- Wenn ganzer Tag Urlaub gesetzt wird steht erst 8h (Urlaub) und dann nur noch 8h
- Feiertage im PDF anzeigen -> DONE Testen noch nicht depoyed
- Oben wenn woche eingereicht anzeigen als hilfestellung
- Ausgefüllte Tage anhand der Tage pro woche gültig setzten
- Überstunden müssen anhand der Tagesstunden auch auf gültig setzten (Tag ausgefüllt wenn weniger als 8h)
- Verplante Urlaubstage müssen auf abgezogen werden, wenn die Woche die gepalnt war eingereicht wurde.
- Oben wenn woche eingereicht anzeigen als hilfestellung -> DONE
- Ausgefüllte Tage anhand der Tage pro woche gültig setzten -> DONE Testen
- Überstunden müssen anhand der Tagesstunden auch auf gültig setzten (Tag ausgefüllt wenn weniger als 8h) -> DONE sollte passen
- Verplante Urlaubstage müssen auf abgezogen werden, wenn die Woche die gepalnt war eingereicht wurde. -> DONE Testen

View File

@@ -117,6 +117,21 @@ function getWeekDatesFromCalendarWeek(year, weekNumber) {
};
}
// Helper: Berechnet week_start (Montag) aus einem Datum
function getWeekStart(dateStr) {
const date = new Date(dateStr);
const day = date.getDay(); // 0 = Sonntag, 1 = Montag, ..., 6 = Samstag
const diff = day === 0 ? -6 : 1 - day; // Montag = 1, Sonntag = 0
const monday = new Date(date);
monday.setDate(date.getDate() + diff);
// Format: YYYY-MM-DD
const year = monday.getFullYear();
const month = String(monday.getMonth() + 1).padStart(2, '0');
const dayOfMonth = String(monday.getDate()).padStart(2, '0');
return `${year}-${month}-${dayOfMonth}`;
}
module.exports = {
hasRole,
getDefaultRole,
@@ -127,5 +142,6 @@ module.exports = {
formatDate,
formatDateTime,
getCalendarWeek,
getWeekDatesFromCalendarWeek
getWeekDatesFromCalendarWeek,
getWeekStart
};

View File

@@ -374,8 +374,11 @@ function renderWeek() {
endDate.setDate(endDate.getDate() + 6);
const calendarWeek = getCalendarWeek(currentWeekStart);
const submittedText = currentEntries._weekSubmitted === true
? `<br><span style="color: #28a745;">(bereits Abgegeben)</span>`
: '';
document.getElementById('weekTitle').innerHTML =
`Kalenderwoche ${calendarWeek}<br>${formatDateDE(currentWeekStart)} - ${formatDateDE(formatDate(endDate))}`;
`Kalenderwoche ${calendarWeek}<br>${formatDateDE(currentWeekStart)} - ${formatDateDE(formatDate(endDate))}${submittedText}`;
let html = `
<div class="timesheet-grid">
@@ -1410,9 +1413,10 @@ function checkWeekComplete() {
let allWeekdaysFilled = true;
const missingFields = [];
// Prüfe nur Werktage (Montag-Freitag, i < 5)
// Samstag und Sonntag (i >= 5) sind optional
for (let i = 0; i < 5; i++) {
// Prüfe nur so viele Tage wie Arbeitstage pro Woche festgelegt sind
// Samstag und Sonntag sind optional
const requiredDays = userArbeitstage || 5; // Fallback auf 5 wenn nicht gesetzt
for (let i = 0; i < requiredDays; i++) {
const date = new Date(startDate);
date.setDate(date.getDate() + i);
const dateStr = formatDate(date);
@@ -1482,16 +1486,19 @@ function checkWeekComplete() {
}
}
// Prüfe ob die Woche bereits eingereicht wurde (nicht der Status einzelner Einträge!)
// Prüfe ob die Woche bereits eingereicht wurde (für Anzeige, aber Button bleibt aktiv für neue Versionen)
const weekIsSubmitted = currentEntries._weekSubmitted === true;
const submitButton = document.getElementById('submitWeek');
if (submitButton) {
submitButton.disabled = weekIsSubmitted || !allWeekdaysFilled;
// Button nur deaktivieren wenn nicht alle Felder ausgefüllt sind
// Resubmission ist erlaubt, da Versionierung unterstützt wird
submitButton.disabled = !allWeekdaysFilled;
if (weekIsSubmitted) {
submitButton.title = 'Diese Woche wurde bereits eingereicht und kann nicht mehr geändert werden.';
} else if (!allWeekdaysFilled) {
submitButton.title = `Bitte füllen Sie alle Werktage (Montag bis Freitag) aus (Start- und Endzeit). Wochenende ist optional. Fehlend: ${missingFields.join(', ')}`;
if (!allWeekdaysFilled) {
const requiredDaysText = requiredDays === 1 ? '1 Tag' : `${requiredDays} Tage`;
submitButton.title = `Bitte füllen Sie alle ${requiredDaysText} (Start- und Endzeit) aus. Wochenende ist optional. Fehlend: ${missingFields.join(', ')}`;
} else if (weekIsSubmitted) {
submitButton.title = 'Diese Woche wurde bereits eingereicht. Beim erneuten Abschicken wird eine neue Version erstellt.';
} else {
submitButton.title = '';
}
@@ -1545,7 +1552,7 @@ async function submitWeek() {
console.log('Prüfe Validierung für Woche:', currentWeekStart);
// Frontend-Validierung: Prüfen ob alle Werktage (Montag-Freitag) ausgefüllt sind
// Frontend-Validierung: Prüfen ob so viele Tage ausgefüllt sind wie Arbeitstage pro Woche festgelegt sind
let missingFields = [];
let firstMissingInput = null;
@@ -1556,7 +1563,8 @@ async function submitWeek() {
el.style.backgroundColor = '';
});
for (let i = 0; i < 5; i++) {
const requiredDays = userArbeitstage || 5; // Fallback auf 5 wenn nicht gesetzt
for (let i = 0; i < requiredDays; i++) {
const date = new Date(startDate);
date.setDate(date.getDate() + i);
const dateStr = formatDate(date);
@@ -1644,7 +1652,8 @@ async function submitWeek() {
}
// Detaillierte Fehlermeldung
const message = `❌ Bitte füllen Sie alle Werktage (Montag bis Freitag) vollständig aus!\n\n` +
const requiredDaysText = requiredDays === 1 ? '1 Tag' : `${requiredDays} Tage`;
const message = `❌ Bitte füllen Sie alle ${requiredDaysText} vollständig aus!\n\n` +
`Fehlende Eingaben:\n${missingFields.map((field, index) => `\n${index + 1}. ${field}`).join('')}\n\n` +
`Die fehlenden Felder wurden rot markiert.\n` +
`Hinweis: Samstag und Sonntag sind optional.`;

View File

@@ -262,7 +262,7 @@ function registerTimesheetRoutes(app) {
// Füge Status-Info hinzu (Bearbeitung ist immer möglich)
const entriesWithStatus = (entries || []).map(entry => ({
...entry,
week_submitted: false, // Immer false, damit Bearbeitung möglich ist
week_submitted: hasSubmittedVersion, // Woche wurde eingereicht wenn weekly_timesheet existiert
latest_version: latestVersion,
has_existing_version: latestVersion > 0
}));
@@ -304,7 +304,7 @@ function registerTimesheetRoutes(app) {
}
});
// Prüfe nur Werktage (Montag-Freitag, erste 5 Tage)
// Prüfe nur so viele Tage wie Arbeitstage pro Woche festgelegt sind
// Samstag und Sonntag sind optional
// Bei ganztägigem Urlaub (vacation_type = 'full') ist der Tag als ausgefüllt zu betrachten
// Bei 8 Überstunden (ganzer Tag) ist der Tag auch als ausgefüllt zu betrachten
@@ -330,7 +330,7 @@ function registerTimesheetRoutes(app) {
.then((holidaySet) => {
let missingDays = [];
for (let i = 0; i < 5; i++) {
for (let i = 0; i < arbeitstage; i++) {
// Datum direkt berechnen ohne Zeitzonenprobleme
const date = new Date(startYear, startMonth, startDay + i);
const year = date.getFullYear();
@@ -380,8 +380,9 @@ function registerTimesheetRoutes(app) {
}
if (missingDays.length > 0) {
const requiredDaysText = arbeitstage === 1 ? '1 Tag' : `${arbeitstage} Tage`;
return res.status(400).json({
error: `Nicht alle Werktage (Montag bis Freitag) sind ausgefüllt. Fehlende Tage: ${missingDays.join(', ')}. Bitte füllen Sie alle Werktage mit Start- und Endzeit aus. Wochenende ist optional.`
error: `Nicht alle ${requiredDaysText} sind ausgefüllt. Fehlende Tage: ${missingDays.join(', ')}. Bitte füllen Sie alle ${requiredDaysText} mit Start- und Endzeit aus. Wochenende ist optional.`
});
}

View File

@@ -154,11 +154,26 @@ function registerUserRoutes(app) {
res.json({ success: true, currentRole: role });
});
// API: Verplante Urlaubstage (alle Wochen, auch nicht-eingereichte)
// API: Verplante Urlaubstage (nur nicht-eingereichte Wochen)
app.get('/api/user/planned-vacation', requireAuth, (req, res) => {
const userId = req.session.userId;
const { getCalendarWeek } = require('../helpers/utils');
const { getCalendarWeek, getWeekStart } = require('../helpers/utils');
// Zuerst alle eingereichten Wochen abrufen
db.all(`SELECT DISTINCT week_start FROM weekly_timesheets
WHERE user_id = ? AND status = 'eingereicht'`,
[userId],
(err, submittedWeeks) => {
if (err) {
return res.status(500).json({ error: 'Fehler beim Abrufen der eingereichten Wochen' });
}
// Set für schnelle Suche nach eingereichten Wochen
const submittedWeekStarts = new Set(
(submittedWeeks || []).map(w => w.week_start)
);
// Alle Urlaubseinträge abrufen
db.all(`SELECT date, vacation_type FROM timesheet_entries
WHERE user_id = ? AND vacation_type IS NOT NULL AND vacation_type != ''`,
[userId],
@@ -171,6 +186,14 @@ function registerUserRoutes(app) {
const weeksMap = {}; // { KW: { year: YYYY, week: KW, days: X } }
entries.forEach(entry => {
// Berechne week_start für diesen Eintrag
const weekStart = getWeekStart(entry.date);
// Überspringe Einträge aus eingereichten Wochen
if (submittedWeekStarts.has(weekStart)) {
return;
}
const dayValue = entry.vacation_type === 'full' ? 1 : 0.5;
plannedDays += dayValue;
@@ -198,6 +221,8 @@ function registerUserRoutes(app) {
});
}
);
}
);
});
// API: Gesamtstatistiken für Mitarbeiter (Überstunden und Urlaubstage)
@@ -237,8 +262,26 @@ function registerUserRoutes(app) {
const overtimeOffsetHours = user.overtime_offset_hours ? parseFloat(user.overtime_offset_hours) : 0;
const vacationOffsetDays = user.vacation_offset_days ? parseFloat(user.vacation_offset_days) : 0;
// Verplante Urlaubstage berechnen (alle Wochen, auch nicht-eingereichte)
const { getCalendarWeek } = require('../helpers/utils');
// Verplante Urlaubstage berechnen (nur nicht-eingereichte Wochen)
const { getCalendarWeek, getWeekStart } = require('../helpers/utils');
// 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 Wochen' });
}
// Set für schnelle Suche nach eingereichten Wochen
const submittedWeekStarts = new Set(
(weeks || []).map(w => w.week_start)
);
// Alle Urlaubseinträge abrufen
db.all(`SELECT date, vacation_type FROM timesheet_entries
WHERE user_id = ? AND vacation_type IS NOT NULL AND vacation_type != ''`,
[userId],
@@ -251,6 +294,14 @@ function registerUserRoutes(app) {
const weeksMap = {}; // { KW: { year: YYYY, week: KW, days: X } }
(allVacationEntries || []).forEach(entry => {
// Berechne week_start für diesen Eintrag
const weekStart = getWeekStart(entry.date);
// Überspringe Einträge aus eingereichten Wochen
if (submittedWeekStarts.has(weekStart)) {
return;
}
const dayValue = entry.vacation_type === 'full' ? 1 : 0.5;
plannedVacationDays += dayValue;
@@ -272,17 +323,7 @@ function registerUserRoutes(app) {
return a.week - b.week;
});
// 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' });
}
// Weiter mit der Verarbeitung der eingereichten Wochen (weeks ist bereits verfügbar)
// Wenn keine Wochen vorhanden
if (!weeks || weeks.length === 0) {
return res.json({