CNR-Handwerker-Plus

Gesamtdokumentation · Sprint 1 bis Sprint 7

17
Tabellen
16
Views
7
Stored Procedures
8
Trigger
35
API-Endpoints
11.03.2026
Letzte Aktualisierung
01

Datenbankentwurf

Konzeptionelles, logisches und physisches Datenmodell · Normalisierung · Sicherheitskonzept

Einleitung & Projektziele

Im Rahmen des Projekts wurde für CNR-Handwerker-Plus eine relationale Datenbank entwickelt. Ziel ist die strukturierte Verwaltung aller relevanten Geschäftsprozesse eines Handwerksbetriebs.

Datenbank dient als zentrale Quelle für:
  • Kundenverwaltung
  • Mitarbeiterverwaltung
  • Auftragsverwaltung
  • Terminplanung
  • Angebots- und Rechnungsverwaltung
Entwicklungsprinzipien:
  • Datenkonsistenz
  • Erweiterbarkeit
  • Normalisierung bis 3NF
  • Klare Tabellenbeziehungen

Die Modellierung orientiert sich an den Anforderungen der IHK für Fachinformatiker Anwendungsentwicklung.

Konzeptionelles Datenmodell
Entitäten
Kunde Mitarbeiter Auftrag Termin Angebot Rechnung Position Login_Log Material Auftrag_Material

Die Entität Kunde wurde bewusst weiter unterteilt, um Privat- und Firmenkunden sauber zu trennen:

kunden kunden_person kunden_firma kunden_adressen kunden_kontakt
Kardinalitäten
BeziehungKardinalitätBedeutung
Kunde – Auftrag1 : NEin Kunde kann mehrere Aufträge besitzen
Auftrag – Position1 : NEin Auftrag enthält mehrere Positionen
Auftrag – Termin1 : NEin Auftrag kann mehrere Termine besitzen
Mitarbeiter – Termin1 : NEin Mitarbeiter kann mehrere Termine durchführen
Auftrag – Rechnung1 : 0..1Ein Auftrag kann optional eine Rechnung erzeugen
Auftrag – MaterialN : MAufgelöst über Verknüpfungstabelle auftrag_material
Logisches Datenmodell — Tabellen
Kunden-Gruppe (5 Tabellen)
kunden (Haupttabelle)
  • kunden_id PK
  • typ — privat / firma
  • ist_stammkunde — BOOLEAN
  • erstellt_am
  • notizen
kunden_person
  • kunden_id PK FK
  • vorname
  • nachname
kunden (1) —— (0..1) kunden_person
kunden_firma
  • kunden_id PK FK
  • firmenname
  • ansprechpartner
  • ust_id
kunden (1) —— (0..1) kunden_firma
kunden_adressen
  • adress_id PK
  • kunden_id FK
  • strasse, hausnummer
  • plz, ort
  • typ
kunden (1) —— (0..N) kunden_adressen
kunden_kontakt
  • kontakt_id PK
  • kunden_id FK
  • typ — Telefon, Email, Mobil…
  • wert
kunden (1) —— (0..N) kunden_kontakt
mitarbeiter
  • mitarbeiter_id PK
  • vorname, nachname
  • email UNIQUE
  • password_hash — Argon2ID / bcrypt
  • rolle — admin, meister, geselle, azubi, buero
  • aktiv — BOOLEAN
  • letzte_login — DATETIME
auftrag — Zentrale Tabelle
  • auftrag_id PK
  • kunden_id FK
  • auftrag_nr UNIQUE — auto-generiert
  • titel, beschreibung
  • status — neu, geplant, aktiv, abgeschlossen, abgerechnet, storniert
  • prioritaet — niedrig, normal, dringend, notfall
  • notiz_intern, kunden_kommentar
  • abgeschlossen_am, erstellt_am
termin
  • termin_id PK
  • auftrag_id FK
  • mitarbeiter_id FK
  • start_datetime, end_datetime
  • status
  • CHECK: end_datetime > start_datetime
rechnung
  • rechnung_id PK
  • auftrag_id FK
  • kunden_id FK
  • rechnungs_datum
  • faellig_am
  • status — entwurf, gesendet, bezahlt, überfällig
  • gesamtbetrag DECIMAL(10,2)
material
  • material_id PK
  • name
  • beschreibung
  • einheit — m, Stk, Pkg…
  • lagerbestand
  • preis_pro_einheit DECIMAL(10,2)
Physisches Datenmodell
Verwendete Datentypen
DatentypVerwendung
INTPrimärschlüssel, IDs
VARCHARTextfelder (Namen, E-Mail)
TEXTBeschreibungen, Notizen
DECIMAL(10,2)Preiswerte (exakt, kein Float!)
DECIMAL(10,3)Mengen (z.B. 0,5 Stunden)
DATE / DATETIMEKalenderdaten / Zeitstempel
BOOLEANJa/Nein-Felder
ENUMStatusfelder, Typen, Rollen
Verwendete Constraints
ConstraintZweck
PRIMARY KEYEindeutige Identifikation
FOREIGN KEYTabellenverknüpfung
NOT NULLPflichtfelder erzwingen
AUTO_INCREMENTAutomatische ID-Generierung
UNIQUEDuplikate verhindern (Email, Nr)
CHECKLogikwächter (Preis ≥ 0, Datum)
Normalisierung

Die Datenbank wurde bis zur 3. Normalform (3NF) normalisiert.

1. Normalform

Alle Attribute sind atomar — keine Mehrfachwerte in einem Feld.

2. Normalform

Alle Nicht-Schlüsselattribute hängen vollständig vom Primärschlüssel ab.

3. Normalform

Keine transitiven Abhängigkeiten — Kundendaten in separate Tabellen ausgelagert.

Bewusste Denormalisierungen (begründet)
  • Preishistorisierung in auftrag_position und rechnung_position: Der Preis wird zum Zeitpunkt der Bestellung kopiert. Preisänderungen in der Zukunft dürfen bestehende Rechnungen nicht rückwirkend verändern (GoBD-konform).
  • MwSt-Satz direkt in Positionstabellen gespeichert: Sichert korrekte Berechnung bei gesetzlichen Steuersatzänderungen.
  • netto_summe in angebot: Bewusste Denormalisierung als Performance-Cache. Verhindert rechenintensive Aggregationen bei jedem Seitenaufruf.
  • auftrag_material: Löst N:M-Beziehung auf und verhindert Materialkosten-Redundanz.
Sicherheitskonzept & Datenintegrität
Datenbankebene
  • Fremdschlüsselbeziehungen mit ON DELETE CASCADE / SET NULL
  • NOT NULL Constraints an Pflichtfeldern
  • ENUM-Werte für kontrollierte Eingaben
  • AUTO_INCREMENT Schlüssel
  • UNIQUE auf E-Mail und Auftragsnummern
Anwendungsebene
  • Passwortspeicherung als Hash (Argon2ID / bcrypt)
  • Login-Protokollierung in login_log
  • Rollenbasierte Zugriffskontrolle (admin → meister → geselle → azubi → buero)
  • Pflichtfelder bei Kundenerstellung
  • Validierung von Datumswerten
  • Trennung von Benutzer- und Kundendaten

