# Projekt: iServ Softwarebestell-System

## Projektübersicht
Entwicklung eines webbasierten Systems zur Verwaltung von Softwarebestellungen für iServ-Schulserver. Schulen bestellen Software über ein Webformular, die Bestellungen werden intern geprüft/bearbeitet/genehmigt und anschließend als ausgefülltes PDF an iServ übermittelt.

---

## Getroffene Entscheidungen ✅

- **PDF-Generierung**: FPDF (Entwickler hat Erfahrung damit)
- **Authentication**: Dual-Login: LDAP (Produktion) + lokaler Admin (Entwicklung/Notfall)
- **E-Mail**: SMTP-Server vorhanden und konfiguriert
- **Hosting**: Eigener Server mit SSH-Zugang, PHP und Datenbank verfügbar
- **HTTPS**: Reverse Proxy vorhanden (kein Let's Encrypt erforderlich)
- **Datenbank**: PostgreSQL (oder MySQL - nach Präferenz)
- **Datenschutz**: Keine besonderen DSGVO-Anforderungen
- **Backups**: Werden vom Systemadministrator selbst durchgeführt
- **PHP-Version**: 8.4

---

## Anforderungen

### Funktionale Anforderungen

#### 1. Webformular (Frontend)
- **Basis:** Bereits vorhandenes HTML-Formular (`Softwarebestellung-v2.htm`)
- **Felder:**
  - Name der Schule (Pflichtfeld)
  - Checkbox: Software noch nicht von iServ paketiert (Pflichtfeld)
  - Software-Grunddaten: Name, Version, Hersteller, Website, ISBN (optional)
  - Bezugsquelle: Radio-Buttons (Kostenfreier Download / Kundenserver / Datenträger)
    - Bei Download: Textfeld für Pfad
  - Lizenzierung: Radio-Buttons (Keine / Lizenzschlüssel / Hardware-Dongle / Lizenzserver)
    - Bei Lizenzschlüssel: Textfeld
    - Bei Lizenzserver: Textarea für Anmerkungen
- **Aktion beim Absenden:**
  - Daten in Datenbank speichern
  - E-Mail-Ticket erstellen (via SMTP)
  - Bestätigung an Absender anzeigen

#### 2. Backend / Datenverwaltung

##### Datenbank-Schema
```sql
-- PostgreSQL-Syntax (für MySQL entsprechend anpassen)
CREATE TABLE bestellungen (
    id SERIAL PRIMARY KEY,
    schul_name VARCHAR(255) NOT NULL,
    status VARCHAR(50) DEFAULT 'neu' CHECK (status IN ('neu', 'in_pruefung', 'korrektur_notwendig', 'genehmigt', 'abgelehnt')),
    
    -- Software-Daten
    software_name VARCHAR(255) NOT NULL,
    version VARCHAR(100) NOT NULL,
    hersteller VARCHAR(255) NOT NULL,
    hersteller_website VARCHAR(500) NOT NULL,
    isbn VARCHAR(50),
    
    -- Bezug
    software_quelle VARCHAR(50) NOT NULL CHECK (software_quelle IN ('downloadFree', 'downloadCustomer', 'physicalMedia')),
    download_pfad TEXT,
    
    -- Lizenzierung
    lizenz_typ VARCHAR(50) NOT NULL CHECK (lizenz_typ IN ('noLicense', 'licenseKey', 'licenseHardware', 'licenseServer')),
    lizenzschluessel TEXT,
    lizenzserver_anmerkungen TEXT,
    
    -- Metadaten
    erstellt_am TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    bearbeitet_am TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    bearbeitet_von VARCHAR(100),
    notizen TEXT
);

CREATE INDEX idx_status ON bestellungen(status);
CREATE INDEX idx_schule ON bestellungen(schul_name);
CREATE INDEX idx_erstellt ON bestellungen(erstellt_am);

-- Optional aber empfohlen: Änderungshistorie
CREATE TABLE bestellungen_historie (
    id SERIAL PRIMARY KEY,
    bestellung_id INTEGER NOT NULL REFERENCES bestellungen(id) ON DELETE CASCADE,
    aenderung_von VARCHAR(100),
    aenderung_am TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    feld VARCHAR(100),
    alter_wert TEXT,
    neuer_wert TEXT
);

CREATE INDEX idx_bestellung ON bestellungen_historie(bestellung_id);
```

##### Admin-Interface (Web-basiert, LDAP-geschützt)
- **Login:** LDAP-Authentifizierung gegen iServ-Server
- **Übersicht:** Liste aller Bestellungen mit Filter (Status, Datum, Schule)
  - Sortierung nach Datum, Status, Schule
  - Pagination bei vielen Einträgen
- **Detailansicht:** Bestellung anzeigen + bearbeiten
- **Funktionen:**
  - Status ändern (Dropdown)
  - Felder korrigieren (alle Formularfelder editierbar)
  - Notizen hinzufügen (Textarea)
  - Änderungshistorie anzeigen (falls implementiert)
  - PDF generieren & als Download anbieten
  - PDF per E-Mail an iServ senden (mit Bestätigung)

#### 3. PDF-Generierung mit FPDF
- **Library:** FPDF (http://www.fpdf.org/)
- **Input:** Daten aus Datenbank (eine Bestellung)
- **Output:** Statisches PDF im Layout des iServ-Formulars
- **Methode:** PDF von Grund auf erstellen (Layout nachbauen)

##### PDF-Struktur (entsprechend Original)
```
+------------------------------------------+
| [iServ Logo/Header]                      |
+------------------------------------------+
| NEUE SOFTWAREPAKETE                      |
|                                          |
| Angaben zur Erstellung eines neuen       |
| Softwarepaketes                          |
|                                          |
| Liste: iserv.eu/deploy/software          |
+------------------------------------------+
| [X] Ich habe geprüft, dass die Software  |
|     noch nicht von IServ paketiert wurde.|
+------------------------------------------+
| Name der Software: ________________      |
| Versionsnummer:    ________________      |
| Hersteller:        ________________      |
| Hersteller-Website:________________      |
| ISBN:              ________________      |
+------------------------------------------+
| Bezug der Software                       |
| [ ] Kostenfreier Download                |
|     Pfad: ___________________________    |
| [ ] Download vom Kundenserver            |
|     Pfad: ___________________________    |
| [ ] Software wird auf Datenträger        |
|     bereitgestellt                       |
+------------------------------------------+
| Lizenzierung der Software                |
| (Lizenzinformationen sind für IServ zum  |
|  Testen der Paketierung zwingend         |
|  erforderlich)                           |
|                                          |
| [ ] keine Lizenz notwendig               |
| [ ] Lizenzschlüssel: ________________    |
| [ ] Lizenz als Hardware (z.B. Dongle)    |
| [ ] Lizenzserver wird benötigt           |
|     Anmerkungen: ____________________    |
|     __________________________________   |
+------------------------------------------+
```

##### FPDF Implementierung - Wichtige Punkte
```php
<?php
require_once('fpdf/fpdf.php');

class IServPDF extends FPDF {
    // Header mit Logo/Branding
    function Header() {
        $this->SetFont('Arial', 'B', 16);
        $this->Cell(0, 10, 'NEUE SOFTWAREPAKETE', 0, 1, 'C');
        $this->Ln(5);
    }
    
    // Checkbox-Funktion
    function Checkbox($x, $y, $checked = false) {
        $this->Rect($x, $y, 4, 4);
        if ($checked) {
            $this->Line($x, $y, $x+4, $y+4);
            $this->Line($x+4, $y, $x, $y+4);
        }
    }
    
    // Textfeld mit Label
    function LabeledField($label, $value, $width = 150) {
        $this->SetFont('Arial', 'B', 10);
        $this->Cell(50, 6, $label, 0, 0);
        $this->SetFont('Arial', '', 10);
        $this->Cell($width, 6, $value, 'B', 1);
    }
}

// Verwendung
$pdf = new IServPDF();
$pdf->AddPage();
$pdf->SetFont('Arial', '', 10);

// Daten aus DB befüllen
$pdf->Checkbox(10, 40, true); // "Software noch nicht paketiert"
$pdf->Text(16, 43, 'Ich habe geprüft, dass die Software noch nicht von IServ paketiert wurde.');

$pdf->SetY(50);
$pdf->LabeledField('Name der Software:', $data['software_name']);
$pdf->LabeledField('Versionsnummer:', $data['version']);
// ... weitere Felder

$pdf->Output('F', 'bestellung_' . $data['id'] . '.pdf');
?>
```

#### 4. E-Mail-Integration (SMTP)

##### Konfiguration
```php
// PHPMailer oder native SMTP-Verbindung
$mail_config = [
    'smtp_host' => 'smtp.example.com',
    'smtp_port' => 587,
    'smtp_user' => 'user@example.com',
    'smtp_pass' => 'password',
    'smtp_secure' => 'tls',  // oder 'ssl'
    'from_email' => 'softwarebestellung@example.com',
    'from_name' => 'Softwarebestell-System'
];
```

##### E-Mail-Szenarien

**A) Beim Formular-Absenden (Neue Bestellung):**
```
An:       tickets@example.com (internes Ticket-System)
Betreff:  Neue Softwarebestellung #{ID}: {Software-Name} - {Schul-Name}
Text:
    Eine neue Softwarebestellung ist eingegangen:
    
    Bestell-ID: {ID}
    Schule: {Schul-Name}
    Software: {Software-Name} v{Version}
    Hersteller: {Hersteller}
    Status: Neu
    
    Erstellt am: {Datum/Uhrzeit}
    
    Link zur Bearbeitung:
    https://example.com/admin/detail.php?id={ID}
    
    Bitte prüfen und bearbeiten Sie die Bestellung.
```

**B) Bei Genehmigung (an iServ):**
```
An:       software@iserv.de (oder entsprechende Adresse)
CC:       {E-Mail der Schule} (falls vorhanden)
Betreff:  Softwarepaketierung: {Software-Name} v{Version}
Text:
    Sehr geehrtes iServ-Team,
    
    anbei finden Sie die ausgefüllten Informationen für ein
    neues Softwarepaket.
    
    Software: {Software-Name}
    Version:  {Version}
    Schule:   {Schul-Name}
    
    Bei Rückfragen stehen wir Ihnen gerne zur Verfügung.
    
    Mit freundlichen Grüßen
    {Absender-Name/Organisation}

Anhang:   Informationen_Softwarepaket_{Software-Name}.pdf
```

