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:
duffyduck
2026-08-03 21:45:52 +02:00
co-authored by Claude Opus 5
commit bf371c8118
59 changed files with 12913 additions and 0 deletions
+220
View File
@@ -0,0 +1,220 @@
/**
* Gliederung der Anlage EÜR.
*
* WICHTIG: Die amtlichen Zeilennummern der Anlage EÜR ändern sich fast jedes
* Jahr. Der fachliche Aufbau (welcher Posten wohin gehört) ist stabil, die
* Nummerierung nicht. Deshalb sind hier fachliche Schlüssel definiert und die
* Zeilennummern pro Veranlagungsjahr in ZEILEN_NUMMERN gepflegt. Vor der ersten
* Abgabe eines Jahres die Nummern gegen das aktuelle amtliche Formular prüfen
* und ggf. hier nachziehen – die Berechnung selbst bleibt davon unberührt.
*/
export type EuerBereich = 'einnahmen' | 'ausgaben';
export interface EuerPosten {
key: string;
bezeichnung: string;
bereich: EuerBereich;
/** Nur bei Regelbesteuerung relevant */
nurRegelbesteuerung?: boolean;
/** Nur bei Kleinunternehmern relevant */
nurKleinunternehmer?: boolean;
/** Anteil, der steuerlich abziehbar ist (Basispunkte); z.B. Bewirtung 70 % */
abziehbarBp?: number;
hinweis?: string;
}
export const EUER_POSTEN: EuerPosten[] = [
// ---- Betriebseinnahmen -------------------------------------------------
{
key: 'be_ku',
bezeichnung: 'Betriebseinnahmen als umsatzsteuerlicher Kleinunternehmer (§19 UStG)',
bereich: 'einnahmen',
nurKleinunternehmer: true,
},
{
key: 'be_pflichtig',
bezeichnung: 'Umsatzsteuerpflichtige Betriebseinnahmen (netto)',
bereich: 'einnahmen',
nurRegelbesteuerung: true,
},
{
key: 'be_frei',
bezeichnung: 'Umsatzsteuerfreie und nicht steuerbare Betriebseinnahmen',
bereich: 'einnahmen',
},
{
key: 'be_ust',
bezeichnung: 'Vereinnahmte Umsatzsteuer',
bereich: 'einnahmen',
nurRegelbesteuerung: true,
},
{
key: 'be_ust_erstattung',
bezeichnung: 'Vom Finanzamt erstattete und ggf. verrechnete Umsatzsteuer',
bereich: 'einnahmen',
nurRegelbesteuerung: true,
},
{
key: 'be_anlagenabgang',
bezeichnung: 'Veräußerung oder Entnahme von Anlagevermögen',
bereich: 'einnahmen',
},
{
key: 'be_kfz_privat',
bezeichnung: 'Private Kfz-Nutzung',
bereich: 'einnahmen',
},
{
key: 'be_entnahmen',
bezeichnung: 'Sonstige Sach-, Nutzungs- und Leistungsentnahmen',
bereich: 'einnahmen',
},
{ key: 'be_sonstige', bezeichnung: 'Übrige Betriebseinnahmen', bereich: 'einnahmen' },
// ---- Betriebsausgaben --------------------------------------------------
{
key: 'ba_waren',
bezeichnung: 'Waren, Roh- und Hilfsstoffe einschließlich Nebenkosten',
bereich: 'ausgaben',
},
{ key: 'ba_fremdleistung', bezeichnung: 'Bezogene Fremdleistungen', bereich: 'ausgaben' },
{ key: 'ba_personal', bezeichnung: 'Ausgaben für eigenes Personal', bereich: 'ausgaben' },
{
key: 'ba_afa_beweglich',
bezeichnung: 'AfA auf bewegliche Wirtschaftsgüter',
bereich: 'ausgaben',
hinweis: 'wird automatisch aus dem Anlagenverzeichnis ermittelt',
},
{
key: 'ba_afa_immateriell',
bezeichnung: 'AfA auf immaterielle Wirtschaftsgüter',
bereich: 'ausgaben',
},
{
key: 'ba_gwg',
bezeichnung: 'Sofortabschreibung geringwertiger Wirtschaftsgüter (GWG, §6 Abs. 2 EStG)',
bereich: 'ausgaben',
},
{
key: 'ba_sammelposten',
bezeichnung: 'Auflösung Sammelposten (§6 Abs. 2a EStG)',
bereich: 'ausgaben',
},
{
key: 'ba_restbuchwert',
bezeichnung: 'Restbuchwert ausgeschiedener Anlagegüter',
bereich: 'ausgaben',
},
{ key: 'ba_miete', bezeichnung: 'Miete/Pacht für Geschäftsräume', bereich: 'ausgaben' },
{ key: 'ba_raum_sonstige', bezeichnung: 'Sonstige Raumkosten', bereich: 'ausgaben' },
{
key: 'ba_arbeitszimmer',
bezeichnung: 'Häusliches Arbeitszimmer / Tagespauschale',
bereich: 'ausgaben',
},
{
key: 'ba_versicherung',
bezeichnung: 'Beiträge, Gebühren, Abgaben und Versicherungen',
bereich: 'ausgaben',
},
{ key: 'ba_werbung', bezeichnung: 'Werbekosten', bereich: 'ausgaben' },
{ key: 'ba_reise', bezeichnung: 'Reisekosten', bereich: 'ausgaben' },
{
key: 'ba_bewirtung',
bezeichnung: 'Bewirtungsaufwendungen',
bereich: 'ausgaben',
abziehbarBp: 7000,
hinweis: 'nur zu 70 % abziehbar (§4 Abs. 5 Nr. 2 EStG); die Vorsteuer bleibt zu 100 % abziehbar',
},
{
key: 'ba_geschenke',
bezeichnung: 'Geschenke an Geschäftsfreunde',
bereich: 'ausgaben',
hinweis: 'nur abziehbar bis 50 EUR je Empfänger und Jahr (§4 Abs. 5 Nr. 1 EStG)',
},
{ key: 'ba_kfz', bezeichnung: 'Kfz-Kosten und andere Fahrtkosten', bereich: 'ausgaben' },
{
key: 'ba_buero',
bezeichnung: 'Porto, Telefon, Bürobedarf und sonstige Betriebsmittel',
bereich: 'ausgaben',
},
{ key: 'ba_fortbildung', bezeichnung: 'Fortbildungskosten', bereich: 'ausgaben' },
{
key: 'ba_beratung',
bezeichnung: 'Rechts- und Steuerberatung, Buchführung',
bereich: 'ausgaben',
},
{ key: 'ba_zinsen', bezeichnung: 'Übrige Schuldzinsen', bereich: 'ausgaben' },
{
key: 'ba_vorsteuer',
bezeichnung: 'Gezahlte Vorsteuerbeträge',
bereich: 'ausgaben',
nurRegelbesteuerung: true,
},
{
key: 'ba_ust_zahlung',
bezeichnung: 'An das Finanzamt gezahlte und ggf. verrechnete Umsatzsteuer',
bereich: 'ausgaben',
nurRegelbesteuerung: true,
},
{
key: 'ba_sonstige',
bezeichnung: 'Übrige unbeschränkt abziehbare Betriebsausgaben',
bereich: 'ausgaben',
},
];
export const POSTEN_NACH_KEY = new Map(EUER_POSTEN.map((p) => [p.key, p]));
/**
* Amtliche Zeilennummern je Veranlagungsjahr. Bewusst separat gehalten, damit
* ein neues Formularjahr eine reine Datenänderung ist.
* Status: an der Formularfassung 2024/2025 orientiert – vor Abgabe verifizieren.
*/
export const ZEILEN_NUMMERN: Record<number, Record<string, number>> = {
2025: {
be_ku: 11,
be_pflichtig: 12,
be_frei: 13,
be_ust: 16,
be_ust_erstattung: 17,
be_anlagenabgang: 18,
be_kfz_privat: 19,
be_entnahmen: 20,
be_sonstige: 15,
ba_waren: 26,
ba_fremdleistung: 27,
ba_personal: 28,
ba_afa_beweglich: 31,
ba_afa_immateriell: 30,
ba_gwg: 34,
ba_sammelposten: 35,
ba_restbuchwert: 36,
ba_miete: 38,
ba_raum_sonstige: 39,
ba_arbeitszimmer: 40,
ba_versicherung: 47,
ba_werbung: 49,
ba_reise: 50,
ba_bewirtung: 51,
ba_geschenke: 52,
ba_kfz: 61,
ba_buero: 55,
ba_fortbildung: 56,
ba_beratung: 57,
ba_zinsen: 44,
ba_vorsteuer: 63,
ba_ust_zahlung: 64,
ba_sonstige: 66,
},
};
export function zeilenNummer(jahr: number, key: string): number | null {
const jahre = Object.keys(ZEILEN_NUMMERN)
.map(Number)
.sort((a, b) => b - a);
const passend = jahre.find((j) => j <= jahr) ?? jahre[jahre.length - 1];
if (passend === undefined) return null;
return ZEILEN_NUMMERN[passend]?.[key] ?? null;
}