02

Technische Datenbankdokumentation

Datentypen · Constraints · Installationsanleitung

Warum DECIMAL und nicht FLOAT?
❌ FLOAT / DOUBLE

Binäre Gleitkommazahlen → Rundungsfehler bei kaufmännischen Berechnungen.

// Klassisches Problem:
0.1 + 0.2 = 0.300000000004
✅ DECIMAL(10,2)

Exakte Festkommazahlen → keine Cent-Abweichungen bei Rechnungen.

// Verwendung in CNR-Handwerker-Plus:
DECIMAL(10,2) ← Preise, Summen, MwSt
DECIMAL(10,3) ← Mengen (0,5 Std, 1,255 kg)

Für ein Handwerker-System, in dem Rechnungen rechtssicher erstellt werden müssen, ist DECIMAL zwingend erforderlich.

Weitere wichtige Datentypen
DatentypEinsatz in CNR-Handwerker-PlusBegründung
ENUM status, rolle, typ Stellt sicher, dass nur vordefinierte Werte eingetragen werden (z.B. Rolle kann nicht "Chef" sein)
BOOLEAN ist_stammkunde, aktiv Intern TINYINT(1) — ideal für Ja/Nein-Werte
DATETIME Login-Log, Erstellung, Termine Für Protokolle mit genauem Zeitpunkt
DATE Rechnungsdatum, Fälligkeitsdatum Für reine Kalendertage ohne Uhrzeit
Erklärung der wichtigsten Constraints

Constraints sichern die Datenintegrität. Ohne sie würde die Datenbank schnell inkonsistent.

ConstraintWirkung
PRIMARY KEYIdentifiziert Datensatz eindeutig, verhindert Dubletten
FOREIGN KEYVerknüpft Tabellen, verhindert Waisen-Datensätze
ON DELETE CASCADELöscht automatisch Adressen/Kontakte wenn Kunde gelöscht wird
ON DELETE SET NULLlogin_log bleibt erhalten, mitarbeiter_id wird NULL (Historie)
CHECKLogikwächter: netto_summe ≥ 0, end_datetime > start_datetime
UNIQUEEmail, auftrag_nr darf systemweit nur einmal vorkommen
-- Beispiel: Fremdschlüssel mit Cascade
ALTER TABLE kunden_adressen
  ADD CONSTRAINT fk_adr_kunde
  FOREIGN KEY (kunden_id)
  REFERENCES kunden(kunden_id)
  ON DELETE CASCADE;

-- CHECK Constraint
CHECK (end_datetime > start_datetime)
CHECK (netto_summe >= 0)
Installation der Datenbank
1
Voraussetzungen

MySQL-Server (z.B. via XAMPP, MariaDB oder Docker) + ein DB-Client (phpMyAdmin, MySQL Workbench, DBeaver oder Konsole).

2
Datenbankstruktur erstellen
-- Zuerst: Struktur anlegen
SOURCE create_db.sql;
3
Testdaten einspielen
-- Danach: Testdaten einfügen
SOURCE test_data.sql;
4
Verbindung prüfen (Konsole)
mysql -u dein_benutzer -p
USE handwerkerpro_db;
SHOW TABLES;
SELECT * FROM mitarbeiter;
5
Erfolgskontrolle

Wenn folgende Abfrage 10 Einträge mit verschiedenen Status-Werten liefert, war die Installation erfolgreich:

SELECT * FROM auftrag; -- Erwartung: 10 Einträge

03

Views & Query-Dokumentation

Tagesgeschäft · Wochenplanung · Monatsreporting · Material & Lager

1. Tagesgeschäft — schneller Überblick
view_wartende_kunden

Was: Alle Anfragen mit Status neu, sortiert nach Wartezeit. Wofür: Schnelle Reaktion auf Neukunden.

kunden_idkundeanfrage_titelerstellt_amtage_warten
7Bauhaus AGIndustrie-Wartung Q12026-02-1515
6Dieter BohlenLeitung verstopft2026-02-1020
view_ueberfaellige_rechnungen

Was: Offene Rechnungen über dem Fälligkeitsdatum. Wofür: Liquiditätskontrolle und Mahnwesen.

rechnung_idfaellig_amtage_verzugbrutto_betrag
42026-03-011214,20 €
102026-02-246450,00 €
view_tagesplan_mitarbeiter

Was: Alle Termine für den heutigen Tag. Wofür: Einsatzplanung.

mitarbeiterstart_datetimeend_datetimeauftragprioritaet
Max Mustermann11:00:0014:00:00Leitung verstopftnormal
Lena Zimmer09:00:0017:00:00Industrie-Wartungdringend
2. Wochenplanung — Ressourcen & Material
view_wochenuebersicht_auftraege

Alle Aufträge, die diese Woche aktiv bearbeitet werden.

auftrag_nrtitelstatusprioritaet
AUF-011Leitung verstopftneunormal
AUF-012Industrie-Wartungneudringend
view_mitarbeiter_auslastung_woche

Geplante Stunden pro Mitarbeiter für die aktuelle KW.

vornamenachnamegeplante_stunden
LenaZimmer38 h
KlausBauer12 h
view_materialbedarf_woche

Benötigtes Material vs. aktueller Lagerbestand für die Woche — ermöglicht vorausschauende Beschaffung.

namebenoetigte_mengeaktueller_bestandeinheit
Kupferrohr 15mm10.00100Meter
Heizungspumpe1.004Stück
3. Monatsreporting — Erfolgskontrolle
view_monatsumsatz_mitarbeiter

Was: Netto-Umsatz pro Mitarbeiter für den laufenden Monat. Wofür: Faire Leistungsbewertung und Provisionsberechnung.

Technische Besonderheit: Durch SELECT DISTINCT mitarbeiter_id, auftrag_id wird sichergestellt, dass ein Auftrag dem Mitarbeiter nur einmal zugerechnet wird, auch wenn er mehrfach vor Ort war. Verhindert künstliche Aufblähung der Umsatzzahlen.
vornamenachnamemonats_umsatz
LenaZimmer1.450,00 €
KlausBauer1.100,00 €
MaxMustermann920,00 €
view_durchschnittliche_bearbeitungszeit

Mittlere Dauer in Tagen von Anfrage bis abgeschlossen_am.

avg_tage_bis_abschluss
6.45

Das Feld abgeschlossen_am wurde explizit eingeführt um präzise Messung zu ermöglichen.

view_top_10_kunden

Wertvollste Kunden mit Klarnamen nach Gesamtumsatz.

kunden_namegesamt_umsatz
Berger Bau GmbH4.250,00 €
Bauhaus AG2.100,00 €
4. Material & Lager-Views
view_material_nachbestellen

Artikel unter 5 Einheiten Lagerbestand.

namelagerbestandeinheit
Heizungspumpe4Stück
Siphon flexibel2Stück
view_meistverwendete_materialien

Ranking nach Gesamtverbrauch über alle Aufträge.

namegesamt_verbrauch
Dichtungssatz54.00
Kupferrohr 15mm45.00
Technische Zusammenfassung
Doppelzählungen eliminiert

