Add Prozent im ADmin für wochenende
This commit is contained in:
16
database.js
16
database.js
@@ -249,6 +249,22 @@ function initDatabase() {
|
|||||||
name TEXT
|
name TEXT
|
||||||
)`);
|
)`);
|
||||||
|
|
||||||
|
// System-Optionen-Tabelle für Wochenend-Prozentsätze
|
||||||
|
db.run(`CREATE TABLE IF NOT EXISTS system_options (
|
||||||
|
id INTEGER PRIMARY KEY DEFAULT 1,
|
||||||
|
saturday_percentage REAL DEFAULT 100,
|
||||||
|
sunday_percentage REAL DEFAULT 100,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CHECK (id = 1)
|
||||||
|
)`);
|
||||||
|
|
||||||
|
// Standard-Eintrag für system_options erstellen falls nicht vorhanden
|
||||||
|
db.run(`INSERT OR IGNORE INTO system_options (id, saturday_percentage, sunday_percentage) VALUES (1, 100, 100)`, (err) => {
|
||||||
|
if (err && !err.message.includes('UNIQUE constraint')) {
|
||||||
|
console.warn('Warnung beim Erstellen des Standard-Eintrags für system_options:', err.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Migration: Bestehende Rollen zu JSON-Arrays konvertieren
|
// Migration: Bestehende Rollen zu JSON-Arrays konvertieren
|
||||||
// Prüfe ob Rollen noch als einfache Strings gespeichert sind (nicht als JSON-Array)
|
// Prüfe ob Rollen noch als einfache Strings gespeichert sind (nicht als JSON-Array)
|
||||||
db.all('SELECT id, role FROM users', (err, users) => {
|
db.all('SELECT id, role FROM users', (err, users) => {
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
Test - Stundenerfassung
|
|
||||||
|
|
||||||
Hallo zusammen,
|
|
||||||
|
|
||||||
Mara ist auf mich mit einer Bitte herangetreten, ob ich die Stundenerfassung digitalisieren kann.
|
|
||||||
Das habe ich die letzten 2 Wochen am abend und am WE gemacht.
|
|
||||||
|
|
||||||
Ich glaube, dass das System jetzt fest fertig ist und ihr es testen könnt
|
|
||||||
Der test soll Fehler finden und mir noch die möglichkeit geben diese dann zu beheben.
|
|
||||||
|
|
||||||
Am Montag würde ich gerne eine kurze Einführung für die Leute im Büro geben.
|
|
||||||
Um ca. 11:00 Uhr für so 10-15 Minuten.
|
|
||||||
|
|
||||||
Achtet bitte am Anfangauf die Überstundenerechnung, da könnte noch der ein oder andere Fehler drin sein.
|
|
||||||
|
|
||||||
Die Seite ist im Browser zu finden unter http://stunden.sds-systemtechnik.de:3333 oder http://192.168.120.64:3333
|
|
||||||
|
|
||||||
|
|
||||||
Viele Grüße
|
|
||||||
Carsten Graf
|
|
||||||
|
|
||||||
@@ -54,6 +54,43 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
// LDAP-Konfiguration laden
|
// LDAP-Konfiguration laden
|
||||||
loadLDAPConfig();
|
loadLDAPConfig();
|
||||||
|
|
||||||
|
// Optionen laden
|
||||||
|
loadOptions();
|
||||||
|
|
||||||
|
// Optionen-Formular
|
||||||
|
const optionsForm = document.getElementById('optionsForm');
|
||||||
|
if (optionsForm) {
|
||||||
|
optionsForm.addEventListener('submit', async function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const formData = {
|
||||||
|
saturday_percentage: document.getElementById('saturdayPercentage').value,
|
||||||
|
sunday_percentage: document.getElementById('sundayPercentage').value
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/admin/options', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(formData)
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
alert('Optionen wurden erfolgreich gespeichert!');
|
||||||
|
} else {
|
||||||
|
alert('Fehler: ' + (result.error || 'Optionen konnten nicht gespeichert werden'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler:', error);
|
||||||
|
alert('Fehler beim Speichern der Optionen');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// LDAP-Konfigurationsformular
|
// LDAP-Konfigurationsformular
|
||||||
const ldapConfigForm = document.getElementById('ldapConfigForm');
|
const ldapConfigForm = document.getElementById('ldapConfigForm');
|
||||||
if (ldapConfigForm) {
|
if (ldapConfigForm) {
|
||||||
@@ -161,6 +198,27 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Optionen laden und Formular ausfüllen
|
||||||
|
async function loadOptions() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/admin/options');
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.config) {
|
||||||
|
const config = result.config;
|
||||||
|
|
||||||
|
if (document.getElementById('saturdayPercentage')) {
|
||||||
|
document.getElementById('saturdayPercentage').value = config.saturday_percentage || 0;
|
||||||
|
}
|
||||||
|
if (document.getElementById('sundayPercentage')) {
|
||||||
|
document.getElementById('sundayPercentage').value = config.sunday_percentage || 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Laden der Optionen:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// LDAP-Konfiguration laden und Formular ausfüllen
|
// LDAP-Konfiguration laden und Formular ausfüllen
|
||||||
async function loadLDAPConfig() {
|
async function loadLDAPConfig() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -4,6 +4,36 @@ let currentWeekStart = getMonday(new Date());
|
|||||||
let currentEntries = {};
|
let currentEntries = {};
|
||||||
let currentHolidayDates = new Set(); // Feiertage der aktuellen Woche (YYYY-MM-DD)
|
let currentHolidayDates = new Set(); // Feiertage der aktuellen Woche (YYYY-MM-DD)
|
||||||
let userWochenstunden = 0; // Wochenstunden des Users
|
let userWochenstunden = 0; // Wochenstunden des Users
|
||||||
|
let weekendPercentages = { saturday: 100, sunday: 100 }; // Wochenend-Prozentsätze (100% = normal)
|
||||||
|
|
||||||
|
// Wochenend-Prozentsätze laden
|
||||||
|
async function loadWeekendPercentages() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/user/weekend-percentages');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Fehler beim Laden der Wochenend-Prozentsätze');
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
weekendPercentages.saturday = data.saturday_percentage || 100;
|
||||||
|
weekendPercentages.sunday = data.sunday_percentage || 100;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Laden der Wochenend-Prozentsätze:', error);
|
||||||
|
// Standardwerte verwenden
|
||||||
|
weekendPercentages.saturday = 100;
|
||||||
|
weekendPercentages.sunday = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hilfsfunktion: Prüft ob ein Datum ein Wochenendtag ist und gibt den Prozentsatz zurück
|
||||||
|
function getWeekendPercentage(date) {
|
||||||
|
const day = date.getDay();
|
||||||
|
if (day === 6) { // Samstag
|
||||||
|
return weekendPercentages.saturday;
|
||||||
|
} else if (day === 0) { // Sonntag
|
||||||
|
return weekendPercentages.sunday;
|
||||||
|
}
|
||||||
|
return 100; // Kein Wochenende = 100% (normal)
|
||||||
|
}
|
||||||
|
|
||||||
// Statistiken laden
|
// Statistiken laden
|
||||||
async function loadUserStats() {
|
async function loadUserStats() {
|
||||||
@@ -110,6 +140,9 @@ document.addEventListener('DOMContentLoaded', async function() {
|
|||||||
// Ping-IP laden
|
// Ping-IP laden
|
||||||
loadPingIP();
|
loadPingIP();
|
||||||
|
|
||||||
|
// Wochenend-Prozentsätze laden
|
||||||
|
loadWeekendPercentages();
|
||||||
|
|
||||||
// Statistiken laden
|
// Statistiken laden
|
||||||
loadUserStats();
|
loadUserStats();
|
||||||
|
|
||||||
@@ -374,11 +407,19 @@ function renderWeek() {
|
|||||||
// Bei ganztägigem Urlaub oder Krank sollten es bereits 8 Stunden sein (vom Backend gesetzt)
|
// Bei ganztägigem Urlaub oder Krank sollten es bereits 8 Stunden sein (vom Backend gesetzt)
|
||||||
// Feiertag: 8h Basis + gearbeitete Stunden (jede gearbeitete Stunde = Überstunde)
|
// Feiertag: 8h Basis + gearbeitete Stunden (jede gearbeitete Stunde = Überstunde)
|
||||||
// Bei halbem Tag Urlaub werden die Urlaubsstunden später in der Überstunden-Berechnung hinzugezählt
|
// Bei halbem Tag Urlaub werden die Urlaubsstunden später in der Überstunden-Berechnung hinzugezählt
|
||||||
|
// Wochenend-Prozentsätze: Nur auf tatsächlich gearbeitete Stunden anwenden (nicht auf Urlaub, Krankheit, Feiertage)
|
||||||
|
let hoursToAdd = 0;
|
||||||
if (isHoliday) {
|
if (isHoliday) {
|
||||||
totalHours += 8 + (hours || 0); // 8h Feiertag + gearbeitete Stunden (= Überstunden)
|
hoursToAdd = 8 + (hours || 0); // 8h Feiertag + gearbeitete Stunden (= Überstunden)
|
||||||
} else {
|
} else {
|
||||||
totalHours += hours;
|
hoursToAdd = hours || 0;
|
||||||
|
// Wochenend-Prozentsatz anwenden (nur auf tatsächlich gearbeitete Stunden, nicht auf Urlaub/Krankheit)
|
||||||
|
const weekendPercentage = getWeekendPercentage(date);
|
||||||
|
if (weekendPercentage >= 100 && hours > 0 && vacationType !== 'full' && !sickStatus && !isFullDayOvertime) {
|
||||||
|
hoursToAdd = hours * (weekendPercentage / 100);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
totalHours += hoursToAdd;
|
||||||
|
|
||||||
// Bearbeitung ist immer möglich, auch nach Abschicken
|
// Bearbeitung ist immer möglich, auch nach Abschicken
|
||||||
// Bei ganztägigem Urlaub oder Krank werden Zeitfelder deaktiviert; Feiertag: Anzeige, Zeitfelder optional (Überstunden)
|
// Bei ganztägigem Urlaub oder Krank werden Zeitfelder deaktiviert; Feiertag: Anzeige, Zeitfelder optional (Überstunden)
|
||||||
@@ -662,10 +703,22 @@ function updateOvertimeDisplay() {
|
|||||||
const end = new Date(`2000-01-01T${endTime}`);
|
const end = new Date(`2000-01-01T${endTime}`);
|
||||||
const diffMs = end - start;
|
const diffMs = end - start;
|
||||||
const hours = (diffMs / (1000 * 60 * 60)) - (breakMinutes / 60);
|
const hours = (diffMs / (1000 * 60 * 60)) - (breakMinutes / 60);
|
||||||
totalHours += hours;
|
// Wochenend-Prozentsatz anwenden (nur auf tatsächlich gearbeitete Stunden)
|
||||||
|
const weekendPercentage = getWeekendPercentage(date);
|
||||||
|
let adjustedHours = hours;
|
||||||
|
if (weekendPercentage >= 100 && hours > 0 && vacationType !== 'full' && !sickStatus && !isFullDayOvertime) {
|
||||||
|
adjustedHours = hours * (weekendPercentage / 100);
|
||||||
|
}
|
||||||
|
totalHours += adjustedHours;
|
||||||
} else if (currentEntries[dateStr]?.total_hours && !isFullDayOvertime) {
|
} else if (currentEntries[dateStr]?.total_hours && !isFullDayOvertime) {
|
||||||
// Fallback auf gespeicherte Werte
|
// Fallback auf gespeicherte Werte
|
||||||
totalHours += parseFloat(currentEntries[dateStr].total_hours) || 0;
|
let hours = parseFloat(currentEntries[dateStr].total_hours) || 0;
|
||||||
|
// Wochenend-Prozentsatz anwenden (nur auf tatsächlich gearbeitete Stunden)
|
||||||
|
const weekendPercentage = getWeekendPercentage(date);
|
||||||
|
if (weekendPercentage >= 100 && hours > 0 && vacationType !== 'full' && !sickStatus) {
|
||||||
|
hours = hours * (weekendPercentage / 100);
|
||||||
|
}
|
||||||
|
totalHours += hours;
|
||||||
}
|
}
|
||||||
} else if (sickStatus) {
|
} else if (sickStatus) {
|
||||||
totalHours += 8; // Krank = 8 Stunden
|
totalHours += 8; // Krank = 8 Stunden
|
||||||
@@ -706,10 +759,22 @@ function updateOvertimeDisplay() {
|
|||||||
const end = new Date(`2000-01-01T${endTime}`);
|
const end = new Date(`2000-01-01T${endTime}`);
|
||||||
const diffMs = end - start;
|
const diffMs = end - start;
|
||||||
const hours = (diffMs / (1000 * 60 * 60)) - (breakMinutes / 60);
|
const hours = (diffMs / (1000 * 60 * 60)) - (breakMinutes / 60);
|
||||||
totalHours += hours;
|
// Wochenend-Prozentsatz anwenden (nur auf tatsächlich gearbeitete Stunden)
|
||||||
|
const weekendPercentage = getWeekendPercentage(date);
|
||||||
|
let adjustedHours = hours;
|
||||||
|
if (weekendPercentage >= 100 && hours > 0 && !isFullDayOvertime) {
|
||||||
|
adjustedHours = hours * (weekendPercentage / 100);
|
||||||
|
}
|
||||||
|
totalHours += adjustedHours;
|
||||||
} else if (currentEntries[dateStr]?.total_hours) {
|
} else if (currentEntries[dateStr]?.total_hours) {
|
||||||
// Fallback auf gespeicherte Werte
|
// Fallback auf gespeicherte Werte
|
||||||
totalHours += parseFloat(currentEntries[dateStr].total_hours) || 0;
|
let hours = parseFloat(currentEntries[dateStr].total_hours) || 0;
|
||||||
|
// Wochenend-Prozentsatz anwenden (nur auf tatsächlich gearbeitete Stunden)
|
||||||
|
const weekendPercentage = getWeekendPercentage(date);
|
||||||
|
if (weekendPercentage >= 100 && hours > 0 && !isFullDayOvertime) {
|
||||||
|
hours = hours * (weekendPercentage / 100);
|
||||||
|
}
|
||||||
|
totalHours += hours;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ function registerAdminRoutes(app) {
|
|||||||
app.get('/admin', requireAdmin, (req, res) => {
|
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, created_at FROM users ORDER BY created_at DESC',
|
||||||
(err, users) => {
|
(err, users) => {
|
||||||
// LDAP-Konfiguration und Sync-Log abrufen
|
// LDAP-Konfiguration, Sync-Log und Optionen abrufen
|
||||||
db.get('SELECT * FROM ldap_config WHERE id = 1', (err, ldapConfig) => {
|
db.get('SELECT * FROM ldap_config WHERE id = 1', (err, ldapConfig) => {
|
||||||
db.all('SELECT * FROM ldap_sync_log ORDER BY sync_started_at DESC LIMIT 10', (err, syncLogs) => {
|
db.all('SELECT * FROM ldap_sync_log ORDER BY sync_started_at DESC LIMIT 10', (err, syncLogs) => {
|
||||||
|
db.get('SELECT * FROM system_options WHERE id = 1', (err, options) => {
|
||||||
// Parse Rollen für jeden User
|
// Parse Rollen für jeden User
|
||||||
const usersWithRoles = (users || []).map(u => {
|
const usersWithRoles = (users || []).map(u => {
|
||||||
let roles = [];
|
let roles = [];
|
||||||
@@ -31,6 +32,7 @@ function registerAdminRoutes(app) {
|
|||||||
users: usersWithRoles,
|
users: usersWithRoles,
|
||||||
ldapConfig: ldapConfig || null,
|
ldapConfig: ldapConfig || null,
|
||||||
syncLogs: syncLogs || [],
|
syncLogs: syncLogs || [],
|
||||||
|
options: options || { saturday_percentage: 100, sunday_percentage: 100 },
|
||||||
user: {
|
user: {
|
||||||
firstname: req.session.firstname,
|
firstname: req.session.firstname,
|
||||||
lastname: req.session.lastname,
|
lastname: req.session.lastname,
|
||||||
@@ -42,6 +44,7 @@ function registerAdminRoutes(app) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Benutzer erstellen
|
// Benutzer erstellen
|
||||||
app.post('/admin/users', requireAdmin, (req, res) => {
|
app.post('/admin/users', requireAdmin, (req, res) => {
|
||||||
@@ -149,6 +152,71 @@ function registerAdminRoutes(app) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Optionen laden
|
||||||
|
app.get('/admin/options', requireAdmin, (req, res) => {
|
||||||
|
db.get('SELECT * FROM system_options WHERE id = 1', (err, options) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: 'Fehler beim Laden der Optionen' });
|
||||||
|
}
|
||||||
|
// Wenn keine Optionen vorhanden, Standardwerte zurückgeben
|
||||||
|
if (!options) {
|
||||||
|
return res.json({
|
||||||
|
config: {
|
||||||
|
saturday_percentage: 100,
|
||||||
|
sunday_percentage: 100
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
res.json({ config: options });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Optionen speichern
|
||||||
|
app.post('/admin/options', requireAdmin, (req, res) => {
|
||||||
|
const { saturday_percentage, sunday_percentage } = req.body;
|
||||||
|
|
||||||
|
// Validierung
|
||||||
|
const satPercent = parseFloat(saturday_percentage);
|
||||||
|
const sunPercent = parseFloat(sunday_percentage);
|
||||||
|
|
||||||
|
if (isNaN(satPercent) || isNaN(sunPercent)) {
|
||||||
|
return res.status(400).json({ error: 'Ungültige Prozentsätze' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (satPercent < 100 || satPercent > 200 || sunPercent < 100 || sunPercent > 200) {
|
||||||
|
return res.status(400).json({ error: 'Prozentsätze müssen zwischen 100 und 200 liegen' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfe ob Eintrag existiert
|
||||||
|
db.get('SELECT id FROM system_options WHERE id = 1', (err, existing) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: 'Fehler beim Prüfen der Optionen' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
// Update
|
||||||
|
db.run('UPDATE system_options SET saturday_percentage = ?, sunday_percentage = ?, updated_at = CURRENT_TIMESTAMP WHERE id = 1',
|
||||||
|
[satPercent, sunPercent],
|
||||||
|
(err) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: 'Fehler beim Speichern der Optionen' });
|
||||||
|
}
|
||||||
|
res.json({ success: true });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Insert
|
||||||
|
db.run('INSERT INTO system_options (id, saturday_percentage, sunday_percentage) VALUES (1, ?, ?)',
|
||||||
|
[satPercent, sunPercent],
|
||||||
|
(err) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: 'Fehler beim Speichern der Optionen' });
|
||||||
|
}
|
||||||
|
res.json({ success: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = registerAdminRoutes;
|
module.exports = registerAdminRoutes;
|
||||||
|
|||||||
@@ -27,6 +27,28 @@ function registerTimesheetRoutes(app) {
|
|||||||
// Normalisiere sick_status: Boolean oder 1/0 zu Boolean
|
// Normalisiere sick_status: Boolean oder 1/0 zu Boolean
|
||||||
const isSick = sick_status === true || sick_status === 1 || sick_status === 'true' || sick_status === '1';
|
const isSick = sick_status === true || sick_status === 1 || sick_status === 'true' || sick_status === '1';
|
||||||
|
|
||||||
|
// Wochenend-Prozentsätze laden
|
||||||
|
db.get('SELECT saturday_percentage, sunday_percentage FROM system_options WHERE id = 1', (err, options) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('Fehler beim Laden der Optionen:', err);
|
||||||
|
return res.status(500).json({ error: 'Fehler beim Laden der Optionen' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const saturdayPercentage = options?.saturday_percentage || 100;
|
||||||
|
const sundayPercentage = options?.sunday_percentage || 100;
|
||||||
|
|
||||||
|
// Hilfsfunktion: Prüft ob ein Datum ein Wochenendtag ist und gibt den Prozentsatz zurück
|
||||||
|
function getWeekendPercentage(dateStr) {
|
||||||
|
const date = new Date(dateStr);
|
||||||
|
const day = date.getDay();
|
||||||
|
if (day === 6) { // Samstag
|
||||||
|
return saturdayPercentage;
|
||||||
|
} else if (day === 0) { // Sonntag
|
||||||
|
return sundayPercentage;
|
||||||
|
}
|
||||||
|
return 100; // Kein Wochenende = 100% (normal)
|
||||||
|
}
|
||||||
|
|
||||||
// User-Daten laden (für Überstunden-Berechnung)
|
// User-Daten laden (für Überstunden-Berechnung)
|
||||||
db.get('SELECT wochenstunden FROM users WHERE id = ?', [userId], (err, user) => {
|
db.get('SELECT wochenstunden FROM users WHERE id = ?', [userId], (err, user) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -73,6 +95,11 @@ function registerTimesheetRoutes(app) {
|
|||||||
const end = new Date(`2000-01-01T${normalizedEndTime}`);
|
const end = new Date(`2000-01-01T${normalizedEndTime}`);
|
||||||
const diffMs = end - start;
|
const diffMs = end - start;
|
||||||
total_hours = (diffMs / (1000 * 60 * 60)) - (break_minutes / 60);
|
total_hours = (diffMs / (1000 * 60 * 60)) - (break_minutes / 60);
|
||||||
|
// Wochenend-Prozentsatz anwenden (nur auf tatsächlich gearbeitete Stunden, nicht auf Urlaub/Krankheit)
|
||||||
|
const weekendPercentage = getWeekendPercentage(date);
|
||||||
|
if (weekendPercentage >= 100 && total_hours > 0 && !isSick && vacation_type !== 'full') {
|
||||||
|
total_hours = total_hours * (weekendPercentage / 100);
|
||||||
|
}
|
||||||
// Bei halbem Tag Urlaub: total_hours bleibt die tatsächlich gearbeiteten Stunden
|
// Bei halbem Tag Urlaub: total_hours bleibt die tatsächlich gearbeiteten Stunden
|
||||||
// Die 4 Stunden Urlaub werden nur in der Überstunden-Berechnung hinzugezählt
|
// Die 4 Stunden Urlaub werden nur in der Überstunden-Berechnung hinzugezählt
|
||||||
}
|
}
|
||||||
@@ -147,6 +174,7 @@ function registerTimesheetRoutes(app) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// API: Feiertage für einen Zeitraum (Dashboard-Anzeige)
|
// API: Feiertage für einen Zeitraum (Dashboard-Anzeige)
|
||||||
app.get('/api/timesheet/holidays', requireAuth, (req, res) => {
|
app.get('/api/timesheet/holidays', requireAuth, (req, res) => {
|
||||||
|
|||||||
@@ -39,6 +39,20 @@ function registerUserRoutes(app) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// API: Wochenend-Prozentsätze abrufen
|
||||||
|
app.get('/api/user/weekend-percentages', requireAuth, (req, res) => {
|
||||||
|
db.get('SELECT saturday_percentage, sunday_percentage FROM system_options WHERE id = 1', (err, options) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: 'Fehler beim Abrufen der Wochenend-Prozentsätze' });
|
||||||
|
}
|
||||||
|
// Wenn keine Optionen vorhanden, Standardwerte zurückgeben
|
||||||
|
res.json({
|
||||||
|
saturday_percentage: options?.saturday_percentage || 100,
|
||||||
|
sunday_percentage: options?.sunday_percentage || 100
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// API: User-Daten abrufen (Wochenstunden)
|
// API: User-Daten abrufen (Wochenstunden)
|
||||||
app.get('/api/user/data', requireAuth, (req, res) => {
|
app.get('/api/user/data', requireAuth, (req, res) => {
|
||||||
const userId = req.session.userId;
|
const userId = req.session.userId;
|
||||||
@@ -187,6 +201,27 @@ function registerUserRoutes(app) {
|
|||||||
app.get('/api/user/stats', requireAuth, (req, res) => {
|
app.get('/api/user/stats', requireAuth, (req, res) => {
|
||||||
const userId = req.session.userId;
|
const userId = req.session.userId;
|
||||||
|
|
||||||
|
// Wochenend-Prozentsätze laden
|
||||||
|
db.get('SELECT saturday_percentage, sunday_percentage FROM system_options WHERE id = 1', (err, options) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: 'Fehler beim Laden der Optionen' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const saturdayPercentage = options?.saturday_percentage || 100;
|
||||||
|
const sundayPercentage = options?.sunday_percentage || 100;
|
||||||
|
|
||||||
|
// Hilfsfunktion: Prüft ob ein Datum ein Wochenendtag ist und gibt den Prozentsatz zurück
|
||||||
|
function getWeekendPercentage(dateStr) {
|
||||||
|
const date = new Date(dateStr);
|
||||||
|
const day = date.getDay();
|
||||||
|
if (day === 6) { // Samstag
|
||||||
|
return saturdayPercentage;
|
||||||
|
} else if (day === 0) { // Sonntag
|
||||||
|
return sundayPercentage;
|
||||||
|
}
|
||||||
|
return 100; // Kein Wochenende = 100% (normal)
|
||||||
|
}
|
||||||
|
|
||||||
// User-Daten abrufen
|
// User-Daten abrufen
|
||||||
db.get('SELECT wochenstunden, urlaubstage, overtime_offset_hours FROM users WHERE id = ?', [userId], (err, user) => {
|
db.get('SELECT wochenstunden, urlaubstage, overtime_offset_hours FROM users WHERE id = ?', [userId], (err, user) => {
|
||||||
if (err || !user) {
|
if (err || !user) {
|
||||||
@@ -397,12 +432,24 @@ function registerUserRoutes(app) {
|
|||||||
weekVacationHours += 4; // Halber Tag = 4 Stunden
|
weekVacationHours += 4; // Halber Tag = 4 Stunden
|
||||||
// Bei halbem Tag Urlaub können noch Arbeitsstunden vorhanden sein
|
// Bei halbem Tag Urlaub können noch Arbeitsstunden vorhanden sein
|
||||||
if (entry.total_hours && !isFullDayOvertime) {
|
if (entry.total_hours && !isFullDayOvertime) {
|
||||||
weekTotalHours += entry.total_hours;
|
let hours = entry.total_hours;
|
||||||
|
// Wochenend-Prozentsatz anwenden (nur auf tatsächlich gearbeitete Stunden)
|
||||||
|
const weekendPercentage = getWeekendPercentage(entry.date);
|
||||||
|
if (weekendPercentage >= 100 && hours > 0 && !entry.sick_status) {
|
||||||
|
hours = hours * (weekendPercentage / 100);
|
||||||
|
}
|
||||||
|
weekTotalHours += hours;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Kein Urlaub - zähle nur Arbeitsstunden (wenn nicht 8 Überstunden)
|
// Kein Urlaub - zähle nur Arbeitsstunden (wenn nicht 8 Überstunden)
|
||||||
if (entry.total_hours && !isFullDayOvertime) {
|
if (entry.total_hours && !isFullDayOvertime) {
|
||||||
weekTotalHours += entry.total_hours;
|
let hours = entry.total_hours;
|
||||||
|
// Wochenend-Prozentsatz anwenden (nur auf tatsächlich gearbeitete Stunden, nicht auf Krankheit)
|
||||||
|
const weekendPercentage = getWeekendPercentage(entry.date);
|
||||||
|
if (weekendPercentage > 0 && hours > 0 && !entry.sick_status) {
|
||||||
|
hours = hours * (1 + weekendPercentage / 100);
|
||||||
|
}
|
||||||
|
weekTotalHours += hours;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -456,13 +503,14 @@ function registerUserRoutes(app) {
|
|||||||
overtimeOffsetHours: overtimeOffsetHours
|
overtimeOffsetHours: overtimeOffsetHours
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}); // getHolidaysForDateRange.then
|
||||||
});
|
}); // db.all (allEntries)
|
||||||
});
|
}); // weeks.forEach
|
||||||
});
|
}); // db.all (weeks)
|
||||||
});
|
}); // db.all (allVacationEntries)
|
||||||
});
|
}); // db.get (user)
|
||||||
});
|
}); // db.get (options)
|
||||||
|
}); // app.get
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = registerUserRoutes;
|
module.exports = registerUserRoutes;
|
||||||
|
|||||||
@@ -142,6 +142,27 @@ function registerVerwaltungRoutes(app) {
|
|||||||
const userId = req.params.id;
|
const userId = req.params.id;
|
||||||
const { week_start, week_end } = req.query;
|
const { week_start, week_end } = req.query;
|
||||||
|
|
||||||
|
// Wochenend-Prozentsätze laden
|
||||||
|
db.get('SELECT saturday_percentage, sunday_percentage FROM system_options WHERE id = 1', (err, options) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: 'Fehler beim Laden der Optionen' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const saturdayPercentage = options?.saturday_percentage || 100;
|
||||||
|
const sundayPercentage = options?.sunday_percentage || 100;
|
||||||
|
|
||||||
|
// Hilfsfunktion: Prüft ob ein Datum ein Wochenendtag ist und gibt den Prozentsatz zurück
|
||||||
|
function getWeekendPercentage(dateStr) {
|
||||||
|
const date = new Date(dateStr);
|
||||||
|
const day = date.getDay();
|
||||||
|
if (day === 6) { // Samstag
|
||||||
|
return saturdayPercentage;
|
||||||
|
} else if (day === 0) { // Sonntag
|
||||||
|
return sundayPercentage;
|
||||||
|
}
|
||||||
|
return 100; // Kein Wochenende = 100% (normal)
|
||||||
|
}
|
||||||
|
|
||||||
// User-Daten abrufen
|
// User-Daten abrufen
|
||||||
db.get('SELECT wochenstunden, urlaubstage, overtime_offset_hours FROM users WHERE id = ?', [userId], (err, user) => {
|
db.get('SELECT wochenstunden, urlaubstage, overtime_offset_hours FROM users WHERE id = ?', [userId], (err, user) => {
|
||||||
if (err || !user) {
|
if (err || !user) {
|
||||||
@@ -190,12 +211,24 @@ function registerVerwaltungRoutes(app) {
|
|||||||
vacationHours += 4; // Halber Tag = 4 Stunden
|
vacationHours += 4; // Halber Tag = 4 Stunden
|
||||||
// Bei halbem Tag Urlaub können noch Arbeitsstunden vorhanden sein
|
// Bei halbem Tag Urlaub können noch Arbeitsstunden vorhanden sein
|
||||||
if (entry.total_hours) {
|
if (entry.total_hours) {
|
||||||
totalHours += entry.total_hours;
|
let hours = entry.total_hours;
|
||||||
|
// Wochenend-Prozentsatz anwenden (nur auf tatsächlich gearbeitete Stunden)
|
||||||
|
const weekendPercentage = getWeekendPercentage(entry.date);
|
||||||
|
if (weekendPercentage >= 100 && hours > 0 && !entry.sick_status) {
|
||||||
|
hours = hours * (weekendPercentage / 100);
|
||||||
|
}
|
||||||
|
totalHours += hours;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Kein Urlaub - zähle nur Arbeitsstunden
|
// Kein Urlaub - zähle nur Arbeitsstunden
|
||||||
if (entry.total_hours) {
|
if (entry.total_hours) {
|
||||||
totalHours += entry.total_hours;
|
let hours = entry.total_hours;
|
||||||
|
// Wochenend-Prozentsatz anwenden (nur auf tatsächlich gearbeitete Stunden, nicht auf Krankheit)
|
||||||
|
const weekendPercentage = getWeekendPercentage(entry.date);
|
||||||
|
if (weekendPercentage > 0 && hours > 0 && !entry.sick_status) {
|
||||||
|
hours = hours * (1 + weekendPercentage / 100);
|
||||||
|
}
|
||||||
|
totalHours += hours;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -249,6 +282,7 @@ function registerVerwaltungRoutes(app) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// API: Admin-Kommentar speichern
|
// API: Admin-Kommentar speichern
|
||||||
app.put('/api/verwaltung/timesheet/:id/comment', requireVerwaltung, (req, res) => {
|
app.put('/api/verwaltung/timesheet/:id/comment', requireVerwaltung, (req, res) => {
|
||||||
|
|||||||
@@ -203,6 +203,43 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="options-section" style="margin-top: 40px;">
|
||||||
|
<div class="collapsible-header" onclick="toggleOptionsSection()" style="cursor: pointer; padding: 15px; background-color: #f5f5f5; border: 1px solid #ddd; border-radius: 4px; display: flex; justify-content: space-between; align-items: center;">
|
||||||
|
<h2 style="margin: 0;">Optionen</h2>
|
||||||
|
<span id="optionsToggleIcon" style="font-size: 18px; transition: transform 0.3s;">▼</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="optionsContent" style="display: none; padding: 20px; border: 1px solid #ddd; border-top: none; border-radius: 0 0 4px 4px; background-color: #fff;">
|
||||||
|
<div class="options-form">
|
||||||
|
<h3>Wochenend-Prozentsätze</h3>
|
||||||
|
<p style="margin-bottom: 20px; color: #666;">Konfigurieren Sie die Prozentsätze für die Wochenendstunden. 100% entspricht normal, 150% entspricht 1,5 mal, 200% entspricht doppelt.</p>
|
||||||
|
<form id="optionsForm">
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="saturdayPercentage">Samstag-Prozentsatz</label>
|
||||||
|
<select id="saturdayPercentage" name="saturday_percentage" class="form-control" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
|
||||||
|
<% for (let i = 100; i <= 200; i += 5) { %>
|
||||||
|
<option value="<%= i %>" <%= (typeof options !== 'undefined' && options && options.saturday_percentage == i) ? 'selected' : '' %>><%= i %>%</option>
|
||||||
|
<% } %>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="sundayPercentage">Sonntag-Prozentsatz</label>
|
||||||
|
<select id="sundayPercentage" name="sunday_percentage" class="form-control" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
|
||||||
|
<% for (let i = 100; i <= 200; i += 5) { %>
|
||||||
|
<option value="<%= i %>" <%= (typeof options !== 'undefined' && options && options.sunday_percentage == i) ? 'selected' : '' %>><%= i %>%</option>
|
||||||
|
<% } %>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">Optionen speichern</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="ldap-sync-section" style="margin-top: 40px;">
|
<div class="ldap-sync-section" style="margin-top: 40px;">
|
||||||
<div class="collapsible-header" onclick="toggleLDAPSection()" style="cursor: pointer; padding: 15px; background-color: #f5f5f5; border: 1px solid #ddd; border-radius: 4px; display: flex; justify-content: space-between; align-items: center;">
|
<div class="collapsible-header" onclick="toggleLDAPSection()" style="cursor: pointer; padding: 15px; background-color: #f5f5f5; border: 1px solid #ddd; border-radius: 4px; display: flex; justify-content: space-between; align-items: center;">
|
||||||
<h2 style="margin: 0;">LDAP-Synchronisation</h2>
|
<h2 style="margin: 0;">LDAP-Synchronisation</h2>
|
||||||
@@ -375,6 +412,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Optionen-Sektion ein-/ausklappen
|
||||||
|
function toggleOptionsSection() {
|
||||||
|
const content = document.getElementById('optionsContent');
|
||||||
|
const icon = document.getElementById('optionsToggleIcon');
|
||||||
|
|
||||||
|
if (content.style.display === 'none') {
|
||||||
|
content.style.display = 'block';
|
||||||
|
icon.style.transform = 'rotate(180deg)';
|
||||||
|
} else {
|
||||||
|
content.style.display = 'none';
|
||||||
|
icon.style.transform = 'rotate(0deg)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Rollenwechsel-Handler
|
// Rollenwechsel-Handler
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const roleSwitcher = document.getElementById('roleSwitcher');
|
const roleSwitcher = document.getElementById('roleSwitcher');
|
||||||
|
|||||||
Reference in New Issue
Block a user