---

## Technischer Stack

```
Frontend:      HTML5, CSS3, Vanilla JavaScript (bereits vorhanden)
Backend:       PHP 8.4
Datenbank:     PostgreSQL (bevorzugt) oder MySQL/MariaDB
PDF:           FPDF (http://www.fpdf.org/)
Webserver:     Apache oder Nginx mit mod_php / PHP-FPM
E-Mail:        PHPMailer oder native SMTP-Funktionen
Auth:          LDAP (ldap_bind gegen iServ-Server) + Fallback lokaler Login
Session:       PHP Sessions für Admin-Login-Status
```

---

## Projekt-Struktur

```
softwarebestellung/
├── public/                          # Öffentlich zugänglich (DocumentRoot)
│   ├── index.php                   # Formular (umbenannt von .html)
│   ├── submit.php                  # Formular-Verarbeitung
│   ├── css/
│   │   └── style.css               # Vorhandenes Styling
│   └── js/
│       └── form.js                 # JavaScript für Formular
│
├── admin/                           # LDAP-geschützt
│   ├── index.php                   # Übersicht aller Bestellungen
│   ├── detail.php                  # Bestellung anzeigen/bearbeiten
│   ├── generate_pdf.php            # PDF-Generierung
│   ├── send_to_iserv.php           # E-Mail an iServ senden
│   ├── login.php                   # LDAP-Login-Formular
│   ├── logout.php                  # Session beenden
│   └── ajax/                       # AJAX-Endpoints (optional)
│       └── update_status.php       # Status via AJAX ändern
│
├── includes/                        # PHP-Backend-Logik (außerhalb DocumentRoot!)
│   ├── config.php                  # Zentrale Konfiguration
│   ├── database.php                # DB-Verbindung + Query-Funktionen
│   ├── email.php                   # E-Mail-Funktionen (SMTP)
│   ├── pdf_generator.php           # FPDF-Wrapper-Klasse
│   ├── auth.php                    # LDAP-Authentication
│   └── functions.php               # Hilfsfunktionen (sanitize, validate, etc.)
│
├── templates/                       # HTML-Templates (optional, falls MVC)
│   ├── header.php
│   ├── footer.php
│   └── admin_layout.php
│
├── vendor/                          # Composer Dependencies (falls genutzt)
│   ├── fpdf/                       # FPDF Library
│   └── phpmailer/                  # PHPMailer (optional)
│
├── data/                            # Außerhalb DocumentRoot!
│   ├── pdfs/                       # Temporäre PDFs (mit .htaccess protected)
│   └── logs/                       # Log-Dateien
│       ├── app.log
│       ├── email.log
│       └── auth.log
│
├── sql/
│   ├── schema.sql                  # DB-Schema (CREATE TABLE statements)
│   └── seed.sql                    # Test-Daten (optional)
│
├── .htaccess                        # Apache-Konfiguration (falls Apache)
├── composer.json                    # PHP-Dependencies (optional)
└── README.md                        # Projekt-Dokumentation
```