Kombination aus Subquery (GROUP BY r.auftrag_id) und SELECT DISTINCT mitarbeiter_id, auftrag_id — ein Umsatz wird pro Mitarbeiter und Auftrag exakt einmal gewertet.

Performance-Indizes

idx_termin_start und idx_rechnung_faellig stellen sicher dass Joins und Filter auch bei großen Datensätzen performant bleiben.

Datenintegrität 100%

Feld abgeschlossen_am ermöglicht präzise Bearbeitungszeitenmessung — nicht verfälschbar durch nachträgliche Notizen.

Struktur & Benennung

Alle Views sprechend benannt (view_...), logisch gegliedert und durchgehend kommentiert. Modularer Aufbau erleichtert zukünftige Dashboard-Erweiterungen.


04

Automatisierung & Integrität

Stored Procedures · Trigger · Transaktionen · Performance · Abnahmetests

1. Stored Procedures

Stored Procedures kapseln komplexe Abläufe in einfache Befehle und validieren Eingaben. Alle kritischen Prozeduren sind mit TRANSACTION + EXIT HANDLER abgesichert.

SPsp_neuer_auftrag

Erstellt einen neuen Auftrag. Prüft vorab ob kunden_id vorhanden ist.

CALL sp_neuer_auftrag(
  KundenID,
  'Titel',
  'Priorität'
);
SPsp_material_zuordnen

Verknüpft Material mit Auftrag. Prüft: Menge positiv + genug Lagerbestand vorhanden.

CALL sp_material_zuordnen(
  AuftragID,
  MaterialID,
  Menge
);
SPsp_rechnung_bezahlen

Markiert eine Rechnung als bezahlt und aktualisiert den Zeitstempel. Wird auch von REST-API aufgerufen.

SPsp_auftrag_abschliessen

Setzt Status auf abgeschlossen und trägt Datum in abgeschlossen_am ein. Löst indirekt den Trigger zur automatischen Rechnungserstellung aus.

SPsp_material_nachbestellen

Erhöht Lagerbestand. Stellt sicher dass keine negativen Mengen nachbestellt werden (Validierung).

CALL sp_material_nachbestellen(
  MaterialID,
  Menge  -- muss > 0 sein
);
SPsp_update_ueberfaellige_rechnungen

Prüft alle offenen Rechnungen. Wenn faellig_am mehr als 30 Tage zurückliegt → Status automatisch auf überfällig. Für Cron-Job geeignet.

2. Trigger

Trigger reagieren in Echtzeit auf Datenbankänderungen — vollautomatisch, ohne Benutzereingriff.

#Trigger-NameEreignisAutomatische Aktion
A tr_auftrag_nummer_gen BEFORE INSERT Auftrag Erzeugt automatisch Auftragsnummer wie 2026-001 via LPAD(MAX()+1, 4)
B tr_lager_abbuchen AFTER INSERT Materialzuordnung Reduziert sofort den Lagerbestand um die verbrauchte Menge
C tr_kunden_loeschschutz BEFORE DELETE Kunden Blockiert Löschen wenn Kunde noch aktive (nicht abgeschlossene) Aufträge hat
D tr_rechnung_status_log
tr_auftrag_status_log
AFTER UPDATE Rechnung/Auftrag Schreibt jede Statusänderung automatisch in status_log Tabelle
E tr_auto_rechnung_erstellen AFTER UPDATE Auftrag Erstellt sofort neue Rechnung wenn Auftrag auf abgeschlossen gesetzt wird
F tr_lager_rueckgabe_delete AFTER DELETE Auftrag_Material Material aus Auftrag entfernt → Lagerbestand wird automatisch erhöht
G tr_lager_anpassung_update AFTER UPDATE Auftrag_Material Mengenänderung → Differenz wird automatisch im Lagerbestand korrigiert
H tr_kunden_archivieren BEFORE DELETE Kunden Kopiert Kundendaten unmittelbar vor dem Löschen in kunden_archiv
3. Transaktionen

Kritische Operationen werden durch Transaktionen gesichert. Bei Fehler werden alle Teilschritte rückgängig gemacht (Rollback).

Beispiel: Auftrag stornieren
  1. Status wird auf storniert gesetzt
  2. Material wird dem Lager wieder gutgeschrieben
Sicherheit: Schlägt die Materialrückbuchung fehl, wird auch die Statusänderung nicht gespeichert (Datenkonsistenz durch Rollback).
BEGIN;
  -- 1. Status setzen
  UPDATE auftrag
    SET status = 'storniert'
    WHERE auftrag_id = ?;

  -- 2. Material zurückbuchen
  UPDATE material
    SET lagerbestand = lagerbestand + ?
    WHERE material_id = ?;
COMMIT;

-- Bei Fehler:
ROLLBACK;
4. Performance-Optimierung (Indizes)
IndexTabelle/SpalteOptimiertSprint
idx_auftrag_statusauftrag.statusSuche nach abgeschlossenen/offenen ProjektenSprint 3
idx_termin_starttermin.start_datetimeTages-/Wochenplan JoinsSprint 3
idx_rechnung_faelligrechnung.faellig_am30-Tage-ÜberfälligkeitscheckSprint 3
idx_rechnung_datumrechnung.rechnungs_datumDatumsbasierte FilterungenSprint 4
idx_mat_bestandmaterial.lagerbestandSchnelle Bestandsprüfung vor MaterialzuordnungSprint 4
idx_log_zeitstatus_log.erstellt_amZeitbasierte Abfragen auf der Log-TabelleSprint 4
5. Abnahmetests — Systemvalidierung

Ein stabiles System muss nicht nur unter Normalbedingungen, sondern auch in Grenzfällen (Edge Cases) die Datenintegrität wahren.

Szenario 1Überprüfung des Lagerbestands (Negativtest)

Vorgang: CALL sp_material_zuordnen(1, [id], 10) bei einem Material mit Bestand = 5

Erwartung: SIGNAL SQLSTATE mit Meldung "Nicht genügend Lagerbestand!" — kein neuer Datensatz in auftrag_material (Rollback-Nachweis)

Szenario 2Kettenreaktion und Fehlerbehandlung

Vorbereitung: NOT NULL Spalte zur rechnung Tabelle hinzufügen, die Trigger nicht bedient.

Vorgang: CALL sp_auftrag_abschliessen(1)Erwartung: EXIT HANDLER verhindert Statusänderung, Rollback auf ursprünglichen Zustand.

Szenario 3Dublettenprüfung und MAX()-Logik

Vorgang: Manuell Auftrag mit Nummer 2026-999 einfügen, dann sp_neuer_auftrag aufrufen.

Erwartung: Neuer Auftrag erhält Nummer 2026-1000 — beweist Stabilität der MAX()-Logik gegenüber COUNT()-Logik.

Szenario 4Kundenschutz (Referenzielle Integrität)

Vorgang: DELETE FROM kunden WHERE kunden_id = 1 bei Kunde mit offenen Aufträgen.

Erwartung: Trigger tr_kunden_loeschschutz verhindert Löschung und gibt Fehlermeldung aus.

Szenario 5Zeitbasierte Automatisierung (Mahnwesen)

