Steuerkram/CLAUDE.md

153 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Steuerkram – Entwicklungsregeln
## Tech Stack
PHP 8.3+, MySQL, PSR-4 (Namespace `Steuerkram\`), Composer, kein Framework.
MVC: `src/Controllers/`, `src/Models/`, `src/Services/`, `src/Views/`.
## Coding-Regeln
- Kein `var_dump`, kein `die()` in Produktionscode.
- Neue Libraries nur nach expliziter Rückfrage einbinden.
- Bestehenden Code-Stil beibehalten (declare strict_types, 4 Spaces).
- SQL ausschließlich über `Database::fetchAll/fetchOne/execute()` mit Prepared Statements.
- **Keine neuen `style="`-Attribute außer für Laufzeitwerte** (siehe unten).
## CSS-Klassen statt Inline-Styles (Issue #160)
`public/static/css/app.css` ist das zentrale Stylesheet. Wiederkehrende Muster
gehören dorthin, nicht als `style="..."` in die Views – sonst ist weder eine
CSP ohne `unsafe-inline` noch Theming möglich, und `tests/97_InlineStyleBudgetTest.php`
schlägt fehl, sobald das Budget überschritten wird.
**Regel:** Inline-Styles nur, wenn der Wert tatsächlich zur Laufzeit aus PHP
kommt (Kontofarbe, Logo-Maße aus den Einstellungen o. ä.) – und auch dann
bevorzugt als CSS-Variable statt vollständiger Deklaration:
```html
<!-- statt -->
<span style="background:<?= $h($k['farbe']) ?>;color:<?= $h($textfarbe) ?>">
<!-- besser -->
<span class="konto-badge" style="--badge-bg: <?= $h($k['farbe']) ?>">
```
Wichtige vorhandene Klassen (Auswahl, siehe `app.css` für die vollständige Liste):
| Klasse | Zweck |
|---|---|
| `.modal-overlay` (+ `.open`, `--scroll`, `--stacked`) | Dialog-Hintergrund, Toggle über `.open`-Klasse oder direktes `style.display` |
| `.modal` / `.modal-card` (+ `-invoice`, `--lg`) | Dialog-Box in zwei etablierten Größen/Stilen |
| `.modal-card-header` / `.modal-card-close` | Titelzeile mit Schließen-Button im Dialog |
| `.icon-btn` | Rahmenloser Icon-Button (SVG-Icon, `--icon-color` für Laufzeitfarbe) |
| `.btn-round-icon` | Runder Icon-Button (z. B. „Position hinzufügen“) |
| `.row-clickable` + `data-row-href` | Klickbare Tabellenzeile (zentrale Delegation in `app.js`), `data-no-row-click` nimmt einzelne Zellen aus |
| `.alert` / `.alert-warning` / `.alert-info` | Hinweisboxen |
| `.text-muted`, `.text-sm`, `.text-danger`, `.nowrap`, `.min-w-0`, `.m-0`, `.w-full`, `.hidden` | Utilities für einzelne Eigenschaften |
Inline-Handler (`onclick=`, `onchange=`, `oninput=`) sind in `src/Views` nicht
mehr erlaubt (Test 97 prüft das). Statt dessen: Element bekommt eine `id`
oder ein `data-action`-Attribut, die Verdrahtung passiert per
`addEventListener()` im zugehörigen Seiten-Skript oder – bei generischen,
seitenübergreifenden Mustern (z. B. `data-modal-open`/`data-modal-close`,
`data-row-href`) – zentral in `app.js`.
## Tests
**Regel: Jedes neue Feature bekommt einen eigenen Test.**
Tests liegen in `tests/` und folgen dem Namensschema `NN_FunktionsnameTest.php`.
Jede Testklasse erweitert `TestCase` und implementiert `run()`.
```bash
./scripts/test.sh # alle Tests ausführen
docker compose exec -u www-data web ./scripts/container/run-tests.sh # Tests + automatische Issue-Erstellung bei Fehlern
docker compose exec -u www-data web ./scripts/container/run-tests.sh 42 # Tests; bei Fehlern Kommentar in Issue #42
```
**Immer `-u www-data`:** ohne die Option laufen die Tests als root und legen
unterhalb von `data/` Dateien an, die Apache/PHP später nicht mehr beschreiben
kann – ein echter Beleg-Upload scheitert dann mit HTTP 500.
### Neuen Test erstellen
1. Datei `tests/NN_MeinFeatureTest.php` anlegen (NN = nächste freie Nummer)
2. Klasse `MeinFeatureTest extends TestCase`
3. `protected string $testName = 'Test NN: Beschreibung';`
4. Methode `public function run(): void` mit Assertions implementieren
5. Transaktions-Isolation ist automatisch (setUp/tearDown in TestCase)
6. Dateien und Verzeichnisse **immer** über `TestCase` anlegen – der Rollback
greift nur in der Datenbank, nicht im Dateisystem:
```php
$pfad = $this->tempPath('.csv'); // Pfad in /tmp, wird aufgeräumt
$dir = $this->makeTempDir($basis . '/2026/BLG-...'); // Verzeichnis anlegen + aufräumen
$this->registerTempPath($schonVorhandenerPfad); // nachträglich anmelden
```
Bleibt trotzdem etwas liegen, meldet `tests/run.php` das am Ende des Laufs.
### Assertion-Methoden
```php
$this->assert(bool, 'Beschreibung');
$this->assertEquals($erwartet, $ist, 'Beschreibung');
$this->assertGreaterThan($min, $wert, 'Beschreibung');
$this->assertNotEmpty($wert, 'Beschreibung');
```
### E2E-Tests (Browser)
Die PHP-Tests erreichen Routing, Views und Formular-JavaScript nicht. Dafür gibt
es Playwright-Tests in `tests/e2e/`, die im Container des e2e-runner laufen
(auf dem Host wird nichts installiert). Details: `tests/e2e/README.md`.
```bash
docker compose exec web php scripts/container/e2e-user.php # einmalig: Testnutzer anlegen
./scripts/e2e.sh # alle E2E-Tests
docker compose exec web php scripts/container/e2e-cleanup.php --force # Reste entfernen
```
Fehlt Docker, der Runner oder das Image, wird mit Exit 0 übersprungen.
**Welche Ebene wofür:** Rechen- und Model-Logik gehört in die PHP-Suite (schneller,
isoliert). In `tests/e2e/` gehört nur, was einen Browser braucht: Formularabläufe,
Redirects, gerenderte Views, JavaScript-Umrechnungen, DOM-XSS.
## Dokumentation aktuell halten
| Datei | Wann aktualisieren |
|---|---|
| `Changes.md` | Bei jedem Feature, Bugfix oder strukturellen Änderung (nicht bei reinen Test- oder Doku-Commits) |
| `README.md` | Wenn sich der Funktionsumfang oder der Tech Stack grundlegend ändert |
| `docs/funktionen.md` | Wenn ein neues Feature hinzukommt oder ein bestehendes wesentlich geändert wird |
| `docs/berechnungslogik.md` | Bei jeder Änderung an `EuerCalculator.php` oder `UstvaCalculator.php` (Zuordnungsregeln, neue Kennzahlen, geänderte Bedingungen) |
| `docs/anlagevermögen.md` | Bei Änderungen am Anlagevermögens-Workflow oder den Asset-Modellen |
## Datenbankmigrationen
Schemaänderungen (neue Spalten, Tabellen, Kontenanpassungen) kommen **ausschließlich** als nummerierte Migrations-Datei in `migrations/`:
- Schema: `NNN_kurze_beschreibung.sql` (NNN = nächste freie dreistellige Nummer, z.B. `011_...`)
- Migrationen werden **nie manuell** eingespielt – `scripts/intern/migrate.php` erledigt das automatisch beim Container-Start
- Jede Migration muss idempotent sein oder mit `IF NOT EXISTS` / `ON DUPLICATE KEY` abgesichert werden
```bash
# Nächste freie Nummer ermitteln:
ls migrations/ | tail -1
# Migration manuell testen (ohne Neustart):
docker compose exec web php scripts/intern/migrate.php
```
## Geplante Aufgaben (Cron)
Alle planmäßigen Aufgaben (z.B. Ideenagent, Automatisierungen) laufen lokal über die System-Crontab des Nutzers. Für außerplanmäßige Ausführung das jeweilige Skript direkt aufrufen:
```bash
crontab -l # alle geplanten Jobs anzeigen
/home/fds/projects/Steuerkram/.claude/ideenagent-run.sh # Ideenagent manuell starten
/home/fds/projects/Steuerkram/.claude/automate-run.sh # Automatisierung manuell starten
```
Remote Triggers (claude.ai) oder Claude Code Cron-Jobs werden **nicht** verwendet.
## Commits
```
Fix #NR: Kurze Beschreibung der Änderung
```
Keine Co-Authored-By-Zeile. Keine langen Commit-Messages.