---

## Entwicklungs-Phasen

### Phase 1: Basis-Setup (Priorität: HOCH)
**Ziel:** Grundfunktionalität zum Laufen bringen

- [ ] Datenbank-Schema erstellen (`sql/schema.sql`)
- [ ] Basis-Konfiguration (`includes/config.php`)
- [ ] DB-Verbindung aufbauen (`includes/database.php`)
- [ ] Formular: POST-Handler implementieren (`public/submit.php`)
  - Validierung aller Pflichtfelder
  - Daten in DB speichern
  - Fehlerbehandlung
- [ ] E-Mail-Versand bei Neubestellung (Ticket-System)
  - SMTP-Konfiguration
  - E-Mail-Template für neue Bestellungen
- [ ] LDAP-Login implementieren (`admin/login.php`, `includes/auth.php`)
  - Login-Formular
  - LDAP-Bind gegen iServ
  - Session-Verwaltung
- [ ] Admin-Übersicht (`admin/index.php`)
  - Liste aller Bestellungen aus DB
  - Basis-Styling
  - Filter nach Status (optional)

**Erwartetes Ergebnis:** 
- Schulen können Bestellungen absenden → landet in DB + E-Mail
- Admins können sich einloggen → sehen Liste der Bestellungen

---

### Phase 2: Bearbeitungsfunktionen (Priorität: HOCH)
**Ziel:** Bestellungen verwalten und korrigieren können

- [ ] Detailansicht (`admin/detail.php`)
  - Bestellung aus DB laden und anzeigen
  - Alle Felder in Formular darstellbar
- [ ] Bearbeiten-Funktion
  - Formular zum Ändern aller Felder
  - Update in Datenbank (UPDATE-Query)
  - Validierung
- [ ] Status-Verwaltung
  - Dropdown für Status-Änderung
  - Automatisches Logging (wer/wann)
- [ ] Notizen-Funktion
  - Textarea für interne Notizen
  - Speicherung in DB
- [ ] **Optional:** Änderungshistorie
  - Bei jedem UPDATE: alten Wert in `bestellungen_historie` speichern
  - Anzeige der Historie in Detailansicht

