@fin.cx/chartdata
Year-versioned German SKR03/SKR04 chart-of-accounts datasets with Automatikkonto functions, BU-key rules, and UStVA-Kennzahl mappings. Pure data plus a small lookup API — no database, no runtime dependencies.
This package is the data backbone for @fin.cx/skr (the bookkeeping core) and
the upcoming filing packages: account→UStVA-Kennzahl mappings feed the
Umsatzsteuer-Voranmeldung engine; HGB §266/§275 mappings and an E-Bilanz taxonomy mapping model
land here as they are curated (A2/A3 milestones).
Issue Reporting and Security
For reporting bugs, issues, or security vulnerabilities, please visit community.foss.global/. This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a code.foss.global/ account to submit Pull Requests directly.
What This Package Provides
- Year-keyed SKR03 and SKR04 datasets (
SKR03_2026,SKR04_2026) - Automatikkonto functions (USt/VSt automatism incl. rate, tax-free accounts) with the allowed BU keys per account ('' or '40' on automatik accounts)
- Account→UStVA-Kennzahl mappings (
ustva: { kennzahl, role: 'base' | 'tax' }) - Scenario-driven Kennzahlen (
USTVA_SCENARIO_KENNZAHLEN) for igE and §13b Bemessungsgrundlagen that cannot be derived from a single account: Kz 46/47 (§13b Abs. 1), Kz 73/74 (§13b Abs. 2 Nr. 3, under the GrEStG) and Kz 84/85 (the other §13b Abs. 2 supplies) - Account roles (
accountRole) where an account's type and HGB position do not tell it apart from the others of its position: a fixed asset's sale proceeds (asset_sale_proceeds) and the book value of a disposed fixed asset (disposal_book_value), listed per chart bygetAccountsByRole - Securities accounts (Wertpapiere) with their securities role
(
securitiesRole): the holdings of fixed and current assets, their income, their sale (net for current assets, gross on a Finanzanlage's disposal accounts), the costs of a sale, write-downs and write-ups, the taxes withheld from their income and the margin interest, listed per chart bygetAccountsBySecuritiesRoleand found one by one withgetSecuritiesAccount - Payroll accounts with their payroll role (
payrollRole): the pay and the flat wage taxes (GuV 6a), the employer's contributions (GuV 6b), the debts of a payroll until they are paid, the company-car benefit and the levy refunds, and the clearing account, one account per role, found withgetPayrollAccount - Expense accounts with their expense role (
expenseRole): an employee's travel, the business's other expenses an employee lays out and claims back, and the Kreditkartenabrechnung, one account per role, found withgetExpenseAccount - DATEV's mark "HB" (
commercialOnly): an account posted in the commercial balance sheet only - The tax rule an income or expense account is kept for (
taxRule):estg_3_40_kstg_8bon every account DATEV names "§ 3 Nr. 40 EStG bzw. § 8b … KStG", whose income DATEV's corporate-tax interface treats as tax-free and whose expenses as non-deductible - Account→Anlage EÜR mappings (
euer) for the Einnahmenüberschussrechnung (§ 4 Abs. 3 EStG, § 60 Abs. 4 EStDV), with the lines of the 2025 and 2026 forms (ANLAGE_EUER_FORMS); every account maps or is excluded with its reason (euerExclusion) - Individual accounts (Individualkonten): the books' own accounts on the
numbers DATEV leaves free after an account, inheriting that account's
mappings (
BooksChart,individualAccountProblemsOf,freeNumbersOf), checked against the numbers the DATEV Kontenrahmen assigns and reserves (getDatevNumbers) - Coverage introspection that fails loudly on mapping gaps
- Report metadata: an independently defined standard KER (
bwaLine,BWA_LINES), balance-side rules (balanceRule), and explicit technical exclusions (hgb266Exclusion) - Provenance on every record
Installation
pnpm add @fin.cx/chartdata
Usage
import {
getChart,
getAccount,
getAccountsByRole,
getMappingCoverage,
USTVA_SCENARIO_KENNZAHLEN,
} from '@fin.cx/chartdata';
const chart = getChart('SKR03', 2026);
const revenue = getAccount('SKR03', 2026, '8400');
// { automatikFunction: { taxType: 'ust', ratePercent: 19 },
// allowedBuKeys: ['', '40'], ustva: { kennzahl: 81, role: 'base' }, ... }
// before generating a UStVA: every used account must be mapped
const coverage = getMappingCoverage('SKR03', 2026, usedAccountNumbers, 'ustva');
if (coverage.missing.length > 0 || coverage.unknown.length > 0) {
throw new Error(`UStVA mapping gaps: ${[...coverage.missing, ...coverage.unknown].join(', ')}`);
}
USTVA_SCENARIO_KENNZAHLEN.intraEuAcquisitionBaseStandard; // 89
// a fixed asset's disposal: DATEV states a book gain net under GuV 4 and a
// book loss under GuV 8, so the role tells the proceeds and the book value
// apart from the other income and expenses there
getAccount('SKR03', 2026, '8820')?.accountRole; // 'asset_sale_proceeds'
getAccount('SKR04', 2026, '6895')?.accountRole; // 'disposal_book_value'
getAccount('SKR03', 2026, '8400')?.accountRole; // undefined
getAccountsByRole('SKR04', 2026, 'disposal_book_value').map((account) => account.accountNumber);
// ['4855', '4856', '4857', '4858', '6895', '6896', '6897', '6898']
Securities
A booking of securities finds its account by the securities role and the tax
rule; within a chart no two accounts share both. DATEV keeps an account under
§ 3 Nr. 40 EStG / § 8b KStG beside most ordinary ones (taxRule: 'estg_3_40_kstg_8b'); whether a dividend or a gain belongs there is the
booking's decision, not the account's: a holding of less than 10 % at the start
of the calendar year receives taxable dividends (§ 8b Abs. 4 KStG), while a
share's sale gain falls under § 8b Abs. 2 KStG whatever the holding.
import { getSecuritiesAccount, getAccountsBySecuritiesRole } from '@fin.cx/chartdata';
getSecuritiesAccount('SKR04', 2026, 'holding_current')?.accountNumber; // '1510' Sonstige Wertpapiere
getSecuritiesAccount('SKR04', 2026, 'sale_gain_current', 'estg_3_40_kstg_8b')?.accountNumber; // '4906'
getSecuritiesAccount('SKR03', 2026, 'income_current')?.accountNumber; // '2654'
getSecuritiesAccount('SKR03', 2026, 'income_current', 'estg_3_40_kstg_8b')?.accountNumber; // '2655'
getSecuritiesAccount('SKR03', 2026, 'sale_costs_gain'); // null: DATEV keeps the costs of a sale only under § 8b
getAccountsBySecuritiesRole('SKR03', 2026, 'sale_proceeds_fixed_gain').map((account) => account.accountNumber);
// ['8838', '8839']: a Finanzanlage's sale stated gross, on the accounts of a fixed asset's disposal
| Role | What it books | SKR03 | SKR04 |
|---|---|---|---|
holding_fixed, holding_fixed_shares, holding_fixed_bonds |
Wertpapiere des Anlagevermögens (A.III.5) | 0525, 0530, 0535 | 0900, 0910, 0920 |
holding_current |
Sonstige Wertpapiere (B.III.2) | 1348 | 1510 |
income_fixed / income_current |
dividends, distributions, coupons (GuV 10 / 11) | 2620 (§ 8b 2625) / 2654 (§ 8b 2655) | 7010 (§ 8b 7014) / 7115 (§ 8b 7103) |
sale_gain_current / sale_loss_current |
a current asset's sale, net (GuV 4 / 8) | 2725 (§ 8b 2726) / 2325 (§ 8b 2326) | 4905 (§ 8b 4906) / 6905 (§ 8b 6906) |
sale_proceeds_fixed_*, book_value_fixed_* |
a Finanzanlage's sale, gross | 8838/8818, 2317/2312 (§ 8b 8839/8819, 2318/2313) | 4851/6891, 4857/6897 (§ 8b 4852/6892, 4858/6898) |
sale_costs_gain / sale_costs_loss |
the costs of a sale under § 8b KStG | 4976 / 4977 | 6857 / 6858 |
writedown_fixed_lasting, writedown_fixed_temporary, writedown_current |
write-downs (GuV 12) | 4870 (§ 8b 4871), 4866, 4875 (§ 8b 4876) | 7200 (§ 8b 7204), 7201, 7210 (§ 8b 7214) |
writeup_fixed, writeup_current, writeup |
write-ups (GuV 4) | 2712, 2715, § 8b 2714 | 4912, 4915, § 8b 4914 |
capital_gains_tax, capital_gains_tax_soli |
Kapitalertragsteuer and its Solidaritätszuschlag (GuV 14) | 2213, 2216 | 7630, 7633 |
foreign_withholding_tax, foreign_tax_exempt_income |
foreign tax to credit or deduct; on income exempt at home (GuV 14) | 2219, 2218 | 7639, 7638 |
foreign_tax_refund |
a foreign tax to be refunded (B.II.4) | 1542 | 1440 |
margin_interest |
interest on a short-term debt (GuV 13) | 2110 | 7310 |
borrow_fees |
interest-like expenses such as the fees of securities borrowed (GuV 13) | 2140 | 7330 |
writedown_other_current |
a write-down of a current asset that is no security, usual amount: an option bought, a margin paid (GuV 8) | 4886 | 6910 |
expected_loss_provision |
the provision for an expected loss from a pending transaction, a written option (B.3; HB) | 0976 | 3092 |
provision_release |
the income of a provision released (GuV 4) | 2735 | 4930 |
None of them carries a tax function or a UStVA figure. DATEV marks the provision
for an expected loss "HB" (commercialOnly): it is posted in the commercial
balance sheet only, as § 5 Abs. 4a EStG allows none in the tax balance sheet;
so do the write-downs of a Finanzanlage for a decline that does not last (SKR03
4866, SKR04 7201), which tax law allows only for a lasting one. In the Anlage EÜR the
holdings are no part of the form (fixed assets reach it by their residual book
value, current ones of § 4 Abs. 3 Satz 4 EStG by their cost when sold:
deferred_current_asset); income and gains on sale are receipts free of VAT or
not taxable for VAT (Kz 103), a loss on sale is cost at the sale (Kz 100), the costs of a
sale other expenses (Kz 183), write-downs and write-ups values at a closing date
(valuation), so are a provision and its release, the margin interest and
interest-like expenses other debt interest (Kz 234), and the taxes on income a
withdrawal (Kz 122, § 12 Nr. 3 EStG).
DATEV holds no account of its own for options bought or written, a future's
variation margin, accrued interest bought with a bond, securities lent or sold
short, the cash collateral of a loan of securities, the results of
Termingeschäfte, a writer's premium earned, a short sale's result and the
compensations paid on securities sold short. Their roles (options_held,
variation_margin_paid, variation_margin_received, accrued_interest_claim,
lending_claim, written_options, short_sale_obligations,
collateral_received, termingeschaeft_gain, termingeschaeft_loss,
stillhalter_income, short_sale_gain, short_sale_loss,
compensation_paid) name no account of the standard charts: the books give
them an individual account, or book them on the account of their position.
Payroll
A payroll run finds its accounts by the payroll role; within a chart each role names one account, and every role has its account in both standard charts. DATEV states each account's position beside it: the pay under "Löhne und Gehälter" (GuV 6a, § 275 Abs. 2 Nr. 6 a HGB), the employer's contributions under "Soziale Abgaben und Aufwendungen für Altersversorgung und für Unterstützung" (GuV 6b). DATEV's collective accounts (SKR03 4100, SKR04 6000 and 6100) keep their positions and take no role.
import { getPayrollAccount } from '@fin.cx/chartdata';
getPayrollAccount('SKR03', 2026, 'salaries')?.accountNumber; // '4120' Gehälter
getPayrollAccount('SKR04', 2026, 'wage_tax')?.accountNumber; // '3730' Verbindlichkeiten aus Lohn- und Kirchensteuer
getPayrollAccount('SKR04', 2026, 'company_car_benefit')?.ustva; // { kennzahl: 81, role: 'base' }
| Role | What it books | SKR03 | SKR04 |
|---|---|---|---|
wages |
Löhne (GuV 6a) | 4110 | 6010 |
salaries |
Gehälter (GuV 6a) | 4120 | 6020 |
managing_director_salaries |
a GmbH's shareholder-managing directors (GuV 6a) | 4124 | 6024 |
temporary_wages |
Aushilfslöhne (GuV 6a) | 4190 | 6030 |
minijob_wages |
Löhne für Minijobs (GuV 6a) | 4195 | 6035 |
benefits_in_kind |
Sachzuwendungen und Dienstleistungen an Arbeitnehmer (GuV 6a) | 4152 | 6072 |
capital_formation_benefits |
vermögenswirksame Leistungen (GuV 6a) | 4170 | 6080 |
commuting_allowances |
Fahrtkostenerstattung Wohnung/Arbeitsstätte (GuV 6a) | 4175 | 6090 |
minijob_flat_tax |
the flat tax of a Minijob (GuV 6a) | 4194 | 6036 |
employee_flat_tax |
the flat tax for employees (GuV 6a) | 4198 | 6039 |
temporary_flat_tax |
the flat tax for temporary staff (GuV 6a) | 4199 | 6040 |
employer_social_security |
the employer's contributions and the levies U1, U2, U3 (GuV 6b) | 4130 | 6110 |
minijob_social_security |
the flat contributions of a Minijob (GuV 6b) | 4144 | 6171 |
accident_insurance |
Berufsgenossenschaft (GuV 6b) | 4138 | 6120 |
pension |
Aufwendungen für Altersversorgung (GuV 6b) | 4165 | 6140 |
pension_flat_tax |
the flat tax on such benefits, Direktversicherungen (GuV 6b) | 4167 | 6147 |
company_car_benefit |
the private use of a company car, 19 % VAT, UStVA Kz 81 (GuV 4) | 8611 | 4947 |
levy_refunds |
Erstattungen nach dem Aufwendungsausgleichsgesetz (GuV 4) | 2749 | 4972 |
levy_refund_claims |
the claim on the health insurer for them | 1520 | 1369 |
net_pay |
the net pay owed | 1740 | 3720 |
wage_tax |
wage tax, church tax and Solidaritätszuschlag owed | 1741 | 3730 |
social_security |
the contributions owed | 1742 | 3740 |
employee_withholdings |
what is withheld from an employee for another (a garnishment) | 1748 | 3725 |
capital_formation_payable |
the savings an employee's contract receives | 1750 | 3770 |
estimated_contributions |
the contributions estimated when due (§ 23 Abs. 1 SGB IV) | 1759 | 3759 |
clearing |
the Lohn- und Gehaltsverrechnungskonto | 1755 | 3790 |
In the Anlage EÜR every payroll expense is Zeile 31 (Kz 120), "einschließlich der gezahlten Lohnsteuer (auch Pauschalsteuer nach § 37b EStG) und anderer Nebenkosten" (Anleitung 2026); the company-car benefit is a taxable receipt (Kz 112), the levy refunds a non-taxable one (Kz 103). An individual account takes no payroll role: a role names the DATEV account.
Expense claims
An expense claim (Reisekostenabrechnung, Auslagenersatz) finds the accounts of
its lines by the expense role; within a chart each role names one account, and
every role has its account in both standard charts. None of them carries a tax
function: a receipt's input VAT is booked by BU key. The claim is settled
through the payroll role clearing (the Lohn- und Gehaltsverrechnungskonto),
which takes no expense role.
import { getExpenseAccount } from '@fin.cx/chartdata';
getExpenseAccount('SKR03', 2026, 'travel_employee_meals')?.accountNumber; // '4664' Reisekosten Arbeitnehmer Verpflegungsmehraufwand
getExpenseAccount('SKR04', 2026, 'business_meals_nondeductible')?.euer; // { kennzahl: 165 }
getExpenseAccount('SKR04', 2026, 'credit_card_settlement')?.hgb266Position; // 'Passiva C.8'
| Role | What it books | SKR03 | SKR04 | Anlage EÜR |
|---|---|---|---|---|
travel_employee |
Reisekosten Arbeitnehmer: travel costs of no other role, such as parking and tolls | 4660 | 6650 | Kz 120 |
travel_employee_transport |
Fahrtkosten: tickets, taxis, a hired car and its fuel | 4663 | 6663 | Kz 120 |
travel_employee_meals |
Verpflegungsmehraufwand | 4664 | 6664 | Kz 120 |
travel_employee_lodging |
Übernachtungsaufwand | 4666 | 6660 | Kz 120 |
mileage_employee |
Kilometergelderstattung Arbeitnehmer | 4668 | 6668 | Kz 120 |
business_meals |
Bewirtungskosten, the deductible 70 % | 4650 | 6640 | Kz 175 |
business_meals_nondeductible |
nicht abzugsfähige Bewirtungskosten, the other 30 % | 4654 | 6644 | Kz 165 |
vehicle_running_costs |
Laufende Fahrzeug-Betriebskosten: a company car's fuel | 4530 | 6530 | Kz 146 |
office_supplies |
Bürobedarf | 4930 | 6815 | Kz 229 |
postage |
Porto | 4910 | 6800 | Kz 229 |
literature |
Zeitschriften, Bücher, digitale Medien (Fachliteratur) | 4940 | 6820 | Kz 229 |
training |
Fortbildungskosten | 4945 | 6821 | Kz 281 |
software |
software licensed or subscribed to (Wartungskosten für Hard- und Software) | 4806 | 6495 | Kz 228 |
other_operating_expenses |
Sonstige betriebliche Aufwendungen | 4900 | 6300 | Kz 183 |
credit_card_settlement |
Kreditkartenabrechnung, a balance (Passiva C.8) | 1730 | 3610 | balance |
The expenses stand under the other operating expenses (GuV 8). An employee's travel goes with the staff in the Anlage EÜR (Zeile 31): "Aufwendungen für Reisen von Arbeitnehmern sind in Zeile 31 zu erfassen" (Anleitung 2026, Zeile 45). Software books on DATEV's Wartungskosten für Hard- und Software, the running EDV costs of the Anlage EÜR (Zeile 51), not on the licences of rights (SKR03 4964, SKR04 6837), which DATEV's trade tax program adds back in part (§ 8 Nr. 1 f GewStG); the books book a licence of a right there themselves. An individual account takes no expense role: a role names the DATEV account.
Funding and reimbursement settlements
The charts classify the following DATEV 2026 accounts. Their availability is not a rule selecting an account for a business or deciding its funding agreement.
| Purpose | SKR03 | SKR04 |
|---|---|---|
| Cash of the books' till (Kasse) | 1000 | 1600 |
| Ordinary payroll clearing (Lohn- und Gehaltsverrechnungskonto) | 1755 | 3790 |
| Special recurring-payroll timing under § 11 Abs. 2 Satz 2 EStG for § 4 Abs. 3 EStG | 1756 | 3791 |
| Privateinlagen where legally applicable | 1890 | 2180 |
| Verbindlichkeiten gegenüber Gesellschaftern | 0730 | 3510 |
| Verbindlichkeiten gegenüber GmbH-Gesellschaftern | 1665 | 3640 |
| Verbindlichkeiten gegenüber persönlich haftenden Gesellschaftern | 1670 | 3645 |
| Verbindlichkeiten gegenüber Kommanditisten | 1675 | 3650 |
The debt accounts are liabilities under Passiva C.8; DATEV also states
receivables there by their debit balance (balanceRule.debitPosition: 'Aktiva B.II.4'). Payroll clearing is curated under Aktiva B.II.4, with a
credit balance under Passiva C.8. These settlement accounts have no tax
automatism, VAT figure or P&L line and are excluded from Anlage EÜR as
balance_sheet. A liability settlement is not a second expense or supplier
receipt; it does not itself change the original receipt's input tax.
1756/3791 do not replace 1755/3790 merely because the books use EÜR. They carry
no payroll role; getPayrollAccount(..., 'clearing') still returns 1755/3790.
The books select the actual cash/payroll counter-account. For private funding,
the payer, legal form in force, partner status and evidenced equity contribution
or reimbursement/repayment right determine the treatment. A liability owed to
a shareholder is not a claim against that shareholder. Do not infer contribution,
loan maturity or an individual partner account from the payment label or chart.
Refuse when the books have not supplied the deciding facts or account. Existing
private-contribution restrictions (sole_trader, partnership) remain; they
do not establish that a particular partner's payment is equity. Separately
agreed corporate equity contributions require their lawful equity account,
not a private-account or debt default.
Sources: DATEV's SKR03 2026, pp. 3, 6, 9-12, and SKR04 2026, pp. 6-7, 12-13; DATEV's business-specific Kontenplan explanation; GoB traceability and accurate recording (§ 238 HGB, § 239 HGB); contribution versus repayment (§ 4 EStG, § 670 BGB, § 272 Abs. 2 Nr. 4 HGB); the original invoice's input tax (§ 15 UStG).
Individual accounts
The books may set up accounts of their own (Individualkonten) on the numbers
the DATEV standard chart leaves free after one of its accounts. DATEV leaves
those numbers for the account's own sub-accounts and passes the account's
marks for its trade and corporate income tax programs ("G", "K") on to them, up
to the next account of the standard chart (DATEV Kontenrahmen, Erläuterungen,
S. 37). So an individual account belongs to the account it follows, its
parent, and is what the parent is: its type, its HGB position, its UStVA and
Anlage EÜR mapping, its account role, its tax rule and DATEV's mark "HB".
Where the declaration changes one of them deliberately (overrides: a value
replaces the parent's, null drops it), the provenance names it. It does not
take the parent's tax automatism or BU-key rules, which DATEV applies only to
the accounts it marks, nor its securities role, which names one account of a
chart: an individual account books a securities role only where its
declaration names one.
The declarations are the books'; this package stores none. The validator refuses a declaration whose number:
- has no four digits;
- is one DATEV assigns or reserves, or one this package holds;
- another declaration takes too;
- has no name;
- has a parent this package does not hold, or a parent that is itself an individual account;
- has a carry-forward, closing or statistical account as its parent;
- lies in another account class than its parent;
- does not follow its parent (another account of the chart stands between them);
- takes a securities role another account books under the same tax rule;
- or has an override that names no HGB position, or one its type does not take (a balance-sheet account has a balance-sheet position, a profit-and-loss account an income-statement position), an Anlage EÜR line no form year holds or a split line on the other side, or both a line and an exclusion.
import { BooksChart, freeNumbersOf, individualAccountProblemsOf, type IIndividualAccountDeclaration } from '@fin.cx/chartdata';
freeNumbersOf('SKR03', 2026, '2700'); // ['2701', '2702', '2703', '2704']
freeNumbersOf('SKR04', 2026, '4905'); // []: DATEV's 4906 (§ 8b KStG) follows at once
const declarations: IIndividualAccountDeclaration[] = [
{ accountNumber: '2701', accountName: 'Erträge aus Termingeschäften', parentAccountNumber: '2700', securitiesRole: 'termingeschaeft_gain' },
{ accountNumber: '2301', accountName: 'Verluste aus Termingeschäften', parentAccountNumber: '2300', securitiesRole: 'termingeschaeft_loss' },
];
individualAccountProblemsOf('SKR03', 2026, declarations); // []
const books = BooksChart.of('SKR03', 2026, declarations); // throws IndividualAccountsError on problems
books.getSecuritiesAccount('termingeschaeft_gain')?.accountNumber; // '2701'
books.getAccount('2701')?.hgb275Position; // 'GuV 4', as 2700
books.getMappingCoverage(usedAccountNumbers, 'hgb275');
A BooksChart is a chart (IChart) with the lookups of the chart over the
DATEV accounts and the books' own: getAccount, getAccountsByRole,
getAccountsBySecuritiesRole, getSecuritiesAccount, getPayrollAccount,
getExpenseAccount, getMappingCoverage, getEuerLines.
INDIVIDUAL_ACCOUNT_TEMPLATES suggests the parent and name of the results the
law keeps apart: the gains and losses of Termingeschäfte, which § 15 Abs. 4
Satz 3 EStG offsets only against each other, under the other operating income
and expenses, and beside them a writer's premium earned, a short sale's gain
and loss and the compensations paid on securities sold short. SKR 03 leaves
four numbers after 2700 and six after 2300, enough for all six; SKR 04 leaves
one after 4830 and one after 6300, so there the Termingeschäfte take them and
the books decide with their advisor where the others go. DATEV's ranges "zur
freien Verfügung" (SKR 03 8000–8099, SKR 04 4000–4099 and others) count as
assigned until this package holds the positions DATEV gives them.
Anlage EÜR
Every account of a chart either maps to the Anlage EÜR or is excluded from it with its reason. A mapping names the line by its Kennzahl, which stays the same from one form year to the next while the line number may change. The 2026 form inserts Zeile 27, so the goods move from Zeile 27 (2025) to Zeile 29 (2026).
import { getEuerLines, euerAmountsOf, getEuerForm, getMappingCoverage } from '@fin.cx/chartdata';
// a fiscal year 2025 booked on the 2026 chart, stated on the 2025 form
getEuerLines('SKR03', 2026, '8400', 2025);
// { kind: 'netted', line: { kennzahl: 112, line: 15, label: 'Umsatzsteuerpflichtige Betriebseinnahmen …',
// section: 'income', sign: 1, side: 'credit' } }
getEuerLines('SKR03', 2026, '1780', 2026);
// { kind: 'split', debitLine: { kennzahl: 186, line: 59, … }, creditLine: { kennzahl: 141, line: 18, … } }
getEuerLines('SKR03', 2026, '1787', 2026); // { kind: 'excluded', exclusion: 'reverse_charge' }
getEuerLines('SKR03', 2026, '8400', 2026, { smallBusiness: true }); // Kz 111, Zeile 12 (Zeile 13 stays with the caller)
// what a period's movements on an account amount to on its lines, in cents
euerAmountsOf(getEuerLines('SKR03', 2026, '8400', 2026)!, 20000, 500000); // [{ line: Kz 112, cents: 480000 }]
// before stating an EÜR: every used account must have a decision
getMappingCoverage('SKR04', 2026, usedAccountNumbers, 'euer').missing; // []
nettedaccounts count both sides on one line, toward its side. A line of receipts counts credits less debits, so a sales deduction lowers the receipts; a line of expenses counts debits less credits, so a received discount lowers the goods.splitaccounts put each side on a line of its own. The VAT prepayment and past-year accounts split what was paid to the tax office (Kz 186) from what it refunded (Kz 141).- Exclusions (
TEuerExclusion):balance_sheet,fixed_asset(reaches the form through depreciation and the residual value),inventory,valuation,pass_through(§ 4 Abs. 3 Satz 2 EStG),reverse_charge(the tax of an intra-Community acquisition or under § 13b UStG: such invoices contain no input tax, Anleitung zur Anlage EÜR, Zeile 58, and the tax is no receipt; what a return leaves to pay counts as paid VAT, Kz 186),closinganddeferred_current_asset(shares, securities, land and buildings held as current assets, whose cost counts at their sale, § 4 Abs. 3 Satz 4 EStG). - A line's
signsays how it enters its section's sum: 1 adds, -1 subtracts (Zeilen 73 and 75), 0 not at all (a "davon" line, the column "nicht abziehbar", the Entnahmen and Einlagen). - The form counts the year's movements, not the opening balances (EB-Werte):
a caller sums a period's bookings over the entries it holds and leaves out
the opening entries, which on a
splitaccount would otherwise count as a refund (Kz 141) or a payment (Kz 186). When a payment counts is the caller's period logic too: a VAT refund or prepayment of the prior year that falls due and is paid within ten days of the year's start belongs to the prior year (§ 11 Abs. 1 Satz 2 and Abs. 2 Satz 2 EStG, Anleitung, Zeilen 18 and 59). - With
smallBusiness, a Kleinunternehmer's taxable and tax-free receipts go to Zeile 12 (Kz 111). Zeile 13 (Kz 119) states within it, "nachrichtlich", the non-taxable receipts and those under § 19 Abs. 2 Satz 1 Nr. 1 und 2 UStG. An account does not tell which of its receipts those are, so no account resolves to Kz 119 and that amount stays with the caller. - The decisions the line texts do not make on their own are written at the
table in
tools/generate-charts.ts. Revenue without a tax function counts with the taxable receipts, and interest, exchange gains and damages with the tax-free ones. Employees' travel goes with the staff (Anleitung, Zeile 45), and another member state's VAT (OSS) is received in Kz 103 and paid in Kz 183.
BWA: independently defined standard KER
BWA_LINES is this package's own short-term income statement (Kurzfristige
Erfolgsrechnung), not DATEV BWA schema 0906300 and not an implementation of
its proprietary account-assignment rules. It combines the existing HGB §275
Gesamtkostenverfahren positions with the public account purposes/ranges of SKR03
and SKR04 2026. Every curated revenue or expense account has exactly one
bwaLine: TBwaPostingLineKey; balance-sheet accounts have none. Unknown HGB
positions fail generation instead of silently reaching an unassigned bucket.
The ordered, exported BWA_LINES: readonly TBwaLine[] contains:
- Posting lines:
{ key, label, kind: 'posting', normalSide: 'debit' | 'credit' }. Use selected-period movements, credit minus debit for a credit-normal line, debit minus credit for a debit-normal line. Account type does not override this sign: disposal proceeds can reduce a neutral expense, and a disposed asset's book value can reduce a neutral income. - Computed lines:
{ key, label, kind: 'subtotal', terms: [{ line, factor }] }.factoris1or-1; every referenced line precedes the subtotal. Sum the already computed amounts times those factors, without posting to subtotals.
Credit-normal posting keys are revenue, inventory_changes,
own_work_capitalized, other_operating_income, interest_income,
other_neutral_income, and unassigned_account_classes. All other posting
keys are debit-normal. unassigned_account_classes is the explicit
"Kontenklassen-unbesetzt" row, zero for the curated charts; it is no fallback
for unknown accounts or missing mappings.
Exact subtotal definitions (the exported terms encode these formulas):
| Subtotal key | Definition |
|---|---|
total_output |
revenue + inventory_changes + own_work_capitalized |
gross_profit |
total_output - material_costs |
operating_gross_profit |
gross_profit + other_operating_income |
total_costs |
personnel_costs + premises_costs + business_taxes + insurance_contributions + special_costs + vehicle_costs + advertising_travel_costs + goods_distribution_costs + depreciation + repairs_maintenance + other_operating_costs |
operating_result |
operating_gross_profit - total_costs |
neutral_expense |
interest_expense + other_neutral_expense |
neutral_income |
interest_income + other_neutral_income |
result_before_taxes |
operating_result - neutral_expense + neutral_income + unassigned_account_classes |
provisional_result |
result_before_taxes - income_taxes |
Derivation order is significant. GuV 1→revenue, 2→inventory_changes,
3→own_work_capitalized, 5→material_costs, 6a/6b→personnel_costs,
7→depreciation, 9/10→other_neutral_income, 11→interest_income,
12→other_neutral_expense, 13→interest_expense, 14→income_taxes,
16→business_taxes. GuV 15 and 17 are results, not posting positions.
In particular, trade tax (4320/7610, GuV 14) is an income tax, not an
operating tax merely because of its number.
GuV 4 disposal-role accounts and neutral-income ranges (SKR03 2500–2699,
2710–2729; SKR04 4840–4929) go to other_neutral_income; remaining GuV 4
accounts go to other_operating_income. GuV 8 disposal-role accounts and
neutral-expense ranges (SKR03 2000–2399, 4976–4977; SKR04 6857–6858,
6880–6909) go to other_neutral_expense. Remaining GuV 8 accounts use these
inclusive, non-overlapping operating-cost ranges, then other_operating_costs:
| Posting key | SKR03 | SKR04 |
|---|---|---|
premises_costs |
4200–4299 | 6310–6349 |
business_taxes |
4340–4359 | 7680–7699 |
insurance_contributions |
4360–4399 | 6400–6449 |
special_costs |
4400–4499 (free-purpose range) | 6390–6399 (donations) |
vehicle_costs |
4500–4599 | 6500–6599 |
advertising_travel_costs |
4600–4699 | 6600–6699 |
goods_distribution_costs |
4700–4799 | 6700–6799 |
repairs_maintenance |
4800–4809 | 6450–6499 |
depreciation |
4810–4899 | 6910–6919 |
The ranges categorise existing GuV accounts; they neither add accounts to the curated selection nor license booking on DATEV-reserved numbers. Financial write-downs stay in the neutral result because GuV 12 is resolved first. The ordinary write-down of other current assets (4886/6910, GuV 8) is included in the KER's depreciation line. This is our published derivation, not a promise of identical intermediate totals to any third-party BWA.
Bilanz: balance rules and exclusions
Use cumulative balances through the cutoff, not only the report-period
movements. hgb266Position remains the normal-side position;
balanceRule: IBalanceRule supplies an exceptional debitPosition or
creditPosition only. An individual account's normal-side HGB override
therefore remains authoritative. The standard metadata specifies:
- Bank credit balances:
Passiva C.2(SKR03 1200–1299, SKR04 1800–1899, restricted to accounts whose normal position isAktiva B.IV). Cash and Geldtransit are not bank-overdraft rules. - Debtor credit balances:
Passiva C.4; creditor debit balances:Aktiva B.II.1. - Current domestic VAT:
vatNetGroup: 'vat_current_year', the cumulative debit-minus-credit sum of the group atAktiva B.II.4when positive andPassiva C.8when negative. SKR03 members are 1570, 1571, 1574, 1576, 1577, 1588, 1770, 1771, 1774, 1776, 1780, 1781, 1784, 1787; SKR04 members are 1400, 1401, 1404, 1406, 1407, 1433, 3800, 3801, 3804, 3806, 3820, 3830, 3837. Do not state individual members again after netting the group. - Prior-year VAT (1790/1791, 3841/3845) and foreign VAT (1728/1767/1768, 3798/3817/3818) have sign-sensitive asset/liability rules but no net group: they remain separate from the current domestic VAT. Payroll taxes do not enter the VAT group.
- Private drawings and contributions (1800/1890, 2100/2180):
legalForms: ['sole_trader', 'partnership'].TLegalFormalso contains'corporation', which these accounts do not permit. A report must explicitly refuse a nonzero private balance for a corporation instead of treating it as corporate equity. This is a conservative report eligibility rule, not a substitute for the distinct partnership equity presentation of §264c HGB. - Exactly 9000/9008/9009/9400/9998:
hgb266Exclusion: 'technical', no balance-sheet position. 9000, 9008 and 9009 are the Saldenvortragskonten, the counter accounts of the opening balances: an opening books each debtor against 9008, each creditor against 9009 and every other balance against 9000, so each of the three carries the opposite of what it opened, and only their sum nets to zero across a complete set of opening entries. Exclusion explains classification, not permission to discard a nonzero balance. A report must refuse a nonzero 9400/9998 balance and a nonzero sum of 9000/9008/9009.
HGB_ANNUAL_RESULT_POSITION is Passiva A.V. Add cumulative, credit-minus-debit
P&L through the cutoff there once, including a negative annual result. Do not
use Passiva A.IV (prior-year retained result) or technical 9400 for it.
getMappingCoverage(..., 'hgb266') treats an explicit technical exclusion as
classified; the report still enforces the nonzero-balance refusal.
getMappingCoverage(..., 'bwa') checks posting-line metadata. In either
dimension, unknown and missing accounts remain distinct errors.
BooksChart individual accounts inherit bwaLine, hgb266Exclusion, and a
copy of balanceRule from their parent. Report consumers use the books' chart,
not a separate hardcoded account map. Mapping and report-generation rules
live in tools/report-mappings.ts and tools/generate-charts.ts.
Primary sources read on 2026-10-09: HGB §266, HGB §275, HGB §264c, UStG §16, and the public DATEV Kontenrahmen 2026 SKR03, Art.-Nr. 11174 (pp. 13–17, 22–25) and SKR04, Art.-Nr. 11175 (pp. 17–24). Account categories come from those public charts; the KER selection, labels, boundaries and calculations above are independently defined.
Data Provenance and Verification
Records carry a provenance string. The datasets are a curated table authored
in this repository — our own selection of accounts and our own functional
designations. Public chart implementations are used at generation time as
verification-only oracles (number↔purpose fact checks); no third-party
strings are emitted into the datasets and no oracle files are committed.
Accounts outside the oracle subsets (collective and closing-account
conventions) are provenance-marked as skr-convention. The DATEV
Steuerschlüssel table is portal-gated; the DATEV Kontenrahmen SKR 03/SKR 04
PDFs (Art.-Nr. 11174/11175) are public, and accounts verified against them say
so in their provenance. The standing verification gates are ERiC validation
of generated filings and periodic manual DATEV imports.
HGB §266/§275 positions are populated and validated against the exported statutory
position trees. The Anlage EÜR lines (ANLAGE_EUER_FORMS) were read from the
BMF's published forms and Anleitungen: Anlage EÜR 2025 (BMF-Schreiben of
2025-08-29) and Anlage EÜR 2026 (BMF-Schreiben of 2026-09-01). Each line's pair
of line number and Kennzahl was checked against the forms. The generator fails
on an account without an EÜR decision, and on a Kennzahl or side a form year
does not hold. E-Bilanz (de-gaap-ci) positions are not populated yet —
getMappingCoverage(..., 'ebilanz') reports them as missing by design.
The numbers the DATEV Kontenrahmen assigns and reserves (getDatevNumbers)
are read from the text of the SKR 03 and SKR 04 PDFs, gültig für 2026, as
pdfjs lays it out (one line per row, each item with its x position; the
extraction script and the texts stay in the untracked .nogit/): every
number DATEV lists, with the marks before it, the groups it lists under one
designation ("AM 8400" … "-09") and the reserved ("R"). The tool checks that
every account this package holds from the Kontenrahmen is among them.
The generation pipeline lives in the unpublished tools/ directory
(tsx tools/generate-charts.ts <oracle csvs>, tsx tools/generate-datev-numbers.ts <skr03 text> <skr04 text>);
datasets are generated files — extend the curated table in the tool, never
edit ts/data/* by hand.
Development
pnpm build
pnpm test
License and Legal Information
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the license file.
Please note: The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
Trademarks
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
Company Information
Task Venture Capital GmbH Registered at District Court Bremen HRB 35230 HB, Germany
For any legal inquiries or further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.