Inputvaledierung für die Projektnummern
This commit is contained in:
@@ -38,6 +38,21 @@ function getWeekendPercentage(date) {
|
||||
return 100; // Kein Wochenende = 100% (normal)
|
||||
}
|
||||
|
||||
/** Plausibilitätsprüfung Projektnummer: 7 Ziffern, beginnt mit 5, dann YY (Jahr), dann 4 Ziffern (z.B. 5260001). */
|
||||
function isValidProjectNumber(value) {
|
||||
if (value === null || value === undefined || String(value).trim() === '') return true;
|
||||
return /^5\d{6}$/.test(String(value).trim());
|
||||
}
|
||||
|
||||
function validateActivityProjectNumbers(activities) {
|
||||
for (let i = 0; i < activities.length; i++) {
|
||||
if (!isValidProjectNumber(activities[i].projectNumber)) {
|
||||
return { valid: false, activityIndex: i + 1, value: activities[i].projectNumber };
|
||||
}
|
||||
}
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
// Statistiken laden
|
||||
async function loadUserStats() {
|
||||
try {
|
||||
@@ -683,7 +698,8 @@ function renderWeek() {
|
||||
data-date="${dateStr}"
|
||||
data-field="activity${idx + 1}_project_number"
|
||||
value="${activity.projectNumber || ''}"
|
||||
placeholder="Projektnummer"
|
||||
placeholder="z. B. 5260001"
|
||||
title="7 Ziffern: 5 + Jahr (YY) + 4 Ziffern"
|
||||
${timeFieldsDisabled} ${disabled}
|
||||
onblur="saveEntry(this)"
|
||||
class="activity-project-input">
|
||||
@@ -1275,6 +1291,19 @@ async function saveEntry(input) {
|
||||
});
|
||||
}
|
||||
|
||||
const projectNumberCheck = validateActivityProjectNumbers(activities);
|
||||
if (!projectNumberCheck.valid) {
|
||||
const idx = projectNumberCheck.activityIndex;
|
||||
const invalidInput = document.querySelector(`input[data-date="${date}"][data-field="activity${idx}_project_number"]`);
|
||||
if (invalidInput) {
|
||||
invalidInput.value = '';
|
||||
}
|
||||
if (activities[idx - 1]) {
|
||||
activities[idx - 1].projectNumber = null;
|
||||
}
|
||||
alert(`Ungültige Projektnummer in Tätigkeit ${projectNumberCheck.activityIndex}: Die Projektnummer muss 7 Ziffern haben, mit 5 beginnen, gefolgt vom Jahr (YY) und 4 Ziffern (z.B. 5260001). Die Eingabe wurde geleert.`);
|
||||
}
|
||||
|
||||
// Aktualisiere currentEntries mit den DOM-Werten
|
||||
currentEntries[date].start_time = start_time;
|
||||
currentEntries[date].end_time = end_time;
|
||||
@@ -1539,6 +1568,11 @@ async function saveEntry(input) {
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
alert(result.error || 'Fehler beim Speichern');
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.success) {
|
||||
// Aktualisiere Stunden-Anzeige
|
||||
const hoursElement = document.getElementById(`hours_${date}`);
|
||||
|
||||
@@ -6,6 +6,29 @@ const { generatePDF } = require('../services/pdf-service');
|
||||
const { getHolidaysForDateRange } = require('../services/feiertage-service');
|
||||
const { hasRole } = require('../helpers/utils');
|
||||
|
||||
/** Plausibilitätsprüfung Projektnummer: 7 Ziffern, beginnt mit 5, dann YY (Jahr), dann 4 freie Ziffern (z.B. 5260001). */
|
||||
function isValidProjectNumber(value) {
|
||||
if (value === null || value === undefined || String(value).trim() === '') return true;
|
||||
const s = String(value).trim();
|
||||
return /^5\d{6}$/.test(s);
|
||||
}
|
||||
|
||||
function validateProjectNumbers(body) {
|
||||
const numbers = [
|
||||
body.activity1_project_number,
|
||||
body.activity2_project_number,
|
||||
body.activity3_project_number,
|
||||
body.activity4_project_number,
|
||||
body.activity5_project_number
|
||||
];
|
||||
for (let i = 0; i < numbers.length; i++) {
|
||||
if (!isValidProjectNumber(numbers[i])) {
|
||||
return { valid: false, activityIndex: i + 1, value: numbers[i] };
|
||||
}
|
||||
}
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
// Routes registrieren
|
||||
function registerTimesheetRoutes(app) {
|
||||
// API: Stundenerfassung speichern
|
||||
@@ -20,6 +43,13 @@ function registerTimesheetRoutes(app) {
|
||||
overtime_taken_hours, vacation_type, sick_status, weekend_travel
|
||||
} = req.body;
|
||||
const userId = req.session.userId;
|
||||
|
||||
const projectNumberCheck = validateProjectNumbers(req.body);
|
||||
if (!projectNumberCheck.valid) {
|
||||
return res.status(400).json({
|
||||
error: `Ungültige Projektnummer in Tätigkeit ${projectNumberCheck.activityIndex}: Die Projektnummer muss 7 Ziffern haben, mit 5 beginnen, gefolgt vom Jahr (YY) und 4 Ziffern (z.B. 5260001).`
|
||||
});
|
||||
}
|
||||
const hasExplicitBreakMinutes = break_minutes !== undefined && break_minutes !== null && break_minutes !== '';
|
||||
const parsedRequestedBreakMinutes = hasExplicitBreakMinutes ? parseInt(break_minutes, 10) : null;
|
||||
const requestedBreakMinutes = Number.isFinite(parsedRequestedBreakMinutes) && parsedRequestedBreakMinutes >= 0
|
||||
|
||||
@@ -144,6 +144,12 @@ function registerVerwaltungRoutes(app) {
|
||||
});
|
||||
});
|
||||
|
||||
// Plausibilitätsprüfung Projektnummer: 7 Ziffern, beginnt mit 5, dann YY, dann 4 Ziffern
|
||||
function isValidProjectNumber(value) {
|
||||
if (!value || String(value).trim() === '') return false;
|
||||
return /^5\d{6}$/.test(String(value).trim());
|
||||
}
|
||||
|
||||
// Projektauswertung nach Mitarbeitern für eine Projektnummer
|
||||
app.get('/verwaltung/projektauswertung', requireVerwaltung, (req, res) => {
|
||||
const projectNumberRaw = req.query.project ? String(req.query.project).trim() : '';
|
||||
@@ -165,6 +171,22 @@ function registerVerwaltungRoutes(app) {
|
||||
});
|
||||
}
|
||||
|
||||
if (!isValidProjectNumber(projectNumber)) {
|
||||
return res.render('projekt-auswertung', {
|
||||
user: {
|
||||
firstname: req.session.firstname,
|
||||
lastname: req.session.lastname,
|
||||
roles: req.session.roles || [],
|
||||
currentRole: req.session.currentRole || 'verwaltung'
|
||||
},
|
||||
projectNumber: projectNumberRaw,
|
||||
results: [],
|
||||
totalProjectHours: 0,
|
||||
hasResults: false,
|
||||
projectNumberError: 'Die Projektnummer muss 7 Ziffern haben, mit 5 beginnen, gefolgt vom Jahr (YY) und 4 Ziffern (z.B. 5260001).'
|
||||
});
|
||||
}
|
||||
|
||||
// Aggregation der Projektstunden pro Mitarbeiter über alle 5 Aktivitäten
|
||||
const sql = `
|
||||
SELECT
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
</div>
|
||||
<div class="nav-right">
|
||||
<span>Verwaltung: <%= user.firstname %> <%= user.lastname %></span>
|
||||
<select id="verwaltungNav" class="role-switcher" style="margin-right: 10px; padding: 5px 10px; border-radius: 4px; border: 1px solid #ddd;">
|
||||
<option value="/verwaltung">Postfach</option>
|
||||
<option value="/verwaltung/projektauswertung" selected>Projektauswertung</option>
|
||||
</select>
|
||||
<% if (user.roles && user.roles.length > 1) { %>
|
||||
<select id="roleSwitcher" class="role-switcher" style="margin-right: 10px; padding: 5px 10px; border-radius: 4px; border: 1px solid #ddd;">
|
||||
<% const roleLabels = { 'mitarbeiter': 'Mitarbeiter', 'verwaltung': 'Verwaltung', 'admin': 'Administrator' }; %>
|
||||
@@ -35,10 +39,6 @@
|
||||
<h2>Projektauswertung nach Mitarbeitern</h2>
|
||||
<p>Geben Sie eine Projektnummer ein, um alle erfassten Stunden pro Mitarbeiter für dieses Projekt auszuwerten.</p>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<a href="/verwaltung" class="btn btn-secondary">« Zurück zur Verwaltung</a>
|
||||
</div>
|
||||
|
||||
<form method="GET" action="/verwaltung/projektauswertung" class="projekt-filter-form" style="margin-bottom: 20px;">
|
||||
<div class="form-group">
|
||||
<label for="projectNumber"><strong>Projektnummer</strong></label>
|
||||
@@ -48,14 +48,18 @@
|
||||
name="project"
|
||||
value="<%= projectNumber || '' %>"
|
||||
class="form-control"
|
||||
placeholder="z. B. 12345"
|
||||
placeholder="z. B. 5260001"
|
||||
required
|
||||
style="max-width: 240px;">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Auswerten</button>
|
||||
</form>
|
||||
|
||||
<% if (projectNumber && !hasResults) { %>
|
||||
<% if (projectNumberError) { %>
|
||||
<div style="max-width: 480px; padding: 12px 16px; background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 4px; color: #721c24;">
|
||||
<%= projectNumberError %>
|
||||
</div>
|
||||
<% } else if (projectNumber && !hasResults) { %>
|
||||
<div class="empty-state">
|
||||
<p>Für das Projekt <strong><%= projectNumber %></strong> wurden keine Stunden gefunden.</p>
|
||||
</div>
|
||||
@@ -167,6 +171,14 @@
|
||||
});
|
||||
}
|
||||
|
||||
const verwaltungNav = document.getElementById('verwaltungNav');
|
||||
if (verwaltungNav) {
|
||||
verwaltungNav.addEventListener('change', function() {
|
||||
const url = this.value;
|
||||
if (url) window.location.href = url;
|
||||
});
|
||||
}
|
||||
|
||||
// Mitarbeiter-Details (Aktivitäten) ein-/ausklappen
|
||||
document.querySelectorAll('.toggle-details-btn').forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
|
||||
@@ -17,7 +17,10 @@
|
||||
</div>
|
||||
<div class="nav-right">
|
||||
<span>Verwaltung: <%= user.firstname %> <%= user.lastname %></span>
|
||||
<a href="/verwaltung/projektauswertung" class="btn btn-secondary" style="margin-right: 10px;">Projektauswertung</a>
|
||||
<select id="verwaltungNav" class="role-switcher" style="margin-right: 10px; padding: 5px 10px; border-radius: 4px; border: 1px solid #ddd;">
|
||||
<option value="/verwaltung" selected>Postfach</option>
|
||||
<option value="/verwaltung/projektauswertung">Projektauswertung</option>
|
||||
</select>
|
||||
<% if (user.roles && user.roles.length > 1) { %>
|
||||
<select id="roleSwitcher" class="role-switcher" style="margin-right: 10px; padding: 5px 10px; border-radius: 4px; border: 1px solid #ddd;">
|
||||
<% const roleLabels = { 'mitarbeiter': 'Mitarbeiter', 'verwaltung': 'Verwaltung', 'admin': 'Administrator' }; %>
|
||||
@@ -1192,6 +1195,14 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const verwaltungNav = document.getElementById('verwaltungNav');
|
||||
if (verwaltungNav) {
|
||||
verwaltungNav.addEventListener('change', function() {
|
||||
const url = this.value;
|
||||
if (url) window.location.href = url;
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<%- include('footer') %>
|
||||
|
||||
Reference in New Issue
Block a user