Vorbereitung: faellig_am auf 35 Tage in der Vergangenheit setzen, Status = gesendet.

Erwartung: CALL sp_update_ueberfaellige_rechnungen() setzt nur diese Rechnung auf überfällig. Statusänderung wird in status_log dokumentiert.

Szenario 6Atomarität und Fehlerbehandlung (Rollback-Test)

Vorgang: sp_material_zuordnen mit nicht existierender auftrag_id aufrufen.

Erwartung: Fehlermeldung + kein Lagerbestandsabzug (keine halben Datenbankzustände).

Szenario 7Kontrolle von multiplen Materialien und Gesamtbetrag

Szenario: Zwei Materialien zu unterschiedlichen Preisen einem Auftrag hinzufügen.

Erwartung: Rechnungsbetrag = (Menge1 × Preis1) + (Menge2 × Preis2) — exakt.

Szenario 8Rückgabe an Lagerbestand nach Stornierung

Ablauf: Material zuweisen → Lagerbestandsabzug prüfen → Auftrag stornieren.

Erwartung: Lagerbestand wird vollständig auf den ursprünglichen Wert zurückgesetzt.

Szenario 9Korrektheit der Archivierung

Ablauf: Kunden ohne Aufträge löschen.

Erwartung: Nicht mehr in kunden — aber vollständig mit allen Daten in kunden_archiv vorhanden.


05

REST API — Slim Framework 4

PHP 8.1 · PDO Prepared Statements · Argon2ID · CORS · 22 Endpoints

Basis-URL
http://localhost/api

Alle Antworten:

Content-Type: application/json; charset=UTF-8
Technologie-Stack
FrameworkSlim Framework 4
SprachePHP 8.1+
DB-ZugriffPDO (Prepared Statements, kein String-Concat!)
PasswortArgon2ID / bcrypt
WebserverApache + mod_rewrite
Antwort-Struktur

✅ Erfolg:

{
  "status":  "success",
  "message": "Beschreibung",
  "data":    { ... }
}

❌ Fehler:

{
  "status":  "error",
  "message": "...",
  "details": { ... }
}

HTTP-Codes:

200 OK
201 Created
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
409 Conflict
422 Unprocessable
500 Server Error
Pagination
GET /api/kunden?page=2&limit=10
// Antwort enthält: total, page, limit, pages
Installation (Slim Framework)
1
Dateien platzieren
C:\xampp\htdocs\
└── slim-api\
    ├── composer.json
    ├── config\        ← database.php, settings.php
    ├── public\        ← Apache DocumentRoot
    └── src\           ← Controllers, Middleware, Models
2
Abhängigkeiten installieren
cd C:\xampp\htdocs\slim-api
composer install
// Installiert: slim/slim, slim/psr7, php-di/php-di, monolog
3
Apache konfigurieren (httpd-vhosts.conf)
<VirtualHost *:80>
    DocumentRoot "C:/xampp/htdocs/slim-api/public"
    ServerName localhost
    <Directory "C:/xampp/htdocs/slim-api/public">
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
4
Datenbank-Zugangsdaten anpassen (config/database.php)
return [
  'host' => 'localhost',
  'name' => 'handwerkerpro_db',
  'user' => 'root',
  'pass' => '',
];
5
Apache restarten → testen
http://localhost/api
http://localhost/docs/testclient.html
Authentifizierung & Rollensystem
Methode 1: API-Key Header
X-API-Key: admin-key-12345
// oder:
Authorization: Bearer admin-key-12345
Methode 2: Session-Login
POST /api/auth/login
{
  "email":    "...",
  "password": "demo123"
}
Demo API-Keys
API-KeyRolleBerechtigungen
admin-key-12345adminVollzugriff inkl. Material löschen
meister-key-67890meisterAlles außer Material löschen
geselle-key-11111geselleLesen, Erstellen, Bearbeiten
// Rollenhierarchie (höher = mehr Rechte):
admin (5) > meister (4) > geselle (3) > azubi (2) > buero (1)

GET    → alle Rollen
POST   → geselle+
PUT    → geselle+
DELETE → meister+ (kunden/auftraege), admin (material)
Sicherheit
Prepared Statements

PDO mit ATTR_EMULATE_PREPARES=false. String-Concatenation für SQL ist vollständig verboten.

Passwort-Hashing

Argon2ID via password_hash() und password_verify(). Niemals Klartext-Passwörter.

Input-Sanitizing

Alle Eingaben via strip_tags() + trim() (XSS-Schutz). ENUM-Werte per Whitelist validiert.

CORS-Header

CorsMiddleware setzt alle nötigen Header. OPTIONS Preflight wird sofort beantwortet.

Fehlerbehandlung & Validierung
Validierungsregeln
  • Pflichtfelder fehlen → 422
  • ENUM-Wert nicht erlaubt → 422
  • Datum ungültig (YYYY-MM-DD) → 422
  • Preis/Menge ≤ 0 → 422
  • E-Mail invalid → 422
  • Ressource nicht gefunden → 404
  • DB-Trigger blockiert (offene Aufträge) → 409
// 422 mit Details:
{
  "status":  "error",
  "message": "Pflichtfelder fehlen.",
  "details": {
    "fehlende_felder": [
      "vorname",
      "nachname"
    ]
  }
}
Endpoints: Auth
POST/api/auth/loginÖffentlich
curl -X POST http://localhost/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email":    "[email protected]",
    "password": "demo123"
  }'
// Antwort 200:
{
  "mitarbeiter_id": 1,
  "rolle": "admin",
  "session_aktiv": true
}
Fehler: 401 falsches Passwort · 422 fehlende Felder
GET/api/auth/mealle Rollen
curl http://localhost/api/auth/me -H "X-API-Key: admin-key-12345"
POST/api/auth/logoutalle Rollen
curl -X POST http://localhost/api/auth/logout -H "X-API-Key: admin-key-12345"
Endpoints: Dashboard
GET/api/dashboardalle Rollen

KPIs, heutige Termine, Notfall-Aufträge, kritische Lagerbestände, Mitarbeiterauslastung.

curl http://localhost/api/dashboard -H "X-API-Key: admin-key-12345"
Endpoints: Kunden
GET/api/kundenalle Rollen

Filter: typ (privat/firma), stammkunde (bool), search, page, limit

curl "http://localhost/api/kunden?typ=firma&stammkunde=true" \
  -H "X-API-Key: admin-key-12345"
GET/api/kunden/{id}alle Rollen

Vollständiger Datensatz inkl. Kontakte, Adressen, Personen/Firmen-Details.

curl http://localhost/api/kunden/1 -H "X-API-Key: admin-key-12345"
GET/api/kunden/{id}/auftraegealle Rollen
curl http://localhost/api/kunden/1/auftraege -H "X-API-Key: admin-key-12345"
POST/api/kundengeselle+

Privatkunde:

{ "typ": "privat", "vorname": "Hans",
  "nachname": "Maier",
  "kontakt_typ": "Mobil",
  "kontakt_wert": "0170-1234567" }

Firmenkunde:

{ "typ": "firma",
  "firmenname": "Muster GmbH",
  "ansprechpartner": "Herr Koch",
  "ust_id": "DE123456789" }
