@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 by getAccountsByRole
  • 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 by getAccountsBySecuritiesRole and found one by one with getSecuritiesAccount
  • 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 with getPayrollAccount
  • 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 with getExpenseAccount
  • 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_8b on 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; // []
  • netted accounts 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. split accounts 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), closing and deferred_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 sign says 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 split account 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 }] }. factor is 1 or -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 is Aktiva 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 at Aktiva B.II.4 when positive and Passiva C.8 when 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']. TLegalForm also 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

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.

S
Description
No description provided
Readme
2.7 MiB
Languages
TypeScript 100%