**Erwartetes Ergebnis:**
- Admin kann Bestellungen öffnen und alle Felder ändern
- Status kann gesetzt werden
- Notizen können hinzugefügt werden

---

### Phase 3: PDF-Generierung (Priorität: HOCH)
**Ziel:** Fertiges PDF für iServ erstellen

- [ ] FPDF-Integration
  - Library einbinden (Composer oder manuell)
  - Basis-Klasse erstellen (`includes/pdf_generator.php`)
- [ ] PDF-Layout implementieren
  - Header/Logo
  - Checkbox-Funktion
  - Alle Formularfelder positionieren
  - Layout dem Original-PDF nachempfinden
- [ ] Daten aus DB ins PDF schreiben
  - Bestellung laden
  - Felder befüllen
  - Checkboxen setzen (checked/unchecked)
- [ ] PDF-Generierung in Admin-Interface
  - Button "PDF generieren" in Detailansicht
  - PDF erstellen und zum Download anbieten
  - **Optional:** PDF-Vorschau im Browser
- [ ] PDF temporär speichern
  - In `data/pdfs/` mit eindeutigem Namen
  - Automatische Löschung alter PDFs (Cronjob oder beim Generieren)

**Erwartetes Ergebnis:**
- Admin kann in Detailansicht auf "PDF generieren" klicken
- PDF wird erstellt und kann heruntergeladen werden
- PDF sieht aus wie das Original-Formular von iServ

---

### Phase 4: E-Mail an iServ (Priorität: HOCH)
**Ziel:** Genehmigte Bestellungen automatisch an iServ senden

- [ ] E-Mail-Versand mit PDF-Anhang
  - SMTP-Funktion für Anhänge erweitern
  - PDF an E-Mail anhängen
- [ ] Versand-Funktion in Admin-Interface
  - Button "An iServ senden" (nur bei Status "genehmigt")
  - Bestätigungsdialog ("Wirklich senden?")
  - Nach Versand: Status auf "versendet" setzen (optional: neuer Status)
- [ ] E-Mail-Template für iServ
  - Höflicher Text (siehe oben)
  - Korrekte Betreffzeile
  - CC an Schule (falls E-Mail vorhanden)
- [ ] Logging
  - Versand in `data/logs/email.log` protokollieren
  - Bei Fehler: Fehlermeldung anzeigen + loggen

**Erwartetes Ergebnis:**
- Admin kann genehmigte Bestellung mit einem Klick an iServ senden
- E-Mail enthält PDF-Anhang
- Versand wird protokolliert

---

### Phase 5: Verbesserungen & Feinschliff (Priorität: MITTEL)
**Ziel:** Benutzererfahrung verbessern

- [ ] Erweiterte Filter/Suche in Admin-Übersicht
  - Nach Schul-Name
  - Nach Software-Name
  - Nach Datum-Range
- [ ] Sortierung in Tabelle
  - Klickbare Spalten-Header
- [ ] Pagination
  - Bei >20 Bestellungen: Seitennavigation
- [ ] Statistiken (optional)
  - Dashboard mit Anzahl Bestellungen pro Status
  - Häufigste Software-Anfragen
- [ ] E-Mail-Templates konfigurierbar
  - In Config auslagern statt hard-coded
- [ ] Automatische Duplikat-Erkennung (optional)
  - Warnung wenn gleiche Software von gleicher Schule schon bestellt
- [ ] Responsive Design für Admin-Interface
  - Mobile-optimiertes CSS

**Erwartetes Ergebnis:**
- Angenehme Bedienung des Admin-Interfaces
- Bessere Übersichtlichkeit bei vielen Bestellungen

---

## LDAP-Authentication + Fallback lokaler Login

**Wichtig:** Das System unterstützt ZWEI Login-Methoden:
1. **LDAP** (Produktion gegen iServ-Server)
2. **Lokaler Admin** (Entwicklung/Notfall ohne LDAP)

### Konfiguration (config.php)
```php
// LDAP-Einstellungen
define('LDAP_ENABLED', true);         // false = nur lokaler Login
define('LDAP_SERVER', 'ldap://iserv.example.com');
define('LDAP_PORT', 389);
define('LDAP_BASE_DN', 'ou=users,dc=iserv,dc=example,dc=com');
define('LDAP_BIND_DN', 'uid=%username%,ou=users,dc=iserv,dc=example,dc=com');

// Optional: Gruppen-basierte Berechtigung
define('LDAP_ADMIN_GROUP', 'cn=it-admins,ou=groups,dc=iserv,dc=example,dc=com');
define('LDAP_USE_GROUP_CHECK', false); // true = nur Gruppenmitglieder dürfen rein

// Lokaler Admin (Fallback für Entwicklung)
define('LOCAL_ADMIN_ENABLED', true);  // Immer aktiviert für Fallback
define('LOCAL_ADMIN_USER', 'admin');
define('LOCAL_ADMIN_PASS_HASH', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi'); // bcrypt hash von "password"
// Hash generieren mit: php -r "echo password_hash('dein_passwort', PASSWORD_BCRYPT);"
```

