Umstellung auf anzeige x h y min
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { db } = require('./database');
|
const { db } = require('./database');
|
||||||
const { getCurrentDate, getCurrentTime, updateTotalHours } = require('./helpers/utils');
|
const { getCurrentDate, getCurrentTime, updateTotalHours, formatHoursMin } = require('./helpers/utils');
|
||||||
|
|
||||||
const checkinApp = express();
|
const checkinApp = express();
|
||||||
const CHECKIN_PORT = 3334;
|
const CHECKIN_PORT = 3334;
|
||||||
@@ -139,7 +139,7 @@ checkinApp.get('/api/checkout/:userId', (req, res) => {
|
|||||||
return sendResponse(req, res, false, { error: 'Fehler beim Aktualisieren', status: 500 });
|
return sendResponse(req, res, false, { error: 'Fehler beim Aktualisieren', status: 500 });
|
||||||
}
|
}
|
||||||
const successTitle = 'Schönen Feierabend, du wurdest erfolgreich ausgecheckt';
|
const successTitle = 'Schönen Feierabend, du wurdest erfolgreich ausgecheckt';
|
||||||
const successMessage = `End-Zeit erfasst: ${currentTime}. Gesamtstunden: ${totalHours.toFixed(2)} h`;
|
const successMessage = `End-Zeit erfasst: ${currentTime}. Gesamtstunden: ${formatHoursMin(totalHours)}`;
|
||||||
sendResponse(req, res, true, {
|
sendResponse(req, res, true, {
|
||||||
title: successTitle,
|
title: successTitle,
|
||||||
message: successMessage,
|
message: successMessage,
|
||||||
|
|||||||
@@ -132,6 +132,22 @@ function getWeekStart(dateStr) {
|
|||||||
return `${year}-${month}-${dayOfMonth}`;
|
return `${year}-${month}-${dayOfMonth}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper: Dezimalstunden in Anzeige "X h Y min" (z. B. 8.25 -> "8 h 15 min", -1.5 -> "-1 h 30 min")
|
||||||
|
function formatHoursMin(decimalHours) {
|
||||||
|
if (decimalHours == null || !Number.isFinite(Number(decimalHours))) return '0 h 0 min';
|
||||||
|
const n = Number(decimalHours);
|
||||||
|
const sign = n < 0 ? -1 : 1;
|
||||||
|
const absVal = Math.abs(n);
|
||||||
|
let h = Math.floor(absVal);
|
||||||
|
let min = Math.round((absVal - h) * 60);
|
||||||
|
if (min >= 60) {
|
||||||
|
h += 1;
|
||||||
|
min = 0;
|
||||||
|
}
|
||||||
|
const prefix = sign < 0 ? '-' : '';
|
||||||
|
return prefix + h + ' h ' + min + ' min';
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
hasRole,
|
hasRole,
|
||||||
getDefaultRole,
|
getDefaultRole,
|
||||||
@@ -143,5 +159,6 @@ module.exports = {
|
|||||||
formatDateTime,
|
formatDateTime,
|
||||||
getCalendarWeek,
|
getCalendarWeek,
|
||||||
getWeekDatesFromCalendarWeek,
|
getWeekDatesFromCalendarWeek,
|
||||||
getWeekStart
|
getWeekStart,
|
||||||
|
formatHoursMin
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ async function loadUserStats() {
|
|||||||
const currentOvertimeEl = document.getElementById('currentOvertime');
|
const currentOvertimeEl = document.getElementById('currentOvertime');
|
||||||
if (currentOvertimeEl) {
|
if (currentOvertimeEl) {
|
||||||
const overtime = stats.currentOvertime || 0;
|
const overtime = stats.currentOvertime || 0;
|
||||||
currentOvertimeEl.textContent = overtime >= 0 ? `+${overtime.toFixed(2)}` : overtime.toFixed(2);
|
currentOvertimeEl.textContent = (overtime >= 0 ? '+' : '') + formatHoursMin(overtime);
|
||||||
currentOvertimeEl.style.color = overtime >= 0 ? '#27ae60' : '#e74c3c';
|
currentOvertimeEl.style.color = overtime >= 0 ? '#27ae60' : '#e74c3c';
|
||||||
// Auch die Border-Farbe des Cards anpassen
|
// Auch die Border-Farbe des Cards anpassen
|
||||||
const overtimeCard = currentOvertimeEl.closest('.stat-card');
|
const overtimeCard = currentOvertimeEl.closest('.stat-card');
|
||||||
@@ -548,27 +548,27 @@ function renderWeek() {
|
|||||||
// Stunden-Anzeige für halben Tag Urlaub berechnen
|
// Stunden-Anzeige für halben Tag Urlaub berechnen
|
||||||
let hoursDisplay = '';
|
let hoursDisplay = '';
|
||||||
if (isFullDayVacation) {
|
if (isFullDayVacation) {
|
||||||
hoursDisplay = fullDayHours.toFixed(2) + ' h (Urlaub)';
|
hoursDisplay = formatHoursMin(fullDayHours) + ' (Urlaub)';
|
||||||
} else if (isHalfDayVacation) {
|
} else if (isHalfDayVacation) {
|
||||||
const halfHours = fullDayHours / 2;
|
const halfHours = fullDayHours / 2;
|
||||||
const workHours = hours || 0; // Das sind die gearbeiteten Stunden (ohne Urlaub)
|
const workHours = hours || 0; // Das sind die gearbeiteten Stunden (ohne Urlaub)
|
||||||
const totalHours = halfHours + workHours;
|
const totalHours = halfHours + workHours;
|
||||||
if (workHours > 0.01) {
|
if (workHours > 0.01) {
|
||||||
hoursDisplay = totalHours.toFixed(2) + ' h (' + halfHours.toFixed(2) + ' h Urlaub + ' + workHours.toFixed(2) + ' h)';
|
hoursDisplay = formatHoursMin(totalHours) + ' (' + formatHoursMin(halfHours) + ' Urlaub + ' + formatHoursMin(workHours) + ')';
|
||||||
} else {
|
} else {
|
||||||
hoursDisplay = halfHours.toFixed(2) + ' h (Urlaub)';
|
hoursDisplay = formatHoursMin(halfHours) + ' (Urlaub)';
|
||||||
}
|
}
|
||||||
} else if (isSick) {
|
} else if (isSick) {
|
||||||
hoursDisplay = fullDayHours.toFixed(2) + ' h (Krank)';
|
hoursDisplay = formatHoursMin(fullDayHours) + ' (Krank)';
|
||||||
} else if (isHoliday && isWeekend) {
|
} else if (isHoliday && isWeekend) {
|
||||||
// Feiertag am Wochenende: keine Tagesarbeitsstunden
|
// Feiertag am Wochenende: keine Tagesarbeitsstunden
|
||||||
hoursDisplay = (hours ? hours.toFixed(2) : '0') + ' h (Feiertag)';
|
hoursDisplay = formatHoursMin(hours || 0) + ' (Feiertag)';
|
||||||
} else if (isHoliday && !hours) {
|
} else if (isHoliday && !hours) {
|
||||||
hoursDisplay = fullDayHours.toFixed(2) + ' h (Feiertag)';
|
hoursDisplay = formatHoursMin(fullDayHours) + ' (Feiertag)';
|
||||||
} else if (isHoliday && hours) {
|
} else if (isHoliday && hours) {
|
||||||
hoursDisplay = fullDayHours.toFixed(2) + ' + ' + hours.toFixed(2) + ' h (Überst.)';
|
hoursDisplay = formatHoursMin(fullDayHours) + ' + ' + formatHoursMin(hours) + ' (Überst.)';
|
||||||
} else {
|
} else {
|
||||||
hoursDisplay = hours.toFixed(2) + ' h';
|
hoursDisplay = formatHoursMin(hours);
|
||||||
}
|
}
|
||||||
|
|
||||||
const requiredBreak = (startTime && endTime) ? calculateRequiredBreakMinutes(startTime, endTime) : null;
|
const requiredBreak = (startTime && endTime) ? calculateRequiredBreakMinutes(startTime, endTime) : null;
|
||||||
@@ -741,7 +741,7 @@ function renderWeek() {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
document.getElementById('timesheetTable').innerHTML = html;
|
document.getElementById('timesheetTable').innerHTML = html;
|
||||||
document.getElementById('totalHours').textContent = totalHours.toFixed(2) + ' h';
|
document.getElementById('totalHours').textContent = formatHoursMin(totalHours);
|
||||||
|
|
||||||
// Überstunden-Berechnung (startDate und endDate sind bereits oben deklariert)
|
// Überstunden-Berechnung (startDate und endDate sind bereits oben deklariert)
|
||||||
|
|
||||||
@@ -1017,14 +1017,14 @@ function updateOvertimeDisplay() {
|
|||||||
if (overtimeSummaryItem && overtimeHoursSpan) {
|
if (overtimeSummaryItem && overtimeHoursSpan) {
|
||||||
overtimeSummaryItem.style.display = 'block';
|
overtimeSummaryItem.style.display = 'block';
|
||||||
const sign = overtimeHours >= 0 ? '+' : '';
|
const sign = overtimeHours >= 0 ? '+' : '';
|
||||||
overtimeHoursSpan.textContent = `${sign}${overtimeHours.toFixed(2)} h`;
|
overtimeHoursSpan.textContent = (sign === '+' ? '+' : '') + formatHoursMin(overtimeHours);
|
||||||
overtimeHoursSpan.style.color = overtimeHours >= 0 ? '#27ae60' : '#e74c3c';
|
overtimeHoursSpan.style.color = overtimeHours >= 0 ? '#27ae60' : '#e74c3c';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gesamtstunden-Anzeige aktualisieren
|
// Gesamtstunden-Anzeige aktualisieren
|
||||||
const totalHoursElement = document.getElementById('totalHours');
|
const totalHoursElement = document.getElementById('totalHours');
|
||||||
if (totalHoursElement) {
|
if (totalHoursElement) {
|
||||||
totalHoursElement.textContent = totalHoursWithVacation.toFixed(2) + ' h';
|
totalHoursElement.textContent = formatHoursMin(totalHoursWithVacation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1272,7 +1272,7 @@ async function saveEntry(input) {
|
|||||||
if (hoursElement) {
|
if (hoursElement) {
|
||||||
if (isFullDayVacation) {
|
if (isFullDayVacation) {
|
||||||
// Ganzer Tag Urlaub: Zeige fullDayHours mit "(Urlaub)" Label
|
// Ganzer Tag Urlaub: Zeige fullDayHours mit "(Urlaub)" Label
|
||||||
hoursElement.textContent = fullDayHours.toFixed(2) + ' h (Urlaub)';
|
hoursElement.textContent = formatHoursMin(fullDayHours) + ' (Urlaub)';
|
||||||
currentEntries[date].total_hours = fullDayHours;
|
currentEntries[date].total_hours = fullDayHours;
|
||||||
} else if (isHalfDayVacation) {
|
} else if (isHalfDayVacation) {
|
||||||
// Halber Tag Urlaub: Berechne Stunden aus Start/Ende falls vorhanden
|
// Halber Tag Urlaub: Berechne Stunden aus Start/Ende falls vorhanden
|
||||||
@@ -1293,9 +1293,9 @@ async function saveEntry(input) {
|
|||||||
|
|
||||||
const totalHours = halfHours + workHours;
|
const totalHours = halfHours + workHours;
|
||||||
if (workHours > 0) {
|
if (workHours > 0) {
|
||||||
hoursElement.textContent = totalHours.toFixed(2) + ' h (' + halfHours.toFixed(2) + ' h Urlaub + ' + workHours.toFixed(2) + ' h)';
|
hoursElement.textContent = formatHoursMin(totalHours) + ' (' + formatHoursMin(halfHours) + ' Urlaub + ' + formatHoursMin(workHours) + ')';
|
||||||
} else {
|
} else {
|
||||||
hoursElement.textContent = halfHours.toFixed(2) + ' h (Urlaub)';
|
hoursElement.textContent = formatHoursMin(halfHours) + ' (Urlaub)';
|
||||||
}
|
}
|
||||||
currentEntries[date].total_hours = totalHours;
|
currentEntries[date].total_hours = totalHours;
|
||||||
} else {
|
} else {
|
||||||
@@ -1303,15 +1303,15 @@ async function saveEntry(input) {
|
|||||||
const d = new Date(date);
|
const d = new Date(date);
|
||||||
const isWeekendHoliday = isHoliday && (d.getDay() === 6 || d.getDay() === 0);
|
const isWeekendHoliday = isHoliday && (d.getDay() === 6 || d.getDay() === 0);
|
||||||
if (isSick) {
|
if (isSick) {
|
||||||
hoursElement.textContent = fullDayHours.toFixed(2) + ' h (Krank)';
|
hoursElement.textContent = formatHoursMin(fullDayHours) + ' (Krank)';
|
||||||
} else if (isWeekendHoliday) {
|
} else if (isWeekendHoliday) {
|
||||||
hoursElement.textContent = (hours ? hours.toFixed(2) : '0') + ' h (Feiertag)';
|
hoursElement.textContent = formatHoursMin(hours || 0) + ' (Feiertag)';
|
||||||
} else if (isHoliday && !hours) {
|
} else if (isHoliday && !hours) {
|
||||||
hoursElement.textContent = fullDayHours.toFixed(2) + ' h (Feiertag)';
|
hoursElement.textContent = formatHoursMin(fullDayHours) + ' (Feiertag)';
|
||||||
} else if (isHoliday && hours) {
|
} else if (isHoliday && hours) {
|
||||||
hoursElement.textContent = fullDayHours.toFixed(2) + ' + ' + hours.toFixed(2) + ' h (Überst.)';
|
hoursElement.textContent = formatHoursMin(fullDayHours) + ' + ' + formatHoursMin(hours) + ' (Überst.)';
|
||||||
} else {
|
} else {
|
||||||
hoursElement.textContent = hours.toFixed(2) + ' h';
|
hoursElement.textContent = formatHoursMin(hours);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1446,10 +1446,10 @@ async function saveEntry(input) {
|
|||||||
const isHalfDayVacation = vacationType === 'half';
|
const isHalfDayVacation = vacationType === 'half';
|
||||||
const fullDayHours = getFullDayHours();
|
const fullDayHours = getFullDayHours();
|
||||||
|
|
||||||
let hoursText = result.total_hours.toFixed(2) + ' h';
|
let hoursText = formatHoursMin(result.total_hours);
|
||||||
|
|
||||||
if (isFullDayVacation) {
|
if (isFullDayVacation) {
|
||||||
hoursText = fullDayHours.toFixed(2) + ' h (Urlaub)';
|
hoursText = formatHoursMin(fullDayHours) + ' (Urlaub)';
|
||||||
} else if (isHalfDayVacation) {
|
} else if (isHalfDayVacation) {
|
||||||
// Bei halbem Tag Urlaub: result.total_hours enthält nur die gearbeiteten Stunden
|
// Bei halbem Tag Urlaub: result.total_hours enthält nur die gearbeiteten Stunden
|
||||||
// Die Urlaubsstunden müssen addiert werden
|
// Die Urlaubsstunden müssen addiert werden
|
||||||
@@ -1458,25 +1458,25 @@ async function saveEntry(input) {
|
|||||||
const totalHours = halfHours + workHours; // Gesamt = Urlaub + gearbeitet
|
const totalHours = halfHours + workHours; // Gesamt = Urlaub + gearbeitet
|
||||||
|
|
||||||
if (workHours > 0.01) {
|
if (workHours > 0.01) {
|
||||||
hoursText = totalHours.toFixed(2) + ' h (' + halfHours.toFixed(2) + ' h Urlaub + ' + workHours.toFixed(2) + ' h)';
|
hoursText = formatHoursMin(totalHours) + ' (' + formatHoursMin(halfHours) + ' Urlaub + ' + formatHoursMin(workHours) + ')';
|
||||||
} else {
|
} else {
|
||||||
hoursText = halfHours.toFixed(2) + ' h (Urlaub)';
|
hoursText = formatHoursMin(halfHours) + ' (Urlaub)';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aktualisiere currentEntries mit den Gesamtstunden
|
// Aktualisiere currentEntries mit den Gesamtstunden
|
||||||
currentEntries[date].total_hours = totalHours;
|
currentEntries[date].total_hours = totalHours;
|
||||||
} else if (isSick) {
|
} else if (isSick) {
|
||||||
hoursText = fullDayHours.toFixed(2) + ' h (Krank)';
|
hoursText = formatHoursMin(fullDayHours) + ' (Krank)';
|
||||||
} else if (isHoliday) {
|
} else if (isHoliday) {
|
||||||
const d = new Date(date);
|
const d = new Date(date);
|
||||||
const isWeekendHoliday = (d.getDay() === 6 || d.getDay() === 0);
|
const isWeekendHoliday = (d.getDay() === 6 || d.getDay() === 0);
|
||||||
if (isWeekendHoliday) {
|
if (isWeekendHoliday) {
|
||||||
hoursText = (result.total_hours || 0).toFixed(2) + ' h (Feiertag)';
|
hoursText = formatHoursMin(result.total_hours || 0) + ' (Feiertag)';
|
||||||
} else if (result.total_hours <= fullDayHours) {
|
} else if (result.total_hours <= fullDayHours) {
|
||||||
hoursText = fullDayHours.toFixed(2) + ' h (Feiertag)';
|
hoursText = formatHoursMin(fullDayHours) + ' (Feiertag)';
|
||||||
} else {
|
} else {
|
||||||
const overtime = result.total_hours - fullDayHours;
|
const overtime = result.total_hours - fullDayHours;
|
||||||
hoursText = fullDayHours.toFixed(2) + ' + ' + overtime.toFixed(2) + ' h (Überst.)';
|
hoursText = formatHoursMin(fullDayHours) + ' + ' + formatHoursMin(overtime) + ' (Überst.)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1492,7 +1492,7 @@ async function saveEntry(input) {
|
|||||||
Object.values(currentEntries).forEach(e => {
|
Object.values(currentEntries).forEach(e => {
|
||||||
totalHours += e.total_hours || 0;
|
totalHours += e.total_hours || 0;
|
||||||
});
|
});
|
||||||
document.getElementById('totalHours').textContent = totalHours.toFixed(2) + ' h';
|
document.getElementById('totalHours').textContent = formatHoursMin(totalHours);
|
||||||
|
|
||||||
// Überstunden-Anzeige aktualisieren (bei jeder Änderung)
|
// Überstunden-Anzeige aktualisieren (bei jeder Änderung)
|
||||||
updateOvertimeDisplay();
|
updateOvertimeDisplay();
|
||||||
@@ -1591,7 +1591,7 @@ function checkWeekComplete() {
|
|||||||
if (overtimeValue > fullDayHours) {
|
if (overtimeValue > fullDayHours) {
|
||||||
if (!startTime || !endTime || startTime === '' || endTime === '') {
|
if (!startTime || !endTime || startTime === '' || endTime === '') {
|
||||||
allWeekdaysFilled = false;
|
allWeekdaysFilled = false;
|
||||||
missingFields.push(formatDateDE(dateStr) + ' (bei Überstunden > ' + fullDayHours.toFixed(2) + 'h müssen Start/Ende vorhanden sein)');
|
missingFields.push(formatDateDE(dateStr) + ' (bei Überstunden > ' + formatHoursMin(fullDayHours) + ' müssen Start/Ende vorhanden sein)');
|
||||||
continue; // Weiter zum nächsten Tag
|
continue; // Weiter zum nächsten Tag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2077,22 +2077,22 @@ function toggleSickStatus(dateStr) {
|
|||||||
|
|
||||||
if (newStatus) {
|
if (newStatus) {
|
||||||
// Krank: Zeige fullDayHours mit "(Krank)" Label
|
// Krank: Zeige fullDayHours mit "(Krank)" Label
|
||||||
hoursElement.textContent = fullDayHours.toFixed(2) + ' h (Krank)';
|
hoursElement.textContent = formatHoursMin(fullDayHours) + ' (Krank)';
|
||||||
currentEntries[dateStr].total_hours = fullDayHours;
|
currentEntries[dateStr].total_hours = fullDayHours;
|
||||||
} else {
|
} else {
|
||||||
// Zurück zu normaler Anzeige basierend auf anderen Status
|
// Zurück zu normaler Anzeige basierend auf anderen Status
|
||||||
const d = new Date(dateStr);
|
const d = new Date(dateStr);
|
||||||
const isWeekendHoliday = isHoliday && (d.getDay() === 6 || d.getDay() === 0);
|
const isWeekendHoliday = isHoliday && (d.getDay() === 6 || d.getDay() === 0);
|
||||||
if (isFullDayVacation) {
|
if (isFullDayVacation) {
|
||||||
hoursElement.textContent = fullDayHours.toFixed(2) + ' h (Urlaub)';
|
hoursElement.textContent = formatHoursMin(fullDayHours) + ' (Urlaub)';
|
||||||
} else if (isWeekendHoliday) {
|
} else if (isWeekendHoliday) {
|
||||||
hoursElement.textContent = (hours ? hours.toFixed(2) : '0') + ' h (Feiertag)';
|
hoursElement.textContent = formatHoursMin(hours || 0) + ' (Feiertag)';
|
||||||
} else if (isHoliday && !hours) {
|
} else if (isHoliday && !hours) {
|
||||||
hoursElement.textContent = fullDayHours.toFixed(2) + ' h (Feiertag)';
|
hoursElement.textContent = formatHoursMin(fullDayHours) + ' (Feiertag)';
|
||||||
} else if (isHoliday && hours) {
|
} else if (isHoliday && hours) {
|
||||||
hoursElement.textContent = fullDayHours.toFixed(2) + ' + ' + hours.toFixed(2) + ' h (Überst.)';
|
hoursElement.textContent = formatHoursMin(fullDayHours) + ' + ' + formatHoursMin(hours) + ' (Überst.)';
|
||||||
} else {
|
} else {
|
||||||
hoursElement.textContent = hours.toFixed(2) + ' h';
|
hoursElement.textContent = formatHoursMin(hours);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19
public/js/format-hours.js
Normal file
19
public/js/format-hours.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// Gleiche Logik wie helpers/utils.js formatHoursMin – für Browser (Dashboard, EJS-Seiten).
|
||||||
|
// Wird global als window.formatHoursMin bereitgestellt.
|
||||||
|
(function () {
|
||||||
|
function formatHoursMin(decimalHours) {
|
||||||
|
if (decimalHours == null || !Number.isFinite(Number(decimalHours))) return '0 h 0 min';
|
||||||
|
var n = Number(decimalHours);
|
||||||
|
var sign = n < 0 ? -1 : 1;
|
||||||
|
var absVal = Math.abs(n);
|
||||||
|
var h = Math.floor(absVal);
|
||||||
|
var min = Math.round((absVal - h) * 60);
|
||||||
|
if (min >= 60) {
|
||||||
|
h += 1;
|
||||||
|
min = 0;
|
||||||
|
}
|
||||||
|
var prefix = sign < 0 ? '-' : '';
|
||||||
|
return prefix + h + ' h ' + min + ' min';
|
||||||
|
}
|
||||||
|
window.formatHoursMin = formatHoursMin;
|
||||||
|
})();
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
const PDFDocument = require('pdfkit');
|
const PDFDocument = require('pdfkit');
|
||||||
const QRCode = require('qrcode');
|
const QRCode = require('qrcode');
|
||||||
const { db } = require('../database');
|
const { db } = require('../database');
|
||||||
const { formatDate, formatDateTime } = require('../helpers/utils');
|
const { formatDate, formatDateTime, formatHoursMin } = require('../helpers/utils');
|
||||||
const { getHolidaysWithNamesForDateRange } = require('./feiertage-service');
|
const { getHolidaysWithNamesForDateRange } = require('./feiertage-service');
|
||||||
|
|
||||||
// Kalenderwoche berechnen
|
// Kalenderwoche berechnen
|
||||||
@@ -190,7 +190,7 @@ function generatePDF(timesheetId, req, res) {
|
|||||||
// Feiertag am Wochenende: keine Tagesarbeitsstunden anzeigen
|
// Feiertag am Wochenende: keine Tagesarbeitsstunden anzeigen
|
||||||
const holidayDay = new Date(row.date + 'T12:00:00').getDay();
|
const holidayDay = new Date(row.date + 'T12:00:00').getDay();
|
||||||
const isWeekendHoliday = (holidayDay === 0 || holidayDay === 6);
|
const isWeekendHoliday = (holidayDay === 0 || holidayDay === 6);
|
||||||
const holidayHoursStr = isWeekendHoliday ? '0 h (Feiertag)' : fullDayHours.toFixed(2) + ' h (Feiertag)';
|
const holidayHoursStr = isWeekendHoliday ? '0 h 0 min (Feiertag)' : formatHoursMin(fullDayHours) + ' (Feiertag)';
|
||||||
const rowData = [formatDate(row.date), '-', '-', '-', holidayHoursStr];
|
const rowData = [formatDate(row.date), '-', '-', '-', holidayHoursStr];
|
||||||
rowData.forEach((data, i) => {
|
rowData.forEach((data, i) => {
|
||||||
doc.text(data, x, y, { width: colWidths[i], align: 'left' });
|
doc.text(data, x, y, { width: colWidths[i], align: 'left' });
|
||||||
@@ -215,7 +215,7 @@ function generatePDF(timesheetId, req, res) {
|
|||||||
entry.start_time || '-',
|
entry.start_time || '-',
|
||||||
entry.end_time || '-',
|
entry.end_time || '-',
|
||||||
entry.break_minutes ? `${entry.break_minutes} min` : '-',
|
entry.break_minutes ? `${entry.break_minutes} min` : '-',
|
||||||
entry.total_hours ? entry.total_hours.toFixed(2) + ' h' : '-'
|
entry.total_hours ? formatHoursMin(entry.total_hours) : '-'
|
||||||
];
|
];
|
||||||
|
|
||||||
rowData.forEach((data, i) => {
|
rowData.forEach((data, i) => {
|
||||||
@@ -250,7 +250,7 @@ function generatePDF(timesheetId, req, res) {
|
|||||||
if (activity.projectNumber) {
|
if (activity.projectNumber) {
|
||||||
activityText += ` (Projekt: ${activity.projectNumber})`;
|
activityText += ` (Projekt: ${activity.projectNumber})`;
|
||||||
}
|
}
|
||||||
activityText += ` - ${activity.hours.toFixed(2)} h`;
|
activityText += ` - ${formatHoursMin(activity.hours)}`;
|
||||||
doc.fontSize(9).font('Helvetica');
|
doc.fontSize(9).font('Helvetica');
|
||||||
doc.text(activityText, 70, doc.y, { width: 360 });
|
doc.text(activityText, 70, doc.y, { width: 360 });
|
||||||
doc.moveDown(0.2);
|
doc.moveDown(0.2);
|
||||||
@@ -264,7 +264,7 @@ function generatePDF(timesheetId, req, res) {
|
|||||||
overtimeInfo.push('Feiertag: ' + (holidayNames.get(entry.date) || 'Feiertag'));
|
overtimeInfo.push('Feiertag: ' + (holidayNames.get(entry.date) || 'Feiertag'));
|
||||||
}
|
}
|
||||||
if (entry.overtime_taken_hours && parseFloat(entry.overtime_taken_hours) > 0) {
|
if (entry.overtime_taken_hours && parseFloat(entry.overtime_taken_hours) > 0) {
|
||||||
overtimeInfo.push(`Überstunden genommen: ${parseFloat(entry.overtime_taken_hours).toFixed(2)} h`);
|
overtimeInfo.push(`Überstunden genommen: ${formatHoursMin(parseFloat(entry.overtime_taken_hours))}`);
|
||||||
}
|
}
|
||||||
if (entry.vacation_type) {
|
if (entry.vacation_type) {
|
||||||
const vacationText = entry.vacation_type === 'full' ? 'Ganzer Tag' : 'Halber Tag';
|
const vacationText = entry.vacation_type === 'full' ? 'Ganzer Tag' : 'Halber Tag';
|
||||||
@@ -308,7 +308,7 @@ function generatePDF(timesheetId, req, res) {
|
|||||||
doc.font('Helvetica-Bold');
|
doc.font('Helvetica-Bold');
|
||||||
// Gesamtstunden = Arbeitsstunden + Urlaubsstunden + Feiertagsstunden (8h pro Feiertag)
|
// Gesamtstunden = Arbeitsstunden + Urlaubsstunden + Feiertagsstunden (8h pro Feiertag)
|
||||||
const totalHoursWithVacation = totalHours + vacationHours + holidayHours;
|
const totalHoursWithVacation = totalHours + vacationHours + holidayHours;
|
||||||
doc.text(`Gesamtstunden: ${totalHoursWithVacation.toFixed(2)} h`, 50, doc.y);
|
doc.text(`Gesamtstunden: ${formatHoursMin(totalHoursWithVacation)}`, 50, doc.y);
|
||||||
|
|
||||||
// Überstunden berechnen und anzeigen
|
// Überstunden berechnen und anzeigen
|
||||||
const wochenstunden = timesheet.wochenstunden || 0;
|
const wochenstunden = timesheet.wochenstunden || 0;
|
||||||
@@ -318,11 +318,11 @@ function generatePDF(timesheetId, req, res) {
|
|||||||
doc.moveDown(0.3);
|
doc.moveDown(0.3);
|
||||||
doc.font('Helvetica-Bold');
|
doc.font('Helvetica-Bold');
|
||||||
if (overtimeHours > 0) {
|
if (overtimeHours > 0) {
|
||||||
doc.text(`Überstunden: +${overtimeHours.toFixed(2)} h`, 50, doc.y);
|
doc.text(`Überstunden: +${formatHoursMin(overtimeHours)}`, 50, doc.y);
|
||||||
} else if (overtimeHours < 0) {
|
} else if (overtimeHours < 0) {
|
||||||
doc.text(`Überstunden: ${overtimeHours.toFixed(2)} h`, 50, doc.y);
|
doc.text(`Überstunden: ${formatHoursMin(overtimeHours)}`, 50, doc.y);
|
||||||
} else {
|
} else {
|
||||||
doc.text(`Überstunden: 0.00 h`, 50, doc.y);
|
doc.text(`Überstunden: ${formatHoursMin(0)}`, 50, doc.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
doc.end();
|
doc.end();
|
||||||
@@ -462,7 +462,7 @@ function generatePDFToBuffer(timesheetId, req) {
|
|||||||
// Feiertag am Wochenende: keine Tagesarbeitsstunden anzeigen
|
// Feiertag am Wochenende: keine Tagesarbeitsstunden anzeigen
|
||||||
const holidayDay = new Date(row.date + 'T12:00:00').getDay();
|
const holidayDay = new Date(row.date + 'T12:00:00').getDay();
|
||||||
const isWeekendHoliday = (holidayDay === 0 || holidayDay === 6);
|
const isWeekendHoliday = (holidayDay === 0 || holidayDay === 6);
|
||||||
const holidayHoursStr = isWeekendHoliday ? '0 h (Feiertag)' : fullDayHoursBuf.toFixed(2) + ' h (Feiertag)';
|
const holidayHoursStr = isWeekendHoliday ? '0 h 0 min (Feiertag)' : formatHoursMin(fullDayHoursBuf) + ' (Feiertag)';
|
||||||
const rowDataBuf = [formatDate(row.date), '-', '-', '-', holidayHoursStr];
|
const rowDataBuf = [formatDate(row.date), '-', '-', '-', holidayHoursStr];
|
||||||
rowDataBuf.forEach((data, i) => {
|
rowDataBuf.forEach((data, i) => {
|
||||||
doc.text(data, x, y, { width: colWidths[i], align: 'left' });
|
doc.text(data, x, y, { width: colWidths[i], align: 'left' });
|
||||||
@@ -487,7 +487,7 @@ function generatePDFToBuffer(timesheetId, req) {
|
|||||||
entry.start_time || '-',
|
entry.start_time || '-',
|
||||||
entry.end_time || '-',
|
entry.end_time || '-',
|
||||||
entry.break_minutes ? `${entry.break_minutes} min` : '-',
|
entry.break_minutes ? `${entry.break_minutes} min` : '-',
|
||||||
entry.total_hours ? entry.total_hours.toFixed(2) + ' h' : '-'
|
entry.total_hours ? formatHoursMin(entry.total_hours) : '-'
|
||||||
];
|
];
|
||||||
|
|
||||||
rowData.forEach((data, i) => {
|
rowData.forEach((data, i) => {
|
||||||
@@ -519,7 +519,7 @@ function generatePDFToBuffer(timesheetId, req) {
|
|||||||
if (activity.projectNumber) {
|
if (activity.projectNumber) {
|
||||||
activityText += ` (Projekt: ${activity.projectNumber})`;
|
activityText += ` (Projekt: ${activity.projectNumber})`;
|
||||||
}
|
}
|
||||||
activityText += ` - ${activity.hours.toFixed(2)} h`;
|
activityText += ` - ${formatHoursMin(activity.hours)}`;
|
||||||
doc.fontSize(9).font('Helvetica');
|
doc.fontSize(9).font('Helvetica');
|
||||||
doc.text(activityText, 70, doc.y, { width: 360 });
|
doc.text(activityText, 70, doc.y, { width: 360 });
|
||||||
doc.moveDown(0.2);
|
doc.moveDown(0.2);
|
||||||
@@ -532,7 +532,7 @@ function generatePDFToBuffer(timesheetId, req) {
|
|||||||
overtimeInfo.push('Feiertag: ' + (holidayNames.get(entry.date) || 'Feiertag'));
|
overtimeInfo.push('Feiertag: ' + (holidayNames.get(entry.date) || 'Feiertag'));
|
||||||
}
|
}
|
||||||
if (entry.overtime_taken_hours && parseFloat(entry.overtime_taken_hours) > 0) {
|
if (entry.overtime_taken_hours && parseFloat(entry.overtime_taken_hours) > 0) {
|
||||||
overtimeInfo.push(`Überstunden genommen: ${parseFloat(entry.overtime_taken_hours).toFixed(2)} h`);
|
overtimeInfo.push(`Überstunden genommen: ${formatHoursMin(parseFloat(entry.overtime_taken_hours))}`);
|
||||||
}
|
}
|
||||||
if (entry.vacation_type) {
|
if (entry.vacation_type) {
|
||||||
const vacationText = entry.vacation_type === 'full' ? 'Ganzer Tag' : 'Halber Tag';
|
const vacationText = entry.vacation_type === 'full' ? 'Ganzer Tag' : 'Halber Tag';
|
||||||
@@ -573,7 +573,7 @@ function generatePDFToBuffer(timesheetId, req) {
|
|||||||
doc.moveDown(0.5);
|
doc.moveDown(0.5);
|
||||||
doc.font('Helvetica-Bold');
|
doc.font('Helvetica-Bold');
|
||||||
const totalHoursWithVacation = totalHours + vacationHours + holidayHours;
|
const totalHoursWithVacation = totalHours + vacationHours + holidayHours;
|
||||||
doc.text(`Gesamtstunden: ${totalHoursWithVacation.toFixed(2)} h`, 50, doc.y);
|
doc.text(`Gesamtstunden: ${formatHoursMin(totalHoursWithVacation)}`, 50, doc.y);
|
||||||
|
|
||||||
const wochenstunden = timesheet.wochenstunden || 0;
|
const wochenstunden = timesheet.wochenstunden || 0;
|
||||||
const overtimeHours = totalHoursWithVacation - wochenstunden;
|
const overtimeHours = totalHoursWithVacation - wochenstunden;
|
||||||
@@ -581,11 +581,11 @@ function generatePDFToBuffer(timesheetId, req) {
|
|||||||
doc.moveDown(0.3);
|
doc.moveDown(0.3);
|
||||||
doc.font('Helvetica-Bold');
|
doc.font('Helvetica-Bold');
|
||||||
if (overtimeHours > 0) {
|
if (overtimeHours > 0) {
|
||||||
doc.text(`Überstunden: +${overtimeHours.toFixed(2)} h`, 50, doc.y);
|
doc.text(`Überstunden: +${formatHoursMin(overtimeHours)}`, 50, doc.y);
|
||||||
} else if (overtimeHours < 0) {
|
} else if (overtimeHours < 0) {
|
||||||
doc.text(`Überstunden: ${overtimeHours.toFixed(2)} h`, 50, doc.y);
|
doc.text(`Überstunden: ${formatHoursMin(overtimeHours)}`, 50, doc.y);
|
||||||
} else {
|
} else {
|
||||||
doc.text(`Überstunden: 0.00 h`, 50, doc.y);
|
doc.text(`Überstunden: ${formatHoursMin(0)}`, 50, doc.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
doc.end();
|
doc.end();
|
||||||
|
|||||||
@@ -52,11 +52,11 @@
|
|||||||
<div class="summary">
|
<div class="summary">
|
||||||
<div class="summary-item">
|
<div class="summary-item">
|
||||||
<strong>Gesamtstunden diese Woche:</strong>
|
<strong>Gesamtstunden diese Woche:</strong>
|
||||||
<span id="totalHours">0.00 h</span>
|
<span id="totalHours">0 h 0 min</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-item" id="overtimeSummaryItem" style="display: none;">
|
<div class="summary-item" id="overtimeSummaryItem" style="display: none;">
|
||||||
<strong>Überstunden diese Woche:</strong>
|
<strong>Überstunden diese Woche:</strong>
|
||||||
<span id="overtimeHours">0.00 h</span>
|
<span id="overtimeHours">0 h 0 min</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -194,6 +194,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="/js/format-hours.js"></script>
|
||||||
<script src="/js/dashboard.js"></script>
|
<script src="/js/dashboard.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// Wochenende-Sektion ein-/ausklappen
|
// Wochenende-Sektion ein-/ausklappen
|
||||||
|
|||||||
@@ -187,6 +187,7 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="/js/format-hours.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// Rollenwechsel
|
// Rollenwechsel
|
||||||
const roleSwitcher = document.getElementById('roleSwitcher');
|
const roleSwitcher = document.getElementById('roleSwitcher');
|
||||||
@@ -231,15 +232,7 @@
|
|||||||
return new Date(s.replace(' ', 'T') + 'Z');
|
return new Date(s.replace(' ', 'T') + 'Z');
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatHours(value) {
|
// formatHoursMin aus format-hours.js (window.formatHoursMin)
|
||||||
const n = Number(value);
|
|
||||||
if (!Number.isFinite(n)) return '';
|
|
||||||
const sign = n > 0 ? '+' : '';
|
|
||||||
let s = sign + n.toFixed(2);
|
|
||||||
s = s.replace(/\.00$/, '');
|
|
||||||
s = s.replace(/(\.\d)0$/, '$1');
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
let correctionsExpanded = false;
|
let correctionsExpanded = false;
|
||||||
function toggleCorrectionsSection() {
|
function toggleCorrectionsSection() {
|
||||||
@@ -286,18 +279,18 @@
|
|||||||
// Zusammenfassung anzeigen
|
// Zusammenfassung anzeigen
|
||||||
const totalOvertimeEl = document.getElementById('totalOvertime');
|
const totalOvertimeEl = document.getElementById('totalOvertime');
|
||||||
totalOvertimeEl.textContent =
|
totalOvertimeEl.textContent =
|
||||||
(totalOvertime >= 0 ? '+' : '') + totalOvertime.toFixed(2) + ' h';
|
(totalOvertime >= 0 ? '+' : '') + formatHoursMin(totalOvertime);
|
||||||
totalOvertimeEl.className =
|
totalOvertimeEl.className =
|
||||||
'summary-value ' + (totalOvertime >= 0 ? 'overtime-positive' : 'overtime-negative');
|
'summary-value ' + (totalOvertime >= 0 ? 'overtime-positive' : 'overtime-negative');
|
||||||
|
|
||||||
const totalOvertimeTakenEl = document.getElementById('totalOvertimeTaken');
|
const totalOvertimeTakenEl = document.getElementById('totalOvertimeTaken');
|
||||||
totalOvertimeTakenEl.textContent =
|
totalOvertimeTakenEl.textContent =
|
||||||
totalOvertimeTaken.toFixed(2) + ' h';
|
formatHoursMin(totalOvertimeTaken);
|
||||||
totalOvertimeTakenEl.className = 'summary-value overtime-positive';
|
totalOvertimeTakenEl.className = 'summary-value overtime-positive';
|
||||||
|
|
||||||
const remainingOvertimeEl = document.getElementById('remainingOvertime');
|
const remainingOvertimeEl = document.getElementById('remainingOvertime');
|
||||||
remainingOvertimeEl.textContent =
|
remainingOvertimeEl.textContent =
|
||||||
(remainingOvertime >= 0 ? '+' : '') + remainingOvertime.toFixed(2) + ' h';
|
(remainingOvertime >= 0 ? '+' : '') + formatHoursMin(remainingOvertime);
|
||||||
remainingOvertimeEl.className =
|
remainingOvertimeEl.className =
|
||||||
'summary-value ' + (remainingOvertime >= 0 ? 'overtime-positive' : 'overtime-negative');
|
'summary-value ' + (remainingOvertime >= 0 ? 'overtime-positive' : 'overtime-negative');
|
||||||
|
|
||||||
@@ -305,7 +298,7 @@
|
|||||||
const offsetItem = document.getElementById('offsetItem');
|
const offsetItem = document.getElementById('offsetItem');
|
||||||
const offsetValue = document.getElementById('overtimeOffset');
|
const offsetValue = document.getElementById('overtimeOffset');
|
||||||
if (overtimeOffset !== 0) {
|
if (overtimeOffset !== 0) {
|
||||||
offsetValue.textContent = (overtimeOffset >= 0 ? '+' : '') + overtimeOffset.toFixed(2) + ' h';
|
offsetValue.textContent = (overtimeOffset >= 0 ? '+' : '') + formatHoursMin(overtimeOffset);
|
||||||
offsetValue.className = 'summary-value ' + (overtimeOffset >= 0 ? 'overtime-positive' : 'overtime-negative');
|
offsetValue.className = 'summary-value ' + (overtimeOffset >= 0 ? 'overtime-positive' : 'overtime-negative');
|
||||||
offsetItem.style.display = 'flex';
|
offsetItem.style.display = 'flex';
|
||||||
} else {
|
} else {
|
||||||
@@ -327,12 +320,12 @@
|
|||||||
corrections.forEach(c => {
|
corrections.forEach(c => {
|
||||||
const dt = parseSqliteDatetime(c.corrected_at);
|
const dt = parseSqliteDatetime(c.corrected_at);
|
||||||
const dateText = dt ? dt.toLocaleDateString('de-DE') : '';
|
const dateText = dt ? dt.toLocaleDateString('de-DE') : '';
|
||||||
const hoursText = formatHours(c.correction_hours);
|
const hoursText = formatHoursMin(c.correction_hours);
|
||||||
const reason = (c && c.reason != null) ? String(c.reason).trim() : '';
|
const reason = (c && c.reason != null) ? String(c.reason).trim() : '';
|
||||||
const li = document.createElement('li');
|
const li = document.createElement('li');
|
||||||
li.textContent = reason
|
li.textContent = reason
|
||||||
? `Korrektur am ${dateText} ${hoursText} h – ${reason}`
|
? `Korrektur am ${dateText} ${hoursText} – ${reason}`
|
||||||
: `Korrektur am ${dateText} ${hoursText} h`;
|
: `Korrektur am ${dateText} ${hoursText}`;
|
||||||
correctionsListEl.appendChild(li);
|
correctionsListEl.appendChild(li);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -361,10 +354,10 @@
|
|||||||
row.innerHTML = `
|
row.innerHTML = `
|
||||||
<td><strong>${week.year} KW${calendarWeekStr}</strong></td>
|
<td><strong>${week.year} KW${calendarWeekStr}</strong></td>
|
||||||
<td>${dateRange}</td>
|
<td>${dateRange}</td>
|
||||||
<td>${week.total_hours.toFixed(2)} h</td>
|
<td>${formatHoursMin(week.total_hours)}</td>
|
||||||
<td>${week.soll_stunden.toFixed(2)} h</td>
|
<td>${formatHoursMin(week.soll_stunden)}</td>
|
||||||
<td class="${overtimeClass}">${overtimeSign}${week.overtime_hours.toFixed(2)} h</td>
|
<td class="${overtimeClass}">${overtimeSign}${formatHoursMin(week.overtime_hours)}</td>
|
||||||
<td>${week.overtime_taken.toFixed(2)} h</td>
|
<td>${formatHoursMin(week.overtime_taken)}</td>
|
||||||
<td>${week.vacation_days > 0 ? week.vacation_days.toFixed(1) : '-'}</td>
|
<td>${week.vacation_days > 0 ? week.vacation_days.toFixed(1) : '-'}</td>
|
||||||
`;
|
`;
|
||||||
tableBodyEl.appendChild(row);
|
tableBodyEl.appendChild(row);
|
||||||
|
|||||||
@@ -318,6 +318,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="/js/format-hours.js"></script>
|
||||||
<script>
|
<script>
|
||||||
async function loadStatsForDiv(statsDiv) {
|
async function loadStatsForDiv(statsDiv) {
|
||||||
const userId = statsDiv.dataset.userId;
|
const userId = statsDiv.dataset.userId;
|
||||||
@@ -341,13 +342,13 @@
|
|||||||
const weekOvertimeColor = data.weekOvertimeHours < 0 ? '#dc3545' : (data.weekOvertimeHours > 0 ? '#28a745' : '#666');
|
const weekOvertimeColor = data.weekOvertimeHours < 0 ? '#dc3545' : (data.weekOvertimeHours > 0 ? '#28a745' : '#666');
|
||||||
const sign = data.weekOvertimeHours >= 0 ? '+' : '';
|
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 style="color: ${weekOvertimeColor};">${sign}${data.weekOvertimeHours.toFixed(2)} h</span>
|
<strong>Überstunden:</strong> <span style="color: ${weekOvertimeColor};">${sign}${formatHoursMin(data.weekOvertimeHours)}</span>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
if (data.overtimeOffsetHours !== undefined && data.overtimeOffsetHours !== 0) {
|
if (data.overtimeOffsetHours !== undefined && data.overtimeOffsetHours !== 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>Offset:</strong> <span>${Number(data.overtimeOffsetHours).toFixed(2)} h</span>
|
<strong>Offset:</strong> <span>${formatHoursMin(Number(data.overtimeOffsetHours))}</span>
|
||||||
${data.remainingOvertimeWithOffset !== undefined ? `<span style="color: #28a745;">(verbleibend inkl. Offset: ${Number(data.remainingOvertimeWithOffset).toFixed(2)} h)</span>` : ''}
|
${data.remainingOvertimeWithOffset !== undefined ? `<span style="color: #28a745;">(verbleibend inkl. Offset: ${formatHoursMin(Number(data.remainingOvertimeWithOffset))})</span>` : ''}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
if (data.totalVacationDays !== undefined || data.vacationDays !== undefined) {
|
if (data.totalVacationDays !== undefined || data.vacationDays !== undefined) {
|
||||||
@@ -513,15 +514,7 @@
|
|||||||
return new Date(s.replace(' ', 'T') + 'Z');
|
return new Date(s.replace(' ', 'T') + 'Z');
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatHours(value) {
|
// formatHoursMin aus format-hours.js (window.formatHoursMin)
|
||||||
const n = Number(value);
|
|
||||||
if (!Number.isFinite(n)) return '';
|
|
||||||
const sign = n > 0 ? '+' : '';
|
|
||||||
let s = sign + n.toFixed(2);
|
|
||||||
s = s.replace(/\.00$/, '');
|
|
||||||
s = s.replace(/(\.\d)0$/, '$1');
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
function showOvertimeCorrectionReasonModal(opts) {
|
function showOvertimeCorrectionReasonModal(opts) {
|
||||||
const title = opts && opts.title ? String(opts.title) : 'Grund für die Korrektur';
|
const title = opts && opts.title ? String(opts.title) : 'Grund für die Korrektur';
|
||||||
@@ -652,12 +645,12 @@
|
|||||||
corrections.forEach(c => {
|
corrections.forEach(c => {
|
||||||
const dt = parseSqliteDatetime(c.corrected_at);
|
const dt = parseSqliteDatetime(c.corrected_at);
|
||||||
const dateText = dt ? dt.toLocaleDateString('de-DE') : '';
|
const dateText = dt ? dt.toLocaleDateString('de-DE') : '';
|
||||||
const hoursText = formatHours(c.correction_hours);
|
const hoursText = formatHoursMin(c.correction_hours);
|
||||||
const reason = (c && c.reason != null) ? String(c.reason).trim() : '';
|
const reason = (c && c.reason != null) ? String(c.reason).trim() : '';
|
||||||
const li = document.createElement('li');
|
const li = document.createElement('li');
|
||||||
li.textContent = reason
|
li.textContent = reason
|
||||||
? `Korrektur am ${dateText} ${hoursText} h – ${reason}`
|
? `Korrektur am ${dateText} ${hoursText} – ${reason}`
|
||||||
: `Korrektur am ${dateText} ${hoursText} h`;
|
: `Korrektur am ${dateText} ${hoursText}`;
|
||||||
if (listEl) listEl.appendChild(li);
|
if (listEl) listEl.appendChild(li);
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -720,7 +713,7 @@
|
|||||||
// Modal: Grund ist Pflicht
|
// Modal: Grund ist Pflicht
|
||||||
showOvertimeCorrectionReasonModal({
|
showOvertimeCorrectionReasonModal({
|
||||||
title: 'Grund für die Überstunden-Korrektur',
|
title: 'Grund für die Überstunden-Korrektur',
|
||||||
prompt: `Korrektur: ${value > 0 ? '+' : ''}${value} h`,
|
prompt: `Korrektur: ${value >= 0 ? '+' : ''}${formatHoursMin(value)}`,
|
||||||
onCancel: () => {
|
onCancel: () => {
|
||||||
delete this.dataset.modalOpen;
|
delete this.dataset.modalOpen;
|
||||||
this.disabled = false;
|
this.disabled = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user