Der Container bringt die gebaute Anwendung mit; einziger Mount ist ./data:/app/data. Die Buchhaltung bleibt damit als gewöhnliche SQLite-Dateien im Projektverzeichnis und lässt sich ohne Docker sichern und einsehen. Der Server band bisher fest an 127.0.0.1 und wäre im Container von außen nicht erreichbar gewesen. Die Adresse ist jetzt über HOST einstellbar und bleibt außerhalb des Containers unverändert auf 127.0.0.1; im Container gilt 0.0.0.0, abgesichert über eine Portfreigabe, die nur auf 127.0.0.1 lauscht. Weitere Festlegungen: - Lauf unter der UID des aufrufenden Kontos, damit ./data nicht root gehört - schreibgeschütztes Containerdateisystem, beschreibbar nur /app/data und /tmp - .env wird nicht ins Image gebacken, sondern von docker-compose eingelesen - ERiC bleibt außen vor und ist bei Bedarf vom Host einzubinden Die Startmeldung nannte bisher nur, ob eine .env-Datei existiert. Im Container gibt es keine, obwohl die Konfiguration vollständig vorliegt – gemeldet wird jetzt der tatsächliche ERiC-Zustand. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
319 lines
13 KiB
Markdown
319 lines
13 KiB
Markdown
# open-eur-webapp
|
||
|
||
Einnahmen-Überschuss-Rechnung nach §4 Abs. 3 EStG für Einzelunternehmen und
|
||
Freiberufler – mit Kleinunternehmerregelung, Umsatzsteuer-Voranmeldung,
|
||
BWA, ELSTER-Anbindung und DATEV-/GoBD-Export.
|
||
|
||
Die Anwendung läuft lokal: ein Node-Prozess, eine SQLite-Datei, keine
|
||
Cloud-Anbindung.
|
||
|
||
```bash
|
||
npm install
|
||
npm run seed # Beispieldaten mit Regimewechsel 2025 -> 2026
|
||
npm run dev # Backend (3311) + Frontend (5173) im Entwicklungsmodus
|
||
```
|
||
|
||
Für den Dauerbetrieb:
|
||
|
||
```bash
|
||
npm run build
|
||
npm start # liefert API und Oberfläche auf http://127.0.0.1:3311
|
||
```
|
||
|
||
`npm test` prüft die Steuerlogik (Regimewechsel, EÜR, UStVA, AfA,
|
||
Steuernummernumsetzung).
|
||
|
||
`npm run seed` bricht ab, wenn bereits Buchungen vorhanden sind – sonst würden
|
||
die Beispieldaten ein zweites Mal eingespielt und alle Auswertungen verdoppeln.
|
||
`npm run seed -- reset` setzt die Bewegungsdaten zurück und legt sie neu an;
|
||
dabei gehen auch selbst erfasste Buchungen verloren.
|
||
|
||
## Betrieb mit Docker
|
||
|
||
```bash
|
||
cp .env.beispiel .env # optional, für ELSTER
|
||
export UID GID=$(id -g) # damit ./data dem eigenen Konto gehört
|
||
docker-compose up -d --build
|
||
```
|
||
|
||
Danach läuft die Anwendung auf <http://localhost:3311>.
|
||
|
||
**Keine benannten Volumes.** Der einzige Mount ist `./data:/app/data` – die
|
||
Buchhaltung liegt als gewöhnliche SQLite-Dateien im Projektverzeichnis und lässt
|
||
sich sichern, kopieren und einsehen, ohne Docker zu bemühen. Der Container läuft
|
||
mit der UID des aufrufenden Kontos, deshalb gehören die Dateien nicht root.
|
||
|
||
Weitere Festlegungen:
|
||
|
||
- **Port nur auf 127.0.0.1 veröffentlicht.** Die Anwendung kennt keine
|
||
Benutzeranmeldung, und hier liegen Steuernummer und Umsätze. Für den Zugriff
|
||
aus dem Netz gehört ein Reverse Proxy mit Authentifizierung davor.
|
||
- Das Dateisystem des Containers ist schreibgeschützt (`read_only`), beschreibbar
|
||
ist nur `/app/data` und ein `tmpfs` unter `/tmp`.
|
||
- `.env` wird **nicht** ins Image gebacken. `docker-compose` liest die Datei für
|
||
die Variablenersetzung und reicht die Werte als Umgebungsvariablen weiter.
|
||
- **ERiC ist nicht enthalten** und darf es auch nicht sein. Wer echt übermitteln
|
||
will, hebt in `docker-compose.yml` die Zeilen für `/opt/eric` und das
|
||
Zertifikat auf und setzt die Pfade in `.env`.
|
||
|
||
Beim Start meldet der Container, woher die Konfiguration stammt und ob ERiC
|
||
eingebunden ist:
|
||
|
||
```
|
||
open-eur-webapp läuft auf http://localhost:3311
|
||
Konfiguration aus der Prozessumgebung.
|
||
ELSTER: Simulationsmodus, es wird nichts übermittelt (…).
|
||
Mandanten: standard – aktiv ohne Angabe: standard
|
||
```
|
||
|
||
Ohne Docker bindet sich der Server weiterhin an `127.0.0.1`; im Container setzt
|
||
`HOST=0.0.0.0`, weil die Portfreigabe den Prozess sonst nicht erreicht.
|
||
|
||
## Mandanten
|
||
|
||
Jeder Mandant hat **eine eigene SQLite-Datei** unter `data/mandanten/`. Das ist
|
||
Absicht: bei einer gemeinsamen Datenbank mit `mandant_id`-Spalte genügt ein
|
||
einziges vergessenes `WHERE`, damit Testbuchungen in der echten EÜR oder in
|
||
einer ELSTER-Übermittlung auftauchen. Getrennte Dateien machen das strukturell
|
||
unmöglich.
|
||
|
||
Umgeschaltet wird oben in der Seitenleiste. Die Anwendung lädt dabei neu, damit
|
||
keine Formularstände des vorherigen Mandanten stehen bleiben.
|
||
|
||
**Testmandanten** sind als solche markiert, auf jeder Seite sichtbar
|
||
gekennzeichnet und **können nichts an das Finanzamt übermitteln** – es sei denn,
|
||
`ELSTER_TESTMERKER` ist gesetzt, dann sind Probeübermittlungen ohne steuerliche
|
||
Wirkung möglich.
|
||
|
||
```bash
|
||
npm run seed -- --mandant=test # legt einen Testmandanten an und befüllt ihn
|
||
```
|
||
|
||
Technisch hängt der Mandant am Request: Header `X-Mandant`, für Download-Links
|
||
(die keine Header setzen können) alternativ `?mandant=<bezeichner>`. Der Server
|
||
führt den Handler in einem AsyncLocalStorage-Kontext aus, sodass jeder
|
||
Datenbankzugriff automatisch auf der richtigen Datei landet.
|
||
|
||
Beim Entfernen eines Mandanten wird die Datei **nicht gelöscht**, sondern mit
|
||
Zeitstempel beiseitegelegt (`….sqlite.entfernt-<zeit>`).
|
||
|
||
## Kontenrahmen: SKR03 und SKR04
|
||
|
||
Beide stehen zur Wahl – **beim Anlegen eines Mandanten**, danach nicht mehr.
|
||
Kontonummern und Bezeichnungen stammen aus den amtlichen DATEV-Kontenrahmen
|
||
(Stand 2026); die Zuordnung zu EÜR-Posten, BWA-Gruppen und UStVA-Kennzahlen ist
|
||
fachliche Auslegung dieser Anwendung und gehört vor dem ersten Export mit der
|
||
Steuerkanzlei abgestimmt.
|
||
|
||
Der Wechsel ist gesperrt, weil dieselbe Kontonummer in beiden Rahmen etwas
|
||
anderes bedeutet:
|
||
|
||
| Konto | SKR03 | SKR04 |
|
||
|---|---|---|
|
||
| 1800 | Privatentnahmen allgemein | **Bank** |
|
||
| 2100 | Zinsen und ähnliche Aufwendungen | Privatentnahmen allgemein |
|
||
| 4830 | Abschreibungen auf Sachanlagen | Sonstige betriebliche Erträge |
|
||
| 4970 | Nebenkosten des Geldverkehrs | Zinserträge |
|
||
|
||
Aus demselben Grund ist das Standard-Gegenkonto rahmenabhängig (SKR03 `1200`,
|
||
SKR04 `1800`) und das Feld `kontenrahmen` in den Stammdaten schreibgeschützt –
|
||
sonst könnte der DATEV-Export einen Rahmen melden, den die Buchungszeilen nicht
|
||
verwenden.
|
||
|
||
## Anlagevermögen oder Sofortabzug
|
||
|
||
Ob eine Ausgabe abzuschreiben ist, entscheidet das **Konto** (Kontotyp `anlage`),
|
||
nicht der Betrag – eine Werbekampagne für 3.000 € bleibt sofort abziehbar, ein
|
||
Drucker für 900 € nicht. Eine Buchung auf ein Anlagekonto wird deshalb immer
|
||
aktiviert; ein stiller Vollabzug ist nicht möglich.
|
||
|
||
Der Betrag entscheidet erst über die **Methode** (bei Kleinunternehmern zählt der
|
||
Bruttobetrag, weil die Umsatzsteuer dann zu den Anschaffungskosten gehört):
|
||
|
||
| Anschaffungskosten | Behandlung |
|
||
|---|---|
|
||
| ≤ 800 € | GWG, im Anschaffungsjahr voll abgesetzt (§6 Abs. 2 EStG) |
|
||
| ≤ 1.000 € | Sammelposten, über 5 Jahre aufgelöst (§6 Abs. 2a EStG) |
|
||
| > 1.000 € | linear über die Nutzungsdauer (§7 Abs. 1 EStG) – Nutzungsdauer ist Pflichtangabe |
|
||
|
||
Buchungen auf Anlagekonten ohne hinterlegtes Anlagegut (etwa aus einer früheren
|
||
Fassung) werden in der EÜR gesondert als prüfbedürftig ausgewiesen. Die Zahlen
|
||
werden dabei bewusst *nicht* stillschweigend korrigiert.
|
||
|
||
## Konfiguration
|
||
|
||
`.env.beispiel` nach `.env` kopieren und anpassen. Die Datei wird beim Start
|
||
automatisch gelesen; bereits gesetzte Umgebungsvariablen haben Vorrang.
|
||
**Relative Pfade darin beziehen sich immer auf das Projektverzeichnis**, nicht
|
||
auf das Verzeichnis, aus dem der Prozess gestartet wurde – sonst entstünden je
|
||
nach Startort verschiedene Datenbanken.
|
||
|
||
Ohne `.env` läuft alles außer der ELSTER-Übermittlung; diese arbeitet dann im
|
||
Simulationsmodus.
|
||
|
||
## Wenn der Start fehlschlägt
|
||
|
||
**`EMFILE: too many open files, watch ...`** (Linux, betrifft `tsx watch` und
|
||
Vite gleichermaßen)
|
||
|
||
Nicht das Dateideskriptor-Limit, sondern die Zahl der erlaubten
|
||
inotify-*Instanzen* ist erschöpft – typischerweise, weil VS Code und andere
|
||
Werkzeuge sie bereits aufgebraucht haben:
|
||
|
||
```bash
|
||
cat /proc/sys/fs/inotify/max_user_instances # meist 128
|
||
find /proc/*/fd -lname anon_inode:inotify | wc -l # tatsächlich belegt
|
||
```
|
||
|
||
Dauerhafte Abhilfe:
|
||
|
||
```bash
|
||
sudo sysctl -w fs.inotify.max_user_instances=1024
|
||
echo 'fs.inotify.max_user_instances=1024' | sudo tee /etc/sysctl.d/60-inotify.conf
|
||
```
|
||
|
||
Ohne Root-Rechte stattdessen:
|
||
|
||
```bash
|
||
npm run dev:polling # Server ohne Watcher, Vite mit Polling statt inotify
|
||
```
|
||
|
||
Der Server startet dann nicht automatisch neu – nach Änderungen am Backend
|
||
manuell neu starten.
|
||
|
||
---
|
||
|
||
## Der Kern: die Zeitachse der Besteuerungsform
|
||
|
||
Der Wechsel von der Kleinunternehmerregelung zur Regelbesteuerung ist nicht als
|
||
Schalter umgesetzt, sondern als **Zeitachse**:
|
||
|
||
| Gültig ab | Besteuerungsform |
|
||
|---|---|
|
||
| 15.01.2024 | Kleinunternehmer (§19 UStG) |
|
||
| 01.01.2026 | Regelbesteuerung |
|
||
|
||
Jede Buchung wird nach dem Regime behandelt, das **zu ihrem Leistungsdatum**
|
||
galt. Daraus folgt:
|
||
|
||
- Altbuchungen bleiben unverändert, wenn ein Wechsel eingetragen wird.
|
||
- Ein **unterjähriger** Wechsel ist möglich – nötig etwa beim Überschreiten der
|
||
100.000-EUR-Grenze, seit 2025 endet die Kleinunternehmerregelung dann sofort
|
||
mit dem überschreitenden Umsatz (§19 Abs. 1 Satz 3 UStG).
|
||
- Ein Jahr mit Wechsel weist Umsätze davor und danach in der EÜR getrennt aus.
|
||
|
||
Die Umsatzgrenzen werden laufend überwacht (25.000 EUR Vorjahr / 100.000 EUR
|
||
laufendes Jahr ab 2025; 22.000 / 50.000 bis 2024) und melden sich, bevor der
|
||
Wechsel fällig wird.
|
||
|
||
Die Regeln stehen an einer Stelle: [`server/src/domain/regime.ts`](server/src/domain/regime.ts).
|
||
|
||
## Was die Anwendung kann
|
||
|
||
**Erfassung**
|
||
- Buchungen mit Beleg-, Leistungs- und Zahlungsdatum; jedes Datum hat eine
|
||
eigene steuerliche Aufgabe (Regime, Soll-Versteuerung, Zuflussprinzip).
|
||
- Live-Vorschau: das Backend entscheidet über Steuersatz, Behandlung und
|
||
Vorsteuerabzug – nicht das Formular.
|
||
- Reverse Charge (§13b), innergemeinschaftliche Erwerbe und Lieferungen,
|
||
steuerfreie und nicht steuerbare Umsätze.
|
||
- Privatanteile, Bewirtung (70 %), offene Posten.
|
||
- Anlagevermögen mit linearer AfA zeitanteilig, GWG-Sofortabschreibung und
|
||
Sammelposten; Kleinunternehmer schreiben vom Bruttobetrag ab.
|
||
- GoBD: keine Löschung, sondern Storno per Gegenbuchung; Festschreibung;
|
||
Änderungsprotokoll.
|
||
|
||
**Auswertungen**
|
||
- **EÜR** in der Gliederung der Anlage EÜR, mit Anlagenverzeichnis.
|
||
- **UStVA** mit den amtlichen Kennzahlen, Ist- oder Soll-Versteuerung,
|
||
monatlich oder vierteljährlich, mit Aufriss auf die Einzelbuchungen.
|
||
- **BWA** im Aufbau der BWA Form 01: Monat, kumuliert, Vorjahresvergleich,
|
||
Quoten – wahlweise nach Zahlungs- oder Leistungszeitpunkt.
|
||
|
||
**Ausgabe**
|
||
- **ELSTER** über ERiC (siehe unten).
|
||
- **DATEV** Buchungsstapel im EXTF-Format (Version 700, Formatversion 13) plus
|
||
Kontenbeschriftungen – das Paket für die Steuerkanzlei.
|
||
- **GoBD-Datenträgerüberlassung** (GDPdU, Format Z3) mit `index.xml` und CSV –
|
||
das Paket für die Betriebsprüfung.
|
||
- EÜR und BWA zusätzlich als CSV.
|
||
|
||
## ELSTER-Übermittlung
|
||
|
||
**Ohne ERiC ist keine Übermittlung möglich – auch nicht mit einem eigenen
|
||
XML-Versand.** ERiC ist die einzige zugelassene Schnittstelle zur
|
||
Finanzverwaltung. Die Bibliothek ist proprietär, darf nicht weitergegeben
|
||
werden und ist deshalb hier nicht enthalten.
|
||
|
||
So wird sie eingebunden:
|
||
|
||
1. Im [ELSTER-Entwicklerportal](https://www.elster.de/elsterweb/entwickler)
|
||
registrieren und ERiC für die eigene Plattform herunterladen.
|
||
2. `.env.beispiel` nach `.env` kopieren und ausfüllen:
|
||
`ERIC_LIB_PATH`, `ERIC_PLUGIN_PATH`, `ELSTER_ZERTIFIKAT`, `ELSTER_PIN`,
|
||
`ELSTER_HERSTELLER_ID`.
|
||
3. Erste Versuche mit `ELSTER_TESTMERKER=700000004` – solche Übermittlungen
|
||
haben keine steuerliche Wirkung.
|
||
|
||
Ist ERiC nicht eingerichtet, läuft die Anwendung im **Simulationsmodus**: das
|
||
XML wird erzeugt, auf Wohlgeformtheit und Vollständigkeit geprüft und unter
|
||
`data/elster-ausgang/` abgelegt. Es wird nichts gesendet, und die Oberfläche
|
||
sagt das ausdrücklich.
|
||
|
||
Die Übermittlung ist verbindlich und nicht zurückzunehmen. Der Endpunkt
|
||
verlangt deshalb `"bestaetigt": true`, die Oberfläche eine Rückfrage.
|
||
|
||
Die Steuernummer wird automatisch vom Länderformat (`123/456/78901`) in das
|
||
13-stellige Bundesschema umgesetzt, das ELSTER erwartet – für alle 16
|
||
Bundesländer, siehe [`server/src/elster/steuernummer.ts`](server/src/elster/steuernummer.ts).
|
||
|
||
## Was vor dem Produktiveinsatz zu prüfen ist
|
||
|
||
Die Rechenwege sind mit Tests abgesichert, aber einige Angaben ändern sich
|
||
jährlich und müssen gegen die amtlichen Vorgaben abgeglichen werden:
|
||
|
||
- **Zeilennummern der Anlage EÜR** – sie ändern sich fast jedes Jahr. Der
|
||
fachliche Aufbau ist stabil, nur die Nummerierung nicht. Gepflegt werden sie
|
||
in `ZEILEN_NUMMERN` in
|
||
[`server/src/domain/euerZeilen.ts`](server/src/domain/euerZeilen.ts), getrennt
|
||
von der Berechnung. Stand: an der Fassung 2024/2025 orientiert.
|
||
- **ELSTER-Schemaversion** – die Datenart-Version (`UStVA_<Jahr>`) und der
|
||
Aufbau des Nutzdatenblocks müssen zu der Version passen, die im installierten
|
||
ERiC steckt. Der erste Lauf sollte mit gesetztem Testmerker erfolgen.
|
||
- **DATEV-Formatversion** – der Export folgt Version 700 / Formatversion 13.
|
||
Vor der ersten Übergabe eine Probedatei mit der Kanzlei einlesen.
|
||
- **Kontenrahmen** – `server/src/kontenrahmen/skr04.ts` ist ein praxisnaher
|
||
Auszug aus dem SKR04, kein vollständiger Kontenrahmen. Konten lassen sich in
|
||
der Oberfläche ergänzen und umwidmen.
|
||
|
||
Die Anwendung ersetzt keine steuerliche Beratung. Für die Richtigkeit der
|
||
Erklärung bleibt die abgebende Person verantwortlich.
|
||
|
||
## Aufbau
|
||
|
||
```
|
||
server/src/
|
||
db.ts SQLite-Schema, Änderungsprotokoll
|
||
domain/
|
||
regime.ts Zeitachse der Besteuerungsform, USt-Ermittlung, §19-Grenzen
|
||
euer.ts Einnahmen-Überschuss-Rechnung
|
||
euerZeilen.ts Gliederung + Zeilennummern je Veranlagungsjahr
|
||
ustva.ts Voranmeldung, Kennzahlen, Ist/Soll
|
||
bwa.ts BWA Form 01
|
||
afa.ts lineare AfA, GWG, Sammelposten
|
||
geld.ts Cent-Arithmetik, kaufmännische Rundung
|
||
elster/
|
||
steuernummer.ts Länderformat -> Bundesschema (alle 16 Länder)
|
||
ustvaXml.ts ELSTER-Datenteil für die UStVA
|
||
eric.ts ERiC-Anbindung über koffi + Simulationsmodus
|
||
export/
|
||
datev.ts EXTF-Buchungsstapel und Kontenbeschriftungen
|
||
gdpdu.ts GoBD-Datenträgerüberlassung
|
||
zip.ts minimaler ZIP-Writer
|
||
routes/ HTTP-Schnittstelle
|
||
web/src/ Oberfläche (React, Vite)
|
||
```
|
||
|
||
Alle Beträge werden als ganzzahlige Cent geführt, Steuersätze als Basispunkte
|
||
(1900 = 19 %). Damit gibt es keine Fließkommafehler in der Steuerberechnung.
|