### Implementation (includes/auth.php)
```php
<?php
function authenticate($username, $password) {
    // Leere Eingaben ablehnen
    if (empty($username) || empty($password)) {
        return false;
    }
    
    // Zuerst lokalen Admin prüfen (immer verfügbar)
    if (LOCAL_ADMIN_ENABLED && $username === LOCAL_ADMIN_USER) {
        if (password_verify($password, LOCAL_ADMIN_PASS_HASH)) {
            return ['method' => 'local', 'username' => $username];
        }
    }
    
    // Dann LDAP versuchen (wenn aktiviert)
    if (LDAP_ENABLED) {
        $ldap_result = ldap_authenticate($username, $password);
        if ($ldap_result) {
            return ['method' => 'ldap', 'username' => $username];
        }
    }
    
    return false;
}

function ldap_authenticate($username, $password) {
    // Verbindung zum LDAP-Server
    $ldap_conn = @ldap_connect(LDAP_SERVER, LDAP_PORT);
    if (!$ldap_conn) {
        error_log('LDAP: Verbindung fehlgeschlagen zu ' . LDAP_SERVER);
        return false;
    }
    
    // LDAP-Optionen setzen
    ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, 3);
    ldap_set_option($ldap_conn, LDAP_OPT_REFERRALS, 0);
    ldap_set_option($ldap_conn, LDAP_OPT_NETWORK_TIMEOUT, 5);
    
    // DN konstruieren
    $bind_dn = str_replace('%username%', $username, LDAP_BIND_DN);
    
    // Authentifizierung
    $bind = @ldap_bind($ldap_conn, $bind_dn, $password);
    
    if ($bind) {
        // Optional: Gruppen-Zugehörigkeit prüfen
        if (LDAP_USE_GROUP_CHECK) {
            $is_admin = check_ldap_group($ldap_conn, $username);
            ldap_close($ldap_conn);
            return $is_admin;
        }
        
        ldap_close($ldap_conn);
        return true;
    } else {
        error_log("LDAP: Auth fehlgeschlagen für User: $username");
        ldap_close($ldap_conn);
        return false;
    }
}

function check_ldap_group($ldap_conn, $username) {
    // Suche nach User in Admin-Gruppe
    $search = ldap_search(
        $ldap_conn,
        LDAP_ADMIN_GROUP,
        "(memberUid=$username)"
    );
    
    if (!$search) {
        return false;
    }
    
    $entries = ldap_get_entries($ldap_conn, $search);
    return ($entries['count'] > 0);
}

// Session-Funktionen
function is_logged_in() {
    return isset($_SESSION['authenticated']) && $_SESSION['authenticated'] === true;
}

function require_login() {
    if (!is_logged_in()) {
        header('Location: login.php');
        exit;
    }
}
?>
```

### Login-Formular (admin/login.php)
```php
<?php
session_start();
require_once('../includes/config.php');
require_once('../includes/auth.php');

// Bereits eingeloggt? Dann weiter zur Übersicht
if (is_logged_in()) {
    header('Location: index.php');
    exit;
}

$error = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = $_POST['username'] ?? '';
    $password = $_POST['password'] ?? '';
    
    $auth_result = authenticate($username, $password);
    
    if ($auth_result) {
        $_SESSION['authenticated'] = true;
        $_SESSION['username'] = $auth_result['username'];
        $_SESSION['auth_method'] = $auth_result['method'];
        $_SESSION['login_time'] = time();
        
        error_log("Login erfolgreich: {$username} via {$auth_result['method']}");
        
        header('Location: index.php');
        exit;
    } else {
        $error = 'Benutzername oder Passwort falsch';
        error_log("Login-Versuch fehlgeschlagen: $username");
    }
}
?>
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <title>Login - Softwarebestellung</title>
    <link rel="stylesheet" href="../public/css/style.css">
</head>
<body>
    <div class="container">
        <div class="login-card">
            <h1>Admin-Login</h1>
            <p style="color: #666; margin-bottom: 20px; font-size: 0.9em;">
                <?php if (LDAP_ENABLED): ?>
                    Login mit LDAP-Account oder lokalem Admin-Account
                <?php else: ?>
                    Login nur mit lokalem Admin-Account (LDAP deaktiviert)
                <?php endif; ?>
            </p>
            
            <?php if ($error): ?>
                <div class="error"><?= htmlspecialchars($error) ?></div>
            <?php endif; ?>
            
            <form method="POST">
                <div class="form-group">
                    <label for="username">Benutzername</label>
                    <input type="text" id="username" name="username" required autofocus>
                </div>
                
                <div class="form-group">
                    <label for="password">Passwort</label>
                    <input type="password" id="password" name="password" required>
                </div>
                
                <button type="submit" class="btn-submit">Anmelden</button>
            </form>
        </div>
    </div>
</body>
</html>
```

---

## Sicherheits-Anforderungen

### 1. SQL-Injection Prevention
```php
// IMMER Prepared Statements verwenden!
$stmt = $pdo->prepare("SELECT * FROM bestellungen WHERE id = ?");
$stmt->execute([$id]);

// ODER mit Named Parameters
$stmt = $pdo->prepare("SELECT * FROM bestellungen WHERE schul_name = :schule");
$stmt->execute(['schule' => $schulname]);
```

### 2. XSS Prevention
```php
// Alle Ausgaben escapen
echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');

// In Attributen
<input value="<?= htmlspecialchars($value, ENT_QUOTES) ?>">
```