PUT/api/kunden/{id}geselle+
curl -X PUT http://localhost/api/kunden/1 \
  -H "X-API-Key: admin-key-12345" \
  -d '{ "ist_stammkunde": true, "notizen": "VIP-Kunde" }'
DELETE/api/kunden/{id}meister+

Trigger tr_kunden_loeschschutz blockiert bei offenen Aufträgen → 409

curl -X DELETE http://localhost/api/kunden/5 -H "X-API-Key: meister-key-67890"
Endpoints: Aufträge
GET/api/auftraegealle Rollen

Filter: status (kommagetrennt), prioritaet, kunden_id, datum_von, datum_bis, search, sort, dir

curl "http://localhost/api/auftraege?status=neu,aktiv&prioritaet=notfall" \
  -H "X-API-Key: admin-key-12345"
POST/api/auftraegegeselle+

Via sp_neuer_auftrag. Auftragsnummer automatisch via Trigger. Pflicht: kunden_id, titel (3–150 Zeichen).

curl -X POST http://localhost/api/auftraege \
  -H "X-API-Key: geselle-key-11111" \
  -d '{
    "kunden_id":  1,
    "titel":      "Heizungsreparatur Keller",
    "prioritaet": "dringend"
  }'
PUT/api/auftraege/{id}/statusgeselle+

Spezialverhalten: abgeschlossen → ruft sp_auftrag_abschliessen auf → Rechnung wird automatisch erstellt. storniertsp_auftrag_stornieren → Material zurückgebucht.

curl -X PUT http://localhost/api/auftraege/3/status \
  -H "X-API-Key: admin-key-12345" \
  -d '{ "status": "abgeschlossen" }'
DELETE/api/auftraege/{id}meister+

Abgerechnete Aufträge können nicht gelöscht werden → 409

curl -X DELETE http://localhost/api/auftraege/15 -H "X-API-Key: meister-key-67890"
Endpoints: Rechnungen
GET/api/rechnungenalle Rollen

Filter: status, kunden_id, ueberfaellig=true, datum_von, datum_bis

curl "http://localhost/api/rechnungen?ueberfaellig=true" \
  -H "X-API-Key: admin-key-12345"
POST/api/rechnungen/{id}/bezahlenalle Rollen

Via sp_rechnung_bezahlen. Bereits bezahlte Rechnung → 409

curl -X POST http://localhost/api/rechnungen/2/bezahlen \
  -H "X-API-Key: admin-key-12345"
PUT/api/rechnungen/{id}geselle+

Aktualisierbar: status, faellig_am (Format: YYYY-MM-DD)

curl -X PUT http://localhost/api/rechnungen/2 \
  -d '{ "status": "gesendet", "faellig_am": "2026-04-01" }'
Endpoints: Material
GET/api/materialalle Rollen

Filter: search, niedrig_bestand, bestellliste, nachbestellen, meistverwendet

curl "http://localhost/api/material?niedrig_bestand=1" \
  -H "X-API-Key: admin-key-12345"

curl "http://localhost/api/material?bestellliste=1" \
  -H "X-API-Key: admin-key-12345"
POST/api/materialmeister+

Pflicht: name, preis_pro_einheit (>0). Lagerbestand darf nicht negativ sein.

curl -X POST http://localhost/api/material \
  -H "X-API-Key: meister-key-67890" \
  -d '{
    "name":               "Kupferrohr 22mm",
    "einheit":           "Meter",
    "lagerbestand":      50,
    "preis_pro_einheit":  15.90
  }'
POST/api/material/{id}/nachbestellengeselle+

Via sp_material_nachbestellen. Menge muss > 0 sein.

curl -X POST http://localhost/api/material/4/nachbestellen \
  -d '{ "menge": 20 }'
DELETE/api/material/{id}admin

Schlägt fehl wenn Material in Aufträgen verwendet wird → 409

curl -X DELETE http://localhost/api/material/6 -H "X-API-Key: admin-key-12345"

06

A. PDF - B. E-Mail - C. Export-Funktionen - D. Reporting & Statistiken - E. Automatisierung

6A

PDF-Generierung

TCPDF · Rechnung · Angebot · Monatsreport · Professionelles Layout

Dateistruktur
slim-api/
├── composer.json          ← tecnickcom/tcpdf hinzugefügt
└── src/
    ├── Pdf/
    │   ├── BasePdf.php          ← TCPDF-Basisklasse (Header, Footer, Stile)
    │   ├── RechnungPdf.php      ← Rechnungs-PDF
    │   ├── AngebotPdf.php       ← Angebots-PDF
    │   └── MonatsreportPdf.php  ← Monatsreport (2 Seiten)
    └── Controllers/
        └── PdfController.php    ← 3 neue Endpoints
Technologie
PDF-BibliothekTCPDF 6.7
Composer-Pakettecnickcom/tcpdf
AusgabeformatA4, Portrait
SchriftHelvetica (built-in)
SpracheDeutsch (UTF-8)
Layout-Prinzip: Alle drei PDFs erben von BasePdf — einheitlicher Header, Footer und Tabellenstil.
Installation
1
Neue Dateien ins Projekt kopieren

Folgende Dateien in die bestehende Slim-Struktur einfügen:

src\Pdf\BasePdf.php           → neu anlegen
src\Pdf\RechnungPdf.php       → neu anlegen
src\Pdf\AngebotPdf.php        → neu anlegen
src\Pdf\MonatsreportPdf.php   → neu anlegen
src\Controllers\PdfController.php → neu anlegen
public\index.php              → bestehende Datei ersetzen
composer.json                 → bestehende Datei ersetzen
2
TCPDF installieren

Im slim-api Ordner einmalig ausführen:

cd C:\xampp\htdocs\slim-api
composer update

Composer lädt tecnickcom/tcpdf automatisch in den vendor/ Ordner. Kein weiterer Konfigurationsaufwand nötig.

3
Apache neu starten & testen

XAMPP Control Panel → Apache → Restart, dann im Browser:

# Rechnung als PDF herunterladen:
http://localhost/api/rechnungen/1/pdf

# Im Browser anzeigen (nicht herunterladen):
http://localhost/api/rechnungen/1/pdf?inline

# Monatsreport März 2026:
http://localhost/api/reports/monat/2026/3

Wenn der Browser ein PDF öffnet oder herunterlädt, ist die Installation erfolgreich.

Rechnungs-PDF — Aufbau
Header
  • Blauer Balken mit Firmenname
  • Absenderzeile (klein, über Anschrift)
  • Empfängeranschrift
  • Info-Box rechts: Rechnungsnr., Datum, Fälligkeitsdatum, Kundennr.
Body
  • Titel "Rechnung" + Auftragsbezug
  • Positionstabelle: Pos., Beschreibung, Typ, Menge, Einzelpreis, Gesamtpreis
  • Arbeitszeit-Kosten (Std.)
  • Material-Kosten (Stk.)
  • Zwischensumme (netto)
  • Mehrwertsteuer 19%
  • Gesamtbetrag (brutto) — blau hervorgehoben
