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>
This commit is contained in:
@@ -0,0 +1,277 @@
|
||||
# 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.
|
||||
|
||||
## 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.
|
||||
Reference in New Issue
Block a user