school-timetracker/README.md

19 KiB

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

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

  • Elm 0.19: Funktionale Programmiersprache für type-safe UI
  • Bulma CSS: Modernes CSS-Framework
  • 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.21+
  • Elm 0.19
  • Node.js 16+ (für Elm-Tooling)
  • SQLite3

🚀 Installation

Option 1: Docker Compose (Produktion)

  1. Repository klonen
git clone <repository-url>
cd zeiterfassung
  1. Umgebungsvariablen konfigurieren
cp .env.example .env
nano .env

Wichtige Variablen in .env:

PORT=8080
DB_PATH=/data/timetracking.db
JWT_SECRET=ihr-sicheres-geheimnis-hier-ändern
TZ=Europe/Berlin
  1. Anwendung starten
docker-compose up -d
  1. 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
go mod download
go build -o timetracking
  1. Frontend kompilieren
cd static
elm make Main.elm --output=elm.js --optimize
cd ..
  1. Umgebungsvariablen setzen
export PORT=8080
export DB_PATH=./timetracking.db
export JWT_SECRET=development-secret
  1. Anwendung starten
./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:

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 <token>

Öffentliche Endpunkte

POST /api/login

Benutzer-Anmeldung

Request:

{
  "username": "admin",
  "password": "<your-initial-admin-password>"
}

Response:

{
  "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:

{
  "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

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

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

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

id              INTEGER PRIMARY KEY
name            TEXT UNIQUE
start_date      DATE
end_date        DATE
is_active       BOOLEAN DEFAULT 0
created_at      DATETIME

Tabelle: audit_logs

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)

    openssl rand -base64 64
    
  2. HTTPS verwenden: In Produktion immer TLS/SSL aktivieren

    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

    go get -u ./...
    docker-compose pull
    

💾 Backup & Wartung

Datenbank-Backup

Docker-Setup:

# 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:

# 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:

#!/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:

services:
  timetracking:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Updates durchführen

# 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

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:

# 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:

# 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:

# 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:
    cd static
    elm make Main.elm --output=elm.js --optimize
    

Datenbankfehler nach Update

Problem: "Database schema error"

Lösung:

# 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.5.0
Letztes Update: November 2025
Entwickelt für: Schulen zur Verwaltung von Flexistunden pädagogischer Mitarbeiter