153 lines
7.1 KiB
Markdown
153 lines
7.1 KiB
Markdown
# 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
|
||
docker compose exec -u www-data web php tests/run.php # alle Tests ausführen
|
||
./scripts/run-tests.sh # Tests + automatische Issue-Erstellung bei Fehlern
|
||
./scripts/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/e2e-user.php # einmalig: Testnutzer anlegen
|
||
tests/e2e/run.sh # alle E2E-Tests
|
||
docker compose exec web php scripts/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/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/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.
|