# Zeiterfassungssystem für pädagogische Mitarbeiter Eine vollständige Webanwendung zur Erfassung und Verwaltung von Flexistunden für pädagogische Mitarbeiter (PM) an Schulen. ## 📋 Inhaltsverzeichnis - [Überblick](#überblick) - [Funktionen](#funktionen) - [Technologie-Stack](#technologie-stack) - [Voraussetzungen](#voraussetzungen) - [Installation](#installation) - [Konfiguration](#konfiguration) - [Verwendung](#verwendung) - [API-Dokumentation](#api-dokumentation) - [Architektur](#architektur) - [Sicherheit](#sicherheit) - [Backup & Wartung](#backup--wartung) - [Fehlerbehebung](#fehlerbehebung) ## 🎯 Überblick Diese Anwendung wurde entwickelt, um die Erfassung von Flexistunden (zusätzliche Arbeitsstunden) für pädagogische Mitarbeiter an Schulen zu vereinfachen. Sie ermöglicht: - **Mitarbeitern**: Wöchentliche Zeiterfassung anhand eines vorkonfigurierten Stundenplans - **Administratoren**: Vollständige Verwaltung von Benutzern, Stundenplänen, Schuljahren und Zeiteinträgen Das System arbeitet mit ISO-Kalenderwochen und unterstützt schuljahrbezogene Auswertungen. ## ✨ Funktionen ### Für Mitarbeiter - **Wochenbasierte Zeiterfassung**: Auswahl der gearbeiteten Zeiten aus dem Stundenplan - **Kalenderwochen-Navigation**: Einfaches Vor- und Zurückblättern zwischen Wochen - **Jahresübersicht**: Anzeige der geleisteten vs. Soll-Arbeitsstunden - **Responsive Design**: Optimiert für Desktop, Tablet und Mobile ### Für Administratoren - **Benutzerverwaltung**: - Benutzer anlegen, bearbeiten und löschen - Jahresarbeitsstunden pro Benutzer festlegen (Standard: 60h) - Passwörter zurücksetzen - **Stundenplan-Management**: - Wochenstundenplan mit Unterrichts- und Pausenzeiten erstellen - Unterrichtsstunden und Pausen unterscheiden - Zeiten mit Titeln versehen (z.B. "Mathematik", "Pause") - **Schuljahrverwaltung**: - Schuljahre mit Start- und Enddatum definieren - Aktives Schuljahr setzen - Jahresberechnungen basierend auf aktivem Schuljahr - **Zeiteintrags-Verwaltung**: - Alle Zeiteinträge einsehen und bearbeiten - Manuelle Stundeneintragungen (positiv = Abzug, negativ = Hinzurechnung) - Einzelne Einträge korrigieren oder löschen - **Berichtswesen**: - Jahresübersicht aller Mitarbeiter - PDF-Export der Jahresübersicht - Wochenweise Stundenauswertung ## 🛠 Technologie-Stack ### Frontend - **Svelte 5**: Reaktivität und Performance. - **Vite**: Build-Tooling. - **Tailwind CSS + DaisyUI**: UI-Komponenten. - **Font Awesome**: Icons - **LocalStorage**: Client-seitige Datenpersistenz für Authentifizierung ### Backend - **Go (Golang)**: Performante Backend-Sprache - **Echo Framework**: Web-Framework für Go - **SQLite**: Embedded SQL-Datenbank - **JWT**: Token-basierte Authentifizierung - **bcrypt**: Passwort-Hashing - **gofpdf**: PDF-Generierung ### Deployment - **Docker**: Containerisierung - **Docker Compose**: Orchestrierung ## 📦 Voraussetzungen ### Für Docker-Deployment (empfohlen) - Docker (Version 20.10+) - Docker Compose (Version 1.29+) ### Für lokale Entwicklung - Go 1.25+ - Node.js 20+ - SQLite3 ## 🚀 Installation ### Option 1: Docker Compose (Produktion) 1. **Repository klonen** ```bash git clone cd zeiterfassung ``` 2. **Umgebungsvariablen konfigurieren** ```bash cp .env.example .env nano .env ``` Wichtige Variablen in `.env`: ```env PORT=8080 DB_PATH=/data/timetracking.db JWT_SECRET=ihr-sicheres-geheimnis-hier-ändern TZ=Europe/Berlin ``` 3. **Anwendung starten** ```bash docker-compose up -d ``` 4. **Anwendung aufrufen** ``` http://localhost:8080 ``` **Standard-Anmeldedaten:** - Benutzername: `admin` - Passwort: Das in `docker-compose.yml` unter `INITIAL_ADMIN_PASSWORD` festgelegte Passwort. ⚠️ **WICHTIG**: Ändern Sie das Admin-Passwort sofort nach der ersten Anmeldung! ### Option 2: Lokale Entwicklung 1. **Backend kompilieren** ```bash go mod download go build -o timetracking ``` 2. **Frontend kompilieren** ```bash cd static elm make Main.elm --output=elm.js --optimize cd .. ``` 3. **Umgebungsvariablen setzen** ```bash export PORT=8080 export DB_PATH=./timetracking.db export JWT_SECRET=development-secret ``` 4. **Anwendung starten** ```bash ./timetracking ``` ## ⚙️ Konfiguration ### Umgebungsvariablen | Variable | Beschreibung | Standard | Erforderlich | | ------------------------ | ------------------------------------------------- | ----------------------------------------------- | ------------ | | `PORT` | HTTP-Server Port | `8080` | Nein | | `DB_PATH` | Pfad zur SQLite-Datenbank | `./timetracking.db` | Nein | | `JWT_SECRET` | Geheimnis für JWT-Token | - | **Ja** | | `INITIAL_ADMIN_PASSWORD` | Initiales Passwort für den Admin-Benutzer | `changeme` | **Ja** | | `TZ` | Zeitzone | `Europe/Berlin` | Nein | | `ENVIRONMENT` | `production` für HTTPS-Redirect und striktes CORS | `development` | Nein | | `CORS_ALLOWED_ORIGINS` | Komma-getrennte Liste von erlaubten Origins | `*` (in dev), `http://localhost:8080` (in prod) | Nein | ### Docker-Volumes Das Docker-Setup erstellt ein persistentes Volume für die Datenbank: ```yaml volumes: timetracking-data: driver: local ``` Die Datenbank wird unter `/data/timetracking.db` im Container gespeichert. ## 📖 Verwendung ### Ersteinrichtung als Administrator 1. **Anmelden** mit den Standard-Credentials (admin/das initiale Passwort aus der Konfiguration) 2. **Admin-Passwort ändern**: - Gehe zu "Benutzer" Tab - Klicke auf "PW Reset" beim Admin-Benutzer - Neues sicheres Passwort eingeben 3. **Schuljahr erstellen**: - Wechsle zum "Schuljahre" Tab - Erstelle ein Schuljahr (z.B. "2024/2025") - Startdatum: 01.08.2024 - Enddatum: 31.07.2025 - Klicke auf "Aktivieren" 4. **Stundenplan erstellen**: - Wechsle zum "Stundenplan" Tab - Füge Unterrichtsstunden hinzu: - Wochentag auswählen - Start- und Endzeit eingeben - Typ: "Unterricht" oder "Pause" - Titel vergeben (z.B. "Mathematik 1a") 5. **Mitarbeiter anlegen**: - Wechsle zum "Benutzer" Tab - "Benutzer anlegen" - Benutzername und Passwort eingeben - Jahresarbeitsstunden festlegen (Standard: 60h) - Admin-Rechte nur für weitere Administratoren ### Zeiterfassung als Mitarbeiter 1. **Anmelden** mit persönlichen Zugangsdaten 2. **Wochenansicht**: - Zeigt aktuelle Kalenderwoche mit Datumsbereich - Navigation: "Vorherige Woche" / "Nächste Woche" 3. **Stunden erfassen**: - Klicke auf die Zeitslots, die du gearbeitet hast - Ausgewählte Zeiten werden grün markiert - Klicke "Speichern" zum Übernehmen 4. **Woche bearbeiten**: - Falls bereits erfasst, erscheint "Bearbeiten"-Button - Aktiviert Bearbeitungsmodus - "Einträge löschen" entfernt alle Einträge der Woche - Neue Auswahl treffen und "Änderungen speichern" 5. **Jahresübersicht prüfen**: - Unten auf der Seite: "Jahresgesamtzeit" - Zeigt: Soll-Stunden, Geleistete Stunden, Verbleibende Stunden - Fortschrittsbalken visualisiert den Status ### Administrative Aufgaben #### Manuelle Stundeneintragung Für Korrekturen oder Sonderfälle: 1. Gehe zu "Zeiteinträge" Tab 2. Sektion "Manuelle Stundeneintragung" 3. Mitarbeiter auswählen 4. Datum eingeben 5. Stunden eingeben: - **Positive Werte** (z.B. `2.5`): Werden **abgezogen** (z.B. Krankheit, Urlaub) - **Negative Werte** (z.B. `-3.0`): Werden **hinzugerechnet** (z.B. Nachholung, Sondereinsatz) #### Zeiteinträge bearbeiten 1. Gehe zu "Zeiteinträge" Tab 2. Liste aller Einträge mit Bearbeitungsmöglichkeit 3. "Bearbeiten" klicken: - Datum ändern - Start-/Endzeit anpassen - Typ ändern (Unterricht/Pause/Manuell) 4. Speichern oder Löschen #### PDF-Export 1. Gehe zu "Zeiteinträge" Tab 2. Sektion "Jahresübersicht" 3. Klicke "PDF exportieren" 4. PDF enthält: - Schuljahrname und Zeitraum - Alle Mitarbeiter mit Soll/Ist/Differenz - Generierungsdatum ## 🔌 API-Dokumentation ### Authentifizierung Alle geschützten Endpunkte erfordern einen JWT-Token im Header: ``` Authorization: Bearer ``` ### Öffentliche Endpunkte #### `POST /api/login` Benutzer-Anmeldung **Request:** ```json { "username": "admin", "password": "" } ``` **Response:** ```json { "token": "eyJhbGc...", "username": "admin", "is_admin": true } ``` ### Geschützte Endpunkte (Benutzer) #### `GET /api/schedules` Alle Stundenpläne abrufen #### `GET /api/my-time-entries` Eigene Zeiteinträge abrufen #### `POST /api/time-entries/batch` Mehrere Zeiteinträge auf einmal erstellen **Request:** ```json { "entries": [ { "schedule_id": 1, "date": "2024-11-04", "type": "lesson", "start_time": "08:00", "end_time": "09:00" } ] } ``` #### `DELETE /api/my-time-entries/week?year=2024&week=45` Alle eigenen Einträge einer Woche löschen #### `GET /api/week-dates?year=2024&week=45` Datumsbereich einer Kalenderwoche abrufen #### `GET /api/week-has-entries?year=2024&week=45` Prüfen, ob für eine Woche bereits Einträge existieren #### `GET /api/yearly-hours-summary` Jahresübersicht für alle Benutzer #### `GET /api/my-info` Eigene Benutzerinformationen abrufen #### `GET /api/school-year/active` Aktives Schuljahr abrufen ### Admin-Endpunkte #### Stundenplan-Verwaltung - `POST /api/admin/schedules` - Stundenplan erstellen - `DELETE /api/admin/schedules/delete?id=1` - Stundenplan löschen #### Benutzerverwaltung - `POST /api/admin/users` - Benutzer erstellen - `GET /api/admin/users/list` - Alle Benutzer auflisten - `PUT /api/admin/users/:id` - Benutzer bearbeiten (Arbeitsstunden) - `PUT /api/admin/users/:id/reset-password` - Passwort zurücksetzen - `DELETE /api/admin/users/delete?id=2` - Benutzer löschen #### Zeiteintrags-Verwaltung - `GET /api/admin/time-entries` - Alle Zeiteinträge - `PUT /api/admin/time-entries/:id` - Zeiteintrag bearbeiten - `DELETE /api/admin/time-entries/:id` - Zeiteintrag löschen - `POST /api/admin/time-entry` - Manueller Zeiteintrag #### Schuljahrverwaltung - `GET /api/admin/school-years` - Alle Schuljahre - `POST /api/admin/school-years` - Schuljahr erstellen - `PUT /api/admin/school-years/:id/activate` - Schuljahr aktivieren - `DELETE /api/admin/school-years/:id` - Schuljahr löschen #### Berichte - `GET /api/admin/yearly-summary/pdf` - Jahresübersicht als PDF ## 🏗 Architektur ### Backend-Struktur ``` . ├── main.go # Einstiegspunkt, Server-Setup ├── handlers.go # HTTP-Handler für alle Endpunkte ├── middleware.go # JWT-Auth, Admin-Check, Rate-Limiting ├── database.go # Datenbanklogik und Queries ├── models.go # Datenstrukturen ├── pdf.go # PDF-Generierung ├── docker-compose.yml # Docker-Orchestrierung └── Dockerfile # Container-Image ``` ### Frontend-Struktur ``` static/ ├── Main.elm # Elm-Hauptanwendung │ ├── Model # Anwendungszustand │ ├── Update # Zustandsänderungen │ ├── View # UI-Rendering │ └── Subscriptions # Event-Handling ├── index.html # HTML-Wrapper └── elm.js # Kompilierte Elm-Anwendung ``` ### Datenbank-Schema #### Tabelle: `users` ```sql id INTEGER PRIMARY KEY username TEXT UNIQUE NOT NULL password TEXT NOT NULL (bcrypt-hashed) is_admin BOOLEAN DEFAULT 0 yearly_hours REAL DEFAULT 60.0 created_at DATETIME ``` #### Tabelle: `schedules` ```sql id INTEGER PRIMARY KEY day_of_week INTEGER (0=Montag, 4=Freitag) start_time TEXT (HH:MM) end_time TEXT (HH:MM) type TEXT (lesson/break) title TEXT created_at DATETIME ``` #### Tabelle: `time_entries` ```sql id INTEGER PRIMARY KEY user_id INTEGER (FK -> users) schedule_id INTEGER (FK -> schedules) date TEXT (YYYY-MM-DD) type TEXT (lesson/break/manual) start_time TEXT end_time TEXT created_at DATETIME ``` #### Tabelle: `school_years` ```sql id INTEGER PRIMARY KEY name TEXT UNIQUE start_date DATE end_date DATE is_active BOOLEAN DEFAULT 0 created_at DATETIME ``` #### Tabelle: `audit_logs` ```sql id INTEGER PRIMARY KEY user_id INTEGER action TEXT details TEXT created_at DATETIME ``` ### Stundenberechnung Die Anwendung berechnet Arbeitsstunden nach folgenden Regeln: 1. **Unterrichtsstunden** (`type: "lesson"`): **1,0 Stunde** (fest) 2. **Pausen** (`type: "break"`): Differenz zwischen End- und Startzeit 3. **Manuelle Einträge** (`type: "manual"`): - Positive Werte werden abgezogen - Negative Werte werden hinzugerechnet **Beispiel:** ``` Unterricht 08:00-09:00 → 1,0h Pause 09:00-09:15 → 0,25h Manueller Eintrag: 2.5 → -2,5h (Abzug) Manueller Eintrag: -1.0 → +1,0h (Zuschlag) ``` ### ISO-Kalenderwochen Die Anwendung verwendet ISO 8601 Kalenderwochen: - Woche beginnt am Montag - Erste Woche des Jahres enthält den 4. Januar - 52 oder 53 Wochen pro Jahr ## 🔒 Sicherheit ### Implementierte Sicherheitsmaßnahmen 1. **Passwort-Hashing**: bcrypt mit Default-Cost (10 Runden) 2. **JWT-Authentifizierung**: HMAC-SHA256 mit 2h Ablaufzeit 3. **CORS-Protection**: Konfigurierbare Origins 4. **Rate Limiting**: 5 Login-Versuche pro Minute pro IP 5. **SQL-Injection-Schutz**: Prepared Statements 6. **Admin-Schutz**: Admin-Benutzer (ID=1) kann nicht gelöscht werden 7. **Input-Validierung**: Server- und clientseitig ### Best Practices 1. **JWT_SECRET ändern**: Verwenden Sie einen starken, zufälligen String (64+ Zeichen) ```bash openssl rand -base64 64 ``` 2. **HTTPS verwenden**: In Produktion immer TLS/SSL aktivieren ```env ENVIRONMENT=production ``` 3. **Regelmäßige Backups**: Sichern Sie die Datenbank täglich 4. **Starke Passwörter**: Mindestens 12 Zeichen, Mix aus Groß-/Kleinbuchstaben, Zahlen, Sonderzeichen 5. **Updates**: Halten Sie Dependencies aktuell ```bash go get -u ./... docker-compose pull ``` ## 💾 Backup & Wartung ### Datenbank-Backup **Docker-Setup:** ```bash # Backup erstellen docker exec school-timetracking sqlite3 /data/timetracking.db ".backup '/data/backup-$(date +%Y%m%d).db'" docker cp school-timetracking:/data/backup-$(date +%Y%m%d).db ./backups/ # Backup wiederherstellen docker cp ./backups/backup-20241108.db school-timetracking:/data/timetracking.db docker-compose restart ``` **Lokales Setup:** ```bash # Backup sqlite3 timetracking.db ".backup 'backup-$(date +%Y%m%d).db'" # Wiederherstellen cp backup-20241108.db timetracking.db ``` ### Automatisches Backup (Cron) Erstellen Sie ein Backup-Script `backup.sh`: ```bash #!/bin/bash BACKUP_DIR="/path/to/backups" DATE=$(date +%Y%m%d-%H%M%S) docker exec school-timetracking sqlite3 /data/timetracking.db ".backup '/data/backup-$DATE.db'" docker cp school-timetracking:/data/backup-$DATE.db $BACKUP_DIR/ # Alte Backups löschen (älter als 30 Tage) find $BACKUP_DIR -name "backup-*.db" -mtime +30 -delete ``` Crontab-Eintrag (täglich um 3 Uhr): ``` 0 3 * * * /path/to/backup.sh ``` ### Log-Rotation Docker Compose Log-Größe begrenzen: ```yaml services: timetracking: logging: driver: "json-file" options: max-size: "10m" max-file: "3" ``` ### Updates durchführen ```bash # Repository aktualisieren git pull # Neue Images bauen docker-compose build # Container neu starten docker-compose down docker-compose up -d # Logs prüfen docker-compose logs -f ``` ## 🐛 Fehlerbehebung ### Anwendung startet nicht **Problem**: Container startet nicht ```bash docker-compose logs timetracking ``` **Häufige Ursachen:** - `JWT_SECRET` nicht gesetzt → Setzen Sie die Variable in `.env` - Port 8080 bereits belegt → Ändern Sie `PORT` in `.env` - Datenbank-Berechtigungen → Prüfen Sie Volume-Permissions ### Login funktioniert nicht **Problem**: "Invalid credentials" trotz korrekten Passworts **Lösung:** ```bash # Admin-Passwort zurücksetzen docker exec -it school-timetracking /bin/sh sqlite3 /data/timetracking.db # In SQLite: UPDATE users SET password = '$2a$10$...' WHERE username = 'admin'; .exit ``` Generieren Sie einen neuen bcrypt-Hash: ```bash # Mit Go echo -n 'neues-passwort' | go run -e 'import "golang.org/x/crypto/bcrypt"; pass, _ := bcrypt.GenerateFromPassword([]byte(os.Args[1]), bcrypt.DefaultCost); fmt.Println(string(pass))' ``` ### Zeiteinträge werden nicht gespeichert **Problem**: Fehler beim Speichern von Zeiteinträgen **Prüfen:** 1. Browser-Konsole auf JavaScript-Fehler prüfen 2. Backend-Logs prüfen: `docker-compose logs -f` 3. JWT-Token gültig? → Neu anmelden 4. Datenbank-Speicherplatz verfügbar? ### PDF-Export schlägt fehl **Problem**: "Failed to generate PDF" **Lösung:** ```bash # Container-Logs prüfen docker-compose logs timetracking | grep -i pdf # Häufig: Fehlende Schriftarten # → Rebuilden Sie das Image docker-compose build --no-cache ``` ### Responsive Layout funktioniert nicht **Problem**: Mobile Ansicht nicht korrekt **Lösung:** - Browser-Cache leeren - Elm neu kompilieren: ```bash cd static elm make Main.elm --output=elm.js --optimize ``` ### Datenbankfehler nach Update **Problem**: "Database schema error" **Lösung:** ```bash # Backup erstellen docker cp school-timetracking:/data/timetracking.db ./backup-pre-migration.db # Migration manuell durchführen docker exec -it school-timetracking sqlite3 /data/timetracking.db # Fehlende Spalten hinzufügen (Beispiel) ALTER TABLE users ADD COLUMN yearly_hours REAL DEFAULT 60.0; .exit ``` ## 📝 Häufig gestellte Fragen (FAQ) **Q: Wie ändere ich die Standard-Arbeitsstunden?** A: Als Admin unter "Benutzer" → Benutzer auswählen → "Arbeitszeit" klicken → Neue Stundenzahl eingeben. **Q: Können Mitarbeiter vergangene Wochen bearbeiten?** A: Ja, über die Wochen-Navigation können alle Wochen bearbeitet werden (sofern im aktuellen Schuljahr). **Q: Wie funktioniert die Schuljahr-Berechnung?** A: Das System berechnet Stunden nur für Einträge innerhalb des aktiven Schuljahres (Start- bis Enddatum). **Q: Was passiert bei 53-Wochen-Jahren?** A: Das System unterstützt ISO-Kalenderwochen inklusive Woche 53 automatisch. **Q: Kann ich mehrere Schuljahre parallel nutzen?** A: Es kann immer nur ein Schuljahr aktiv sein. Berechnungen basieren auf diesem Zeitraum. **Q: Wie funktionieren negative Stunden?** A: Negative Werte bei manuellen Einträgen werden **zum** Stundenkonto **hinzugerechnet** (für Zusatzleistungen). ## 📄 Lizenz Todo ## 👥 Kontakt & Support Todo --- **Version**: 1.7.0 **Letztes Update**: Januar 2026 **Entwickelt für**: Schulen zur Verwaltung von Flexistunden pädagogischer Mitarbeiter