87 lines
4.0 KiB
JavaScript
87 lines
4.0 KiB
JavaScript
/**
|
|
* Steuer-Auswertung der Finanzen (rein, ohne I/O): Rücklage auf den Gewinn des laufenden Jahres, Zähler für die
|
|
* Kleinunternehmer-Grenzen (§ 19 UStG) und die nächste Einkommensteuer-Vorauszahlung. Beträge sind ganze Cent;
|
|
* "heute" kommt als Berliner Kalendertag von außen.
|
|
*/
|
|
import { daysBetween } from './dates.js'
|
|
|
|
const GRENZE_FOLGEJAHR_CENTS = 2_500_000 // 25.000 EUR: wer im Jahr darüber liegt, ist im Folgejahr kein Kleinunternehmer mehr
|
|
const GRENZE_HART_CENTS = 10_000_000 // 100.000 EUR: darüber endet die Regelung sofort
|
|
const GEWERBESTEUER_FREIBETRAG_CENTS = 2_450_000 // 24.500 EUR Gewinn bei Personengesellschaften
|
|
const DEFAULT_RESERVE_PERCENT = 30 // wie DEFAULT_SETTINGS.taxReservePercent (settings.js, hier nicht importiert: Modul bleibt rein)
|
|
const EST_MONTHS = [3, 6, 9, 12] // ESt-Vorauszahlung jeweils am 10.
|
|
const EST_DAY = 10
|
|
|
|
const isCents = (n) => Number.isSafeInteger(n)
|
|
const pad = (n) => String(n).padStart(2, '0')
|
|
|
|
/** Nächster ESt-Vorauszahlungstermin ab heute (heute zählt mit) als YYYY-MM-DD. */
|
|
function nextEstDate(today) {
|
|
const year = Number(today.slice(0, 4))
|
|
for (const month of EST_MONTHS) {
|
|
const date = `${String(year).padStart(4, '0')}-${pad(month)}-${pad(EST_DAY)}`
|
|
if (date >= today) return date
|
|
}
|
|
return `${String(year + 1).padStart(4, '0')}-${pad(EST_MONTHS[0])}-${pad(EST_DAY)}`
|
|
}
|
|
|
|
/**
|
|
* @param {object} args
|
|
* @param {string} args.today Berliner Tag YYYY-MM-DD
|
|
* @param {Array} [args.transactions] Bankbuchungen { status, bookingDate, amountCents (+ Eingang / - Ausgang), category }
|
|
* @param {Array} [args.invoices] Stripe-Rechnungen (gemappt) { paidCents, paidDate }
|
|
* @param {object} [args.settings] { taxReservePercent, previousYearRevenueCents, estPrepaymentCents }
|
|
*
|
|
* Einnahmen und Ausgaben: gebuchte Bankbuchungen (BOOK) des Kalenderjahres von today ohne Kategorie "privat".
|
|
* Der Kleinunternehmer-Zähler nimmt statt der Bankeingänge die Zahltage der Stripe-Rechnungen plus Buchungen
|
|
* "rechnung-extern"; Stripe-Auszahlungen und Rechnungsbuchungen zählen dort nicht (sonst doppelt).
|
|
*/
|
|
export function computeTax({ today, transactions = [], invoices = [], settings } = {}) {
|
|
daysBetween(today, today) // wirft bei einem ungültigen Tag
|
|
const jahr = Number(today.slice(0, 4))
|
|
const prefix = `${today.slice(0, 4)}-`
|
|
|
|
let einnahmenCents = 0
|
|
let ausgabenCents = 0
|
|
let externCents = 0
|
|
for (const tx of transactions || []) {
|
|
if (!tx || tx.status !== 'BOOK' || typeof tx.bookingDate !== 'string' || !tx.bookingDate.startsWith(prefix)) continue
|
|
if (!isCents(tx.amountCents) || tx.category === 'privat') continue
|
|
if (tx.amountCents > 0) {
|
|
einnahmenCents += tx.amountCents
|
|
if (tx.category === 'rechnung-extern') externCents += tx.amountCents
|
|
} else {
|
|
ausgabenCents -= tx.amountCents
|
|
}
|
|
}
|
|
|
|
let stripeCents = 0
|
|
for (const invoice of invoices || []) {
|
|
if (!invoice || typeof invoice.paidDate !== 'string' || !invoice.paidDate.startsWith(prefix)) continue
|
|
if (isCents(invoice.paidCents) && invoice.paidCents > 0) stripeCents += invoice.paidCents
|
|
}
|
|
|
|
const gewinnCents = einnahmenCents - ausgabenCents
|
|
const prozent = Number.isFinite(settings?.taxReservePercent) ? settings.taxReservePercent : DEFAULT_RESERVE_PERCENT
|
|
const jahrCents = stripeCents + externCents
|
|
|
|
const prepayment = settings?.estPrepaymentCents
|
|
return {
|
|
jahr,
|
|
einnahmenCents,
|
|
ausgabenCents,
|
|
gewinnCents,
|
|
ruecklageCents: Math.round((Math.max(0, gewinnCents) * prozent) / 100),
|
|
prozent,
|
|
gewerbesteuerHinweis: gewinnCents > GEWERBESTEUER_FREIBETRAG_CENTS,
|
|
kleinunternehmer: {
|
|
jahrCents,
|
|
vorjahrCents: isCents(settings?.previousYearRevenueCents) ? settings.previousYearRevenueCents : null,
|
|
grenzeFolgejahrCents: GRENZE_FOLGEJAHR_CENTS,
|
|
grenzeHartCents: GRENZE_HART_CENTS,
|
|
anteilFolgejahr: jahrCents / GRENZE_FOLGEJAHR_CENTS,
|
|
},
|
|
naechsteVorauszahlung: isCents(prepayment) && prepayment > 0 ? { date: nextEstDate(today), amountCents: prepayment } : null,
|
|
}
|
|
}
|