@fin.cx/chartdata 2.12.1 → 2.14.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.
@@ -1,5 +1,6 @@
1
1
  export * from './chartdata.types.js';
2
2
  export * from './chartdata.lookup.js';
3
3
  export * from './chartdata.hgb.js';
4
+ export * from './chartdata.euer.js';
4
5
  export { SKR03_2026 } from './data/skr03.2026.js';
5
6
  export { SKR04_2026 } from './data/skr04.2026.js';
package/dist_ts/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './chartdata.types.js';
2
2
  export * from './chartdata.lookup.js';
3
3
  export * from './chartdata.hgb.js';
4
+ export * from './chartdata.euer.js';
4
5
  export { SKR03_2026 } from './data/skr03.2026.js';
5
6
  export { SKR04_2026 } from './data/skr04.2026.js';
6
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLHNCQUFzQixDQUFDO0FBQ3JDLGNBQWMsdUJBQXVCLENBQUM7QUFDdEMsY0FBYyxvQkFBb0IsQ0FBQztBQUNuQyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDbEQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHNCQUFzQixDQUFDIn0=
7
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLHNCQUFzQixDQUFDO0FBQ3JDLGNBQWMsdUJBQXVCLENBQUM7QUFDdEMsY0FBYyxvQkFBb0IsQ0FBQztBQUNuQyxjQUFjLHFCQUFxQixDQUFDO0FBQ3BDLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUNsRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sc0JBQXNCLENBQUMifQ==
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@fin.cx/chartdata",
3
- "version": "2.12.1",
3
+ "version": "2.14.0",
4
4
  "private": false,
5
- "description": "Year-versioned German SKR03/SKR04 chart-of-accounts datasets with Automatikkonto functions, BU-key rules, and UStVA-Kennzahl mappings",
5
+ "description": "Year-versioned German SKR03/SKR04 chart-of-accounts datasets with Automatikkonto functions, BU-key rules, UStVA-Kennzahl and Anlage EÜR mappings",
6
6
  "main": "dist_ts/index.js",
7
7
  "typings": "dist_ts/index.d.ts",
8
8
  "type": "module",
@@ -28,10 +28,10 @@
28
28
  },
29
29
  "homepage": "https://code.foss.global/fin.cx/chartdata#readme",
30
30
  "devDependencies": {
31
- "@git.zone/tsbuild": "^5.0.0",
31
+ "@git.zone/tsbuild": "^5.1.2",
32
32
  "@git.zone/tsrun": "^3.0.2",
33
- "@git.zone/tstest": "^6.3.2",
34
- "@types/node": "^26.6.2"
33
+ "@git.zone/tstest": "^7.2.0",
34
+ "@types/node": "^26.6.3"
35
35
  },
