Finanzen: Vorschau, Steuer und Aufgaben genauer (Fixkosten ohne ESt, verspaetete Auszahlungen, Abos nicht ladbar)
M6: fixkosten30Cents zaehlt nur Fixkosten, die ESt-Vorauszahlung bleibt sicherer Posten. M7: verspaetete Auszahlung (Ankunftstag vorbei, noch nicht bezahlt) kommt heute. M8: ohne Stripe sind Kleinunternehmer-Zaehler und Anteil unbekannt (null), keine Warnung daraus. M12: Verbindungsdokument ohne Sitzung zaehlt als keine Verbindung. M13: Buchungen privat/fixkosten gehen nicht in die Fixkosten-Vorschlaege. M14: Vorjahresumsatz ueber 25.000 EUR ist eine hohe Aufgabe. M15: scheitert das Laden der Abos, meldet abos.fehler statt die ganze Uebersicht zu kippen. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
83ee9d5a25
commit
039fdcaf27
6 files changed
+295
-20
No files matched your search
@@ -192,7 +192,8 @@ test('ESt-Vorauszahlung 50000: 10.12. bei Horizont 90, keine bei Horizont 30; oh
|
||||
const settings = { estPrepaymentCents: 50000 }
|
||||
const r90 = buildForecast({ today: TODAY, horizonDays: 90, balanceCents: BALANCE, settings })
|
||||
assert.deepEqual(r90.posten, [{ date: '2026-12-10', label: 'ESt-Vorauszahlung', amountCents: -50000, sicherheit: 'sicher' }])
|
||||
assert.equal(r90.fixkosten30Cents, 50000)
|
||||
// die ESt-Vorauszahlung ist keine Fixkosten-Position: sie steckt schon in der Steuerruecklage (M6)
|
||||
assert.equal(r90.fixkosten30Cents, 0)
|
||||
assert.deepEqual(r90.minSicher, { amountCents: -39107, date: '2026-12-10' })
|
||||
assert.deepEqual(buildForecast({ today: TODAY, horizonDays: 30, balanceCents: BALANCE, settings }).posten, [])
|
||||
assert.deepEqual(buildForecast({ today: TODAY, horizonDays: 90, balanceCents: BALANCE, settings: { estPrepaymentCents: 0 } }).posten, [])
|
||||
@@ -205,7 +206,7 @@ test('ESt-Vorauszahlung: alle vier Termine, auch über den Jahreswechsel', () =>
|
||||
assert.deepEqual(dates(buildForecast({ today: '2026-12-10', horizonDays: 0, balanceCents: 0, settings: { estPrepaymentCents: 100 } })), ['2026-12-10'])
|
||||
})
|
||||
|
||||
test('Auszahlung in_transit 1500 am 03.10.: sicher +1500; pending zählt, paid/canceled/failed und vergangene nicht', () => {
|
||||
test('Auszahlung in_transit 1500 am 03.10.: sicher +1500; pending zählt, paid/canceled/failed nicht, eine verspätete (Ankunftstag vorbei) kommt heute (M7)', () => {
|
||||
const payouts = [
|
||||
{ id: 'po_1', amountCents: 1500, arrivalDate: '2026-10-03', status: 'in_transit' },
|
||||
{ id: 'po_2', amountCents: 700, arrivalDate: '2026-10-06', status: 'pending' },
|
||||
@@ -217,14 +218,52 @@ test('Auszahlung in_transit 1500 am 03.10.: sicher +1500; pending zählt, paid/c
|
||||
]
|
||||
const r = base({ payouts })
|
||||
assert.deepEqual(r.posten, [
|
||||
{ date: TODAY, label: 'Stripe-Auszahlung', amountCents: 999, sicherheit: 'sicher' },
|
||||
{ date: '2026-10-03', label: 'Stripe-Auszahlung', amountCents: 1500, sicherheit: 'sicher' },
|
||||
{ date: '2026-10-06', label: 'Stripe-Auszahlung', amountCents: 700, sicherheit: 'sicher' },
|
||||
])
|
||||
assert.equal(r.endeSicherCents, 13093)
|
||||
assert.equal(r.endeErwartetCents, 13093)
|
||||
assert.equal(r.endeSicherCents, 14092)
|
||||
assert.equal(r.endeErwartetCents, 14092)
|
||||
assert.equal(r.fixkosten30Cents, 0)
|
||||
})
|
||||
|
||||
test('verspätete Auszahlung: Ankunftstag gestern oder vor Wochen -> heute, auch in_transit; ungültige Tage fallen weg (M7)', () => {
|
||||
const r = base({
|
||||
payouts: [
|
||||
{ id: 'po_1', amountCents: 4000, arrivalDate: '2026-09-30', status: 'in_transit' },
|
||||
{ id: 'po_2', amountCents: 500, arrivalDate: '2026-08-01', status: 'pending' },
|
||||
{ id: 'po_3', amountCents: 900, arrivalDate: 'gestern', status: 'pending' },
|
||||
],
|
||||
})
|
||||
assert.deepEqual(r.posten.map((p) => [p.date, p.amountCents]), [[TODAY, 4000], [TODAY, 500]])
|
||||
assert.deepEqual(r.minSicher, { amountCents: 10893, date: TODAY })
|
||||
assert.equal(r.endeSicherCents, 15393)
|
||||
// ein Abfluss am selben Tag kommt weiter zuerst
|
||||
const mixed = base({
|
||||
payouts: [{ id: 'po_1', amountCents: 4000, arrivalDate: '2026-09-30', status: 'in_transit' }],
|
||||
fixedCosts: [monthly({ nextDate: TODAY })],
|
||||
})
|
||||
assert.deepEqual(mixed.posten.map((p) => p.amountCents), [-449, 4000])
|
||||
})
|
||||
|
||||
test('fixkosten30Cents zählt nur Fixkosten, nicht die ESt-Vorauszahlung; die ESt bleibt ein sicherer Posten (M6)', () => {
|
||||
const r = buildForecast({
|
||||
today: TODAY,
|
||||
horizonDays: 90,
|
||||
balanceCents: BALANCE,
|
||||
settings: { estPrepaymentCents: 50000 },
|
||||
fixedCosts: [monthly({ nextDate: '2026-10-10' })],
|
||||
})
|
||||
// 449 monatlich: 10.10., 10.11., 10.12., 10.01. liegt hinter dem Horizont (30.12.)
|
||||
assert.equal(r.fixkosten30Cents, 3 * 449)
|
||||
assert.deepEqual(r.posten.filter((p) => p.label === 'ESt-Vorauszahlung'), [{ date: '2026-12-10', label: 'ESt-Vorauszahlung', amountCents: -50000, sicherheit: 'sicher' }])
|
||||
// die Linie rechnet die ESt weiter ein: 10893 - 3 * 449 - 50000
|
||||
assert.equal(r.endeSicherCents, 10893 - 3 * 449 - 50000)
|
||||
|
||||
// nur ESt, keine Fixkosten: 0
|
||||
assert.equal(buildForecast({ today: TODAY, horizonDays: 90, balanceCents: 0, settings: { estPrepaymentCents: 50000 } }).fixkosten30Cents, 0)
|
||||
})
|
||||
|
||||
test('Sortierung: nach Datum, am selben Tag Abflüsse zuerst', () => {
|
||||
const r = base({
|
||||
payouts: [{ id: 'po_1', amountCents: 1500, arrivalDate: '2026-10-14', status: 'in_transit' }],
|
||||
|
||||
@@ -446,7 +446,10 @@ test('GET /uebersicht: Stripe 500 -> 200, Bankteil, Kopf und Aufgaben aus Bank u
|
||||
assert.equal(body.kopf.kontostandCents, 10893)
|
||||
assert.equal(body.kopf.verfuegbarCents, 4176)
|
||||
assert.deepEqual(body.vorschau.posten.map((p) => p.label), ['Hetzner', 'Porkbun', 'Abo-Verlängerung'])
|
||||
assert.equal(body.steuer.kleinunternehmer.jahrCents, 0)
|
||||
// Kleinunternehmer-Zaehler ohne Stripe-Zahltage waere zu niedrig: unbekannt statt 0 (M8)
|
||||
assert.equal(body.steuer.kleinunternehmer.jahrCents, null)
|
||||
assert.equal(body.steuer.kleinunternehmer.anteilFolgejahr, null)
|
||||
assert.equal(body.steuer.kleinunternehmer.grenzeFolgejahrCents, 2500000)
|
||||
assert.deepEqual(body.rechnungen.altersgruppen.map((g) => g.anzahl), [0, 0, 0, 0])
|
||||
assert.deepEqual(body.rechnungen.entwuerfe, { anzahl: 0, summeCents: 0 })
|
||||
|
||||
@@ -487,6 +490,136 @@ test('buildOverview: Stripe antwortet rechtzeitig -> ok, die Wartezeit haelt den
|
||||
assert.equal(body.stand.stripe.ok, true)
|
||||
})
|
||||
|
||||
test('GET /uebersicht: ohne Stripe kein Kleinunternehmer-Zähler und keine Warnung daraus, mit Stripe schon (M8)', async () => {
|
||||
seedConnection()
|
||||
seedSettings()
|
||||
// 19.000 EUR "Rechnung ausserhalb von Stripe" im laufenden Jahr = 76 % der Grenze
|
||||
aw.seed(config.collections.bankTransactions, tx('x1', { amountCents: 1_900_000, category: 'rechnung-extern', counterpartyName: 'GROSSKUNDE AG' }))
|
||||
|
||||
const ok = await uebersicht()
|
||||
assert.equal(ok.steuer.kleinunternehmer.jahrCents, 1_900_000)
|
||||
assert.equal(ok.steuer.kleinunternehmer.anteilFolgejahr, 0.76)
|
||||
assert.equal(ok.aufgaben.filter((t) => t.art === 'kleinunternehmer').length, 1)
|
||||
|
||||
invalidateFinanceCache()
|
||||
stripeDown()
|
||||
const down = await uebersicht()
|
||||
assert.equal(down.stand.stripe.ok, false)
|
||||
assert.equal(down.steuer.kleinunternehmer.jahrCents, null)
|
||||
assert.equal(down.steuer.kleinunternehmer.anteilFolgejahr, null)
|
||||
assert.deepEqual(down.aufgaben.filter((t) => t.art === 'kleinunternehmer'), [])
|
||||
// Gewinn und Ruecklage kommen aus der Bank und bleiben
|
||||
assert.equal(down.steuer.einnahmenCents, 1_900_000)
|
||||
assert.equal(down.steuer.ruecklageCents, ok.steuer.ruecklageCents)
|
||||
})
|
||||
|
||||
test('GET /uebersicht: Verbindungsdokument ohne Sitzung -> wie keine Verbindung, Aufgabe "Bankkonto verbinden" (M12)', async () => {
|
||||
aw.seed(config.collections.bankConnections, { $id: 'finom', authState: 'abc123', authStartedAt: new Date().toISOString() })
|
||||
const body = await uebersicht()
|
||||
assert.equal(body.stand.bank, null)
|
||||
assert.equal(body.kopf.kontostandCents, null)
|
||||
assert.equal(body.kopf.verfuegbarCents, null)
|
||||
assert.deepEqual(ART(body), ['bank-zugang'])
|
||||
assert.equal(body.aufgaben[0].titel, 'Bankkonto verbinden')
|
||||
|
||||
// mit Sitzung (auch bei einer neuen Freigabe in Arbeit) bleibt es ein Zugang
|
||||
await deleteDocument(config.collections.bankConnections, 'finom')
|
||||
seedConnection({ authState: 'neu', authStartedAt: new Date().toISOString() })
|
||||
const connected = await uebersicht()
|
||||
assert.equal(connected.stand.bank.status, 'aktiv')
|
||||
assert.ok(!ART(connected).includes('bank-zugang'))
|
||||
})
|
||||
|
||||
test('GET /uebersicht: Buchungen "privat" und "fixkosten" zählen nicht für Fixkosten-Vorschläge (M13)', async () => {
|
||||
seedConnection()
|
||||
const hetzner = (id, days, over = {}) => tx(id, { bookingDate: at(days), amountCents: -1000, counterpartyName: 'HETZNER ONLINE GMBH', ...over })
|
||||
const seedThree = (prefix, over = {}) => [-58, -28, 0].forEach((days, i) => aw.seed(config.collections.bankTransactions, hetzner(`${prefix}${i}`, days, over)))
|
||||
|
||||
seedThree('open')
|
||||
const suggested = (await uebersicht()).aufgaben.filter((t) => t.art === 'fixkosten-vorschlag')
|
||||
assert.equal(suggested.length, 1)
|
||||
assert.match(suggested[0].text, /HETZNER ONLINE GMBH/)
|
||||
for (const id of ['open0', 'open1', 'open2']) await deleteDocument(config.collections.bankTransactions, id)
|
||||
|
||||
seedThree('privat', { category: 'privat' })
|
||||
assert.deepEqual((await uebersicht()).aufgaben.filter((t) => t.art === 'fixkosten-vorschlag'), [])
|
||||
for (const id of ['privat0', 'privat1', 'privat2']) await deleteDocument(config.collections.bankTransactions, id)
|
||||
|
||||
seedThree('fix', { category: 'fixkosten', fixedCostId: 'fk1' })
|
||||
assert.deepEqual((await uebersicht()).aufgaben.filter((t) => t.art === 'fixkosten-vorschlag'), [])
|
||||
for (const id of ['fix0', 'fix1', 'fix2']) await deleteDocument(config.collections.bankTransactions, id)
|
||||
|
||||
// zwei private und eine offene: ohne den Filter waeren es drei gleiche Abbuchungen
|
||||
aw.seed(config.collections.bankTransactions, hetzner('m0', -58, { category: 'privat' }))
|
||||
aw.seed(config.collections.bankTransactions, hetzner('m1', -28, { category: 'privat' }))
|
||||
aw.seed(config.collections.bankTransactions, hetzner('m2', 0))
|
||||
assert.deepEqual((await uebersicht()).aufgaben.filter((t) => t.art === 'fixkosten-vorschlag'), [])
|
||||
})
|
||||
|
||||
test('GET /uebersicht: Vorjahresumsatz über 25.000 € aus den Einstellungen wird eine hohe Aufgabe (M14)', async () => {
|
||||
seedConnection()
|
||||
seedSettings({ previousYearRevenueCents: 2_600_000 })
|
||||
const body = await uebersicht()
|
||||
const todo = body.aufgaben.find((t) => t.titel === 'Vorjahresumsatz über 25.000 €')
|
||||
assert.ok(todo, JSON.stringify(body.aufgaben))
|
||||
assert.equal(todo.art, 'kleinunternehmer')
|
||||
assert.equal(todo.dringlichkeit, 'hoch')
|
||||
assert.match(todo.text, /26\.000,00/)
|
||||
// hoch steht vor den mittleren Bank-Aufgaben
|
||||
assert.equal(body.aufgaben[0].dringlichkeit, 'hoch')
|
||||
|
||||
await deleteDocument(config.collections.financeSettings, 'main')
|
||||
seedSettings({ previousYearRevenueCents: 2_500_000 })
|
||||
assert.ok(!(await uebersicht()).aufgaben.some((t) => t.titel === 'Vorjahresumsatz über 25.000 €'))
|
||||
})
|
||||
|
||||
test('GET /uebersicht: Abos nicht ladbar -> 200 mit abos.fehler, der Rest bleibt, nichts aus Abos in Vorschau und Aufgaben (M15)', async () => {
|
||||
seedAll()
|
||||
seedSub('sub_c', { customerId: 'c1', status: 'past_due' })
|
||||
const error = mock.method(console, 'error', () => {})
|
||||
aw.failNext({ collection: config.collections.subscriptions, method: 'GET', status: 500, message: 'Appwrite kaputt 10.0.0.9' })
|
||||
const res = await rawGet(`${base}/uebersicht`, admin)
|
||||
assert.equal(res.status, 200, JSON.stringify(res.body))
|
||||
assert.deepEqual(res.body.abos, {
|
||||
monatlichCents: null,
|
||||
zahlendeKunden: null,
|
||||
gefaehrdetCents: null,
|
||||
liste: [],
|
||||
fehler: 'Abos konnten nicht geladen werden.',
|
||||
})
|
||||
assert.ok(!JSON.stringify(res.body).includes('10.0.0.9'))
|
||||
assert.equal(res.body.stand.bank.balanceBookedCents, 10893)
|
||||
assert.equal(res.body.stand.stripe.ok, true)
|
||||
assert.ok(!ART(res.body).includes('abo-zahlung'))
|
||||
assert.ok(!res.body.vorschau.posten.some((p) => p.label === 'Abo-Verlängerung'))
|
||||
assert.ok(res.body.vorschau.posten.some((p) => p.label === 'Porkbun'), 'Fixkosten bleiben')
|
||||
assert.ok(ART(res.body).includes('ueberfaellig'))
|
||||
assert.equal(error.mock.callCount(), 1)
|
||||
assert.deepEqual(error.mock.calls[0].arguments, ['[finanzen] Abos laden fehlgeschlagen:', 'Error', 500, 500])
|
||||
|
||||
// der naechste Aufruf laedt wieder und hat kein fehler-Feld
|
||||
mock.restoreAll()
|
||||
const again = await uebersicht()
|
||||
assert.equal(again.abos.fehler, undefined)
|
||||
assert.equal(again.abos.liste.length, 3)
|
||||
})
|
||||
|
||||
test('buildOverview: ESt-Vorauszahlung bleibt ein sicherer Posten, ist aber nicht in fixkosten30Cents und verfuegbarCents (M6)', async () => {
|
||||
seedConnection()
|
||||
seedSettings({ estPrepaymentCents: 50_000, minBufferCents: 0 })
|
||||
aw.seed(config.collections.financeFixedCosts, { $id: 'fk1', name: 'Hetzner', amountCents: 449, interval: 'monat', nextDate: '2026-11-25', counterpartyMatch: '', active: true })
|
||||
const body = await buildOverview({ now: new Date('2026-11-20T10:00:00Z') })
|
||||
assert.deepEqual(body.vorschau.posten.map((p) => [p.date, p.label, p.amountCents]), [
|
||||
['2026-11-25', 'Hetzner', -449],
|
||||
['2026-12-10', 'ESt-Vorauszahlung', -50_000],
|
||||
])
|
||||
assert.equal(body.vorschau.fixkosten30Cents, 449)
|
||||
assert.equal(body.kopf.aufschluesselung.fixkosten30Cents, 449)
|
||||
// 10.893 - Ruecklage 0 - Fixkosten 449 - Puffer 0: die ESt ist Sache der Ruecklage, nicht noch einmal abgezogen
|
||||
assert.equal(body.kopf.verfuegbarCents, 10893 - 449)
|
||||
assert.equal(body.vorschau.endeSicherCents, 10893 - 449 - 50_000)
|
||||
})
|
||||
|
||||
test('GET /uebersicht: nur die Auszahlungen scheitern -> Rechnungen bleiben, Stripe-Block meldet den Fehler', async () => {
|
||||
seedAll()
|
||||
st.failNext({ method: 'GET', path: '/v1/payouts', status: 400, message: 'Geheimer Stripe-Text' })
|
||||
|
||||
@@ -9,7 +9,7 @@ const NOW = new Date('2026-10-01T10:00:00.000Z')
|
||||
const HOUR = 3_600_000
|
||||
const ago = (hours) => new Date(NOW.getTime() - hours * HOUR).toISOString()
|
||||
|
||||
const connection = (extra = {}) => ({ status: 'aktiv', validUntil: '2027-03-29T00:00:00.000Z', lastSyncAt: ago(1), lastSyncOk: true, lastError: '', ...extra })
|
||||
const connection = (extra = {}) => ({ sessionId: 'sess-1', status: 'aktiv', validUntil: '2027-03-29T00:00:00.000Z', lastSyncAt: ago(1), lastSyncOk: true, lastError: '', ...extra })
|
||||
const invoice = (extra = {}) => ({
|
||||
id: 'in_1', number: 'WK-2026-0001', status: 'ueberfaellig', customerName: 'Muster GmbH', createdDate: '2026-08-01', dueDate: addDays(TODAY, -5),
|
||||
remainingCents: 50000, paidCents: 0, paidDate: null, overdueDays: 5, source: 'projekt', hostedUrl: 'https://pay.example/in_1', ...extra,
|
||||
@@ -51,6 +51,19 @@ test('bank-zugang: keine Verbindung -> „Bankkonto verbinden“ hoch', () => {
|
||||
}
|
||||
})
|
||||
|
||||
test('bank-zugang: Verbindungsdokument ohne Sitzung (erste Freigabe nie abgeschlossen) zählt als keine Verbindung (M12)', () => {
|
||||
for (const abandoned of [{ sessionId: undefined }, { sessionId: null }, { sessionId: '' }, { sessionId: ' ' }]) {
|
||||
// sonst kaeme "Bankabruf veraltet", weil es nie einen Abruf gab: es bleibt bei der einen Aufgabe
|
||||
const list = todos({ connection: { authState: 'abc', authStartedAt: '2026-10-01T09:00:00.000Z', ...abandoned } })
|
||||
assert.equal(list.length, 1, JSON.stringify(list))
|
||||
assert.equal(list[0].titel, 'Bankkonto verbinden')
|
||||
assert.equal(list[0].dringlichkeit, 'hoch')
|
||||
assert.deepEqual(list[0].aktion, { typ: 'bank-verbinden' })
|
||||
}
|
||||
// mit Sitzung ist es ein Zugang (hier ruhig)
|
||||
assert.deepEqual(todos({ connection: connection({ sessionId: 'sess-x' }) }), [])
|
||||
})
|
||||
|
||||
test('bank-zugang: Zugang gültig bis 20.10. (19 Tage) -> mittel, bis 10.10. (9 Tage) -> hoch', () => {
|
||||
const mid = only(todos({ connection: connection({ validUntil: '2026-10-20T00:00:00.000Z' }) }), 'bank-zugang')
|
||||
assert.equal(mid.length, 1)
|
||||
@@ -358,6 +371,38 @@ test('kleinunternehmer: über 100.000 Euro nennt die harte Grenze', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('kleinunternehmer: Vorjahresumsatz über 25.000 € -> hoch mit festem Titel und Text (M14)', () => {
|
||||
const list = only(todos({ settings: { previousYearRevenueCents: 2_600_050 } }), 'kleinunternehmer')
|
||||
assert.equal(list.length, 1)
|
||||
assert.deepEqual(list[0], {
|
||||
art: 'kleinunternehmer',
|
||||
dringlichkeit: 'hoch',
|
||||
titel: 'Vorjahresumsatz über 25.000 €',
|
||||
text: `Im Vorjahr lagen die Einnahmen bei ${euroDe(2_600_050)}. Damit gilt die Kleinunternehmerregelung (§ 19 UStG) in diesem Jahr nicht. Bitte Rechnungen und Rechnungsfuß mit der Steuerberatung klären.`,
|
||||
datum: TODAY,
|
||||
aktion: { typ: 'einstellungen' },
|
||||
})
|
||||
assert.match(list[0].text, /26\.000,50/)
|
||||
})
|
||||
|
||||
test('kleinunternehmer: Vorjahresumsatz genau 25.000 €, darunter, fehlend oder unlesbar -> keine Vorjahres-Aufgabe (M14)', () => {
|
||||
for (const previousYearRevenueCents of [2_500_000, 100, 0, null, undefined, '3000000', 30_000.5]) {
|
||||
assert.deepEqual(only(todos({ settings: { previousYearRevenueCents } }), 'kleinunternehmer'), [], String(previousYearRevenueCents))
|
||||
}
|
||||
assert.deepEqual(only(todos({ settings: null }), 'kleinunternehmer'), [])
|
||||
assert.deepEqual(only(todos(), 'kleinunternehmer'), [])
|
||||
// 25.000,01 € ist darüber
|
||||
assert.equal(only(todos({ settings: { previousYearRevenueCents: 2_500_001 } }), 'kleinunternehmer').length, 1)
|
||||
})
|
||||
|
||||
test('kleinunternehmer: Vorjahres-Aufgabe und Aufgabe zum laufenden Jahr stehen nebeneinander, hoch vor mittel (M14)', () => {
|
||||
const list = only(todos({ tax: tax(0.72), settings: { previousYearRevenueCents: 3_000_000 } }), 'kleinunternehmer')
|
||||
assert.deepEqual(list.map((t) => [t.dringlichkeit, t.titel]), [
|
||||
['hoch', 'Vorjahresumsatz über 25.000 €'],
|
||||
['mittel', 'Kleinunternehmer-Grenze rückt näher'],
|
||||
])
|
||||
})
|
||||
|
||||
test('kleinunternehmer: ohne tax oder mit unlesbarem Anteil keine Aufgabe', () => {
|
||||
assert.deepEqual(only(todos({ tax: null }), 'kleinunternehmer'), [])
|
||||
assert.deepEqual(only(todos({ tax: {} }), 'kleinunternehmer'), [])
|
||||
|
||||
Reference in new issue
Block a user