import { db } from '../db.js'; import type { Regime, RegimeEintrag, UstBehandlung } from './typen.js'; import { ustAusBrutto, ustAusNetto } from './geld.js'; /** * Grenzen der Kleinunternehmerregelung (§19 UStG) ab dem Besteuerungszeitraum 2025. * Bis einschließlich 2024 galten 22.000 EUR (Vorjahr, brutto) / 50.000 EUR (Prognose). */ export const KU_GRENZEN = { bis2024: { vorjahr: 22_000_00, laufend: 50_000_00, laufendIstPrognose: true }, ab2025: { vorjahr: 25_000_00, laufend: 100_000_00, laufendIstPrognose: false }, } as const; export function grenzenFuer(jahr: number) { return jahr >= 2025 ? KU_GRENZEN.ab2025 : KU_GRENZEN.bis2024; } export function alleRegime(): RegimeEintrag[] { return db .prepare('SELECT * FROM steuer_regime ORDER BY gueltig_ab ASC') .all() as RegimeEintrag[]; } /** * Löst das umsatzsteuerliche Regime für ein Datum auf: der jüngste Eintrag, * dessen `gueltig_ab` nicht in der Zukunft des Datums liegt. Ohne passenden * Eintrag gilt Kleinunternehmer (konservativer Default für Gründer). */ export function regimeAm(datum: string): Regime { const row = db .prepare( 'SELECT modus FROM steuer_regime WHERE gueltig_ab <= ? ORDER BY gueltig_ab DESC LIMIT 1', ) .get(datum) as { modus: Regime } | undefined; return row?.modus ?? 'kleinunternehmer'; } /** Gibt es innerhalb des Zeitraums einen Regimewechsel? Wichtig für UStVA-Hinweise. */ export function regimeWechselIm(von: string, bis: string): RegimeEintrag[] { return db .prepare('SELECT * FROM steuer_regime WHERE gueltig_ab > ? AND gueltig_ab <= ? ORDER BY gueltig_ab') .all(von, bis) as RegimeEintrag[]; } export interface UstErgebnis { brutto: number; netto: number; ust: number; satzBp: number; behandlung: UstBehandlung; vorsteuerAbziehbar: boolean; hinweise: string[]; } export interface UstEingabe { art: 'einnahme' | 'ausgabe'; /** Maßgeblich für das Regime: der Zeitpunkt der Leistungsausführung. */ leistungsdatum: string; /** Genau eines von beiden angeben. */ brutto?: number; netto?: number; satzBp: number; behandlung: UstBehandlung; } /** * Zentrale USt-Ermittlung. Sie erzwingt die Regeln des zum Leistungsdatum * geltenden Regimes – dadurch bleibt ein unterjähriger Wechsel automatisch * konsistent, ohne dass Altbuchungen angefasst werden müssen. */ export function berechneUst(e: UstEingabe): UstErgebnis { const regime = regimeAm(e.leistungsdatum); const hinweise: string[] = []; let behandlung = e.behandlung; let satzBp = e.satzBp; let vorsteuerAbziehbar = true; if (regime === 'kleinunternehmer') { if (e.art === 'einnahme') { if (behandlung === 'regel' && satzBp > 0) { hinweise.push( 'Zum Leistungsdatum galt die Kleinunternehmerregelung (§19 UStG): ' + 'der Umsatz ist steuerfrei, es wurde keine Umsatzsteuer angesetzt.', ); } behandlung = 'kleinunternehmer'; satzBp = 0; } else { // Vorsteuerabzug ist nach §19 Abs. 1 Satz 4 UStG ausgeschlossen -> // die Ausgabe geht brutto in die EÜR ein. if (satzBp > 0 && behandlung === 'regel') { hinweise.push( 'Kein Vorsteuerabzug als Kleinunternehmer – die Betriebsausgabe wird mit dem Bruttobetrag erfasst.', ); } vorsteuerAbziehbar = false; // Reverse-Charge/i.g. Erwerb bleiben auch für Kleinunternehmer steuerpflichtig. if (behandlung !== 'reverse_charge_13b' && behandlung !== 'ig_erwerb') { behandlung = 'kleinunternehmer'; satzBp = 0; } else { hinweise.push( 'Steuerschuld des Leistungsempfängers besteht auch für Kleinunternehmer – ' + 'eine USt-Voranmeldung ist für diesen Zeitraum abzugeben (§18 Abs. 4a UStG), ' + 'ein Vorsteuerabzug daraus jedoch nicht möglich.', ); vorsteuerAbziehbar = false; } } } else { if (behandlung === 'kleinunternehmer') { hinweise.push( 'Zum Leistungsdatum galt bereits die Regelbesteuerung – Behandlung auf "regel" gesetzt.', ); behandlung = 'regel'; } if ( behandlung === 'steuerfrei_ohne_vst' || behandlung === 'nicht_steuerbar' ) { vorsteuerAbziehbar = behandlung === 'nicht_steuerbar'; satzBp = 0; } if (behandlung === 'steuerfrei_mit_vst' || behandlung === 'ig_lieferung') { satzBp = 0; } } let brutto: number; let netto: number; let ust: number; if (behandlung === 'reverse_charge_13b' || behandlung === 'ig_erwerb') { // Der Rechnungsbetrag ist netto; die Steuer schuldet der Leistungsempfänger. netto = e.netto ?? e.brutto ?? 0; ust = ustAusNetto(netto, satzBp); brutto = netto; // gezahlt wird nur der Nettobetrag } else if (e.brutto !== undefined) { brutto = e.brutto; ust = ustAusBrutto(brutto, satzBp); netto = brutto - ust; } else { netto = e.netto ?? 0; ust = ustAusNetto(netto, satzBp); brutto = netto + ust; } return { brutto, netto, ust, satzBp, behandlung, vorsteuerAbziehbar, hinweise }; } export interface GrenzenStatus { jahr: number; vorjahresumsatz: number; laufenderUmsatz: number; grenzeVorjahr: number; grenzeLaufend: number; regimeJetzt: Regime; vorjahrUeberschritten: boolean; laufendUeberschritten: boolean; /** Kleinunternehmerregelung entfällt zum 1. Januar des Folgejahres */ wechselImFolgejahr: boolean; warnungen: string[]; } /** * Überwacht die §19-Grenzen. Maßgeblich ist der Gesamtumsatz nach §19 Abs. 2 UStG * (vereinnahmte Entgelte); hier näherungsweise die Summe der steuerbaren * Betriebseinnahmen nach Zuflussprinzip. */ export function grenzenStatus(jahr: number): GrenzenStatus { const umsatz = (j: number): number => { const r = db .prepare( `SELECT COALESCE(SUM(betrag_brutto),0) AS s FROM buchungen WHERE art='einnahme' AND storniert=0 AND ust_behandlung NOT IN ('nicht_steuerbar') AND zahlungsdatum BETWEEN ? AND ?`, ) .get(`${j}-01-01`, `${j}-12-31`) as { s: number }; return r.s; }; const g = grenzenFuer(jahr); const vorjahresumsatz = umsatz(jahr - 1); const laufenderUmsatz = umsatz(jahr); const regimeJetzt = regimeAm(`${jahr}-12-31`); const warnungen: string[] = []; const vorjahrUeberschritten = vorjahresumsatz > g.vorjahr; const laufendUeberschritten = laufenderUmsatz > g.laufend; const folgejahr = grenzenFuer(jahr + 1); const wechselImFolgejahr = regimeJetzt === 'kleinunternehmer' && laufenderUmsatz > folgejahr.vorjahr; if (regimeJetzt === 'kleinunternehmer') { if (wechselImFolgejahr) { warnungen.push( `Der Umsatz ${jahr} liegt mit ${(laufenderUmsatz / 100).toLocaleString('de-DE')} EUR über der ` + `Vorjahresgrenze von ${(folgejahr.vorjahr / 100).toLocaleString('de-DE')} EUR. ` + `Die Kleinunternehmerregelung entfällt damit ab dem 01.01.${jahr + 1} – bitte zu diesem ` + 'Datum einen Wechsel zur Regelbesteuerung anlegen und ab dann Umsatzsteuer ausweisen.', ); } if (vorjahrUeberschritten) { warnungen.push( `Der Vorjahresumsatz ${jahr - 1} liegt über ${(g.vorjahr / 100).toLocaleString('de-DE')} EUR. ` + `Ab dem 01.01.${jahr} greift die Regelbesteuerung – bitte einen Regimewechsel zum 01.01.${jahr} anlegen.`, ); } if (laufendUeberschritten && jahr >= 2025) { warnungen.push( `Die laufende Grenze von ${(g.laufend / 100).toLocaleString('de-DE')} EUR wurde in ${jahr} überschritten. ` + 'Seit 2025 endet die Kleinunternehmerregelung sofort mit dem Umsatz, der die Grenze übersteigt ' + '(§19 Abs. 1 Satz 3 UStG) – der Regimewechsel ist unterjährig zu diesem Datum einzutragen.', ); } if (!laufendUeberschritten && laufenderUmsatz > (g.laufend * 8) / 10) { warnungen.push( `Achtung: ${((laufenderUmsatz / g.laufend) * 100).toFixed(0)} % der laufenden Umsatzgrenze erreicht.`, ); } } return { jahr, vorjahresumsatz, laufenderUmsatz, grenzeVorjahr: g.vorjahr, grenzeLaufend: g.laufend, regimeJetzt, vorjahrUeberschritten, laufendUeberschritten, wechselImFolgejahr, warnungen, }; }