Footer
  • Zahlungshinweis-Box mit IBAN, BIC, Frist
  • Grußformel
  • Dreispaltig: Bankverbindung · Kontakt · Steuerdaten
  • Seitenzahl
Angebots-PDF — Aufbau

Gleiche Struktur wie Rechnungs-PDF, mit angebotsspezifischen Unterschieden:

ElementInhalt
Info-BoxAngebotsnr., Datum, Gültig bis, Kundennr.
SummenblockNettobetrag · MwSt 19% · Angebotssumme (brutto)
AbschlussGültigkeitshinweis + AGB-Hinweis
Standardgültigkeit30 Tage ab Ausstellungsdatum
// Angebot-PDF abrufen:
GET /api/angebote/1/pdf

// Im Browser öffnen:
GET /api/angebote/1/pdf?inline
Monatsreport-PDF — Aufbau

Zweiseitiger Management-Report. Alle Daten werden per DB-Abfrage für den gewählten Monat aggregiert.

Seite 1
  • 6 KPI-Kacheln: Gesamtumsatz, abgeschlossene Aufträge, offene Aufträge, unbezahlte Rechnungen, offener Betrag, neue Kunden
  • Top 5 Kunden nach Umsatz mit Auftragsanzahl
  • Mitarbeiter-Auslastung: Geplante Stunden, Auftragsanzahl, Monatsumsatz
Seite 2
  • Offene Aufträge mit Status, Priorität und Wert
  • Unbezahlte Rechnungen — überfällige Zeilen rot hervorgehoben
  • Materialverbrauch Top 10 mit Gesamtmenge und Wert
  • Erstellungszeitstempel im Footer
// Monatsreport März 2026:
GET /api/reports/monat/2026/3

// Im Browser öffnen:
GET /api/reports/monat/2026/3?inline
Neue Endpoints (Sprint 6)
GET/api/rechnungen/{id}/pdfalle Rollen

Generiert eine Rechnung als PDF. Query-Parameter ?inline zeigt das PDF im Browser an, ohne ?inline wird es heruntergeladen.

curl http://localhost/api/rechnungen/1/pdf \
  -H "X-API-Key: $API_KEY_ADMIN" \
  --output Rechnung_R-2026-0001.pdf
GET/api/angebote/{id}/pdfalle Rollen

Generiert ein Angebot als PDF inkl. Gültigkeitsdatum und AGB-Hinweis.

curl http://localhost/api/angebote/1/pdf \
  -H "X-API-Key: $API_KEY_ADMIN" \
  --output Angebot_ANG-2026-0001.pdf
GET/api/reports/monat/{jahr}/{monat}alle Rollen

Generiert den Monatsreport als zweiseitiges PDF. Jahr: 2020–2099, Monat: 1–12.

curl http://localhost/api/reports/monat/2026/3 \
  -H "X-API-Key: $API_KEY_ADMIN" \
  --output Monatsreport_2026_03.pdf

# Im Browser öffnen:
http://localhost/api/reports/monat/2026/3?inline

6B

E-Mail-Versand

PHPMailer · SMTP · HTML-Templates · PDF-Anhang · Mahnwesen · Benachrichtigungen

Dateistruktur
slim-api/
├── composer.json          ← phpmailer/phpmailer hinzugefügt
├── .env                   ← SMTP-Zugangsdaten (neu)
├── config/
│   └── settings.php       ← mail[] Konfigurationsblock (neu)
└── src/
    ├── Mail/
    │   ├── Mailer.php         ← PHPMailer Wrapper (SMTP, Anhänge)
    │   └── MailTemplates.php  ← 4 HTML-E-Mail-Vorlagen
    └── Controllers/
        └── MailController.php ← 4 neue Endpoints
Technologie
BibliothekPHPMailer 6.9
Composer-Paketphpmailer/phpmailer
ProtokollSMTP (TLS / SSL)
FormatHTML + Plaintext Fallback
AnhangPDF (base64, kein temp-File)
Test-ToolMailtrap.io (kostenlos)
4 E-Mail-Templates:
Rechnung · Mahnung (3 Stufen) · Auftrag-Status · SMTP-Test
Installation & SMTP-Konfiguration
1
Neue Dateien ins Projekt kopieren
src\Mail\Mailer.php          → neu anlegen
src\Mail\MailTemplates.php   → neu anlegen
src\Controllers\MailController.php → neu anlegen
config\settings.php          → bestehende Datei ersetzen (mail[] Block neu)
public\index.php             → bestehende Datei ersetzen (neue Routes)
composer.json                → bestehende Datei ersetzen
2
PHPMailer installieren
cd C:\xampp\htdocs\slim-api
composer update

Composer lädt phpmailer/phpmailer automatisch in den vendor/ Ordner.

3
SMTP-Zugangsdaten in .env eintragen

Für die Entwicklung empfehlen wir Mailtrap.io (kostenlos, fängt alle E-Mails ab ohne echten Versand):

# Mailtrap — Entwicklung (https://mailtrap.io)
MAIL_SMTP_HOST=sandbox.smtp.mailtrap.io
MAIL_SMTP_PORT=587
MAIL_SMTP_USER=dein-mailtrap-user
MAIL_SMTP_PASS=dein-mailtrap-pass
MAIL_SMTP_SECURE=tls
[email protected]
MAIL_FROM_NAME=Klaus Meier GmbH
MAIL_DEBUG=false

# Produktion — z.B. eigener Mailserver
MAIL_SMTP_HOST=mail.klausmeier-gmbh.de
MAIL_SMTP_PORT=465
MAIL_SMTP_SECURE=ssl
4
SMTP-Verbindung testen
curl -X POST http://localhost/api/mail/test \
  -H "X-API-Key: $API_KEY_ADMIN" \
  -H "Content-Type: application/json" \
  -d '{ "email": "[email protected]" }'

Bei Erfolg erscheint die Test-Mail im Mailtrap-Postfach.

E-Mail-Templates
Rechnungs-Mail

Professionelle HTML-E-Mail mit PDF-Anhang.

  • Rechnungsnummer, Datum, Fälligkeitsdatum
  • Gesamtbetrag farbig hervorgehoben (blau)
  • Positionstabelle (optional)
  • Bankverbindungshinweis
  • PDF automatisch generiert & angehängt
Mahnungs-Mail (3 Stufen)

Farbcodierte Mahnung nach Eskalationsstufe.

  • Stufe 1 — Zahlungserinnerung (orange)
  • Stufe 2 — 1. Mahnung (rot)
  • Stufe 3 — 2. Mahnung / Letzte Aufforderung (dunkelrot)
  • Verzug in Tagen und offener Betrag
Auftrags-Benachrichtigung

Statusänderung automatisch an Kunden melden.

  • Geplant · In Bearbeitung
  • Abgeschlossen · Storniert
  • Status-Badge farbig im Mail-Body
  • Kontaktdaten der Firma im Footer
SMTP Test-Mail

Prüft ob SMTP-Konfiguration korrekt ist.

  • Grüne Bestätigungs-Box
  • Erstellungszeitstempel
  • Nur für Meister+ zugänglich
Neue Endpoints (Sprint 6B)
POST/api/rechnungen/{id}/emailgeselle+

