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>
221 lines
6.6 KiB
TypeScript
221 lines
6.6 KiB
TypeScript
/**
|
||
* 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;
|
||
}
|