### 3. CSRF Protection
```php
// Token generieren (bei Session-Start)
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// In Formularen
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">

// Bei Verarbeitung prüfen
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'] ?? '')) {
    die('CSRF-Token ungültig');
}
```

### 4. Session Security
```php
// In config.php
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);      // Reverse Proxy terminiert HTTPS
ini_set('session.use_strict_mode', 1);
session_start();

// Session-Timeout (30 Minuten)
if (isset($_SESSION['login_time']) && (time() - $_SESSION['login_time'] > 1800)) {
    session_destroy();
    header('Location: login.php');
    exit;
}
```

### 5. File Access Protection
```php
// .htaccess in data/ Verzeichnis
# Deny access to all files
Order deny,allow
Deny from all

# PDFs nur durch PHP ausliefern (mit Access-Check)
```

### 6. Input Validation
```php
function validate_url($url) {
    return filter_var($url, FILTER_VALIDATE_URL) !== false;
}

function validate_email($email) {
    return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}

// Pflichtfelder prüfen
$required_fields = ['schulName', 'softwareName', 'version', /* ... */];
foreach ($required_fields as $field) {
    if (empty($_POST[$field])) {
        $errors[] = "Feld '$field' ist erforderlich";
    }
}
```

### 7. Error Handling
```php
// In Produktion: Keine Fehler anzeigen
error_reporting(E_ALL);
ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', '/path/to/data/logs/php_errors.log');

// Custom Error Handler
set_error_handler(function($errno, $errstr, $errfile, $errline) {
    error_log("[$errno] $errstr in $errfile:$errline");
    // User-freundliche Meldung
    die('Ein Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.');
});
```

---

## Konfigurations-Datei (includes/config.php)

```php
<?php
// Fehlerbehandlung
error_reporting(E_ALL);
ini_set('display_errors', '1');  // In Produktion auf '0'!
ini_set('log_errors', '1');
ini_set('error_log', __DIR__ . '/../data/logs/php_errors.log');

// Timezone
date_default_timezone_set('Europe/Berlin');

// Datenbank (PostgreSQL)
define('DB_TYPE', 'pgsql');           // oder 'mysql'
define('DB_HOST', 'localhost');
define('DB_PORT', '5432');            // 3306 für MySQL
define('DB_NAME', 'softwarebestellung');
define('DB_USER', 'db_user');
define('DB_PASS', 'db_password');

// LDAP
define('LDAP_SERVER', 'ldap://iserv.example.com');
define('LDAP_PORT', 389);
define('LDAP_BASE_DN', 'ou=users,dc=iserv,dc=example,dc=com');
define('LDAP_BIND_DN', 'uid=%username%,' . LDAP_BASE_DN);
define('LDAP_USE_GROUP_CHECK', false);
define('LDAP_ADMIN_GROUP', 'cn=it-admins,ou=groups,dc=iserv,dc=example,dc=com');

// E-Mail (SMTP)
define('SMTP_HOST', 'smtp.example.com');
define('SMTP_PORT', 587);
define('SMTP_USER', 'user@example.com');
define('SMTP_PASS', 'smtp_password');
define('SMTP_SECURE', 'tls');         // 'tls' oder 'ssl'
define('SMTP_FROM_EMAIL', 'softwarebestellung@example.com');
define('SMTP_FROM_NAME', 'Softwarebestell-System');

// E-Mail-Adressen
define('MAIL_TICKET_TO', 'tickets@example.com');
define('MAIL_ISERV_TO', 'software@iserv.de');

// Pfade
define('BASE_PATH', dirname(__DIR__) . '/');
define('PUBLIC_PATH', BASE_PATH . 'public/');
define('ADMIN_PATH', BASE_PATH . 'admin/');
define('DATA_PATH', BASE_PATH . 'data/');
define('PDF_PATH', DATA_PATH . 'pdfs/');
define('LOG_PATH', DATA_PATH . 'logs/');

// URLs
define('BASE_URL', 'https://example.com/softwarebestellung');
define('ADMIN_URL', BASE_URL . '/admin');

// Sonstiges
define('PDF_RETENTION_DAYS', 7);      // PDFs nach 7 Tagen löschen
define('SESSION_TIMEOUT', 1800);      // 30 Minuten

// Session-Konfiguration
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);       // OK da Reverse Proxy HTTPS terminiert
ini_set('session.use_strict_mode', 1);
?>
```

---

## Test-Szenarien

### Test 1: Neue Bestellung erstellen
1. Öffne `public/index.php` im Browser
2. Fülle alle Pflichtfelder aus
3. Wähle Bezugsquelle und Lizenztyp
4. Klicke "Formular absenden"
5. **Erwartung:**
   - Erfolgsmeldung wird angezeigt
   - Eintrag in `bestellungen` Tabelle vorhanden
   - E-Mail an Ticket-System erhalten
   - Bestellung-ID in E-Mail enthalten

### Test 2: Admin-Login (LDAP)
1. Öffne `admin/login.php`
2. Gib gültigen LDAP-Benutzernamen + Passwort ein
3. **Erwartung:**
   - Weiterleitung zu `admin/index.php`
   - Session aktiv
4. Versuche Login mit falschen Credentials
5. **Erwartung:**
   - Fehlermeldung "Benutzername oder Passwort falsch"
   - Kein Login möglich