Generiert die Rechnung als PDF und versendet sie per E-Mail. Empfänger-Adresse aus DB oder aus Request-Body.

FeldTypBeschreibung
emailstringOptionalÜberschreibt die E-Mail aus der Kundendatenbank
curl -X POST http://localhost/api/rechnungen/1/email \
  -H "X-API-Key: $API_KEY_ADMIN" \
  -H "Content-Type: application/json" \
  -d '{ "email": "[email protected]" }'

// Antwort 200:
{
  "rechnung_id":   1,
  "empfaenger":    "[email protected]",
  "betreff":       "Rechnung R-2026-0001 von Klaus Meier GmbH",
  "pdf_angehängt": "Rechnung_R-2026-0001.pdf",
  "gesendet_am":   "2026-03-10T14:23:00+01:00"
}
POST/api/rechnungen/mahnungengeselle+

Sendet Mahnungen an alle überfälligen Rechnungen. Mit Sicherheitslimit und Mahnstufen-Auswahl.

FeldTypBeschreibung
mahnstufeintOptional1 = Erinnerung · 2 = 1. Mahnung · 3 = 2. Mahnung (Standard: 1)
min_tage_verzugintOptionalNur Rechnungen mit mindestens X Tagen Verzug (Standard: 1)
max_anzahlintOptionalMaximale Anzahl Mahnungen pro Aufruf (Standard: 50)
curl -X POST http://localhost/api/rechnungen/mahnungen \
  -H "X-API-Key: $API_KEY_ADMIN" \
  -d '{ "mahnstufe": 1, "min_tage_verzug": 7 }'

// Antwort 200:
{
  "gesendet": 3,
  "fehler":   1,
  "details": [
    { "rechnung_id": 4, "empfaenger": "...", "tage_verzug": 12, "status": "gesendet" },
    { "rechnung_id": 7, "status": "fehler", "grund": "Keine E-Mail hinterlegt" }
  ]
}
POST/api/notifications/auftrag/{id}geselle+

Sendet dem Kunden eine HTML-Benachrichtigung über den aktuellen Auftragsstatus.

curl -X POST http://localhost/api/notifications/auftrag/3 \
  -H "X-API-Key: $API_KEY_ADMIN" \
  -d '{ "email": "[email protected]" }'
POST/api/mail/testmeister+

Sendet eine Test-E-Mail zur Überprüfung der SMTP-Konfiguration.

FeldTypBeschreibung
emailstringPflichtZiel-Adresse für die Test-Mail
curl -X POST http://localhost/api/mail/test \
  -H "X-API-Key: $API_KEY_ADMIN" \
  -d '{ "email": "[email protected]" }'

6C

CSV-Export & Backup

UTF-8 · Excel-kompatibel · Semikolon-Trennung · ZIP-Komplett-Backup

Dateistruktur
slim-api/
├── public/
│   └── index.php                  ← aktualisiert
└── src/
    └── Controllers/
        └── ExportController.php   ← neu (6 Endpoints)

Keine neuen Abhängigkeiten — ZipArchive ist in PHP 8 standardmäßig enthalten.

Technologie
FormatCSV (RFC 4180)
EncodingUTF-8 mit BOM
TrennzeichenSemikolon (;)
ZeilenumbruchCRLF (Windows)
Backup-FormatZIP (alle CSVs)
Berechtigungmeister+
Installation
1
Neue Datei ins Projekt kopieren
src\Controllers\ExportController.php  → neu anlegen

Keine weiteren Abhängigkeiten. ZipArchive ist in PHP 8 standardmäßig aktiv.

2
index.php ersetzen (neue Routes)
public\index.php  → bestehende Datei ersetzen

