# Steuerkram Self-hosted Buchhaltungs- und Steueranwendung für deutsche Freiberufler und Kleinunternehmer. Entwickelt als datensouveräne Alternative zu SaaS-Diensten – mit Unterstützung von [Claude Code](https://claude.ai/code). ![PHP](https://img.shields.io/badge/PHP-777BB4?logo=php&logoColor=white) ![MariaDB](https://img.shields.io/badge/MariaDB-003545?logo=mariadb&logoColor=white) ![Docker](https://img.shields.io/badge/Docker-2496ED?logo=docker&logoColor=white) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE.md) --- ## Funktionsumfang | Bereich | Beschreibung | |---|---| | **Kontenrahmen SKR03** | DATEV-konform, 157 Konten, mit UStVA-Kennzahlen und EÜR-Zeilennummern | | **Belegverwaltung** | PDF, PNG, TIFF, ZUGFeRD-XML; SHA-256 Integritätsprüfung; optionale KI-Kontierung | | **Buchungen** | EÜR-Modell (Einnahme/Ausgabe); GoBD-konforme Trigger; Stornobuchungen; Journal | | **Anlagevermögen** | Anlagenspiegel; automatische Anlagen-Nummerierung; Buchungsworkflow mit Asset-Verknüpfung | | **Ausgangsrechnungen** | DIN 5008, PDF-Export via mPDF | | **UStVA** | Kennzahlen-Berechnung, ELSTER XML-Export, Drill-down | | **EÜR** | Jahresauswertung, PDF-Export, Drill-down | | **Kundenverwaltung** | CRUD | | **Einstellungen** | Firmendaten, Logo, ELSTER-Steuernummer, Nummernkreise | | **Benutzerverwaltung** | Mehrbenutzer, Session-Auth mit 24 h Timeout | --- ## Schnellstart ```bash curl -fsSL https://codeberg.org/fds2610/Steuerkram/raw/branch/main/install.sh -o install.sh sudo bash install.sh cd /opt/Steuerkram docker compose up -d ``` --- ### Manuelle Installation #### 1. Repository klonen ```bash git clone steuerkram cd steuerkram ``` #### 2. Konfiguration anpassen Kopiere `.env.example` nach `.env` und passe die Werte an: ```bash cp .env.example .env ``` ```dotenv DB_PASS=sicheres-passwort # Datenbankpasswort – in docker-compose.yml spiegeln APP_SECRET=zufaelliger-string # Zufälligen langen String eintragen (für Sessions) ``` Außerdem in `docker-compose.yml` das `MARIADB_PASSWORD` im `db`-Service auf denselben Wert wie `DB_PASS` setzen. #### 3. Container starten ```bash docker compose up -d ``` Beim ersten Start werden automatisch: - die Datenbank initialisiert (`migrations/001_initial_schema.sql`) - der SKR03-Kontenrahmen eingespielt (`migrations/002_seed_skr03.sql`) - das Docker-Image mit PHP-Abhängigkeiten (mPDF) gebaut #### 4. Anwendung aufrufen ``` http://localhost:8080 ``` Beim ersten Aufruf erscheint die **Ersteinrichtung**: dort den ersten Benutzer-Account anlegen. Danach ist die Anwendung passwortgeschützt. Weitere Benutzer können nach dem Login unter **Benutzer** in der Seitenleiste verwaltet werden. #### 5. KI-Beleganalyse einrichten (optional) Die KI-gestützte Belegerkennung ist standardmäßig deaktiviert. Sie kann unter **Einstellungen → KI-Kontierungsvorschlag** aktiviert werden, sobald ein LLM erreichbar ist. **Option A – Lokales Ollama** (kein API-Schlüssel nötig, aber hoher Ressourcenbedarf): ```bash ollama pull mistral # oder llama3.2 etc. ``` In `.env` ist keine weitere Konfiguration nötig – die App erkennt Ollama auf `localhost:11434` automatisch. Modell und URL können optional überschrieben werden: ```dotenv OLLAMA_URL=http://localhost:11434 OLLAMA_MODEL=mistral ``` **Option B – Remote-API** (OpenAI-kompatibel, z. B. OpenAI, OpenRouter, LM Studio): ```dotenv LLM_API_URL=https://api.openai.com/v1 LLM_API_KEY=sk-... LLM_MODEL=gpt-4o-mini ``` > **Hinweis:** Die Ergebnisqualität hängt stark vom gewählten Modell ab. Tests mit `mistral` (lokal via Ollama) lieferten bisher mäßige Ergebnisse. #### 6. Firmendaten hinterlegen Nach dem ersten Login unter **Einstellungen** Firmendaten, Logo und ELSTER-Steuernummer eintragen. Diese Angaben erscheinen auf Ausgangsrechnungen und im EÜR-Export. ## Port ändern Der Standard-Port ist `8080`. Um ihn zu ändern, passe in `docker-compose.yml` die `ports`-Zeile an: ```yaml ports: - "9090:80" # Beispiel: Port 9090 auf dem Host ``` ## Upload-Limit anpassen Die maximale Dateigröße für Beleganhänge ist auf **20 MB** voreingestellt (`php/uploads.ini`). Um das Limit zu ändern, die Werte in dieser Datei anpassen und den Container neu bauen: ```ini upload_max_filesize = 20M post_max_size = 20M ``` ```bash docker compose up -d --build ``` ## Belege automatisch aus Nextcloud importieren Belege können automatisch aus einem Nextcloud-Ordner via WebDAV importiert werden. Das CLI-Skript `scripts/nextcloud-import.php` liest alle unterstützten Dateien (PDF, JPEG, PNG, TIFF, XML) aus dem konfigurierten Ordner, legt sie als Belege an und verschiebt sie anschließend in einen Erledigte-Ordner (oder löscht sie, wenn kein Ziel-Ordner konfiguriert ist). ### Konfiguration Die Zugangsdaten werden in der Steuerkram-UI gepflegt: **Einstellungen → Nextcloud-Belegimport**. Empfehlung: Ein Nextcloud-App-Passwort verwenden (Nextcloud → Einstellungen → Sicherheit → App-Passwörter). ### Manueller Aufruf ```bash docker compose exec web php scripts/nextcloud-import.php ``` ### Automatisch per Cron (auf dem Host) Für den automatischen Abruf muss ein Cron-Eintrag auf dem Host angelegt werden. Dabei gibt es zwei Varianten: **Option 1: Root-Crontab** (`sudo crontab -e`) ```cron */15 * * * * docker compose -f /opt/steuerkram/docker-compose.yml exec -T web \ php scripts/nextcloud-import.php >> /var/log/steuerkram-import.log 2>&1 ``` Kein `sudo` im Befehl nötig. Nachteil: Root-Crons sind schwerer zu überblicken. **Option 2: User-Crontab** (`crontab -e`) – empfohlen Damit `docker compose exec` ohne `sudo` funktioniert, muss der User in der Docker-Gruppe sein: ```bash sudo usermod -aG docker $USER # Danach neu einloggen ``` Anschließend in der User-Crontab (ohne `sudo`): ```cron */15 * * * * docker compose -f /home/fds/projects/Steuerkram/docker-compose.yml exec -T web \ php scripts/nextcloud-import.php >> /var/log/steuerkram-import.log 2>&1 ``` > **Hinweis:** Das `-T`-Flag ist bei Cron zwingend erforderlich – ohne TTY schlägt `docker compose exec` sonst still fehl. > > **Empfehlung:** User-Crontab + Docker-Gruppe. Das ist konsistent mit dem bestehenden Setup (alle anderen Jobs laufen ebenfalls in der User-Crontab). Das Skript gibt beim Start eine Zusammenfassung aus (importiert / übersprungen / Fehler) und beendet sich mit Exit-Code 1 bei Fehlern. ## Datenpersistenz Alle persistenten Nutzdaten liegen gebündelt unter `./data/` – getrennt nach Art, damit erkennbar bleibt, was wo liegt (Details: [`data/README.md`](data/README.md)): | Pfad | Inhalt | |---|---| | `./data/mariadb/` | MariaDB-Datenbankdateien | | `./data/belege/` | Hochgeladene Belege | | `./data/logo/` | Firmenlogo | Diese Pfade liegen außerhalb des Containers und bleiben bei `docker compose down` erhalten. `data/belege` und `data/logo` liegen bewusst außerhalb des Web-Roots und werden nur über die Anwendung ausgeliefert (`/belege/{id}/file`, `/settings/logo/file`). `./data/` ist per `.gitignore` vom Repo ausgenommen und wird von `git clean` nicht angefasst. ## Systemd-Service einrichten Damit Steuerkram automatisch beim Systemstart startet und als Dienst verwaltet werden kann, liegt im Repository die Datei `steuerkram.service`. #### 1. Service-Datei installieren ```bash sudo cp steuerkram.service /etc/systemd/system/steuerkram.service ``` Die Datei setzt `WorkingDirectory=/opt/Steuerkram` voraus. Falls das Repository in ein anderes Verzeichnis geklont wurde, den Pfad in der Service-Datei anpassen: ```bash sudo nano /etc/systemd/system/steuerkram.service # WorkingDirectory= auf das tatsächliche Installationsverzeichnis setzen ``` #### 2. Service aktivieren und starten ```bash sudo systemctl daemon-reload sudo systemctl enable steuerkram # automatisch beim Systemstart sudo systemctl start steuerkram ``` #### 3. Status prüfen ```bash sudo systemctl status steuerkram journalctl -u steuerkram -f # Live-Log inkl. Migrations-Ausgabe ``` #### Wichtige Service-Befehle | Befehl | Wirkung | |---|---| | `sudo systemctl start steuerkram` | Container starten | | `sudo systemctl stop steuerkram` | Container stoppen | | `sudo systemctl restart steuerkram` | Neustart (z.B. nach Update) | | `sudo systemctl status steuerkram` | Aktueller Status | ## Backup ```bash bash scripts/backup.sh ``` Das Skript erstellt ein Dump der Datenbank sowie ein Archiv der Uploads im Verzeichnis `backups/`. ## Update Seit dem 11.04.2026 bin ich mit meiner Software für mich selber produktiv. Ab diesem Zeitpunkt sollten keine breaking changes mehr vorkommen. Datenbankmigrationen werden **automatisch** beim nächsten Container-Start eingespielt – kein manueller Eingriff nötig. Das Script `scripts/migrate.php` prüft beim Start, welche Migrations-Dateien noch nicht angewendet wurden, und führt sie der Reihe nach aus. ### Vollständige Update-Anleitung Die folgenden Schritte sichern einen vollständigen und sicheren Update-Ablauf. Alle Befehle im Steuerkram-Verzeichnis ausführen. #### Schritt 1 – Datenbank sichern Vor jedem Update ein Backup erstellen: ```bash bash scripts/backup.sh ``` Das Backup landet in `backups/`. Alternativ manuell: ```bash docker compose exec db mariadb-dump -u steuerkram -psteuerkram steuerkram \ > ~/steuerkram_backup_$(date +%Y%m%d_%H%M).sql ``` #### Schritt 2 – Aktuellen Code holen ```bash git pull ``` Prüfen, ob neue Migrations-Dateien enthalten sind: ```bash git log --oneline -5 ``` #### Schritt 3 – Container neu bauen und starten ```bash docker compose up -d --build ``` Dieser Befehl baut das PHP-Image neu (falls sich `Dockerfile` oder `composer.json` geändert haben) und startet alle Container. Der Web-Container führt beim Start automatisch `scripts/migrate.php` aus. #### Schritt 4 – Migrationen prüfen ```bash docker compose logs web | grep migrate ``` Erwartete Ausgabe bei erfolgreicher Migration: ``` [migrate] Starte Migrations-Prüfung... [migrate] Führe aus: NNN_beschreibung.sql [migrate] N Migration(en) erfolgreich angewendet. ``` Oder wenn alles bereits aktuell ist: ``` [migrate] Datenbank ist aktuell – keine Migrationen notwendig. ``` Falls die Ausgabe einen Fehler enthält, das Backup aus Schritt 1 einspielen: ```bash docker compose exec -T db mariadb -u steuerkram -psteuerkram steuerkram \ < ~/steuerkram_backup_YYYYMMDD_HHMM.sql ``` #### Schritt 5 – Anwendung prüfen ```bash docker compose ps # alle Container müssen "running" sein docker compose logs web # keine PHP-Fehler oder Exceptions ``` Anschließend im Browser unter `http://localhost:8080` (oder dem konfigurierten Port) einloggen und stichprobenartig Belege und Buchungen prüfen. --- ## Dokumentation | Dokument | Inhalt | |---|---| | [Installation](docs/installation.md) | Docker-Setup, Systemd-Service, Port, Upload-Limit | | [Konfiguration](docs/konfiguration.md) | Firmendaten, Nummernkreise, KI-Beleganalyse, Nextcloud-Import | | [Funktionen](docs/funktionen.md) | Alle Features im Detail | | [Anlagevermögen](docs/anlagevermögen.md) | Anlagenspiegel, Buchungsworkflow, Nummernkreis | | [Update & Backup](docs/update-backup.md) | Update-Anleitung, Backup, Datenpersistenz | | [Automatisierung](docs/automatisierung.md) | Automatisierungsagent, Ideenagent, Support | | [Datenbankstruktur](docs/datenbankstruktur.md) | Tabellen, Spalten, Fremdschlüssel, GoBD-Mechanismen | | [Berechnungslogik](docs/berechnungslogik.md) | UStVA-Kennzahlen und EÜR-Zuordnungsregeln | | [Kontenrahmen](docs/kontenrahmen.md) | Vollständige SKR03-Kontenliste | | [E2E-Tests](tests/e2e/README.md) | Browser-Tests mit Playwright über den e2e-runner | --- ## Tests ```bash docker compose exec -u www-data web php tests/run.php # PHP-Suite (Models, Services, Berechnungen) tests/e2e/run.sh # Browser-Tests (Routing, Views, Formular-JS) ``` Die PHP-Suite läuft ohne weitere Voraussetzungen. Das `-u www-data` gehört dazu: sonst laufen die Tests als root und hinterlassen in `data/` Dateien, die Apache/PHP später nicht mehr beschreiben kann. Die E2E-Tests brauchen Docker, das [e2e-runner-Projekt](../e2e-runner/) und einen Testnutzer (`docker compose exec web php scripts/e2e-user.php`); fehlt etwas davon, werden sie mit einer Warnung übersprungen. Details in [tests/e2e/README.md](tests/e2e/README.md). --- ## GoBD-Hinweise - Gebuchte Sätze können nicht geändert oder gelöscht werden (MariaDB `BEFORE UPDATE/DELETE` Trigger). - Korrekturen erfolgen ausschließlich über Stornobuchungen. - Jeder hochgeladene Beleg erhält einen SHA-256 Hash zur Integritätsprüfung.