### Test 3: Bestellung bearbeiten
1. Logge dich als Admin ein
2. Öffne Bestellung in Detailansicht
3. Ändere ein Feld (z.B. Versionsnummer)
4. Klicke "Speichern"
5. **Erwartung:**
   - Änderung in DB gespeichert
   - Erfolgsmeldung angezeigt
   - `bearbeitet_am` aktualisiert
6. **Optional:** Prüfe Historie-Tabelle
   - Alter Wert gespeichert

### Test 4: PDF generieren
1. Öffne genehmigte Bestellung
2. Klicke "PDF generieren"
3. **Erwartung:**
   - PDF-Download startet
   - PDF enthält alle Daten der Bestellung
   - Checkboxen korrekt gesetzt
   - Layout sieht professionell aus
4. Öffne PDF und prüfe:
   - Software-Name korrekt
   - Lizenzinformationen korrekt
   - Bezugsquelle korrekt

### Test 5: E-Mail an iServ senden
1. Setze Bestellung auf Status "genehmigt"
2. Klicke "An iServ senden"
3. Bestätige Dialog
4. **Erwartung:**
   - E-Mail mit PDF-Anhang an `MAIL_ISERV_TO` gesendet
   - Bestätigung im Admin-Interface
   - Eintrag in `data/logs/email.log`
5. Prüfe empfangene E-Mail:
   - PDF im Anhang
   - Korrekter Betreff
   - Höflicher Text

### Test 6: Fehlerbehandlung
1. Versuche Bestellung ohne Pflichtfelder abzusenden
2. **Erwartung:**
   - Fehlermeldungen für fehlende Felder
   - Keine Speicherung in DB
3. Versuche ungültige URL einzugeben
4. **Erwartung:**
   - Validierungsfehler
5. Simuliere DB-Ausfall (DB stoppen)
6. **Erwartung:**
   - Benutzer-freundliche Fehlermeldung
   - Fehler geloggt in `php_errors.log`

### Test 7: Session-Timeout
1. Logge dich ein
2. Warte 31 Minuten (oder setze `SESSION_TIMEOUT` auf 60 Sekunden für Test)
3. Versuche eine Aktion im Admin-Bereich
4. **Erwartung:**
   - Weiterleitung zu Login
   - Meldung "Session abgelaufen"

### Test 8: CSRF-Protection
1. Öffne Formular im Browser
2. Kopiere HTML-Source
3. Erstelle separate HTML-Datei und ändere Action-URL
4. Sende Formular ohne CSRF-Token
5. **Erwartung:**
   - Anfrage wird abgelehnt
   - Fehler "CSRF-Token ungültig"

---

## Deployment-Anleitung

### 1. Server-Vorbereitung
```bash
# Als root oder mit sudo

# PHP 8.4 installieren (falls nicht vorhanden)
apt update
apt install php8.4 php8.4-fpm php8.4-pgsql php8.4-ldap php8.4-mbstring php8.4-gd

# PostgreSQL installieren
apt install postgresql postgresql-contrib

# Webserver (Apache oder Nginx)
apt install apache2
# ODER
apt install nginx
```

### 2. Datenbank einrichten
```bash
# Als postgres-User
sudo -u postgres psql

# In psql:
CREATE DATABASE softwarebestellung;
CREATE USER softwarebestellung_user WITH PASSWORD 'sicheres_passwort';
GRANT ALL PRIVILEGES ON DATABASE softwarebestellung TO softwarebestellung_user;
\q

# Schema importieren
psql -U softwarebestellung_user -d softwarebestellung -f /path/to/sql/schema.sql
```

### 3. Projekt deployen
```bash
# Repository klonen oder Dateien hochladen
cd /var/www/
git clone <repo-url> softwarebestellung
# ODER
scp -r softwarebestellung/ user@server:/var/www/

# Verzeichnisse erstellen
cd softwarebestellung
mkdir -p data/pdfs data/logs
chmod 750 data
chmod 770 data/pdfs data/logs
chown www-data:www-data data/pdfs data/logs

# FPDF installieren (falls nicht im Repo)
cd includes
wget http://www.fpdf.org/en/download/fpdf184.zip
unzip fpdf184.zip
rm fpdf184.zip
```

### 4. Konfiguration anpassen
```bash
# config.php mit echten Werten befüllen
nano includes/config.php

# Wichtig: DB-Credentials, LDAP-Server, SMTP-Zugangsdaten eintragen
```

### 5. Apache VirtualHost (Beispiel)
```apache
<VirtualHost *:80>
    ServerName softwarebestellung.example.com
    DocumentRoot /var/www/softwarebestellung/public
    
    <Directory /var/www/softwarebestellung/public>
        AllowOverride All
        Require all granted
    </Directory>
    
    # Admin-Bereich (falls extra Protection gewünscht)
    <Directory /var/www/softwarebestellung/admin>
        AllowOverride All
        Require all granted
    </Directory>
    
    ErrorLog ${APACHE_LOG_DIR}/softwarebestellung_error.log
    CustomLog ${APACHE_LOG_DIR}/softwarebestellung_access.log combined
</VirtualHost>

# HTTPS wird durch Reverse Proxy terminiert
# Keine direkte HTTPS-Konfiguration im Apache erforderlich
```