Die neuen /api/export/* Routes werden eingetragen. Alle bestehenden Routes bleiben unverändert.

3
Apache neu starten & testen
# Kunden als CSV herunterladen:
curl http://localhost/api/export/kunden \
  -H "X-API-Key: $API_KEY_ADMIN" \
  --output kunden.csv

# Kompletter Backup als ZIP:
curl http://localhost/api/export/backup \
  -H "X-API-Key: $API_KEY_ADMIN" \
  --output backup.zip
CSV-Format & Excel-Kompatibilität
Formatdetails
EncodingUTF-8 mit BOM (EF BB BF)
TrennzeichenSemikolon ; — Deutsches Excel-Standard
ZeilenumbruchCRLF \r\n — Windows-kompatibel
DezimalzahlKomma 19,99 — Kein Punkt
DatumsformatTT.MM.JJJJ — z.B. 15.03.2026
TextfelderIn "Anführungszeichen" bei Sonderzeichen
NullwerteLeere Zelle (nicht NULL)
Exportierte Tabellen
EndpointInhaltSpalten
/export/kundenAlle Kunden16
/export/auftraegeAlle Aufträge + Summen12
/export/rechnungenRechnungen + Verzug9
/export/materialLager + Verbrauch8
/export/mitarbeiterPersonal + Termine8
/export/backupAlle 5 als ZIP
ZIP-Backup enthält außerdem eine LIES_MICH.txt mit Encoding-Hinweisen und Erstellungszeitstempel.
Beispiel — kunden_export.csv
Kunden-ID;Typ;Vorname;Nachname;Firmenname;Ansprechpartner;...
1;privat;Hans;Müller;;;;Ja;[email protected];0201-123456;...
2;firma;;;Beispiel GmbH;Herr Schmidt;DE123456789;Ja;...
Neue Endpoints (Sprint 7)
GET /api/export/kunden /api/export/auftraege /api/export/rechnungen /api/export/material /api/export/mitarbeiter meister+

Jeder Endpoint exportiert die jeweilige Tabelle als UTF-8 CSV-Datei. Der Download startet automatisch im Browser.

# Kunden exportieren:
curl http://localhost/api/export/kunden \
  -H "X-API-Key: $API_KEY_ADMIN" \
  --output kunden_export_20260310.csv

# Aufträge exportieren:
curl http://localhost/api/export/auftraege \
  -H "X-API-Key: $API_KEY_ADMIN" \
  --output auftraege_export_20260310.csv

Dateiname: Automatisch mit Datum — z.B. kunden_export_20260310.csv

GET/api/export/backupmeister+

Generiert alle 5 CSV-Dateien und verpackt sie in einem ZIP-Archiv. Ideal für tägliche Datensicherung.

# Kompletter Backup als ZIP:
curl http://localhost/api/export/backup \
  -H "X-API-Key: $API_KEY_ADMIN" \
  --output handwerkerpro_backup_20260310.zip

// ZIP-Inhalt:
handwerkerpro_backup_20260310.zip
├── kunden_export_10.03.2026.csv
├── auftraege_export_10.03.2026.csv
├── rechnungen_export_10.03.2026.csv
├── material_export_10.03.2026.csv
├── mitarbeiter_export_10.03.2026.csv
└── LIES_MICH.txt

6D

Reporting & Statistiken

Interaktives Dashboard · Chart.js · Umsatzstatistik · Mitarbeiterauslastung · Top Kunden

Dateistruktur
slim-api/
├── public/
│   └── docs/
│       └── dashboard.html        ← Interaktives Reporting-Dashboard
└── src/
    └── Controllers/
        └── ReportingController.php ← 4 neue Endpoints
Technologie
Chart-BibliothekChart.js 4.4
DiagrammtypenBar · Line · Doughnut
Berechtigungalle Rollen
FilterJahr · Monat · API-Key
Dashboard öffnen →
📊
6 KPI-Karten

Monat · Trend · Jahrestotal

📈
Umsatz-Chart

Monatlich + Vorjahresvergleich

👥
MA-Auslastung

Stunden gestapelt pro Monat

🏆
Top Kunden

Donut + Tabelle mit Anteil %

Installation
1
Neue Dateien ins Projekt kopieren
src\Controllers\ReportingController.php  → neu anlegen
public\docs\dashboard.html              → neu anlegen
public\index.php                        → bestehende Datei ersetzen (neue Routes)

Keine neuen Composer-Pakete nötig. Chart.js wird per CDN geladen.

2
Dashboard im Browser öffnen
http://localhost/docs/dashboard.html

API-Key und Jahr/Monat oben einstellen → Aktualisieren klicken. Alle Daten kommen live aus der Datenbank.

Neue Endpoints (Sprint 6D)
GET/api/reporting/kennzahlenalle Rollen

KPI-Übersicht: Monatswerte, Jahreswerte, Trend ggü. Vormonat und offene Posten.

ParameterTypBeschreibung
jahrintOptionalJahr (Standard: aktuelles Jahr)
monatintOptionalMonat 1–12 (Standard: aktueller Monat)
curl "http://localhost/api/reporting/kennzahlen?jahr=2026&monat=3" \
  -H "X-API-Key: $API_KEY_ADMIN"
GET/api/reporting/umsatzalle Rollen

Umsatz nach Zeit. Ohne monat: 12 Monatswerte + Vorjahr. Mit monat: Tageswerte für diesen Monat.

# Jahresübersicht mit Vorjahresvergleich:
curl "http://localhost/api/reporting/umsatz?jahr=2026" \
  -H "X-API-Key: $API_KEY_ADMIN"

# Tageswerte März 2026:
curl "http://localhost/api/reporting/umsatz?jahr=2026&monat=3" \
  -H "X-API-Key: $API_KEY_ADMIN"
GET/api/reporting/mitarbeiter/auslastungalle Rollen

Arbeitsstunden pro Mitarbeiter aufgeteilt nach Monat. Ideal für gestapelte Balkendiagramme.

curl "http://localhost/api/reporting/mitarbeiter/auslastung?jahr=2026" \
  -H "X-API-Key: $API_KEY_ADMIN"
GET/api/reporting/top-kundenalle Rollen

Top Kunden nach Umsatz mit Anteil in Prozent. Max. 50 Einträge.

ParameterTypBeschreibung
limitintOptionalAnzahl Kunden (Standard: 10, max: 50)
jahrintOptionalJahr (Standard: aktuelles Jahr)
curl "http://localhost/api/reporting/top-kunden?limit=10&jahr=2026" \
  -H "X-API-Key: $API_KEY_ADMIN"

6E

Automatisierung

Cronjobs · Backups

Dateistruktur
slim-api/
├── bin/                 ←   [NEU] Für PHP-Befehlszeilen-Skripte (CLI)
│   └── cron-tasks.php   ←   neu anlegen
├── scripts/             ←   [NEU] Für Python- oder Shell-Skripte
│   └── backup.py        ←   neu anlegen
├── logs/                ←   [NEU] Für Fehlerprotokolle
    ├── backup.log       ←   Automatisch erstellt (Sicherungsprotokoll)
    └── cron.log         ←   Automatisch erstellt (PHP-Jobs-Protokoll)
Technische Spezifikation

Backup-ArchivierungZIP-Kompression
Aufbewahrungsfrist7 Tage Rotation (automatisches Purging)
UmgebungCLI-Modus für PHP & Python
Berechtigungmeister+
Systembeschreibung
Python-basiertes Backup-System (scripts/backup.py)

Dieses Skript läuft auf Systemebene und erstellt eine vollständige Kopie der Datenbank mithilfe des mysqldump-Befehls.

Logik des Skripts:

  • Datenbankverbindung: Baut eine Verbindung zur MariaDB-Instanz auf.
  • SQL-Dump: Erstellt eine .sql-Datei mit einem Zeitstempel im Dateinamen.
  • Die Datei wird zur Speicherplatzoptimierung mit gzip komprimiert.
  • Backups, die älter als 7 Tage sind, werden automatisch gelöscht, um eine Überfüllung des Speichers zu vermeiden.
Aktualisierungen via PHP CLI (bin/cron-tasks.php)

Dieses Skript wird unabhängig von der API über das Terminal ausgeführt und nutzt das bestehende Database.php-Modell.

Logik des Skripts:

  • Rechnungsprüfung: Rechnungen, deren Fälligkeitsdatum überschritten ist und die sich noch im Status "ausstehend" befinden, werden automatisch auf den Status "überfällig" gesetzt.
  • Terminerinnerung: Das System identifiziert Kunden mit Terminen am Folgetag und versendet automatisch Erinnerungs-E-Mails über das in Sprint 6-b implementierte PHPMailer-System.
Cronjob-Einrichtung
1
Neue Datei ins Projekt kopieren
bin\cron-tasks.php     → neu anlegen
scripts\backup.py.php  → neu anlegen
slim-api\logs          → Neuen Ordner anlegen!!!
2
Auszuführen

Um diese Skripte zeitgesteuert auszuführen, müssen sie in die crontab eingetragen werden. Öffnen Sie das Terminal, geben Sie crontab -e ein und fügen Sie folgende Zeilen am Ende hinzu:

# Täglich um 02:00 Uhr: Datenbank-Backup (Python)
0 2 * * * /usr/bin/python3 /var/www/html/slim-api/scripts/backup.py >> /var/www/html/slim-api/logs/backup.log 2>&1
# Täglich um 08:00 Uhr: Rechnungs- & Termincheck (PHP)
0 8 * * * /usr/bin/php /var/www/html/slim-api/bin/cron-tasks.php >> /var/www/html/slim-api/logs/cron.log 2>&1
3
Wichtige Hinweise zur Beachtung

  • Berechtigungen (Permissions): Die Dateien in den Verzeichnissen scripts/ und bin/ müssen ausführbar sein. Führen Sie dazu folgenden Befehl im Terminal aus:
    chmod +x /var/www/html/slim-api/scripts/backup.py
  • Log-Verzeichnis: Stellen Sie sicher, dass das Verzeichnis /logs/ Schreibrechte besitzt, da andernfalls keine Fehlerprotokolle erstellt werden können.
  • Python-Bibliotheken: Falls das Python-Skript zusätzliche Bibliotheken verwendet, müssen diese auf dem Server installiert sein (in der Regel sind die Standard-Bibliotheken ausreichend).


CNR-Handwerker-Plus — Gesamtdokumentation Sprint 1–7
MariaDB · PHP 8.1 · Slim Framework 4 · TCPDF · PHPMailer · Chart.js · PDO · Argon2ID · CORS