Projektauswertung hinzugefügt
This commit is contained in:
@@ -10,6 +10,18 @@ const { getCurrentOvertimeForUser } = require('../services/overtime-service');
|
||||
|
||||
// Routes registrieren
|
||||
function registerVerwaltungRoutes(app) {
|
||||
// Helper: Minuten in Format h:mm (z. B. 90 -> "1:30", -45 -> "-0:45")
|
||||
function minutesToHhMm(totalMinutes) {
|
||||
if (totalMinutes == null || !Number.isFinite(Number(totalMinutes))) return '0:00';
|
||||
const n = Number(totalMinutes);
|
||||
const sign = n < 0 ? -1 : 1;
|
||||
const absVal = Math.abs(n);
|
||||
let h = Math.floor(absVal / 60);
|
||||
let min = Math.round(absVal - h * 60);
|
||||
const prefix = sign < 0 ? '-' : '';
|
||||
return prefix + h + ':' + String(min).padStart(2, '0');
|
||||
}
|
||||
|
||||
// Verwaltungs-Bereich
|
||||
app.get('/verwaltung', requireVerwaltung, (req, res) => {
|
||||
db.all(`
|
||||
@@ -132,6 +144,204 @@ function registerVerwaltungRoutes(app) {
|
||||
});
|
||||
});
|
||||
|
||||
// Projektauswertung nach Mitarbeitern für eine Projektnummer
|
||||
app.get('/verwaltung/projektauswertung', requireVerwaltung, (req, res) => {
|
||||
const projectNumberRaw = req.query.project ? String(req.query.project).trim() : '';
|
||||
const projectNumber = projectNumberRaw || null;
|
||||
|
||||
if (!projectNumber) {
|
||||
// Nur Formular anzeigen, noch keine Auswertung
|
||||
return res.render('projekt-auswertung', {
|
||||
user: {
|
||||
firstname: req.session.firstname,
|
||||
lastname: req.session.lastname,
|
||||
roles: req.session.roles || [],
|
||||
currentRole: req.session.currentRole || 'verwaltung'
|
||||
},
|
||||
projectNumber: '',
|
||||
results: [],
|
||||
totalProjectHours: 0,
|
||||
hasResults: false
|
||||
});
|
||||
}
|
||||
|
||||
// Aggregation der Projektstunden pro Mitarbeiter über alle 5 Aktivitäten
|
||||
const sql = `
|
||||
SELECT
|
||||
u.id AS user_id,
|
||||
u.firstname,
|
||||
u.lastname,
|
||||
(
|
||||
SUM(CASE WHEN te.activity1_project_number = ? THEN COALESCE(te.activity1_hours, 0) ELSE 0 END) +
|
||||
SUM(CASE WHEN te.activity2_project_number = ? THEN COALESCE(te.activity2_hours, 0) ELSE 0 END) +
|
||||
SUM(CASE WHEN te.activity3_project_number = ? THEN COALESCE(te.activity3_hours, 0) ELSE 0 END) +
|
||||
SUM(CASE WHEN te.activity4_project_number = ? THEN COALESCE(te.activity4_hours, 0) ELSE 0 END) +
|
||||
SUM(CASE WHEN te.activity5_project_number = ? THEN COALESCE(te.activity5_hours, 0) ELSE 0 END)
|
||||
) AS total_hours,
|
||||
ROUND(
|
||||
(
|
||||
SUM(CASE WHEN te.activity1_project_number = ? THEN COALESCE(te.activity1_hours, 0) ELSE 0 END) +
|
||||
SUM(CASE WHEN te.activity2_project_number = ? THEN COALESCE(te.activity2_hours, 0) ELSE 0 END) +
|
||||
SUM(CASE WHEN te.activity3_project_number = ? THEN COALESCE(te.activity3_hours, 0) ELSE 0 END) +
|
||||
SUM(CASE WHEN te.activity4_project_number = ? THEN COALESCE(te.activity4_hours, 0) ELSE 0 END) +
|
||||
SUM(CASE WHEN te.activity5_project_number = ? THEN COALESCE(te.activity5_hours, 0) ELSE 0 END)
|
||||
) * 60
|
||||
) AS total_minutes
|
||||
FROM timesheet_entries te
|
||||
JOIN users u ON u.id = te.user_id
|
||||
GROUP BY u.id, u.firstname, u.lastname
|
||||
HAVING total_minutes <> 0
|
||||
ORDER BY u.lastname, u.firstname
|
||||
`;
|
||||
|
||||
const params = [
|
||||
projectNumber, projectNumber, projectNumber, projectNumber, projectNumber,
|
||||
projectNumber, projectNumber, projectNumber, projectNumber, projectNumber
|
||||
];
|
||||
|
||||
db.all(sql, params, (err, rows) => {
|
||||
if (err) {
|
||||
console.error('Fehler bei der Projektauswertung:', err);
|
||||
return res.status(500).send('Fehler bei der Projektauswertung');
|
||||
}
|
||||
|
||||
const rawResults = (rows || []).map((row) => {
|
||||
const totalMinutes = row.total_minutes || 0;
|
||||
const totalHours = row.total_hours || 0;
|
||||
return {
|
||||
userId: row.user_id,
|
||||
firstname: row.firstname,
|
||||
lastname: row.lastname,
|
||||
totalHours,
|
||||
totalMinutes,
|
||||
totalHoursFormatted: minutesToHhMm(totalMinutes)
|
||||
};
|
||||
});
|
||||
|
||||
const results = rawResults;
|
||||
|
||||
const totalProjectMinutes = results.reduce((sum, r) => sum + (r.totalMinutes || 0), 0);
|
||||
const totalProjectHours = totalProjectMinutes / 60;
|
||||
const totalProjectHoursFormatted = minutesToHhMm(totalProjectMinutes);
|
||||
|
||||
if (results.length === 0) {
|
||||
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,
|
||||
totalProjectHoursFormatted,
|
||||
hasResults: false,
|
||||
breakdownByUser: {}
|
||||
});
|
||||
}
|
||||
|
||||
// Details pro Mitarbeiter (Aktivitäten) laden
|
||||
const breakdownByUser = {};
|
||||
const userIds = results.map((r) => r.userId);
|
||||
let pending = userIds.length;
|
||||
|
||||
const detailSql = `
|
||||
SELECT
|
||||
date,
|
||||
activity1_desc, activity1_hours, activity1_project_number,
|
||||
activity2_desc, activity2_hours, activity2_project_number,
|
||||
activity3_desc, activity3_hours, activity3_project_number,
|
||||
activity4_desc, activity4_hours, activity4_project_number,
|
||||
activity5_desc, activity5_hours, activity5_project_number
|
||||
FROM timesheet_entries
|
||||
WHERE user_id = ?
|
||||
AND (
|
||||
activity1_project_number = ? OR
|
||||
activity2_project_number = ? OR
|
||||
activity3_project_number = ? OR
|
||||
activity4_project_number = ? OR
|
||||
activity5_project_number = ?
|
||||
)
|
||||
ORDER BY date
|
||||
`;
|
||||
|
||||
userIds.forEach((userId) => {
|
||||
db.all(
|
||||
detailSql,
|
||||
[userId, projectNumber, projectNumber, projectNumber, projectNumber, projectNumber],
|
||||
(detailErr, rowsDetail) => {
|
||||
if (detailErr) {
|
||||
console.error('Fehler beim Laden der Projekt-Aktivitäten:', detailErr);
|
||||
breakdownByUser[userId] = [];
|
||||
} else {
|
||||
const activities = [];
|
||||
(rowsDetail || []).forEach((row) => {
|
||||
const date = row.date;
|
||||
|
||||
const pushActivity = (desc, hours) => {
|
||||
if (!hours || !Number.isFinite(Number(hours))) return;
|
||||
const decimal = Number(hours);
|
||||
const minutes = Math.round(decimal * 60);
|
||||
if (minutes === 0) return;
|
||||
activities.push({
|
||||
date,
|
||||
description: desc || '',
|
||||
hoursDecimal: decimal,
|
||||
minutes,
|
||||
formatted: minutesToHhMm(minutes)
|
||||
});
|
||||
};
|
||||
|
||||
if (row.activity1_project_number === projectNumber) {
|
||||
pushActivity(row.activity1_desc, row.activity1_hours);
|
||||
}
|
||||
if (row.activity2_project_number === projectNumber) {
|
||||
pushActivity(row.activity2_desc, row.activity2_hours);
|
||||
}
|
||||
if (row.activity3_project_number === projectNumber) {
|
||||
pushActivity(row.activity3_desc, row.activity3_hours);
|
||||
}
|
||||
if (row.activity4_project_number === projectNumber) {
|
||||
pushActivity(row.activity4_desc, row.activity4_hours);
|
||||
}
|
||||
if (row.activity5_project_number === projectNumber) {
|
||||
pushActivity(row.activity5_desc, row.activity5_hours);
|
||||
}
|
||||
});
|
||||
|
||||
// Nach Datum sortieren
|
||||
activities.sort((a, b) => {
|
||||
if (a.date === b.date) return 0;
|
||||
return a.date < b.date ? -1 : 1;
|
||||
});
|
||||
|
||||
breakdownByUser[userId] = activities;
|
||||
}
|
||||
|
||||
pending -= 1;
|
||||
if (pending === 0) {
|
||||
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,
|
||||
totalProjectHoursFormatted,
|
||||
hasResults: results.length > 0,
|
||||
breakdownByUser
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// API: Überstunden-Offset für einen User setzen (positiv/negativ)
|
||||
app.put('/api/verwaltung/user/:id/overtime-offset', requireVerwaltung, (req, res) => {
|
||||
const userId = req.params.id;
|
||||
|
||||
Reference in New Issue
Block a user