fixes
This commit is contained in:
@@ -64,6 +64,9 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
// Ping-IP laden
|
||||
loadPingIP();
|
||||
|
||||
// Statistiken laden
|
||||
loadUserStats();
|
||||
|
||||
loadWeek();
|
||||
|
||||
document.getElementById('prevWeek').addEventListener('click', function() {
|
||||
@@ -520,6 +523,7 @@ function updateOvertimeDisplay() {
|
||||
|
||||
// Gesamtstunden berechnen - direkt aus DOM-Elementen lesen für Echtzeit-Aktualisierung
|
||||
let totalHours = 0;
|
||||
let vacationHours = 0;
|
||||
const startDateObj = new Date(startDate);
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const date = new Date(startDateObj);
|
||||
@@ -532,8 +536,30 @@ function updateOvertimeDisplay() {
|
||||
const sickCheckbox = document.querySelector(`input[data-date="${dateStr}"][data-field="sick_status"]`);
|
||||
const sickStatus = sickCheckbox ? sickCheckbox.checked : (currentEntries[dateStr]?.sick_status || false);
|
||||
|
||||
// Wenn Urlaub oder Krank, zähle nur diese Stunden (nicht zusätzlich Arbeitsstunden)
|
||||
if (vacationType === 'full') {
|
||||
totalHours += 8; // Ganzer Tag Urlaub = 8 Stunden
|
||||
vacationHours += 8; // Ganzer Tag Urlaub = 8 Stunden
|
||||
} else if (vacationType === 'half') {
|
||||
vacationHours += 4; // Halber Tag Urlaub = 4 Stunden
|
||||
// Bei halbem Tag Urlaub können noch Arbeitsstunden vorhanden sein
|
||||
const startInput = document.querySelector(`input[data-date="${dateStr}"][data-field="start_time"]`);
|
||||
const endInput = document.querySelector(`input[data-date="${dateStr}"][data-field="end_time"]`);
|
||||
const breakInput = document.querySelector(`input[data-date="${dateStr}"][data-field="break_minutes"]`);
|
||||
|
||||
const startTime = startInput ? startInput.value : '';
|
||||
const endTime = endInput ? endInput.value : '';
|
||||
const breakMinutes = parseInt(breakInput ? breakInput.value : 0) || 0;
|
||||
|
||||
if (startTime && endTime) {
|
||||
const start = new Date(`2000-01-01T${startTime}`);
|
||||
const end = new Date(`2000-01-01T${endTime}`);
|
||||
const diffMs = end - start;
|
||||
const hours = (diffMs / (1000 * 60 * 60)) - (breakMinutes / 60);
|
||||
totalHours += hours;
|
||||
} else if (currentEntries[dateStr]?.total_hours) {
|
||||
// Fallback auf gespeicherte Werte
|
||||
totalHours += parseFloat(currentEntries[dateStr].total_hours) || 0;
|
||||
}
|
||||
} else if (sickStatus) {
|
||||
totalHours += 8; // Krank = 8 Stunden
|
||||
} else {
|
||||
@@ -559,24 +585,6 @@ function updateOvertimeDisplay() {
|
||||
}
|
||||
}
|
||||
|
||||
// Urlaubsstunden berechnen (Urlaub zählt als normale Arbeitszeit)
|
||||
let vacationHours = 0;
|
||||
const startDateObj2 = new Date(startDate);
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const date = new Date(startDateObj2);
|
||||
date.setDate(date.getDate() + i);
|
||||
const dateStr = formatDate(date);
|
||||
|
||||
const vacationSelect = document.querySelector(`select[data-date="${dateStr}"][data-field="vacation_type"]`);
|
||||
const vacationType = vacationSelect ? vacationSelect.value : (currentEntries[dateStr]?.vacation_type || '');
|
||||
|
||||
if (vacationType === 'full') {
|
||||
vacationHours += 8; // Ganzer Tag = 8 Stunden
|
||||
} else if (vacationType === 'half') {
|
||||
vacationHours += 4; // Halber Tag = 4 Stunden
|
||||
}
|
||||
}
|
||||
|
||||
// Genommene Überstunden berechnen - direkt aus DOM lesen
|
||||
let overtimeTaken = 0;
|
||||
const startDateObj3 = new Date(startDate);
|
||||
@@ -1172,7 +1180,8 @@ async function submitWeekWithReason(versionReason) {
|
||||
const versionText = result.version ? ` (Version ${result.version})` : '';
|
||||
alert(`Stundenzettel wurde erfolgreich eingereicht${versionText}!`);
|
||||
loadWeek(); // Neu laden um Status zu aktualisieren
|
||||
// Statistiken werden durch updateOvertimeDisplay() aktualisiert
|
||||
// Statistiken aktualisieren
|
||||
loadUserStats();
|
||||
} else {
|
||||
console.error('Fehler-Details:', result);
|
||||
alert(result.error || 'Fehler beim Einreichen des Stundenzettels');
|
||||
|
||||
@@ -260,18 +260,27 @@ function registerUserRoutes(app) {
|
||||
let weekVacationHours = 0;
|
||||
|
||||
entries.forEach(entry => {
|
||||
if (entry.total_hours) {
|
||||
weekTotalHours += entry.total_hours;
|
||||
}
|
||||
if (entry.overtime_taken_hours) {
|
||||
weekOvertimeTaken += entry.overtime_taken_hours;
|
||||
}
|
||||
|
||||
// Urlaub hat Priorität - wenn Urlaub, zähle nur Urlaubsstunden, nicht zusätzlich Arbeitsstunden
|
||||
if (entry.vacation_type === 'full') {
|
||||
weekVacationDays += 1;
|
||||
weekVacationHours += 8;
|
||||
weekVacationHours += 8; // Ganzer Tag = 8 Stunden
|
||||
// Bei vollem Tag Urlaub werden keine Arbeitsstunden gezählt
|
||||
} else if (entry.vacation_type === 'half') {
|
||||
weekVacationDays += 0.5;
|
||||
weekVacationHours += 4;
|
||||
weekVacationHours += 4; // Halber Tag = 4 Stunden
|
||||
// Bei halbem Tag Urlaub können noch Arbeitsstunden vorhanden sein
|
||||
if (entry.total_hours) {
|
||||
weekTotalHours += entry.total_hours;
|
||||
}
|
||||
} else {
|
||||
// Kein Urlaub - zähle nur Arbeitsstunden
|
||||
if (entry.total_hours) {
|
||||
weekTotalHours += entry.total_hours;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -146,18 +146,27 @@ function registerVerwaltungRoutes(app) {
|
||||
let vacationHours = 0;
|
||||
|
||||
entries.forEach(entry => {
|
||||
if (entry.total_hours) {
|
||||
totalHours += entry.total_hours;
|
||||
}
|
||||
if (entry.overtime_taken_hours) {
|
||||
overtimeTaken += entry.overtime_taken_hours;
|
||||
}
|
||||
|
||||
// Urlaub hat Priorität - wenn Urlaub, zähle nur Urlaubsstunden, nicht zusätzlich Arbeitsstunden
|
||||
if (entry.vacation_type === 'full') {
|
||||
vacationDays += 1;
|
||||
vacationHours += 8; // Ganzer Tag = 8 Stunden
|
||||
// Bei vollem Tag Urlaub werden keine Arbeitsstunden gezählt
|
||||
} else if (entry.vacation_type === 'half') {
|
||||
vacationDays += 0.5;
|
||||
vacationHours += 4; // Halber Tag = 4 Stunden
|
||||
// Bei halbem Tag Urlaub können noch Arbeitsstunden vorhanden sein
|
||||
if (entry.total_hours) {
|
||||
totalHours += entry.total_hours;
|
||||
}
|
||||
} else {
|
||||
// Kein Urlaub - zähle nur Arbeitsstunden
|
||||
if (entry.total_hours) {
|
||||
totalHours += entry.total_hours;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -221,15 +221,21 @@ function generatePDF(timesheetId, req, res) {
|
||||
doc.fontSize(10);
|
||||
}
|
||||
|
||||
if (entry.total_hours) {
|
||||
totalHours += entry.total_hours;
|
||||
}
|
||||
|
||||
// Urlaubsstunden für Überstunden-Berechnung sammeln
|
||||
// Urlaub hat Priorität - wenn Urlaub, zähle nur Urlaubsstunden, nicht zusätzlich Arbeitsstunden
|
||||
if (entry.vacation_type === 'full') {
|
||||
vacationHours += 8; // Ganzer Tag = 8 Stunden
|
||||
// Bei vollem Tag Urlaub werden keine Arbeitsstunden gezählt
|
||||
} else if (entry.vacation_type === 'half') {
|
||||
vacationHours += 4; // Halber Tag = 4 Stunden
|
||||
// Bei halbem Tag Urlaub können noch Arbeitsstunden vorhanden sein
|
||||
if (entry.total_hours) {
|
||||
totalHours += entry.total_hours;
|
||||
}
|
||||
} else {
|
||||
// Kein Urlaub - zähle nur Arbeitsstunden
|
||||
if (entry.total_hours) {
|
||||
totalHours += entry.total_hours;
|
||||
}
|
||||
}
|
||||
|
||||
doc.moveDown(0.5);
|
||||
@@ -249,9 +255,9 @@ function generatePDF(timesheetId, req, res) {
|
||||
|
||||
// Überstunden berechnen und anzeigen
|
||||
const wochenstunden = timesheet.wochenstunden || 0;
|
||||
// Überstunden = Gesamtstunden - Wochenstunden
|
||||
// Urlaub zählt als normale Arbeitszeit, daher sind Urlaubsstunden bereits in totalHours enthalten
|
||||
const overtimeHours = totalHours - wochenstunden;
|
||||
// Überstunden = (Tatsächliche Stunden + Urlaubsstunden) - Wochenstunden
|
||||
const totalHoursWithVacation = totalHours + vacationHours;
|
||||
const overtimeHours = totalHoursWithVacation - wochenstunden;
|
||||
|
||||
doc.moveDown(0.3);
|
||||
doc.font('Helvetica-Bold');
|
||||
@@ -432,14 +438,21 @@ function generatePDFToBuffer(timesheetId, req) {
|
||||
doc.fontSize(10);
|
||||
}
|
||||
|
||||
if (entry.total_hours) {
|
||||
totalHours += entry.total_hours;
|
||||
}
|
||||
|
||||
// Urlaub hat Priorität - wenn Urlaub, zähle nur Urlaubsstunden, nicht zusätzlich Arbeitsstunden
|
||||
if (entry.vacation_type === 'full') {
|
||||
vacationHours += 8;
|
||||
vacationHours += 8; // Ganzer Tag = 8 Stunden
|
||||
// Bei vollem Tag Urlaub werden keine Arbeitsstunden gezählt
|
||||
} else if (entry.vacation_type === 'half') {
|
||||
vacationHours += 4;
|
||||
vacationHours += 4; // Halber Tag = 4 Stunden
|
||||
// Bei halbem Tag Urlaub können noch Arbeitsstunden vorhanden sein
|
||||
if (entry.total_hours) {
|
||||
totalHours += entry.total_hours;
|
||||
}
|
||||
} else {
|
||||
// Kein Urlaub - zähle nur Arbeitsstunden
|
||||
if (entry.total_hours) {
|
||||
totalHours += entry.total_hours;
|
||||
}
|
||||
}
|
||||
|
||||
doc.moveDown(0.5);
|
||||
@@ -457,7 +470,9 @@ function generatePDFToBuffer(timesheetId, req) {
|
||||
doc.text(`Gesamtstunden: ${totalHours.toFixed(2)} h`, 50, doc.y);
|
||||
|
||||
const wochenstunden = timesheet.wochenstunden || 0;
|
||||
const overtimeHours = totalHours - wochenstunden;
|
||||
// Überstunden = (Tatsächliche Stunden + Urlaubsstunden) - Wochenstunden
|
||||
const totalHoursWithVacation = totalHours + vacationHours;
|
||||
const overtimeHours = totalHoursWithVacation - wochenstunden;
|
||||
|
||||
doc.moveDown(0.3);
|
||||
doc.font('Helvetica-Bold');
|
||||
|
||||
@@ -55,6 +55,53 @@
|
||||
<p class="help-text">Stunden werden automatisch gespeichert. Am Ende der Woche können Sie die Stunden abschicken.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rechte Seitenleiste mit Statistiken und Erfassungs-URLs -->
|
||||
<div class="user-stats-panel">
|
||||
<!-- Statistik-Karten -->
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">Aktuelle Überstunden</div>
|
||||
<div class="stat-value" id="currentOvertime">-</div>
|
||||
<div class="stat-unit">Stunden</div>
|
||||
</div>
|
||||
<div class="stat-card stat-vacation">
|
||||
<div class="stat-label">Verbleibende Urlaubstage</div>
|
||||
<div class="stat-value" id="remainingVacation">-</div>
|
||||
<div class="stat-unit">von <span id="totalVacation">-</span> Tagen</div>
|
||||
</div>
|
||||
|
||||
<!-- URL-Erfassung -->
|
||||
<div style="margin-top: 20px; padding-top: 20px; border-top: 1px solid #e0e0e0;">
|
||||
<h3 style="font-size: 14px; margin-bottom: 15px; color: #2c3e50;">Zeiterfassung per URL</h3>
|
||||
<div class="form-group" style="margin-bottom: 15px;">
|
||||
<label style="font-size: 12px; color: #666; margin-bottom: 5px;">Check-in URL</label>
|
||||
<div style="display: flex; gap: 5px;">
|
||||
<input type="text" id="checkinUrl" readonly style="flex: 1; padding: 8px; font-size: 11px; border: 1px solid #ddd; border-radius: 4px; background: #f8f9fa;">
|
||||
<button onclick="copyToClipboard('checkinUrl')" class="btn btn-sm btn-secondary" style="padding: 8px 12px;">Kopieren</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" style="margin-bottom: 15px;">
|
||||
<label style="font-size: 12px; color: #666; margin-bottom: 5px;">Check-out URL</label>
|
||||
<div style="display: flex; gap: 5px;">
|
||||
<input type="text" id="checkoutUrl" readonly style="flex: 1; padding: 8px; font-size: 11px; border: 1px solid #ddd; border-radius: 4px; background: #f8f9fa;">
|
||||
<button onclick="copyToClipboard('checkoutUrl')" class="btn btn-sm btn-secondary" style="padding: 8px 12px;">Kopieren</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- IP-Erfassung -->
|
||||
<div style="margin-top: 20px; padding-top: 20px; border-top: 1px solid #e0e0e0;">
|
||||
<h3 style="font-size: 14px; margin-bottom: 15px; color: #2c3e50;">IP-basierte Zeiterfassung</h3>
|
||||
<div class="form-group" style="margin-bottom: 15px;">
|
||||
<label style="font-size: 12px; color: #666; margin-bottom: 5px;">Ping-IP Adresse</label>
|
||||
<div style="display: flex; gap: 5px;">
|
||||
<input type="text" id="pingIpInput" placeholder="z.B. 192.168.1.100" style="flex: 1; padding: 8px; font-size: 12px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
<button onclick="window.savePingIP()" class="btn btn-sm btn-success" style="padding: 8px 12px;">Speichern</button>
|
||||
</div>
|
||||
<p style="font-size: 11px; color: #666; margin-top: 5px; font-style: italic;">Ihre IP-Adresse für automatische Zeiterfassung</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user