@fin.cx/tax 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/smallbusiness.d.ts +45 -13
- package/dist_ts/smallbusiness.js +13 -9
- package/package.json +1 -1
- package/readme.md +17 -7
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/smallbusiness.ts +52 -20
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@fin.cx/tax',
|
|
6
|
-
version: '2.
|
|
6
|
+
version: '2.6.0',
|
|
7
7
|
description: 'German tax computation engine: UStVA Kennzahl aggregation from ledger balances with form rounding, Zahllast and reconciliation checks — exact cents'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxhQUFhO0lBQ25CLE9BQU8sRUFBRSxPQUFPO0lBQ2hCLFdBQVcsRUFBRSxxSkFBcUo7Q0FDbkssQ0FBQSJ9
|
|
@@ -30,14 +30,29 @@
|
|
|
30
30
|
* tax) and did not exempt the supplies but levied no tax on them; a year
|
|
31
31
|
* before 2025 answers `before_2025` and is not told.
|
|
32
32
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
33
|
+
* The year the business starts: the statute names no limit of its own for it
|
|
34
|
+
* (there is no previous year). The reasoning of the Jahressteuergesetz 2024
|
|
35
|
+
* (BT-Drs. 20/12780, read on dserver.bundestag.de 2026-09-30, p. 183 f., zu
|
|
36
|
+
* § 19 Abs. 1) does: "Nimmt der Unternehmer seine gewerbliche oder berufliche
|
|
37
|
+
* Tätigkeit auf, ist § 19 Absatz 1 Satz 1 UStG mit der Maßgabe anzuwenden,
|
|
38
|
+
* dass der Gesamtumsatz im laufenden Kalenderjahr den Betrag von 25 000 Euro
|
|
39
|
+
* nicht überschreitet. Bereits der Umsatz, mit dem die Grenze überschritten
|
|
40
|
+
* wird, unterliegt der Regelbesteuerung." (Art. 288a Abs. 1 UAbs. 3 RL
|
|
41
|
+
* 2006/112 EG); "Die bis zum Zeitpunkt der Überschreitung bewirkten Umsätze
|
|
42
|
+
* sind steuerfrei." And zu Abs. 2: "Übt der Unternehmer seine gewerbliche
|
|
43
|
+
* oder berufliche Tätigkeit nur für einen Teil des Kalenderjahres aus,
|
|
44
|
+
* entfällt die bisherige Umrechnung in einen Jahresgesamtumsatz": a part of a
|
|
45
|
+
* year counts as it is, in the year the business starts and as the next
|
|
46
|
+
* year's previous year. The Anleitung zur Umsatzsteuer-Voranmeldung 2026
|
|
47
|
+
* (p. 8, Zeile 12) reads the founding year the same way.
|
|
48
|
+
*
|
|
49
|
+
* The supply by which the running year exceeds 100 000 €: the reasoning says
|
|
50
|
+
* the scheme no longer applies "ab diesem Zeitpunkt", and the statute exempts
|
|
51
|
+
* a supply while the running year's Gesamtumsatz "nicht überschreitet"; both
|
|
52
|
+
* are read so that it is taxed: `endsWith` names the receipt at which the sum
|
|
53
|
+
* first exceeds the limit, and every receipt from it on is taxed. Not read:
|
|
54
|
+
* the BMF-Schreiben of 18 March 2025 (UStAE Abschn. 19.1 ff.), which the BMF
|
|
55
|
+
* site did not serve.
|
|
41
56
|
*/
|
|
42
57
|
import type { TCents } from '@fin.cx/calculation';
|
|
43
58
|
/** The first calendar year of the version of § 19 UStG this module applies (Jahressteuergesetz 2024, from 1 January 2025). */
|
|
@@ -46,6 +61,8 @@ export declare const SMALL_BUSINESS_FIRST_YEAR = 2025;
|
|
|
46
61
|
export declare const SMALL_BUSINESS_PREVIOUS_YEAR_LIMIT_CENTS: TCents;
|
|
47
62
|
/** 100 000 €: the running calendar year's limit (§ 19 Abs. 1 Satz 1 UStG) */
|
|
48
63
|
export declare const SMALL_BUSINESS_RUNNING_YEAR_LIMIT_CENTS: TCents;
|
|
64
|
+
/** 25 000 €: the running calendar year's limit in the year the business starts (BT-Drs. 20/12780, p. 183 f., zu § 19 Abs. 1 UStG) */
|
|
65
|
+
export declare const SMALL_BUSINESS_FOUNDING_YEAR_LIMIT_CENTS: TCents;
|
|
49
66
|
/**
|
|
50
67
|
* What a receipt is to the Gesamtumsatz (§ 19 Abs. 2 UStG):
|
|
51
68
|
* - `counted`: an Entgelt for a steuerbarer Umsatz (§ 1 Abs. 1 Nr. 1), taxed or
|
|
@@ -76,7 +93,12 @@ export interface IGesamtumsatzOfYear {
|
|
|
76
93
|
totalCents: TCents;
|
|
77
94
|
leftOutCents: Record<Exclude<TGesamtumsatzPart, 'counted'>, TCents>;
|
|
78
95
|
}
|
|
79
|
-
/**
|
|
96
|
+
/**
|
|
97
|
+
* Why a year starts with the exemption or without it. `within_limits` and
|
|
98
|
+
* `business_started_this_year` start it exempt: the one by the previous
|
|
99
|
+
* year's Gesamtumsatz, the other as the year the business starts, which has
|
|
100
|
+
* none (its running limit is 25 000 €).
|
|
101
|
+
*/
|
|
80
102
|
export type TSmallBusinessReason = 'within_limits' | 'previous_year_over' | 'waived' | 'previous_year_unknown' | 'business_started_this_year'
|
|
81
103
|
/** a year before 2025, under the version with other limits (`SMALL_BUSINESS_FIRST_YEAR`): not told */
|
|
82
104
|
| 'before_2025';
|
|
@@ -100,9 +122,17 @@ export interface ISmallBusinessStatus {
|
|
|
100
122
|
atStart: 'exempt' | 'taxable' | 'unknown';
|
|
101
123
|
reason: TSmallBusinessReason;
|
|
102
124
|
/**
|
|
103
|
-
* Where the year starts exempt
|
|
104
|
-
*
|
|
105
|
-
*
|
|
125
|
+
* Where the year starts exempt: the running year's Gesamtumsatz up to which
|
|
126
|
+
* it stays exempt, 100 000 € (`SMALL_BUSINESS_RUNNING_YEAR_LIMIT_CENTS`), or
|
|
127
|
+
* 25 000 € in the year the business starts
|
|
128
|
+
* (`SMALL_BUSINESS_FOUNDING_YEAR_LIMIT_CENTS`)
|
|
129
|
+
*/
|
|
130
|
+
runningLimitCents?: TCents;
|
|
131
|
+
/**
|
|
132
|
+
* Where the year starts exempt and its Gesamtumsatz exceeds
|
|
133
|
+
* `runningLimitCents`: the receipt by which it first does (its day, its
|
|
134
|
+
* place in the receipts as given, the sum with it). It and every later
|
|
135
|
+
* receipt are taxed.
|
|
106
136
|
*/
|
|
107
137
|
endsWith?: {
|
|
108
138
|
receivedOn: string;
|
|
@@ -118,7 +148,9 @@ export declare function gesamtumsatzOf(receiptsArg: readonly IGesamtumsatzReceip
|
|
|
118
148
|
* Whether a calendar year's supplies are exempt under § 19 Abs. 1 UStG: from
|
|
119
149
|
* its start where the previous year's Gesamtumsatz did not exceed 25 000 € and
|
|
120
150
|
* no waiver is in force, until the receipt by which the running year's
|
|
121
|
-
* Gesamtumsatz exceeds 100 000 € (`endsWith`).
|
|
151
|
+
* Gesamtumsatz exceeds 100 000 € (`endsWith`). In the year the business
|
|
152
|
+
* starts (`previousYearCents: 'business_started'`) from its start, until the
|
|
153
|
+
* receipt by which it exceeds 25 000 €, counted as it is, not as a whole year.
|
|
122
154
|
*/
|
|
123
155
|
export declare function smallBusinessStatusOf(inputArg: ISmallBusinessYearInput): ISmallBusinessStatus;
|
|
124
156
|
/** The last calendar year a waiver effective for `yearArg` binds (§ 19 Abs. 3 Satz 3 UStG: at least five calendar years). */
|
package/dist_ts/smallbusiness.js
CHANGED
|
@@ -4,6 +4,8 @@ export const SMALL_BUSINESS_FIRST_YEAR = 2025;
|
|
|
4
4
|
export const SMALL_BUSINESS_PREVIOUS_YEAR_LIMIT_CENTS = 2_500_000;
|
|
5
5
|
/** 100 000 €: the running calendar year's limit (§ 19 Abs. 1 Satz 1 UStG) */
|
|
6
6
|
export const SMALL_BUSINESS_RUNNING_YEAR_LIMIT_CENTS = 10_000_000;
|
|
7
|
+
/** 25 000 €: the running calendar year's limit in the year the business starts (BT-Drs. 20/12780, p. 183 f., zu § 19 Abs. 1 UStG) */
|
|
8
|
+
export const SMALL_BUSINESS_FOUNDING_YEAR_LIMIT_CENTS = 2_500_000;
|
|
7
9
|
export const GESAMTUMSATZ_PARTS = ['counted', 'exempt_section_4_listed', 'exempt_ancillary', 'fixed_assets', 'not_taxable_here'];
|
|
8
10
|
const isCalendarDay = (valueArg) => {
|
|
9
11
|
if (typeof valueArg !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(valueArg)) {
|
|
@@ -52,7 +54,9 @@ export function gesamtumsatzOf(receiptsArg, yearArg) {
|
|
|
52
54
|
* Whether a calendar year's supplies are exempt under § 19 Abs. 1 UStG: from
|
|
53
55
|
* its start where the previous year's Gesamtumsatz did not exceed 25 000 € and
|
|
54
56
|
* no waiver is in force, until the receipt by which the running year's
|
|
55
|
-
* Gesamtumsatz exceeds 100 000 € (`endsWith`).
|
|
57
|
+
* Gesamtumsatz exceeds 100 000 € (`endsWith`). In the year the business
|
|
58
|
+
* starts (`previousYearCents: 'business_started'`) from its start, until the
|
|
59
|
+
* receipt by which it exceeds 25 000 €, counted as it is, not as a whole year.
|
|
56
60
|
*/
|
|
57
61
|
export function smallBusinessStatusOf(inputArg) {
|
|
58
62
|
assertYear(inputArg.year);
|
|
@@ -76,23 +80,23 @@ export function smallBusinessStatusOf(inputArg) {
|
|
|
76
80
|
if (inputArg.waived === true) {
|
|
77
81
|
return { ...base, atStart: 'taxable', reason: 'waived' };
|
|
78
82
|
}
|
|
79
|
-
if (previous === 'business_started') {
|
|
80
|
-
return { ...base, atStart: 'unknown', reason: 'business_started_this_year' };
|
|
81
|
-
}
|
|
82
83
|
if (previous === undefined) {
|
|
83
84
|
return { ...base, atStart: 'unknown', reason: 'previous_year_unknown' };
|
|
84
85
|
}
|
|
85
|
-
if (previous > SMALL_BUSINESS_PREVIOUS_YEAR_LIMIT_CENTS) {
|
|
86
|
+
if (previous !== 'business_started' && previous > SMALL_BUSINESS_PREVIOUS_YEAR_LIMIT_CENTS) {
|
|
86
87
|
return { ...base, atStart: 'taxable', reason: 'previous_year_over' };
|
|
87
88
|
}
|
|
89
|
+
const reason = previous === 'business_started' ? 'business_started_this_year' : 'within_limits';
|
|
90
|
+
const runningLimitCents = previous === 'business_started' ? SMALL_BUSINESS_FOUNDING_YEAR_LIMIT_CENTS : SMALL_BUSINESS_RUNNING_YEAR_LIMIT_CENTS;
|
|
91
|
+
const exempt = { ...base, atStart: 'exempt', reason, runningLimitCents };
|
|
88
92
|
let sum = 0;
|
|
89
93
|
for (const { receipt, index } of counted) {
|
|
90
94
|
sum += receipt.entgeltCents;
|
|
91
|
-
if (sum >
|
|
92
|
-
return { ...
|
|
95
|
+
if (sum > runningLimitCents) {
|
|
96
|
+
return { ...exempt, endsWith: { receivedOn: receipt.receivedOn, index, runningCents: sum } };
|
|
93
97
|
}
|
|
94
98
|
}
|
|
95
|
-
return
|
|
99
|
+
return exempt;
|
|
96
100
|
}
|
|
97
101
|
/** The last calendar year a waiver effective for `yearArg` binds (§ 19 Abs. 3 Satz 3 UStG: at least five calendar years). */
|
|
98
102
|
export function smallBusinessWaiverBindsUntil(yearArg) {
|
|
@@ -110,4 +114,4 @@ export function smallBusinessWaiverDeadline(yearArg) {
|
|
|
110
114
|
const leap = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
111
115
|
return `${year}-02-${leap ? '29' : '28'}`;
|
|
112
116
|
}
|
|
113
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
117
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hbGxidXNpbmVzcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYWxsYnVzaW5lc3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBMERBLDhIQUE4SDtBQUM5SCxNQUFNLENBQUMsTUFBTSx5QkFBeUIsR0FBRyxJQUFJLENBQUM7QUFFOUMsNkVBQTZFO0FBQzdFLE1BQU0sQ0FBQyxNQUFNLHdDQUF3QyxHQUFXLFNBQVMsQ0FBQztBQUMxRSw2RUFBNkU7QUFDN0UsTUFBTSxDQUFDLE1BQU0sdUNBQXVDLEdBQVcsVUFBVSxDQUFDO0FBQzFFLHFJQUFxSTtBQUNySSxNQUFNLENBQUMsTUFBTSx3Q0FBd0MsR0FBVyxTQUFTLENBQUM7QUFrQjFFLE1BQU0sQ0FBQyxNQUFNLGtCQUFrQixHQUFpQyxDQUFDLFNBQVMsRUFBRSx5QkFBeUIsRUFBRSxrQkFBa0IsRUFBRSxjQUFjLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztBQXVFL0osTUFBTSxhQUFhLEdBQUcsQ0FBQyxRQUFpQixFQUFzQixFQUFFO0lBQzlELElBQUksT0FBTyxRQUFRLEtBQUssUUFBUSxJQUFJLENBQUMscUJBQXFCLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7UUFDMUUsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBQ0QsTUFBTSxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsR0FBRyxDQUFDLEdBQUcsUUFBUSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDM0QsTUFBTSxJQUFJLEdBQUcsSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsS0FBSyxHQUFHLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQ3RELE9BQU8sSUFBSSxDQUFDLGNBQWMsRUFBRSxLQUFLLElBQUksSUFBSSxJQUFJLENBQUMsV0FBVyxFQUFFLEtBQUssS0FBSyxHQUFHLENBQUMs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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fin.cx/tax",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "German tax computation engine: UStVA Kennzahl aggregation from ledger balances with form rounding, Zahllast and reconciliation checks — exact cents",
|
|
6
6
|
"main": "dist_ts/index.js",
|
package/readme.md
CHANGED
|
@@ -89,16 +89,26 @@ const status = tax.smallBusinessStatusOf({
|
|
|
89
89
|
previousYearCents: tax.gesamtumsatzOf(receipts, 2025).totalCents,
|
|
90
90
|
receipts, // [{ receivedOn: '2026-03-01', entgeltCents: 120000, part: 'counted' }, …]
|
|
91
91
|
});
|
|
92
|
-
// { atStart: 'exempt', reason: 'within_limits', endsWith?: { receivedOn, index, runningCents }, runningCents }
|
|
92
|
+
// { atStart: 'exempt', reason: 'within_limits', runningLimitCents: 10000000, endsWith?: { receivedOn, index, runningCents }, runningCents }
|
|
93
93
|
```
|
|
94
94
|
|
|
95
95
|
`endsWith` names the receipt by which the running year first exceeds
|
|
96
|
-
100 000
|
|
97
|
-
year `taxable`; `smallBusinessWaiverDeadline`,
|
|
98
|
-
`…RevocableFrom` give its dates.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
`runningLimitCents` (100 000 €); it and every later one are taxed. A waiver
|
|
97
|
+
(§ 19 Abs. 3) makes the year `taxable`; `smallBusinessWaiverDeadline`,
|
|
98
|
+
`…BindsUntil` and `…RevocableFrom` give its dates.
|
|
99
|
+
|
|
100
|
+
The year the business starts (`previousYearCents: 'business_started'`) has no
|
|
101
|
+
previous year, and the statute names no limit of its own for it. The reasoning
|
|
102
|
+
of the Jahressteuergesetz 2024 (BT-Drs. 20/12780, p. 183 f.) does: the running
|
|
103
|
+
year's Gesamtumsatz must not exceed 25 000 €, and the supply by which it does is
|
|
104
|
+
taxed already. So that year starts `exempt` (`business_started_this_year`) with
|
|
105
|
+
`runningLimitCents` 25 000 € (`SMALL_BUSINESS_FOUNDING_YEAR_LIMIT_CENTS`), and
|
|
106
|
+
`endsWith` names the receipt above it. A part of a year is not converted to a
|
|
107
|
+
whole year any longer (same source, zu Abs. 2): three months with 24 000 € are
|
|
108
|
+
24 000 €, in the year the business starts and as the next year's previous year.
|
|
109
|
+
|
|
110
|
+
A year before 2025 answers `unknown` (`before_2025`), under the version with
|
|
111
|
+
other limits (22 000 € / 50 000 €, with the tax). The rules cover businesses resident in Germany or the areas of
|
|
102
112
|
§ 1 Abs. 3 UStG; the new vehicles of § 19 Abs. 6 are not modelled.
|
|
103
113
|
|
|
104
114
|
## German VAT rates, dated
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/smallbusiness.ts
CHANGED
|
@@ -30,14 +30,29 @@
|
|
|
30
30
|
* tax) and did not exempt the supplies but levied no tax on them; a year
|
|
31
31
|
* before 2025 answers `before_2025` and is not told.
|
|
32
32
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
33
|
+
* The year the business starts: the statute names no limit of its own for it
|
|
34
|
+
* (there is no previous year). The reasoning of the Jahressteuergesetz 2024
|
|
35
|
+
* (BT-Drs. 20/12780, read on dserver.bundestag.de 2026-09-30, p. 183 f., zu
|
|
36
|
+
* § 19 Abs. 1) does: "Nimmt der Unternehmer seine gewerbliche oder berufliche
|
|
37
|
+
* Tätigkeit auf, ist § 19 Absatz 1 Satz 1 UStG mit der Maßgabe anzuwenden,
|
|
38
|
+
* dass der Gesamtumsatz im laufenden Kalenderjahr den Betrag von 25 000 Euro
|
|
39
|
+
* nicht überschreitet. Bereits der Umsatz, mit dem die Grenze überschritten
|
|
40
|
+
* wird, unterliegt der Regelbesteuerung." (Art. 288a Abs. 1 UAbs. 3 RL
|
|
41
|
+
* 2006/112 EG); "Die bis zum Zeitpunkt der Überschreitung bewirkten Umsätze
|
|
42
|
+
* sind steuerfrei." And zu Abs. 2: "Übt der Unternehmer seine gewerbliche
|
|
43
|
+
* oder berufliche Tätigkeit nur für einen Teil des Kalenderjahres aus,
|
|
44
|
+
* entfällt die bisherige Umrechnung in einen Jahresgesamtumsatz": a part of a
|
|
45
|
+
* year counts as it is, in the year the business starts and as the next
|
|
46
|
+
* year's previous year. The Anleitung zur Umsatzsteuer-Voranmeldung 2026
|
|
47
|
+
* (p. 8, Zeile 12) reads the founding year the same way.
|
|
48
|
+
*
|
|
49
|
+
* The supply by which the running year exceeds 100 000 €: the reasoning says
|
|
50
|
+
* the scheme no longer applies "ab diesem Zeitpunkt", and the statute exempts
|
|
51
|
+
* a supply while the running year's Gesamtumsatz "nicht überschreitet"; both
|
|
52
|
+
* are read so that it is taxed: `endsWith` names the receipt at which the sum
|
|
53
|
+
* first exceeds the limit, and every receipt from it on is taxed. Not read:
|
|
54
|
+
* the BMF-Schreiben of 18 March 2025 (UStAE Abschn. 19.1 ff.), which the BMF
|
|
55
|
+
* site did not serve.
|
|
41
56
|
*/
|
|
42
57
|
import type { TCents } from '@fin.cx/calculation';
|
|
43
58
|
|
|
@@ -48,6 +63,8 @@ export const SMALL_BUSINESS_FIRST_YEAR = 2025;
|
|
|
48
63
|
export const SMALL_BUSINESS_PREVIOUS_YEAR_LIMIT_CENTS: TCents = 2_500_000;
|
|
49
64
|
/** 100 000 €: the running calendar year's limit (§ 19 Abs. 1 Satz 1 UStG) */
|
|
50
65
|
export const SMALL_BUSINESS_RUNNING_YEAR_LIMIT_CENTS: TCents = 10_000_000;
|
|
66
|
+
/** 25 000 €: the running calendar year's limit in the year the business starts (BT-Drs. 20/12780, p. 183 f., zu § 19 Abs. 1 UStG) */
|
|
67
|
+
export const SMALL_BUSINESS_FOUNDING_YEAR_LIMIT_CENTS: TCents = 2_500_000;
|
|
51
68
|
|
|
52
69
|
/**
|
|
53
70
|
* What a receipt is to the Gesamtumsatz (§ 19 Abs. 2 UStG):
|
|
@@ -83,7 +100,12 @@ export interface IGesamtumsatzOfYear {
|
|
|
83
100
|
leftOutCents: Record<Exclude<TGesamtumsatzPart, 'counted'>, TCents>;
|
|
84
101
|
}
|
|
85
102
|
|
|
86
|
-
/**
|
|
103
|
+
/**
|
|
104
|
+
* Why a year starts with the exemption or without it. `within_limits` and
|
|
105
|
+
* `business_started_this_year` start it exempt: the one by the previous
|
|
106
|
+
* year's Gesamtumsatz, the other as the year the business starts, which has
|
|
107
|
+
* none (its running limit is 25 000 €).
|
|
108
|
+
*/
|
|
87
109
|
export type TSmallBusinessReason =
|
|
88
110
|
| 'within_limits'
|
|
89
111
|
| 'previous_year_over'
|
|
@@ -114,9 +136,17 @@ export interface ISmallBusinessStatus {
|
|
|
114
136
|
atStart: 'exempt' | 'taxable' | 'unknown';
|
|
115
137
|
reason: TSmallBusinessReason;
|
|
116
138
|
/**
|
|
117
|
-
* Where the year starts exempt
|
|
118
|
-
*
|
|
119
|
-
*
|
|
139
|
+
* Where the year starts exempt: the running year's Gesamtumsatz up to which
|
|
140
|
+
* it stays exempt, 100 000 € (`SMALL_BUSINESS_RUNNING_YEAR_LIMIT_CENTS`), or
|
|
141
|
+
* 25 000 € in the year the business starts
|
|
142
|
+
* (`SMALL_BUSINESS_FOUNDING_YEAR_LIMIT_CENTS`)
|
|
143
|
+
*/
|
|
144
|
+
runningLimitCents?: TCents;
|
|
145
|
+
/**
|
|
146
|
+
* Where the year starts exempt and its Gesamtumsatz exceeds
|
|
147
|
+
* `runningLimitCents`: the receipt by which it first does (its day, its
|
|
148
|
+
* place in the receipts as given, the sum with it). It and every later
|
|
149
|
+
* receipt are taxed.
|
|
120
150
|
*/
|
|
121
151
|
endsWith?: { receivedOn: string; index: number; runningCents: TCents };
|
|
122
152
|
/** the year's Gesamtumsatz over all its receipts */
|
|
@@ -173,7 +203,9 @@ export function gesamtumsatzOf(receiptsArg: readonly IGesamtumsatzReceipt[], yea
|
|
|
173
203
|
* Whether a calendar year's supplies are exempt under § 19 Abs. 1 UStG: from
|
|
174
204
|
* its start where the previous year's Gesamtumsatz did not exceed 25 000 € and
|
|
175
205
|
* no waiver is in force, until the receipt by which the running year's
|
|
176
|
-
* Gesamtumsatz exceeds 100 000 € (`endsWith`).
|
|
206
|
+
* Gesamtumsatz exceeds 100 000 € (`endsWith`). In the year the business
|
|
207
|
+
* starts (`previousYearCents: 'business_started'`) from its start, until the
|
|
208
|
+
* receipt by which it exceeds 25 000 €, counted as it is, not as a whole year.
|
|
177
209
|
*/
|
|
178
210
|
export function smallBusinessStatusOf(inputArg: ISmallBusinessYearInput): ISmallBusinessStatus {
|
|
179
211
|
assertYear(inputArg.year);
|
|
@@ -197,23 +229,23 @@ export function smallBusinessStatusOf(inputArg: ISmallBusinessYearInput): ISmall
|
|
|
197
229
|
if (inputArg.waived === true) {
|
|
198
230
|
return { ...base, atStart: 'taxable', reason: 'waived' };
|
|
199
231
|
}
|
|
200
|
-
if (previous === 'business_started') {
|
|
201
|
-
return { ...base, atStart: 'unknown', reason: 'business_started_this_year' };
|
|
202
|
-
}
|
|
203
232
|
if (previous === undefined) {
|
|
204
233
|
return { ...base, atStart: 'unknown', reason: 'previous_year_unknown' };
|
|
205
234
|
}
|
|
206
|
-
if (previous > SMALL_BUSINESS_PREVIOUS_YEAR_LIMIT_CENTS) {
|
|
235
|
+
if (previous !== 'business_started' && previous > SMALL_BUSINESS_PREVIOUS_YEAR_LIMIT_CENTS) {
|
|
207
236
|
return { ...base, atStart: 'taxable', reason: 'previous_year_over' };
|
|
208
237
|
}
|
|
238
|
+
const reason: TSmallBusinessReason = previous === 'business_started' ? 'business_started_this_year' : 'within_limits';
|
|
239
|
+
const runningLimitCents = previous === 'business_started' ? SMALL_BUSINESS_FOUNDING_YEAR_LIMIT_CENTS : SMALL_BUSINESS_RUNNING_YEAR_LIMIT_CENTS;
|
|
240
|
+
const exempt = { ...base, atStart: 'exempt' as const, reason, runningLimitCents };
|
|
209
241
|
let sum = 0;
|
|
210
242
|
for (const { receipt, index } of counted) {
|
|
211
243
|
sum += receipt.entgeltCents;
|
|
212
|
-
if (sum >
|
|
213
|
-
return { ...
|
|
244
|
+
if (sum > runningLimitCents) {
|
|
245
|
+
return { ...exempt, endsWith: { receivedOn: receipt.receivedOn, index, runningCents: sum } };
|
|
214
246
|
}
|
|
215
247
|
}
|
|
216
|
-
return
|
|
248
|
+
return exempt;
|
|
217
249
|
}
|
|
218
250
|
|
|
219
251
|
/** The last calendar year a waiver effective for `yearArg` binds (§ 19 Abs. 3 Satz 3 UStG: at least five calendar years). */
|