Files
SDSStundenerfassung/database.js

357 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const sqlite3 = require('sqlite3').verbose();
const bcrypt = require('bcryptjs');
const path = require('path');
// Datenbank-Pfad: Umgebungsvariable oder Standard-Pfad
const dbPath = process.env.DB_PATH || path.join(__dirname, 'stundenerfassung.db');
const db = new sqlite3.Database(dbPath);
// Datenbank initialisieren
function initDatabase() {
db.serialize(() => {
// Benutzer-Tabelle
db.run(`CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
firstname TEXT NOT NULL,
lastname TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'mitarbeiter',
last_week_start TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`);
// Migration: last_week_start Spalte hinzufügen falls sie nicht existiert
db.run(`ALTER TABLE users ADD COLUMN last_week_start TEXT`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
});
// Migration: default_break_minutes (Standard-Pausenzeit pro Mitarbeiter)
db.run(`ALTER TABLE users ADD COLUMN default_break_minutes INTEGER DEFAULT 30`, (err) => {
if (err && !err.message.includes('duplicate column')) {
console.warn('Warnung beim Hinzufügen der Spalte default_break_minutes:', err.message);
}
});
// Stundenerfassung-Tabelle
db.run(`CREATE TABLE IF NOT EXISTS timesheet_entries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
date TEXT NOT NULL,
start_time TEXT,
end_time TEXT,
break_minutes INTEGER DEFAULT 0,
total_hours REAL,
notes TEXT,
activity1_desc TEXT,
activity1_hours REAL,
activity2_desc TEXT,
activity2_hours REAL,
activity3_desc TEXT,
activity3_hours REAL,
activity4_desc TEXT,
activity4_hours REAL,
activity5_desc TEXT,
activity5_hours REAL,
status TEXT DEFAULT 'offen',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
)`);
// Migration: Tätigkeitsfelder hinzufügen falls sie nicht existieren
const activityColumns = [
'activity1_desc', 'activity1_hours',
'activity2_desc', 'activity2_hours',
'activity3_desc', 'activity3_hours',
'activity4_desc', 'activity4_hours',
'activity5_desc', 'activity5_hours'
];
activityColumns.forEach(col => {
const colType = col.includes('_hours') ? 'REAL' : 'TEXT';
db.run(`ALTER TABLE timesheet_entries ADD COLUMN ${col} ${colType}`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
});
});
// Wöchentliche Stundenzettel-Tabelle
db.run(`CREATE TABLE IF NOT EXISTS weekly_timesheets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
week_start TEXT NOT NULL,
week_end TEXT NOT NULL,
version INTEGER DEFAULT 1,
status TEXT DEFAULT 'eingereicht',
submitted_at DATETIME DEFAULT CURRENT_TIMESTAMP,
reviewed_by INTEGER,
reviewed_at DATETIME,
pdf_downloaded_at DATETIME,
pdf_downloaded_by INTEGER,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (reviewed_by) REFERENCES users(id),
FOREIGN KEY (pdf_downloaded_by) REFERENCES users(id)
)`);
// Migration: version Spalte hinzufügen falls sie nicht existiert
db.run(`ALTER TABLE weekly_timesheets ADD COLUMN version INTEGER DEFAULT 1`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
// Wenn Spalte neu erstellt wurde, bestehende Einträge haben automatisch version = 1
});
// Migration: pdf_downloaded_at Spalte hinzufügen falls sie nicht existiert
db.run(`ALTER TABLE weekly_timesheets ADD COLUMN pdf_downloaded_at DATETIME`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
});
// Migration: pdf_downloaded_by Spalte hinzufügen falls sie nicht existiert
db.run(`ALTER TABLE weekly_timesheets ADD COLUMN pdf_downloaded_by INTEGER`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
});
// Migration: version_reason Spalte hinzufügen
db.run(`ALTER TABLE weekly_timesheets ADD COLUMN version_reason TEXT`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
if (err && !err.message.includes('duplicate column')) {
console.warn('Warnung beim Hinzufügen der Spalte version_reason:', err.message);
}
});
// Migration: admin_comment Spalte hinzufügen
db.run(`ALTER TABLE weekly_timesheets ADD COLUMN admin_comment TEXT`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
if (err && !err.message.includes('duplicate column')) {
console.warn('Warnung beim Hinzufügen der Spalte admin_comment:', err.message);
}
});
// Migration: Projektnummern für Tätigkeiten hinzufügen
const projectNumberColumns = [
'activity1_project_number', 'activity2_project_number',
'activity3_project_number', 'activity4_project_number',
'activity5_project_number'
];
projectNumberColumns.forEach(col => {
db.run(`ALTER TABLE timesheet_entries ADD COLUMN ${col} TEXT`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
if (err && !err.message.includes('duplicate column')) {
console.warn(`Warnung beim Hinzufügen der Spalte ${col}:`, err.message);
}
});
});
// Migration: Überstunden und Urlaub hinzufügen
db.run(`ALTER TABLE timesheet_entries ADD COLUMN overtime_taken_hours REAL`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
if (err && !err.message.includes('duplicate column')) {
console.warn('Warnung beim Hinzufügen der Spalte overtime_taken_hours:', err.message);
}
});
db.run(`ALTER TABLE timesheet_entries ADD COLUMN vacation_type TEXT`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
if (err && !err.message.includes('duplicate column')) {
console.warn('Warnung beim Hinzufügen der Spalte vacation_type:', err.message);
}
});
// Migration: Krank-Status hinzufügen
db.run(`ALTER TABLE timesheet_entries ADD COLUMN sick_status INTEGER DEFAULT 0`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
if (err && !err.message.includes('duplicate column')) {
console.warn('Warnung beim Hinzufügen der Spalte sick_status:', err.message);
}
});
// Migration: Pausen-Zeiten für API-Zeiterfassung hinzufügen
db.run(`ALTER TABLE timesheet_entries ADD COLUMN pause_start_time TEXT`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
if (err && !err.message.includes('duplicate column')) {
console.warn('Warnung beim Hinzufügen der Spalte pause_start_time:', err.message);
}
});
db.run(`ALTER TABLE timesheet_entries ADD COLUMN pause_end_time TEXT`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
if (err && !err.message.includes('duplicate column')) {
console.warn('Warnung beim Hinzufügen der Spalte pause_end_time:', err.message);
}
});
// Migration: Wochenend-Reise und angewendeter Wochenend-Prozentsatz hinzufügen
db.run(`ALTER TABLE timesheet_entries ADD COLUMN weekend_travel INTEGER DEFAULT 0`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
if (err && !err.message.includes('duplicate column')) {
console.warn('Warnung beim Hinzufügen der Spalte weekend_travel:', err.message);
}
});
db.run(`ALTER TABLE timesheet_entries ADD COLUMN applied_weekend_percentage REAL DEFAULT NULL`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
if (err && !err.message.includes('duplicate column')) {
console.warn('Warnung beim Hinzufügen der Spalte applied_weekend_percentage:', err.message);
}
});
// Migration: User-Felder hinzufügen (Personalnummer, Wochenstunden, Urlaubstage)
db.run(`ALTER TABLE users ADD COLUMN personalnummer TEXT`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
});
db.run(`ALTER TABLE users ADD COLUMN wochenstunden REAL`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
});
db.run(`ALTER TABLE users ADD COLUMN urlaubstage REAL`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
});
// Migration: Überstunden-Offset (manuelle Korrektur durch Verwaltung)
db.run(`ALTER TABLE users ADD COLUMN overtime_offset_hours REAL DEFAULT 0`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
if (err && !err.message.includes('duplicate column')) {
console.warn('Warnung beim Hinzufügen der Spalte overtime_offset_hours:', err.message);
}
});
// Migration: Urlaubstage-Offset (manuelle Korrektur durch Verwaltung)
db.run(`ALTER TABLE users ADD COLUMN vacation_offset_days REAL DEFAULT 0`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
if (err && !err.message.includes('duplicate column')) {
console.warn('Warnung beim Hinzufügen der Spalte vacation_offset_days:', err.message);
}
});
// Migration: Arbeitstage pro Woche hinzufügen
db.run(`ALTER TABLE users ADD COLUMN arbeitstage INTEGER DEFAULT 5`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
if (err && !err.message.includes('duplicate column')) {
console.warn('Warnung beim Hinzufügen der Spalte arbeitstage:', err.message);
}
});
// Migration: ping_ip Spalte hinzufügen
db.run(`ALTER TABLE users ADD COLUMN ping_ip TEXT`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
});
// Ping-Status-Tabelle für IP-basierte Zeiterfassung
db.run(`CREATE TABLE IF NOT EXISTS ping_status (
user_id INTEGER NOT NULL,
date TEXT NOT NULL,
last_successful_ping DATETIME,
failed_ping_count INTEGER DEFAULT 0,
start_time_set INTEGER DEFAULT 0,
first_failed_ping_time DATETIME,
PRIMARY KEY (user_id, date),
FOREIGN KEY (user_id) REFERENCES users(id)
)`, (err) => {
if (err && !err.message.includes('duplicate column')) {
console.warn('Warnung beim Erstellen der ping_status Tabelle:', err.message);
}
});
// LDAP-Konfiguration-Tabelle
db.run(`CREATE TABLE IF NOT EXISTS ldap_config (
id INTEGER PRIMARY KEY AUTOINCREMENT,
enabled INTEGER DEFAULT 0,
url TEXT,
bind_dn TEXT,
bind_password TEXT,
base_dn TEXT,
user_search_filter TEXT,
username_attribute TEXT DEFAULT 'sAMAccountName',
firstname_attribute TEXT DEFAULT 'givenName',
lastname_attribute TEXT DEFAULT 'sn',
sync_interval INTEGER DEFAULT 0,
last_sync DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`);
// LDAP-Sync-Log-Tabelle
db.run(`CREATE TABLE IF NOT EXISTS ldap_sync_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sync_type TEXT NOT NULL,
status TEXT NOT NULL,
users_synced INTEGER DEFAULT 0,
error_message TEXT,
sync_started_at DATETIME DEFAULT CURRENT_TIMESTAMP,
sync_completed_at DATETIME
)`);
// Feiertage (öffentliche Feiertage BW) API wird nur 1x pro Jahr aufgerufen
db.run(`CREATE TABLE IF NOT EXISTS public_holidays (
date TEXT PRIMARY KEY,
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: checkin_root_url Spalte hinzufügen
db.run(`ALTER TABLE system_options ADD COLUMN checkin_root_url TEXT`, (err) => {
// Fehler ignorieren wenn Spalte bereits existiert
if (err && !err.message.includes('duplicate column')) {
console.warn('Warnung beim Hinzufügen der Spalte checkin_root_url:', err.message);
}
});
// Migration: Bestehende Rollen zu JSON-Arrays konvertieren
// Prüfe ob Rollen noch als einfache Strings gespeichert sind (nicht als JSON-Array)
db.all('SELECT id, role FROM users', (err, users) => {
if (!err && users) {
users.forEach(user => {
let roleValue = user.role;
// Prüfe ob es bereits ein JSON-Array ist
try {
const parsed = JSON.parse(roleValue);
// Wenn erfolgreich geparst und es ist ein Array, nichts tun
if (Array.isArray(parsed)) {
return; // Bereits JSON-Array
}
} catch (e) {
// Nicht JSON, konvertiere zu JSON-Array
}
// Konvertiere zu JSON-Array
const roleArray = JSON.stringify([roleValue]);
db.run('UPDATE users SET role = ? WHERE id = ?', [roleArray, user.id], (err) => {
if (err) {
console.warn(`Warnung beim Konvertieren der Rolle für User ${user.id}:`, err.message);
}
});
});
}
});
// Standard Admin-Benutzer erstellen
const adminPassword = bcrypt.hashSync('admin123', 10);
db.run(`INSERT OR IGNORE INTO users (id, username, password, firstname, lastname, role)
VALUES (1, 'admin', ?, 'System', 'Administrator', ?)`,
[adminPassword, JSON.stringify(['admin'])]);
// Standard Verwaltungs-Benutzer erstellen
const verwaltungPassword = bcrypt.hashSync('verwaltung123', 10);
db.run(`INSERT OR IGNORE INTO users (id, username, password, firstname, lastname, role)
VALUES (2, 'verwaltung', ?, 'Verwaltung', 'User', ?)`,
[verwaltungPassword, JSON.stringify(['verwaltung'])]);
});
}
module.exports = { db, initDatabase };