Files
Kundenverwaltung/CLAUDE.md
T
adminandClaude Opus 5 f3f97a55ce CLAUDE.md mit Architektur-Ueberblick ergaenzt
Dokumentiert die Punkte, die sich nur aus dem Zusammenspiel mehrerer
Dateien erschliessen: Instanz-Ableitung aus dem Arbeitsverzeichnis,
externe Konfiguration, Request-Ablauf ueber checkRightsNInclude(),
das positionsbasierte Rechte-Schema, die bei jedem Request laufenden
Schema-Migrationen sowie tote Pfade und Stolperfallen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 16:58:14 +02:00

6.7 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Kundenverwaltung für ein Ingenieurbüro: Kunden, Termine, Projekte, Angebote, Lieferscheine, Rechnungen, Zeiterfassung. Gewachsene PHP-Anwendung (seit ~2011) mit Smarty-Templates und MariaDB/MySQL. Domänenbegriffe, Bezeichner und UI-Texte sind durchgehend deutsch — das bitte beibehalten.

Tooling

Es gibt kein Composer, npm, Build, Linter oder Testsuite. Abhängigkeiten liegen als Kopie in incs/ und werden dort direkt gepflegt. Ein Update einer dieser Bibliotheken ist immer ein manueller Eingriff, kein Paketmanager-Aufruf.

Syntaxprüfung der eigenen Dateien:

for f in $(git ls-files "*.php" | grep -vE "^(incs/(tcpdf|tcpdf_old|pChart|PHPMailer|WebAuthn|drawer|font)|smarty|smarty3|spellcheck|js)/"); do php -l "$f"; done

Zum Ausführen braucht es einen Webserver mit PHP, eine MariaDB/MySQL-Instanz und die externe Konfigurationsdatei (siehe unten). Ohne die Konfiguration springt index.php in den Setup-Modus (update/firststart.php).

Die zwei zentralen Mechanismen

1. Das Verzeichnis bestimmt die Instanz

incs/constants.php leitet aus getcwd() den Namen des Installationsverzeichnisses ab und baut daraus:

Konstante Wert bei Installation in …/kunden
CONFIG_FILE kunden_settings.conf
UPLOADS_PATH uploads_kunden/
SESSION_NAME KUNDENSESS

Aus einer Codebasis laufen dadurch mehrere getrennte Instanzen — die uploads_dev, uploads_ingwa, uploads_kunden, uploads_lsg im Baum sind die Spuren davon. Jede Instanz hat eigene Config, eigene Session, eigene Uploads.

Konsequenz für alles, was per CLI/Cron läuft: Das Arbeitsverzeichnis muss das Installationsverzeichnis sein, sonst wird die falsche (oder keine) Konfiguration geladen.

cd /home/intranet/kunden && php db-backup.php

constants.php kompensiert dabei nur die Aufrufe aus ajax/ und ajax/auto/, indem es ein bzw. drei Ebenen hochgeht.

2. Zugangsdaten liegen außerhalb des Repos

CONFIG_PATH zeigt auf /volume1/homes/wagner/kundenconfig/ (Linux) bzw. c:/kundenverwaltung/config/ (Windows). Geschrieben wird die Datei von update/rebuild-settings.php beim Ersteinrichten.

Die Datei deklariert $mysql_user, $mysql_pw, $mysql_server, $mysql_db, $firma und weitere ausdrücklich als global und weist sie erst danach zu. Nur deshalb funktioniert readsettings() in incs/settings.php: Das include passiert im Funktionsrumpf, ohne die global-Zeilen blieben die Variablen dort lokal. Wer die Config-Struktur ändert, muss diese Eigenheit erhalten.

Zugangsdaten gehören nie in den Code — db-backup.php zeigt das Muster für Skripte außerhalb des Request-Zyklus (Config einlesen, Passwort über eine temporäre Optionsdatei an mysqldump, nicht per Kommandozeile).

Request-Ablauf

index.php ist der einzige Einstieg für die Oberfläche:

  1. Konstanten, Session, Konfiguration, DB-Verbindung
  2. Schema-Migration (siehe unten)
  3. head.tpl
  4. bei gültiger Session: checkRightsNInclude() aus incs/funcs.php — ein switch über $_GET["action"], das Rechte prüft und den Pfad in sites/ zurückgibt, der dann inkludiert wird
  5. foot.tpl

