Files
open-eur-buchhaltung/README.md
T
duffyduckandClaude Opus 5 bf371c8118 Einnahmen-Überschuss-Rechnung mit ELSTER, DATEV und Mandantenfähigkeit
Gewinnermittlung nach §4 Abs. 3 EStG für Einzelunternehmen und Freiberufler.
Node/Fastify mit SQLite im Backend, React/Vite im Frontend; die Anwendung
läuft lokal ohne Cloud-Anbindung.

Fachlicher Kern ist eine Zeitachse der Besteuerungsform: das umsatzsteuerliche
Regime wird je Buchung aus dem Leistungsdatum aufgelöst, statt aus einer
globalen Ja/Nein-Flagge. Dadurch ist auch ein unterjähriger Wechsel von der
Kleinunternehmerregelung zur Regelbesteuerung abbildbar, wie ihn §19 Abs. 1
Satz 3 UStG seit 2025 beim Überschreiten der 100.000-EUR-Grenze vorsieht.

Enthalten:
- Buchungserfassung nach dem Zu-/Abflussprinzip mit Storno statt Löschung
- Anlagevermögen mit AfA (linear, GWG, Sammelposten); die Aktivierung
  entscheidet das Konto, die Methode der Betrag
- Anlage EÜR, BWA im Aufbau der Form 01, Umsatzsteuer-Voranmeldung
- ELSTER-Anbindung über ERiC mit validierendem Simulationsmodus
- DATEV-Buchungsstapel (EXTF 700) und GoBD-Datenträgerüberlassung (Z3)
- Kontenrahmen SKR03 und SKR04, je Mandant beim Anlegen wählbar
- Mandantenfähigkeit mit einer SQLite-Datei je Mandant; Testmandanten sind
  von der Übermittlung ans Finanzamt ausgeschlossen

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 21:45:52 +02:00

12 KiB
Raw Blame History

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.

npm install
npm run seed          # Beispieldaten mit Regimewechsel 2025 -> 2026
npm run dev           # Backend (3311) + Frontend (5173) im Entwicklungsmodus

Für den Dauerbetrieb:

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.

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.

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:

cat /proc/sys/fs/inotify/max_user_instances            # meist 128
find /proc/*/fd -lname anon_inode:inotify | wc -l      # tatsächlich belegt

Dauerhafte Abhilfe:

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:

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.

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 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.

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, 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.