diff --git a/README.md b/README.md new file mode 100644 index 0000000..684b448 --- /dev/null +++ b/README.md @@ -0,0 +1,773 @@ +# 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: 1800h) + - 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** + +```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: `admin123` + +⚠️ **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** | +| `TZ` | Zeitzone | `Europe/Berlin` | Nein | +| `ENVIRONMENT` | `production` für HTTPS-Redirect | - | 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/admin123) + +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: 1800h) + - 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": "admin123" +} +``` + +**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 1800.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 1800.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.0.0 +**Letztes Update**: November 2024 +**Entwickelt für**: Schulen zur Verwaltung von Flexistunden pädagogischer Mitarbeiter diff --git a/backend/database.go b/backend/database.go index be4fcb3..bd15b02 100644 --- a/backend/database.go +++ b/backend/database.go @@ -35,7 +35,7 @@ func createTables(db *sql.DB) { username TEXT UNIQUE NOT NULL, password TEXT NOT NULL, is_admin BOOLEAN NOT NULL DEFAULT 0, - yearly_hours REAL NOT NULL DEFAULT 1800.0, -- 40 Stunden/Woche * 45 Schulwochen + yearly_hours REAL NOT NULL DEFAULT 60.0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )`, `CREATE TABLE IF NOT EXISTS schedules ( diff --git a/backend/handlers.go b/backend/handlers.go index e45c2aa..e9567d6 100644 --- a/backend/handlers.go +++ b/backend/handlers.go @@ -438,7 +438,7 @@ func (app *App) CreateUserHandler(c echo.Context) error { } if req.YearlyHours == 0 { - req.YearlyHours = 1800.0 + req.YearlyHours = 60.0 } if err := CreateUser(app.DB, req.Username, string(hashedPassword), req.IsAdmin, req.YearlyHours); err != nil {