Eine neue Seite anzulegen heißt: Datei in sites/ plus ein case in checkRightsNInclude(). Ohne den case ist die Seite nicht erreichbar.

Die Endpunkte in ajax/ gehen nicht über index.php. Sie bootstrappen selbst über incs/connectmysql.php (Konstanten, Session, Config, DB, Escape-Wrapper) und rendern eigene Fragmente nach ajax/templates_c/.

Rechtesystem

$_SESSION["rights"] ist ein String, keine Zahl. Die Zeichenposition benennt den Bereich, der Zeichenwert ist eine Bitmaske:

  • Position: RIGHTS_KUNDEN=0, RIGHTS_TERMINE=1, RIGHTS_ANGEBOTE=2, RIGHTS_LIEFER=3, RIGHTS_RECHNUNGEN=4, RIGHTS_SUPERADMIN=5
  • Wert: RIGHTS_LOOK=1, RIGHTS_CHANGE=2, RIGHTS_ADD=4

Geprüft wird immer nach diesem Muster:

if(substr($_SESSION["rights"], RIGHTS_KUNDEN, 1) & RIGHTS_LOOK)

Schema-Migrationen laufen bei jedem Seitenaufruf

Es gibt keine SQL-Migrationsdateien. Der Stand steht in update/thisversion/currentversion.txt (dreistellig, aktuell 010), die Zielversion ist der erste Eintrag in $versions in update/versions.php. index.php ruft bei jedem Request checknewversion() und manageupdate() aus incs/update.php; liegt die Zielversion höher, arbeitet update_tables() in update/database-update.php sich in einem switch von Version zu Version hoch und schreibt die neue Nummer zurück.

Eine Schemaänderung besteht also aus: neuer Eintrag vorn in update/versions.php und passender case in update_tables(). update/thisversion/ muss für den Webserver schreibbar sein.

Datenbankzugriff

Prozedurales mysqli. $dbconn wird zusätzlich in $_SESSION["dbconn"] abgelegt, damit incs/mysql_ecape_wrapper.php eine globale Funktion mysql_escape_string() als Shim über diese Verbindung bereitstellen kann — ein Überbleibsel der mysql_*-Ära, das in altem Code noch aufgerufen wird.

SQL wird durchgehend per String-Verkettung gebaut. Escaping ist Handarbeit über mysqli_real_escape_string($dbconn, …) und muss bei jeder Änderung mitgedacht werden.

Fallstricke

  • smarty/ ist Smarty 4.5.6, smarty3/ ist Smarty 3.1.6. Die Namen führen in die Irre. Alle 73 einbindenden Dateien nutzen smarty/libs/; smarty3/ ist tot.
  • incs/fpdf.php und sites/makepdf_OLD.php sind tot und parsen ab PHP 8.0 nicht mehr ($s{$i}-Syntax). Der lebende PDF-Pfad ist incs/makepdf.phpincs/tcpdf. incs/tcpdf_old und incs/users_old.php sind ebenfalls Altlasten.
  • index.php setzt error_reporting(E_ERROR | E_PARSE). Warnungen und Notices sind unterdrückt, und der Code verlässt sich darauf — nicht ohne Not hochdrehen, sonst überflutet es die Ausgabe.
  • templates_c/, ajax/templates_c/ und temp/ müssen für den Webserver schreibbar sein. Sie sind git-ignoriert und nur über .gitkeep im Repo vorhanden.
  • uploads_*/{notes,signs} enthalten personenbezogene Kundendaten (Rechnungen, Scans, Unterschriften) und sind bewusst git-ignoriert. Nie committen.
  • Zeilenenden werden über .gitattributes auf LF normalisiert.

Authentifizierung

Klassischer Login plus WebAuthn/FIDO2. Server-Seite: authServer.php mit incs/WebAuthn (Bibliothek von Lukas Buchs) und incs/authHelper.php; Client-Seite js/auth.js. Schlüsselverwaltung über addKey.php und sites/userkeys.php.

Historie

Das Repository wurde am 2026-08-28 neu initialisiert. Die mitgelieferte Historie von 2014 war irreparabel beschädigt und wurde verworfen — der erste Commit ist der Ausgangsstand, nicht der Projektbeginn.