### 6. Cronjob für alte PDFs (optional)
```bash
# Crontab editieren
crontab -e

# Täglich um 2 Uhr alte PDFs löschen (älter als 7 Tage)
0 2 * * * find /var/www/softwarebestellung/data/pdfs -type f -mtime +7 -delete
```

### 8. Log-Rotation einrichten
```bash
# /etc/logrotate.d/softwarebestellung
/var/www/softwarebestellung/data/logs/*.log {
    weekly
    rotate 4
    compress
    delaycompress
    missingok
    notifempty
    create 0640 www-data www-data
}
```

### 9. Test nach Deployment
```bash
# PHP-Syntax prüfen
find /var/www/softwarebestellung -name "*.php" -exec php -l {} \;

# DB-Verbindung testen
php /var/www/softwarebestellung/includes/database.php

# Log-Dateien beobachten
tail -f /var/www/softwarebestellung/data/logs/*.log
```

---

## Monitoring & Wartung

### Log-Dateien regelmäßig prüfen
```bash
# Application Log
tail -f data/logs/app.log

# E-Mail Log
tail -f data/logs/email.log

# Auth Log
tail -f data/logs/auth.log

# PHP Errors
tail -f data/logs/php_errors.log
```

### Performance-Überwachung
```bash
# DB-Performance
psql -U softwarebestellung_user -d softwarebestellung -c "
SELECT 
    schemaname,
    tablename,
    pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables 
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
"

# Anzahl Bestellungen pro Status
psql -U softwarebestellung_user -d softwarebestellung -c "
SELECT status, COUNT(*) FROM bestellungen GROUP BY status;
"
```

### Backup-Strategie
```bash
# DB-Backup (täglich via Cronjob)
pg_dump -U softwarebestellung_user softwarebestellung | gzip > /backup/softwarebestellung_$(date +%Y%m%d).sql.gz

# Datei-Backup (data/ Verzeichnis)
tar -czf /backup/softwarebestellung_data_$(date +%Y%m%d).tar.gz /var/www/softwarebestellung/data
```

---

## Troubleshooting

### Problem: LDAP-Login funktioniert nicht
```bash
# LDAP-Verbindung testen
ldapsearch -x -H ldap://iserv.example.com -b "ou=users,dc=iserv,dc=example,dc=com"

# PHP-LDAP-Extension installiert?
php -m | grep ldap

# Falls nicht:
apt install php8.1-ldap
systemctl restart apache2
```

### Problem: E-Mails werden nicht versendet
```bash
# SMTP-Verbindung testen
telnet smtp.example.com 587

# Log prüfen
tail -f data/logs/email.log

# PHP-Mail-Funktion testen
php -r "mail('test@example.com', 'Test', 'Test');"
```

### Problem: PDF-Generierung schlägt fehl
```bash
# FPDF installiert?
ls -la includes/fpdf/

# GD-Library installiert? (für Bilder in PDF)
php -m | grep gd

# Schreibrechte auf data/pdfs/?
ls -la data/
```

### Problem: DB-Verbindung schlägt fehl
```bash
# PostgreSQL läuft?
systemctl status postgresql

# Verbindung von außen erlaubt?
# /etc/postgresql/*/main/pg_hba.conf prüfen

# Netzwerk-Test
nc -zv localhost 5432
```

---

## Erweitungsmöglichkeiten (Zukunft)

- **Multi-Mandanten-Fähigkeit:** Mehrere Organisationen/Träger verwalten
- **REST-API:** Externe Systeme können Bestellungen abrufen/erstellen
- **Workflow-Engine:** Mehrstufige Genehmigungsprozesse
- **Attachment-Upload:** Lizenzdokumente, Screenshots anhängen
- **Automatische Benachrichtigungen:** E-Mail bei Status-Änderungen
- **Export-Funktionen:** Excel/CSV-Export aller Bestellungen
- **Dashboard:** Grafische Auswertungen, Statistiken
- **Versionsverwaltung:** Alte Versionen von Bestellungen wiederherstellen

---

## Zusammenfassung

**Ziel:** Funktionierendes Softwarebestell-System mit:
- ✅ Webformular für Schulen
- ✅ Datenbank-Backend (PostgreSQL)
- ✅ Dual-Login: LDAP-geschützt (Produktion) + lokaler Admin (Entwicklung)
- ✅ Bearbeitungs-/Korrektur-Funktionen
- ✅ PDF-Generierung mit FPDF
- ✅ E-Mail-Integration (SMTP)
- ✅ Sicherheit (SQL-Injection, XSS, CSRF)
- ✅ Reverse Proxy ready (kein Let's Encrypt nötig)

**Technologien:**
- PHP 8.4
- PostgreSQL
- FPDF
- LDAP + lokaler Fallback
- SMTP

**Erfolgskriterien:**
- Schulen können Software bestellen → landet in DB + Ticket-E-Mail
- Admins können sich per LDAP einloggen
- Bestellungen können bearbeitet/korrigiert werden
- PDFs werden korrekt generiert
- E-Mail-Versand an iServ funktioniert
- System ist sicher und wartbar

---

**Viel Erfolg bei der Umsetzung! 🚀**

Bei Fragen oder Unklarheiten während der Entwicklung stehe ich gerne zur Verfügung.