36
36
  "files": [
37
37
  "ts/**/*",
package/readme.md CHANGED
@@ -23,6 +23,14 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
23
23
  Bemessungsgrundlagen that cannot be derived from a single account: Kz 46/47
24
24
  (§13b Abs. 1), Kz 73/74 (§13b Abs. 2 Nr. 3, under the GrEStG) and Kz 84/85
25
25
  (the other §13b Abs. 2 supplies)
26
+ - Account roles (`accountRole`) where an account's type and HGB position do
27
+ not tell it apart from the others of its position: a fixed asset's sale
28
+ proceeds (`asset_sale_proceeds`) and the book value of a disposed fixed
29
+ asset (`disposal_book_value`), listed per chart by `getAccountsByRole`
30
+ - Account→Anlage EÜR mappings (`euer`) for the Einnahmenüberschussrechnung
31
+ (§ 4 Abs. 3 EStG, § 60 Abs. 4 EStDV), with the lines of the 2025 and 2026
32
+ forms (`ANLAGE_EUER_FORMS`); every account maps or is excluded with its
33
+ reason (`euerExclusion`)
26
34
  - Coverage introspection that fails loudly on mapping gaps
27
35
  - Provenance on every record
28
36
 
@@ -38,6 +46,7 @@ pnpm add @fin.cx/chartdata
38
46
  import {
39
47
  getChart,
40
48
  getAccount,
49
+ getAccountsByRole,
41
50
  getMappingCoverage,
42
51
  USTVA_SCENARIO_KENNZAHLEN,
43
52
  } from '@fin.cx/chartdata';
@@ -55,8 +64,78 @@ if (coverage.missing.length > 0 || coverage.unknown.length > 0) {
55
64
  }
56
65
 
57
66
  USTVA_SCENARIO_KENNZAHLEN.intraEuAcquisitionBaseStandard; // 89
67
+
68
+ // a fixed asset's disposal: DATEV states a book gain net under GuV 4 and a
69
+ // book loss under GuV 8, so the role tells the proceeds and the book value
70
+ // apart from the other income and expenses there
71
+ getAccount('SKR03', 2026, '8820')?.accountRole; // 'asset_sale_proceeds'
72
+ getAccount('SKR04', 2026, '6895')?.accountRole; // 'disposal_book_value'
73
+ getAccount('SKR03', 2026, '8400')?.accountRole; // undefined
74
+ getAccountsByRole('SKR04', 2026, 'disposal_book_value').map((account) => account.accountNumber);
75
+ // ['4855', '4856', '4857', '4858', '6895', '6896', '6897', '6898']
58
76
  ```
59
77
 
78
+ ### Anlage EÜR
79
+
80
+ Every account of a chart either maps to the Anlage EÜR or is excluded from it
81
+ with its reason. A mapping names the line by its Kennzahl, which stays the same
82
+ from one form year to the next while the line number may change. The 2026 form
83
+ inserts Zeile 27, so the goods move from Zeile 27 (2025) to Zeile 29 (2026).
84
+
85
+ ```ts
86
+ import { getEuerLines, euerAmountsOf, getEuerForm, getMappingCoverage } from '@fin.cx/chartdata';
87
+
88
+ // a fiscal year 2025 booked on the 2026 chart, stated on the 2025 form
89
+ getEuerLines('SKR03', 2026, '8400', 2025);
90
+ // { kind: 'netted', line: { kennzahl: 112, line: 15, label: 'Umsatzsteuerpflichtige Betriebseinnahmen …',
91
+ // section: 'income', sign: 1, side: 'credit' } }
92
+ getEuerLines('SKR03', 2026, '1780', 2026);
93
+ // { kind: 'split', debitLine: { kennzahl: 186, line: 59, … }, creditLine: { kennzahl: 141, line: 18, … } }
94
+ getEuerLines('SKR03', 2026, '1787', 2026); // { kind: 'excluded', exclusion: 'reverse_charge' }
95
+ getEuerLines('SKR03', 2026, '8400', 2026, { smallBusiness: true }); // Kz 111, Zeile 12 (Zeile 13 stays with the caller)
96
+
97
+ // what a period's movements on an account amount to on its lines, in cents
98
+ euerAmountsOf(getEuerLines('SKR03', 2026, '8400', 2026)!, 20000, 500000); // [{ line: Kz 112, cents: 480000 }]
99
+
100
+ // before stating an EÜR: every used account must have a decision
101
+ getMappingCoverage('SKR04', 2026, usedAccountNumbers, 'euer').missing; // []
102
+ ```
103
+
104
+ - `netted` accounts count both sides on one line, toward its side. A line of
105
+ receipts counts credits less debits, so a sales deduction lowers the
106
+ receipts; a line of expenses counts debits less credits, so a received
107
+ discount lowers the goods. `split` accounts put each side on a line of its
108
+ own. The VAT prepayment and past-year accounts split what was paid to the
109
+ tax office (Kz 186) from what it refunded (Kz 141).
110
+ - Exclusions (`TEuerExclusion`): `balance_sheet`, `fixed_asset` (reaches the
111
+ form through depreciation and the residual value), `inventory`, `valuation`,
112
+ `pass_through` (§ 4 Abs. 3 Satz 2 EStG), `reverse_charge` (the tax of an
113
+ intra-Community acquisition or under § 13b UStG: such invoices contain no
114
+ input tax, Anleitung zur Anlage EÜR, Zeile 58, and the tax is no receipt;
115
+ what a return leaves to pay counts as paid VAT, Kz 186) and `closing`.
116
+ - A line's `sign` says how it enters its section's sum: 1 adds, -1 subtracts
117
+ (Zeilen 73 and 75), 0 not at all (a "davon" line, the column "nicht
118
+ abziehbar", the Entnahmen and Einlagen).
119
+ - The form counts the year's movements, not the opening balances (EB-Werte):
120
+ a caller sums a period's bookings over the entries it holds and leaves out
121
+ the opening entries, which on a `split` account would otherwise count as a
122
+ refund (Kz 141) or a payment (Kz 186). When a payment counts is the
123
+ caller's period logic too: a VAT refund or prepayment of the prior year
124
+ that falls due and is paid within ten days of the year's start belongs to
125
+ the prior year (§ 11 Abs. 1 Satz 2 and Abs. 2 Satz 2 EStG, Anleitung,
126
+ Zeilen 18 and 59).
127
+ - With `smallBusiness`, a Kleinunternehmer's taxable and tax-free receipts go
128
+ to Zeile 12 (Kz 111). Zeile 13 (Kz 119) states within it, "nachrichtlich",
129
+ the non-taxable receipts and those under § 19 Abs. 2 Satz 1 Nr. 1 und 2
130
+ UStG. An account does not tell which of its receipts those are, so no
131
+ account resolves to Kz 119 and that amount stays with the caller.
132
+ - The decisions the line texts do not make on their own are written at the
133
+ table in `tools/generate-charts.ts`. Revenue without a tax function counts
134
+ with the taxable receipts, and interest, exchange gains and damages with the
135
+ tax-free ones. Employees' travel goes with the staff (Anleitung, Zeile 45),
136
+ and another member state's VAT (OSS) is received in Kz 103 and paid in
137
+ Kz 183.
138
+
60
139
  ## Data Provenance and Verification
61
140
 
62
141
  Records carry a `provenance` string. The datasets are a curated table authored
@@ -71,7 +150,12 @@ PDFs (Art.-Nr. 11174/11175) are public, and accounts verified against them say
71
150
  so in their provenance. The standing verification gates are ERiC validation
72
151
  of generated filings and periodic manual DATEV imports.
73
152
  HGB §266/§275 positions are populated and validated against the exported statutory
74
- position trees. E-Bilanz (de-gaap-ci) positions are not populated yet —
153
+ position trees. The Anlage EÜR lines (`ANLAGE_EUER_FORMS`) were read from the
154
+ BMF's published forms and Anleitungen: Anlage EÜR 2025 (BMF-Schreiben of
155
+ 2025-08-29) and Anlage EÜR 2026 (BMF-Schreiben of 2026-09-01). Each line's pair
156
+ of line number and Kennzahl was checked against the forms. The generator fails
157
+ on an account without an EÜR decision, and on a Kennzahl or side a form year
158
+ does not hold. E-Bilanz (de-gaap-ci) positions are not populated yet —
75
159
  `getMappingCoverage(..., 'ebilanz')` reports them as missing by design.
76
160
 
77
161
  The generation pipeline lives in the unpublished `tools/` directory
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@fin.cx/chartdata',
6
- version: '2.12.1',
7
- description: 'Year-versioned German SKR03/SKR04 chart-of-accounts datasets with Automatikkonto functions, BU-key rules, and UStVA-Kennzahl mappings'
6
+ version: '2.14.0',
7
+ description: 'Year-versioned German SKR03/SKR04 chart-of-accounts datasets with Automatikkonto functions, BU-key rules, UStVA-Kennzahl and Anlage EÜR mappings'
8
8
  }
@@ -0,0 +1,219 @@
1
+ /**
2
+ * The Anlage EÜR (standardised Einnahmenüberschussrechnung, § 60 Abs. 4
3
+ * EStDV) of the form years the datasets serve: the lines an account's
4
+ * movements can go to, with their Kennzahlen, their line numbers per year and
5
+ * the form's wording (amtliches Werk, § 5 UrhG — public domain).
6
+ *
7
+ * Read from the BMF's published forms: "Standardisierte Einnahmenüberschuss-
8
+ * rechnung nach § 60 Absatz 4 EStDV; Anlage EÜR 2025" (BMF-Schreiben of
9
+ * 2025-08-29, IV C 6 - S 2142/00023/010/001) and "… Anlage EÜR 2026"
10
+ * (2026-09-01, IV C 6 - S 2142/00024/010/001), with their Anleitungen. The
11
+ * two years list the same lines under the same Kennzahlen; 2026 inserts
12
+ * Zeile 27 (Bildung Rücklage Tierbewertung, from the Anlage LuF), so the
13
+ * goods are Zeile 27 in 2025 and 29 in 2026, and every later line is one
14
+ * number higher in 2026.
15
+ *
16
+ * The table holds the lines of the Betriebseinnahmen, the Betriebsausgaben
17
+ * and the Entnahmen and Einlagen that carry a Kennzahl of their own. Sums,
18
+ * transfers ("Übertrag") and the transfers from the Anlage LuF (2026
19
+ * Zeilen 25 to 27) are not lines an account's movements go to.
20
+ */
21
+ import type { IChartAccount, IEuerForm, IEuerFormLine, TEuerAccountLines, TEuerSection } from './chartdata.types.js';
22
+
23
+ type TRow = [
24
+ kennzahl: number,
25
+ line2025: number,
26
+ line2026: number,
27
+ section: TEuerSection,
28
+ sign: IEuerFormLine['sign'],
29
+ label: string,
30
+ ];
31
+
32
+ const INCOME = 'income';
33
+ const EXPENSE = 'expense';
34
+
35
+ /** Every line once, with its line numbers on the 2025 and the 2026 form. */
36
+ const ROWS: TRow[] = [
37
+ // 1. Betriebseinnahmen
38
+ [111, 12, 12, INCOME, 1, 'Betriebseinnahmen als umsatzsteuerlicher Kleinunternehmer (nach § 19 Abs. 1 UStG)'],
39
+ [119, 13, 13, INCOME, 0, 'davon nicht steuerbare Umsätze sowie Umsätze nach § 19 Abs. 2 Satz 1 Nr. 1 und 2 sowie Satz 2 UStG'],
40
+ [104, 14, 14, INCOME, 1, 'Betriebseinnahmen als Land- und Forstwirt, soweit die Durchschnittssatzbesteuerung nach § 24 UStG angewandt wird'],
41
+ [112, 15, 15, INCOME, 1, 'Umsatzsteuerpflichtige Betriebseinnahmen (ohne solche zum ermäßigten Steuersatz von 0 %)'],
42
+ [103, 16, 16, INCOME, 1, 'Betriebseinnahmen, die umsatzsteuerfrei (§ 4 UStG) oder nicht umsatzsteuerbar sind oder nach § 12 Abs. 3 UStG dem ermäßigten Steuersatz von 0 % unterliegen oder für die der Leistungsempfänger die Umsatzsteuer nach § 13b UStG schuldet'],
43
+ [140, 17, 17, INCOME, 1, 'Vereinnahmte Umsatzsteuer sowie Umsatzsteuer auf unentgeltliche Wertabgaben'],
44
+ [141, 18, 18, INCOME, 1, 'Vom Finanzamt erstattete und ggf. verrechnete Umsatzsteuer'],
45
+ [102, 19, 19, INCOME, 1, 'Veräußerung oder Entnahme von Anlagevermögen'],
46
+ [106, 20, 20, INCOME, 1, 'Private Kfz-Nutzung'],
47
+ [108, 21, 21, INCOME, 1, 'Sonstige Sach-, Nutzungs- und Leistungsentnahmen'],
48
+ // 2. Betriebsausgaben
49
+ [195, 24, 24, EXPENSE, 1, 'Betriebsausgabenpauschale für bestimmte Berufsgruppen'],
50
+ [100, 27, 29, EXPENSE, 1, 'Waren, Rohstoffe und Hilfsstoffe einschließlich der Nebenkosten'],
51
+ [110, 29, 30, EXPENSE, 1, 'Bezogene Fremdleistungen'],
52
+ [120, 30, 31, EXPENSE, 1, 'Ausgaben für eigenes Personal (z. B. Gehälter, Löhne und Versicherungsbeiträge)'],
53
+ [136, 31, 32, EXPENSE, 1, 'AfA auf Grundstücke/grundstücksgleiche Rechte'],
54
+ [131, 32, 33, EXPENSE, 1, 'AfA auf immaterielle Wirtschaftsgüter'],
55
+ [130, 33, 34, EXPENSE, 1, 'AfA auf bewegliche Wirtschaftsgüter'],
56
+ [134, 34, 35, EXPENSE, 1, 'Sonderabschreibungen nach § 7b EStG und § 7g Abs. 5 und 6 EStG'],
57
+ [138, 35, 36, EXPENSE, 1, 'Herabsetzungsbeträge nach § 7g Abs. 2 Satz 3 EStG'],
58
+ [132, 36, 37, EXPENSE, 1, 'Aufwendungen für geringwertige Wirtschaftsgüter nach § 6 Abs. 2 EStG'],
59
+ [137, 37, 38, EXPENSE, 1, 'Auflösung Sammelposten nach § 6 Abs. 2a EStG'],
60
+ [135, 38, 39, EXPENSE, 1, 'Restbuchwerte der ausgeschiedenen Anlagegüter'],
61
+ [150, 39, 40, EXPENSE, 1, 'Miete/Pacht für Geschäftsräume und betrieblich genutzte Grundstücke'],
62
+ [152, 40, 41, EXPENSE, 1, 'Aufwendungen für doppelte Haushaltsführung (z. B. Miete)'],
63
+ [151, 41, 42, EXPENSE, 1, 'Sonstige Aufwendungen für betrieblich genutzte Grundstücke (ohne Schuldzinsen und AfA)'],
64
+ [153, 42, 43, EXPENSE, 0, 'davon Erhaltungsaufwendungen'],
65
+ [280, 43, 44, EXPENSE, 1, 'Aufwendungen für Telekommunikation (z. B. Telefon, Internet)'],
66
+ [221, 44, 45, EXPENSE, 1, 'Übernachtungs- und Reisenebenkosten bei Geschäftsreisen des Steuerpflichtigen'],
67
+ [281, 45, 46, EXPENSE, 1, 'Fortbildungskosten (ohne Reisekosten)'],
68
+ [194, 46, 47, EXPENSE, 1, 'Kosten für Rechts- und Steuerberatung, Buchführung'],
69
+ [222, 47, 48, EXPENSE, 1, 'Miete/Leasing für bewegliche Wirtschaftsgüter (ohne Kfz)'],
70
+ [225, 48, 49, EXPENSE, 1, 'Erhaltungsaufwendungen (z. B. Instandhaltung, Wartung, Reparatur; ohne solche für Gebäude und Kfz)'],
71
+ [223, 49, 50, EXPENSE, 1, 'Beiträge, Gebühren, Abgaben und Versicherungen (ohne solche für Gebäude und Kfz)'],
72
+ [228, 50, 51, EXPENSE, 1, 'Laufende EDV-Kosten (z. B. Beratung, Wartung, Reparatur)'],
73
+ [229, 51, 52, EXPENSE, 1, 'Arbeitsmittel (z. B. Bürobedarf, Porto, Fachliteratur)'],
74
+ [226, 52, 53, EXPENSE, 1, 'Kosten für Abfallbeseitigung und Entsorgung'],
75
+ [227, 53, 54, EXPENSE, 1, 'Kosten für Verpackung und Transport'],
76
+ [224, 54, 55, EXPENSE, 1, 'Werbekosten (z. B. Inserate, Werbespots, Plakate)'],
77
+ [232, 55, 56, EXPENSE, 1, 'Schuldzinsen zur Finanzierung von Anschaffungs- und Herstellungskosten von Wirtschaftsgütern des Anlagevermögens (ohne häusliches Arbeitszimmer)'],
78
+ [234, 56, 57, EXPENSE, 1, 'Übrige Schuldzinsen'],
79
+ [185, 57, 58, EXPENSE, 1, 'Gezahlte und nach § 15 UStG abziehbare Vorsteuerbeträge'],
80
+ [186, 58, 59, EXPENSE, 1, 'An das Finanzamt gezahlte und ggf. verrechnete Umsatzsteuer'],
81
+ [183, 60, 61, EXPENSE, 1, 'Übrige unbeschränkt abziehbare Betriebsausgaben'],
82
+ [164, 62, 63, EXPENSE, 0, 'Geschenke (nicht abziehbar)'],
83
+ [174, 62, 63, EXPENSE, 1, 'Geschenke (abziehbar)'],
84
+ [165, 63, 64, EXPENSE, 0, 'Bewirtungsaufwendungen (nicht abziehbar)'],
85
+ [175, 63, 64, EXPENSE, 1, 'Bewirtungsaufwendungen (abziehbar)'],
86
+ [171, 64, 65, EXPENSE, 1, 'Verpflegungsmehraufwendungen'],
87
+ [162, 65, 66, EXPENSE, 0, 'Aufwendungen für ein häusliches Arbeitszimmer (nicht abziehbar)'],
88
+ [172, 65, 66, EXPENSE, 1, 'Aufwendungen für ein häusliches Arbeitszimmer (abziehbar)'],
89
+ [163, 66, 67, EXPENSE, 1, 'Tagespauschale für die Tätigkeit in der häuslichen Wohnung'],
90
+ [168, 67, 68, EXPENSE, 0, 'Sonstige beschränkt abziehbare Betriebsausgaben (nicht abziehbar)'],
91
+ [177, 67, 68, EXPENSE, 1, 'Sonstige beschränkt abziehbare Betriebsausgaben (abziehbar)'],
92
+ [144, 68, 69, EXPENSE, 1, 'Kfz-Kosten und andere Fahrtkosten: Leasingkosten'],
93
+ [145, 69, 70, EXPENSE, 1, 'Kfz-Kosten und andere Fahrtkosten: Steuern, Versicherungen und Maut'],
94
+ [146, 70, 71, EXPENSE, 1, 'Sonstige tatsächliche Fahrtkosten ohne AfA und Zinsen (z. B. Reparaturen, Wartungen, Treibstoff, Kosten für Flugstrecken, Kosten für öffentliche Verkehrsmittel)'],
95
+ [147, 71, 72, EXPENSE, 1, 'Fahrtkosten für nicht zum Betriebsvermögen gehörende Fahrzeuge (Nutzungseinlage)'],
96
+ [142, 72, 73, EXPENSE, -1, 'Fahrtkosten für Wege zwischen Wohnung und erster Betriebsstätte; Familienheimfahrten (pauschaliert oder tatsächlich)'],
97
+ [176, 73, 74, EXPENSE, 1, 'Mindestens abziehbare Fahrtkosten für Wege zwischen Wohnung und erster Betriebsstätte (Entfernungspauschale); Familienheimfahrten'],
98
+ [139, 74, 75, EXPENSE, -1, 'Nicht abziehbare Beträge'],
99
+ // 5. Zusätzliche Angaben bei Einzelunternehmen: Entnahmen und Einlagen i. S. d. § 4 Abs. 4a EStG
100
+ [122, 106, 107, 'private', 0, 'Entnahmen einschließlich Sach-, Leistungs- und Nutzungsentnahmen'],
101
+ [123, 107, 108, 'private', 0, 'Einlagen einschließlich Sach-, Leistungs- und Nutzungseinlagen'],
102
+ ];
103
+
104
+ /** The side a line counts: receipts and Einlagen credit, expenses and Entnahmen debit. */
105
+ const sideOf = (kennzahlArg: number, sectionArg: TEuerSection): IEuerFormLine['side'] =>
106
+ sectionArg === 'income' || kennzahlArg === 123 ? 'credit' : 'debit';
107
+
108
+ const formOf = (yearArg: 2025 | 2026, provenanceArg: string): IEuerForm => ({
109
+ year: yearArg,
110
+ lines: ROWS.map(([kennzahl, line2025, line2026, section, sign, label]) => ({
111
+ kennzahl,
112
+ line: yearArg === 2025 ? line2025 : line2026,
113
+ label,
114
+ section,
115
+ sign,
116
+ side: sideOf(kennzahl, section),
117
+ })),
118
+ provenance: provenanceArg,
119
+ });
120
+
121
+ /** The Anlage EÜR of each form year, by year. */
122
+ export const ANLAGE_EUER_FORMS: IEuerForm[] = [
123
+ formOf(2025, 'BMF-Schreiben 2025-08-29 (IV C 6 - S 2142/00023/010/001), Anlage EÜR 2025 and its Anleitung; lines read 2026-10-07'),
124
+ formOf(2026, 'BMF-Schreiben 2026-09-01 (IV C 6 - S 2142/00024/010/001), Anlage EÜR 2026 and its Anleitung; lines read 2026-10-07'),
125
+ ];
126
+
127
+ /**
128
+ * The Kennzahl a Kleinunternehmer (§ 19 Abs. 1 UStG) states every receipt
129
+ * under but those of Zeilen 18 to 22: "Die Zeilen 15 bis 17 sind von
130
+ * Kleinunternehmern in der Regel nicht zu verwenden" (Anleitung, Zeile 12).
131
+ * `euerLinesOf` and `getEuerLines` with `smallBusiness` take the taxable (Kz 112) and the
132
+ * tax-free or non-taxable receipts (Kz 103) there. Zeile 13 (Kz 119), the
133
+ * "davon" of the non-taxable receipts and those under § 19 Abs. 2 Satz 1
134
+ * Nr. 1 und 2 UStG within Zeile 12, is stated "nachrichtlich" (Anleitung,
135
+ * Zeile 13): no account resolves to it, since an account does not tell which
136
+ * of its receipts fall under it, so that amount stays with the caller.
137
+ */
138
+ export const EUER_SMALL_BUSINESS_KENNZAHL = 111;
139
+
140
+ /** The form years the table holds, ascending. */
141
+ export function listEuerFormYears(): number[] {
142
+ return ANLAGE_EUER_FORMS.map((formArg) => formArg.year);
143
+ }
144
+
145
+ /**
146
+ * The Anlage EÜR of a form year. Throws for a year the table does not hold —
147
+ * a form's lines must never fall back to another year's silently.
148
+ */
149
+ export function getEuerForm(year: number): IEuerForm {
150
+ const form = ANLAGE_EUER_FORMS.find((candidateArg) => candidateArg.year === year);
151
+ if (!form) {
152
+ throw new Error(`No Anlage EÜR for ${year}. Available: ${listEuerFormYears().join(', ')}`);
153
+ }
154
+ return form;
155
+ }
156
+
157
+ /** A line of a form year by its Kennzahl; throws where the year's table has none. */
158
+ export function getEuerFormLine(year: number, kennzahl: number): IEuerFormLine {
159
+ const line = getEuerForm(year).lines.find((candidateArg) => candidateArg.kennzahl === kennzahl);
160
+ if (!line) {
161
+ throw new Error(`The Anlage EÜR ${year} has no line of Kz ${kennzahl}`);
162
+ }
163
+ return line;
164
+ }
165
+
166
+ /** Options of `getEuerLines`. */
167
+ export interface IEuerLinesOptions {
168
+ /** the business is a Kleinunternehmer (§ 19 Abs. 1 UStG): its taxable and tax-free receipts go to Kz 111 */
169
+ smallBusiness?: boolean;
170
+ }
171
+
172
+ /**
173
+ * The lines of an account in a form year, resolved from the account's
174
+ * mapping (`IChartAccount.euer`, `euerExclusion`). Throws for an account that
175
+ * has neither, which a dataset of this package never holds.
176
+ */
177
+ export function euerLinesOf(account: IChartAccount, formYear: number, options: IEuerLinesOptions = {}): TEuerAccountLines {
178
+ const lineOf = (kennzahlArg: number): IEuerFormLine =>
179
+ getEuerFormLine(
180
+ formYear,
181
+ options.smallBusiness && (kennzahlArg === 112 || kennzahlArg === 103) ? EUER_SMALL_BUSINESS_KENNZAHL : kennzahlArg,
182
+ );
183
+ if (account.euer && 'kennzahl' in account.euer) {
184
+ return { kind: 'netted', line: lineOf(account.euer.kennzahl) };
185
+ }
186
+ if (account.euer) {
187
+ return { kind: 'split', debitLine: lineOf(account.euer.debitKennzahl), creditLine: lineOf(account.euer.creditKennzahl) };
188
+ }
189
+ if (account.euerExclusion) {
190
+ return { kind: 'excluded', exclusion: account.euerExclusion };
191
+ }
192
+ throw new Error(`Account ${account.accountNumber} has neither an Anlage EÜR mapping nor an exclusion`);
193
+ }
194
+
195
+ /** An amount on a line of the Anlage EÜR, in integer cents. */
196
+ export interface IEuerAmount {
197
+ line: IEuerFormLine;
198
+ cents: number;
199
+ }
200
+
201
+ /**
202
+ * What an account's movements of a period, its debits and its credits in
203
+ * integer cents, amount to on its lines: a netted line counts its own side
204
+ * less the other (it may come out negative: a credit note's reversal), a split
205
+ * account each side on its line, an excluded one nothing.
206
+ */
207
+ export function euerAmountsOf(lines: TEuerAccountLines, debitCents: number, creditCents: number): IEuerAmount[] {
208
+ switch (lines.kind) {
209
+ case 'netted':
210
+ return [{ line: lines.line, cents: lines.line.side === 'credit' ? creditCents - debitCents : debitCents - creditCents }];
211
+ case 'split':
212
+ return [
213
+ { line: lines.debitLine, cents: debitCents },
214
+ { line: lines.creditLine, cents: creditCents },
215
+ ];
216
+ case 'excluded':
217
+ return [];
218
+ }
219
+ }
@@ -3,9 +3,12 @@ import type {
3
3
  IChartAccount,
4
4
  IMappingCoverage,
5
5
  IScenarioKennzahlen,
6
+ TAccountRole,
6
7
  TChartName,
8
+ TEuerAccountLines,
7
9
  TMappingDimension,
8
10
  } from './chartdata.types.js';
11
+ import { euerLinesOf, type IEuerLinesOptions } from './chartdata.euer.js';
9
12
  import { SKR03_2026 } from './data/skr03.2026.js';
10
13
  import { SKR04_2026 } from './data/skr04.2026.js';
11
14
 
@@ -68,6 +71,18 @@ export function getAccount(
68
71
  );
69
72
  }
70
73
 
74
+ /**
75
+ * The accounts of a chart with the role (`IChartAccount.accountRole`), in the
76
+ * chart's order. Throws for unknown chart/year combinations, as `getChart`.
77
+ */
78
+ export function getAccountsByRole(
79
+ chart: TChartName,
80
+ year: number,
81
+ role: TAccountRole,
82
+ ): IChartAccount[] {
83
+ return getChart(chart, year).accounts.filter((account) => account.accountRole === role);
84
+ }
85
+
71
86
  function hasMapping(account: IChartAccount, dimension: TMappingDimension): boolean {
72
87
  switch (dimension) {
73
88
  case 'ustva':
@@ -78,6 +93,8 @@ function hasMapping(account: IChartAccount, dimension: TMappingDimension): boole
78
93
  return account.hgb275Position !== undefined;
79
94
  case 'ebilanz':
80
95
  return account.ebilanzPosition !== undefined;
96
+ case 'euer':
97
+ return account.euer !== undefined || account.euerExclusion !== undefined;
81
98
  }
82
99
  }
83
100
 
@@ -107,3 +124,21 @@ export function getMappingCoverage(
107
124
  }
108
125
  return { dimension, requested: usedAccounts.length, covered, missing, unknown };
109
126
  }
127
+
128
+ /**
129
+ * The Anlage EÜR lines of a chart's account in a form year (`euerLinesOf`):
130
+ * where its movements go, or why they are no part of the form. `null` for an
131
+ * account the chart does not hold. Throws for an unknown chart, chart year or
132
+ * form year. The chart year and the form year are apart on purpose: a host
133
+ * that books a year on the nearest chart dataset states it on that year's form.
134
+ */
135
+ export function getEuerLines(
136
+ chart: TChartName,
137
+ chartYear: number,
138
+ accountNumber: string,
139
+ formYear: number,
140
+ options: IEuerLinesOptions = {},
141
+ ): TEuerAccountLines | null {
142
+ const account = getAccount(chart, chartYear, accountNumber);
143
+ return account ? euerLinesOf(account, formYear, options) : null;
144
+ }
@@ -2,6 +2,20 @@ export type TChartName = 'SKR03' | 'SKR04';
2
2
 
3
3
  export type TAccountType = 'asset' | 'liability' | 'equity' | 'revenue' | 'expense';
4
4
 
5
+ /**
6
+ * What an account is for where its type and HGB position do not tell it apart
7
+ * from the other accounts of its position. A fixed asset's disposal: DATEV
8
+ * states a sale's book gain net under the other operating income (GuV 4) and
9
+ * a book loss under the other operating expenses (GuV 8), the proceeds
10
+ * against the book value, beside the other income and expenses there.
11
+ * - 'asset_sale_proceeds': what a fixed asset fetched when it was sold
12
+ * ("Erlöse aus Verkäufen Sachanlagevermögen", immaterieller
13
+ * Vermögensgegenstände, Finanzanlagen), a revenue account;
14
+ * - 'disposal_book_value': the book value a disposed fixed asset leaves the
15
+ * books with ("Anlagenabgänge … (Restbuchwert …)"), an expense account.
16
+ */
17
+ export type TAccountRole = 'asset_sale_proceeds' | 'disposal_book_value';
18
+
5
19
  /**
6
20
  * Built-in DATEV tax automatism of an account. Lines on automatik accounts
7
21
  * must carry no BU key (automatism applies) or key 40 (automatism cancelled).
@@ -21,11 +35,108 @@ export interface IUstvaMapping {
21
35
  role: 'base' | 'tax';
22
36
  }
23
37
 
38
+ /**
39
+ * Where an account's movements go in the Anlage EÜR (the standardised
40
+ * Einnahmenüberschussrechnung, § 60 Abs. 4 EStDV), by the Kennzahl of the
41
+ * form's line (`ANLAGE_EUER_FORMS`; the line number itself changes with the
42
+ * form year, the Kennzahl does not):
43
+ * - `{ kennzahl }`: the movements of both sides, netted toward the line's own
44
+ * side: a line of receipts counts credits less debits (a sales deduction
45
+ * lowers the receipts), a line of expenses debits less credits (a received
46
+ * discount lowers the goods);
47
+ * - `{ debitKennzahl, creditKennzahl }`: each side on a line of its own, not
48
+ * netted: the VAT paid to the tax office (Kz 186) and the VAT it refunded
49
+ * (Kz 141) on one account.
50
+ */
51
+ export type TEuerMapping = { kennzahl: number } | { debitKennzahl: number; creditKennzahl: number };
52
+
53
+ /**
54
+ * Why an account's movements are no part of the Anlage EÜR, which counts
55
+ * receipts and expenses when paid (§ 4 Abs. 3, § 11 EStG):
56
+ * - 'balance_sheet': a payment moves between the business's own money, claims
57
+ * and debts (bank, cash, receivables, payables, loans, provisions, equity);
58
+ * - 'fixed_asset': a fixed asset's cost reaches the form through its
59
+ * depreciation and, at its disposal, its residual book value (AfA, GWG,
60
+ * Restbuchwerte), not through the asset account;
61
+ * - 'inventory': stock and its changes: goods count when paid, and a
62
+ * cash-basis book keeps no inventory;
63
+ * - 'valuation': a value at a closing date, which a cash-basis book does not
64
+ * state (currency revaluation, the valuation of the cash funds);
65
+ * - 'pass_through': durchlaufende Posten, received and spent in another's name
66
+ * and for another's account (§ 4 Abs. 3 Satz 2 EStG);
67
+ * - 'reverse_charge': the tax a recipient owes on an intra-Community
68
+ * acquisition or under § 13b UStG and deducts as input tax in the same
69
+ * return: no receipt and no payment of its own (Anleitung zur Anlage EÜR,
70
+ * Zeile 58); what a return leaves to pay counts as paid VAT (Kz 186);
71
+ * - 'closing': a carry-forward, closing or statistical account, which records
72
+ * no business transaction.
73
+ */
74
+ export type TEuerExclusion =
75
+ | 'balance_sheet'
76
+ | 'fixed_asset'
77
+ | 'inventory'
78
+ | 'valuation'
79
+ | 'pass_through'
80
+ | 'reverse_charge'
81
+ | 'closing';
82
+
83
+ /**
84
+ * The part of the Anlage EÜR a line belongs to: Betriebseinnahmen (Zeilen
85
+ * 12 bis 22), Betriebsausgaben (Zeilen 24 bis 76 of the 2026 form), or the
86
+ * private Entnahmen and Einlagen of a sole proprietor (Zeilen 107 and 108).
87
+ */
88
+ export type TEuerSection = 'income' | 'expense' | 'private';
89
+
90
+ /** One line of the Anlage EÜR of a form year that an account's movements can go to. */
91
+ export interface IEuerFormLine {
92
+ /**
93
+ * The line's Kennzahl on the form. Unique among the lines of a form's
94
+ * table; the form reuses some numbers in its general and closing sections,
95
+ * which the table does not hold.
96
+ */
97
+ kennzahl: number;
98
+ /** the line ("Zeile") on the year's form */
99
+ line: number;
100
+ /** the line's text on the form; a line of two columns names its column ("nicht abziehbar", "abziehbar") */
101
+ label: string;
102
+ section: TEuerSection;
103
+ /**
104
+ * How the line enters its section's sum (Zeilen 23 and 76): 1 adds, -1
105
+ * subtracts (the travel to the first place of business, Zeile 73, and the
106
+ * non-deductible amounts, Zeile 75), 0 not at all (a "davon" line, the
107
+ * column "nicht abziehbar", the Entnahmen and Einlagen).
108
+ */
109
+ sign: 1 | -1 | 0;
110
+ /** the side whose movements the line counts, net of the other: receipts and Einlagen credit, expenses and Entnahmen debit */
111
+ side: 'debit' | 'credit';
112
+ }
113
+
114
+ /** The lines of the Anlage EÜR of one form year, as the BMF published the form. */
115
+ export interface IEuerForm {
116
+ year: number;
117
+ lines: IEuerFormLine[];
118
+ /** the published form these lines were read from */
119
+ provenance: string;
120
+ }
121
+
122
+ /**
123
+ * The lines an account's movements go to in a form year:
124
+ * - 'netted': both sides, netted, on one line;
125
+ * - 'split': each side on a line of its own;
126
+ * - 'excluded': none, and why.
127
+ */
128
+ export type TEuerAccountLines =
129
+ | { kind: 'netted'; line: IEuerFormLine }
130
+ | { kind: 'split'; debitLine: IEuerFormLine; creditLine: IEuerFormLine }
131
+ | { kind: 'excluded'; exclusion: TEuerExclusion };
132
+
24
133
  export interface IChartAccount {
25
134
  accountNumber: string;
26
135
  accountName: string;
27
136
  accountClass: number;
28
137
  accountType: TAccountType;
138
+ /** what the account is for beyond its type and HGB position; unset = they say it all */
139
+ accountRole?: TAccountRole;
29
140
  description?: string;
30
141
  automatikFunction?: TAutomatikFunction;
31
142
  /** BU keys allowed on lines of this account ('' = none). Unset = all. */
@@ -37,6 +148,10 @@ export interface IChartAccount {
37
148
  hgb275Position?: string;
38
149
  /** E-Bilanz de-gaap-ci taxonomy position (A3 — not yet populated) */
39
150
  ebilanzPosition?: string;
151
+ /** the Anlage EÜR line(s) the account's movements go to; every account has this or `euerExclusion` */
152
+ euer?: TEuerMapping;
153
+ /** why the account's movements are no part of the Anlage EÜR */
154
+ euerExclusion?: TEuerExclusion;
40
155
  /** where this record's facts come from (source, date) */
41
156
  provenance: string;
42
157
  /** first year this record is valid for */
@@ -50,7 +165,8 @@ export interface IChart {
50
165
  accounts: IChartAccount[];
51
166
  }
52
167
 
53
- export type TMappingDimension = 'ustva' | 'hgb266' | 'hgb275' | 'ebilanz';
168
+ /** 'euer' counts an account covered when it maps to the Anlage EÜR or is excluded from it with a reason. */
169
+ export type TMappingDimension = 'ustva' | 'hgb266' | 'hgb275' | 'ebilanz' | 'euer';
54
170
 
55
171
  export interface IMappingCoverage {
56
172
  dimension: TMappingDimension;