Finanzen: Steuerkarte mit Orientierungshinweis und neuer Anteilsformulierung, Ladetext auf Deutsch
TaxCard zeigt am Ende immer "Orientierungswert, ersetzt keine Steuerberatung."; der Anteil heisst "<x> % der Grenze von 25.000 €" statt "<x> % der 25.000-€-Grenze". AdminRoute zeigt "Wird geladen …" statt "Loading...". Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0b1101d384
commit
e9cfae28ee
5 files changed
+36
-6
No files matched your search
@@ -9,7 +9,7 @@ export default function AdminRoute({ children }) {
|
||||
return (
|
||||
<div className="text-center p-4" style={{ marginTop: 80 }}>
|
||||
<div className="spinner"></div>
|
||||
<p>Loading...</p>
|
||||
<p>Wird geladen …</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { taxView } from '../../lib/financeView'
|
||||
* Block 4: „Steuer“. Rücklage (Betrag und woraus sie sich ergibt), Kleinunternehmer-Zähler gegen die Grenzen,
|
||||
* Vorjahr (nur wenn eingetragen), Gewerbesteuer-Hinweis (nur wenn nötig) und die nächste ESt-Vorauszahlung
|
||||
* (nur wenn eingestellt). Ist der Zähler unbekannt (Stripe nicht erreichbar), steht das als Text da, nie eine 0.
|
||||
* Am Ende steht immer der Hinweis „Orientierungswert, ersetzt keine Steuerberatung.“
|
||||
*/
|
||||
export default function TaxCard({ steuer }) {
|
||||
const v = taxView(steuer)
|
||||
@@ -44,6 +45,8 @@ export default function TaxCard({ steuer }) {
|
||||
<div className="fin-fact-sub">am {v.prepayment.dateText}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p className="fin-hint fin-tax-note">{v.noteText}</p>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
|
||||
@@ -292,10 +292,13 @@ const KLEIN_LIMIT_CENTS = 2_500_000
|
||||
const KLEIN_HARD_LIMIT_CENTS = 10_000_000
|
||||
const GEWERBE_ALLOWANCE_CENTS = 2_450_000
|
||||
|
||||
/** Dauerhafter Hinweis unter den Steuerwerten: die Zahlen sind Näherungen. */
|
||||
export const TAX_NOTE = 'Orientierungswert, ersetzt keine Steuerberatung.'
|
||||
|
||||
/**
|
||||
* Texte der Steuerkarte aus `steuer`:
|
||||
* { reserve: { amountText, basisText }, small: { known, counterText, shareText, limitText, hardText, previousText },
|
||||
* gewerbeText, prepayment: { dateText, amountText } | null }.
|
||||
* gewerbeText, prepayment: { dateText, amountText } | null, noteText }.
|
||||
* Ist der Kleinunternehmer-Zähler null (Stripe nicht erreichbar), steht „unbekannt“ statt einer Zahl.
|
||||
*/
|
||||
export function taxView(steuer) {
|
||||
@@ -331,7 +334,7 @@ export function taxView(steuer) {
|
||||
counterText: known ? `${euro(k.jahrCents)} von ${euroWhole(limit)}${year !== null ? ` (${year})` : ''}` : 'unbekannt (Stripe nicht erreichbar)',
|
||||
shareText:
|
||||
known && isNum(k.anteilFolgejahr)
|
||||
? `${percentDe(k.anteilFolgejahr * 100, 1)} % der ${euroWhole(limit).replace(' €', '-€')}-Grenze`
|
||||
? `${percentDe(k.anteilFolgejahr * 100, 1)} % der Grenze von ${euroWhole(limit)}`
|
||||
: null,
|
||||
limitText: `Über ${euroWhole(limit)} im Jahr${year !== null ? ` ${year}` : ''}: ab ${
|
||||
year !== null ? year + 1 : 'dem Folgejahr'
|
||||
@@ -344,6 +347,7 @@ export function taxView(steuer) {
|
||||
? `Der Gewinn${year !== null ? ` ${year}` : ''} liegt über dem Gewerbesteuer-Freibetrag von ${euroWhole(GEWERBE_ALLOWANCE_CENTS)} (natürliche Personen und Personengesellschaften, § 11 GewStG). Auf den übersteigenden Gewerbeertrag kann Gewerbesteuer anfallen.`
|
||||
: null,
|
||||
prepayment,
|
||||
noteText: TAX_NOTE,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..')
|
||||
const source = readFileSync(path.join(root, 'src/components/AdminRoute.jsx'), 'utf8')
|
||||
|
||||
// AdminRoute braucht React und den Auth-Kontext (nicht unter Node ladbar): der Ladetext wird am Quelltext geprüft
|
||||
test('AdminRoute: Ladetext auf Deutsch, kein englisches „Loading...“', () => {
|
||||
assert.match(source, /<p>Wird geladen …<\/p>/)
|
||||
assert.doesNotMatch(source, /Loading/)
|
||||
})
|
||||
@@ -20,6 +20,7 @@ const {
|
||||
forecastRows,
|
||||
forecastNotes,
|
||||
taxView,
|
||||
TAX_NOTE,
|
||||
aboView,
|
||||
productLabel,
|
||||
aboStatus,
|
||||
@@ -453,17 +454,25 @@ test('taxView: Kleinunternehmer-Zähler „<Betrag> von 25.000 € (2026)“ und
|
||||
const v = taxView(steuer())
|
||||
assert.equal(v.small.known, true)
|
||||
assert.equal(v.small.counterText, '80,00 € von 25.000 € (2026)')
|
||||
assert.equal(v.small.shareText, '0,3 % der 25.000-€-Grenze')
|
||||
assert.equal(v.small.shareText, '0,3 % der Grenze von 25.000 €')
|
||||
assert.equal(v.small.limitText, 'Über 25.000 € im Jahr 2026: ab 2027 keine Kleinunternehmerregelung.')
|
||||
assert.equal(
|
||||
v.small.hardText,
|
||||
'Grenze 100.000 €: Ab dem Umsatz, der diese Grenze übersteigt, gilt schon im laufenden Jahr die Regelbesteuerung (Umsatzsteuer).'
|
||||
)
|
||||
assert.equal(v.small.previousText, null)
|
||||
assert.equal(taxView(steuer({}, { anteilFolgejahr: 0.704 })).small.shareText, '70,4 % der 25.000-€-Grenze')
|
||||
assert.equal(taxView(steuer({}, { anteilFolgejahr: 0.704 })).small.shareText, '70,4 % der Grenze von 25.000 €')
|
||||
assert.equal(taxView(steuer({}, { jahrCents: 0, anteilFolgejahr: 0 })).small.counterText, '0,00 € von 25.000 € (2026)')
|
||||
})
|
||||
|
||||
test('taxView: Orientierungshinweis steht immer dabei, Anteil ohne „-€-Grenze“', () => {
|
||||
assert.equal(TAX_NOTE, 'Orientierungswert, ersetzt keine Steuerberatung.')
|
||||
assert.equal(taxView(steuer()).noteText, TAX_NOTE)
|
||||
assert.equal(taxView(null).noteText, TAX_NOTE)
|
||||
assert.equal(taxView(steuer({}, { jahrCents: null, anteilFolgejahr: null })).noteText, TAX_NOTE)
|
||||
for (const t of [taxView(steuer()), taxView(steuer({}, { anteilFolgejahr: 1.2 }))]) assert.doesNotMatch(t.small.shareText, /-€-Grenze/)
|
||||
})
|
||||
|
||||
test('taxView: Stripe nicht erreichbar -> unbekannt statt 0', () => {
|
||||
const v = taxView(steuer({}, { jahrCents: null, anteilFolgejahr: null }))
|
||||
assert.equal(v.small.known, false)
|
||||
@@ -492,7 +501,7 @@ test('taxView: Vorjahr nur wenn gesetzt, über der Grenze mit Hinweis', () => {
|
||||
test('taxView: Grenzen kommen aus dem Server (Rückfall nur ohne Wert), Jahr fehlt -> Folgejahr', () => {
|
||||
const custom = taxView(steuer({ jahr: 2030 }, { grenzeFolgejahrCents: 3000000, grenzeHartCents: 12000000, anteilFolgejahr: 0.5, vorjahrCents: 3000001 }))
|
||||
assert.equal(custom.small.counterText, '80,00 € von 30.000 € (2030)')
|
||||
assert.equal(custom.small.shareText, '50 % der 30.000-€-Grenze')
|
||||
assert.equal(custom.small.shareText, '50 % der Grenze von 30.000 €')
|
||||
assert.equal(custom.small.limitText, 'Über 30.000 € im Jahr 2030: ab 2031 keine Kleinunternehmerregelung.')
|
||||
assert.match(custom.small.hardText, /^Grenze 120\.000 €: Ab dem Umsatz, der diese Grenze übersteigt, gilt schon im laufenden Jahr die Regelbesteuerung \(Umsatzsteuer\)\.$/)
|
||||
assert.equal(custom.small.previousText, 'Vorjahr: 30.000,01 € (über 30.000 €: im laufenden Jahr keine Kleinunternehmerregelung)')
|
||||
|
||||
Reference in new issue
Block a user