2026-07-09 14:21:25 +00:00
2025-08-10 17:11:53 +00:00
2026-07-09 14:21:25 +00:00
2026-07-09 14:21:25 +00:00

@fin.cx/skr

@fin.cx/skr is a TypeScript library for German double-entry bookkeeping with built-in SKR03 and SKR04 chart initialization, MongoDB-backed persistence, reporting, DATEV export, GoBD-oriented Jahresabschluss export, and e-invoice workflows.

It is built for developers who need a programmable accounting core instead of a pile of CSV glue code: initialize a chart of accounts, post validated transactions and journal entries, generate reports, and archive year-end data in a structured export format.

Version 2 makes the ledger filing-grade:

  • Exact integer-cent amounts internally (no float drift; balance checks are ===)
  • Atomic posting via MongoDB transactions (replica set required)
  • GoBD audit trail: gapless sequence numbers, SHA-256 hash chain, immutable posted entries, Storno-only corrections
  • Festschreibung: period locking with cryptographic chain verification
  • A pure, stateless booking core (core export): BU keys, tax-scenario detection, booking recipes, validation — no database needed
  • DATEV Buchungsstapel export in the real EXTF 700 / Formatversion 13 layout (125 columns)

Breaking changes in v2

  • MongoDB must run as a replica set (single-node is fine); initialize() fails hard without transaction support. gitzone services start creates a suitable MongoDB automatically.
  • postTransaction() / reverseTransaction() return transaction-shaped views (ITransactionView), not Transaction documents; the Transaction collection is frozen as a legacy read model.
  • Posted entries are immutable: reversing never mutates the original (status stays posted; linkage via reversalOf).
  • exportToDATEV() now emits the EXTF 700 format; persist the string as CP1252.
  • IJournalEntryLine.postingKey is optional; the v2 line fields (side, amountCents, counterAccount, string buKey) are authoritative.
  • Existing v1 data: run api.migrateToV2() once (idempotent; keeps all legacy fields; returns a report to archive).

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 Library Does

  • Initializes SKR03 or SKR04 account sets in MongoDB
  • Enforces double-entry bookkeeping rules for transactions and journal entries
  • Supports DATEV posting keys and VAT-aware journal lines
  • Prevents direct posting to automatic accounts that require personal accounts
  • Generates trial balance, income statement, balance sheet, general ledger, and cash flow reports
  • Exports accounting data as CSV, DATEV, and GoBD-style Jahresabschluss packages
  • Imports, stores, searches, books, and exports EN16931-style e-invoices
  • Adds signing and timestamp helpers for audit-oriented export workflows

Why It Is Useful

  • You get a real accounting domain model, not just account lists
  • SKR03 and SKR04 are both supported behind one API
  • Tests cover initialization, posting, reversals, reports, pagination, DATEV export, and full year-end flows
  • The package exports the lower-level classes too, so you can stay high-level with SkrApi or build around the primitives

Requirements

  • Node.js 20+ with ESM support
  • A reachable MongoDB instance running as a replica set (transactions are mandatory for atomic posting; a single-node replica set is sufficient — gitzone services start provides one locally)
  • pnpm

The test setup reads MongoDB connection details from .nogit/ via @push.rocks/qenv, but the runtime API only needs a mongoDbUrl and an optional dbName.

If you use the invoice helpers, pass invoiceExportPath to control where content-addressed invoice XML, PDF files, metadata, validation output, and the registry are stored. Without it, invoices are stored under ./exports/invoices from the current working directory.

Installation

pnpm add @fin.cx/skr

Quick Start

import { SkrApi } from '@fin.cx/skr';

const api = new SkrApi({
  mongoDbUrl: 'mongodb://localhost:27017',
  dbName: 'accounting_demo',
  invoiceExportPath: './exports/invoices',
});

await api.initialize('SKR03');

await api.postTransaction({
  date: new Date(),
  debitAccount: '1200',
  creditAccount: '8400',
  amount: 1000,
  description: 'Test sale',
  reference: 'INV-001',
  skrType: 'SKR03',
});

const trialBalance = await api.generateTrialBalance();

console.log(trialBalance.isBalanced);

await api.close();

SKR03 vs SKR04

initialize('SKR03') loads the process-oriented chart.

  • Class 3: Wareneingang
  • Class 4: betriebliche Aufwendungen
  • Class 8: Erlöse
  • Class 9: Vortrags- und Abschlusskonten

initialize('SKR04') loads the financial-statement-oriented chart.

  • Class 4: betriebliche Erträge
  • Class 5 and 6: betriebliche Aufwendungen
  • Class 2: Eigenkapital, Class 3: Fremdkapital

The test suite exercises both variants and includes full Jahresabschluss scenarios for each.

Posting Model

Simple postings use postTransaction().

await api.postTransaction({
  date: new Date(),
  debitAccount: '5400',
  creditAccount: '70001',
  amount: 119,
  description: 'Purchase including VAT',
  skrType: 'SKR03',
  vatAmount: 19,
  reference: 'VAT-001',
});

Complex bookings use postJournalEntry() with explicit DATEV posting keys.

await api.postJournalEntry({
  date: new Date(),
  description: 'Complex distribution',
  reference: 'COMPLEX-001',
  lines: [
    { accountNumber: '5000', debit: 500, description: 'Materials', postingKey: 40 },
    { accountNumber: '6000', debit: 300, description: 'Wages', postingKey: 40 },
    { accountNumber: '7100', debit: 200, description: 'Rent', postingKey: 40 },
    { accountNumber: '1200', credit: 1000, description: 'Bank payment', postingKey: 40 },
  ],
  skrType: 'SKR03',
});

Important behavior from the code and tests:

  • debit and credit totals must balance
  • debit and credit account cannot be the same in a simple transaction
  • inactive accounts cannot be posted to
  • automatic accounts such as debtor or creditor control accounts are meant to be replaced by personal accounts for direct postings

Common Workflows

Create custom accounts:

await api.createAccount({
  accountNumber: '4999',
  accountName: 'Custom Revenue Account',
  accountClass: 4,
  accountType: 'revenue',
  description: 'Test custom account',
});

Batch operations:

await api.createBatchAccounts([
  {
    accountNumber: '10001',
    accountName: 'Kunde Mustermann GmbH',
    accountClass: 1,
    accountType: 'asset',
    skrType: 'SKR03',
  },
  {
    accountNumber: '70001',
    accountName: 'Lieferant Test GmbH',
    accountClass: 7,
    accountType: 'liability',
    skrType: 'SKR03',
  },
]);

Pagination:

const page1 = await api.getAccountsPaginated(1, 10);
console.log(page1.total, page1.totalPages, page1.data.length);

Reversals and validation:

const ok = api.validateDoubleEntry(100, 100);
const reversed = await api.reverseTransaction(transactionId);

Period, balance, and audit helpers:

await api.closePeriod('2024-12', '9400');

const bankBalance = await api.getAccountBalance('1200', new Date('2024-12-31'));

await api.recalculateBalances();

const auditFindings = await api.getUnbalancedTransactions();

Account CSV import and export:

const imported = await api.importAccountsFromCSV(csvContent);
const accountCsv = await api.exportAccountsToCSV();

v2 Ledger: Recipes, Festschreibung, Audit Chain

The stateless core builds validated drafts; the poster writes them atomically:

import { SkrApi, core } from '@fin.cx/skr';

const api = new SkrApi({ mongoDbUrl, dbName });
await api.initialize('SKR03');
const policy = api.getDefaultPolicy();

// booking recipes are pure functions — usable without any database
const draft = core.bookVendorInvoice(
  {
    date: new Date(),
    vendorAccount: '70001',
    grossCents: 11900,
    scenario: 'domestic_standard',
    invoiceNumber: 'RE-1001',
    description: 'Bürobedarf',
  },
  policy,
);

const entry = await api.postDraft(draft); // gapless sequence + hash chain
const storno = await api.postStorno(entry); // corrections never mutate

await api.festschreibePeriod('2026-03'); // GoBD period lock
const audit = await api.verifyAuditChain(2026); // recompute the whole chain
const balance = await api.getAccountBalanceCents('1200'); // exact cents

const { csv, filename } = await api.exportDatevBuchungsstapel({
  dateFrom: new Date('2026-03-01'),
  dateTo: new Date('2026-03-31'),
  consultantNumber: 1001,
  clientNumber: 456,
  fiscalYearStart: new Date('2026-01-01'),
  festschreibung: true,
});
// persist csv as CP1252 — DATEV expects ANSI encoding

Also in core: bookCustomerInvoice (incl. intra-EU supply, export, §13b), bookBankPayment (Skonto with VAT correction, tolerance write-offs), bookInternalTransfer (Geldtransit), bookToSuspense/resolveSuspense, fxDifferenceLines, detectTaxScenario (confidence-scored cascade), the BU-key table (BU_KEY_RULES), and validateJournalDraft.

Reports And Exports

Available reporting methods on SkrApi:

  • generateTrialBalance()
  • generateIncomeStatement()
  • generateBalanceSheet()
  • generateGeneralLedger()
  • generateCashFlowStatement()
  • exportReportToCSV()
  • exportToDATEV()

Year-end archival export:

const exportPath = await api.exportJahresabschluss({
  exportPath: './exports',
  fiscalYear: 2024,
  dateFrom: new Date('2024-01-01'),
  dateTo: new Date('2024-12-31'),
  includeDocuments: true,
  generatePdfReports: true,
  signExport: false,
  timestampExport: false,
  companyInfo: {
    name: 'Example GmbH',
    taxId: 'DE123456789',
    registrationNumber: 'HRB 12345',
    address: 'Example Street 1, 28195 Bremen',
  },
});

console.log(exportPath);

The export code creates a BagIt-style folder structure with metadata, accounting data, report output, document storage, and manifest hashes.

E-Invoice Workflows

The package includes invoice types and API helpers for importing, storing, booking, searching, exporting, and generating e-invoices.

Supported invoice directions:

  • inbound
  • outbound

Supported formats in the invoice model:

  • xrechnung
  • zugferd
  • facturx
  • peppol
  • ubl

Example import and booking flow:

const invoice = await api.importInvoice('./fixtures/invoice.xml', 'inbound', {
  autoBook: true,
  confidenceThreshold: 80,
});

const hits = await api.searchInvoices({
  invoiceNumber: invoice.invoiceNumber,
});

const exported = await api.exportInvoice(invoice, {
  format: 'xrechnung',
  embedInPdf: true,
});

The API also exposes:

  • bookInvoice()
  • getInvoice()
  • getInvoiceStatistics()
  • createInvoiceComplianceReport()
  • generateInvoice()

Invoice imports are stored by content hash, deduplicated, indexed in an NDJSON registry, and searchable by direction, date range, party, amount range, invoice number, and status metadata.

Public Exports

Top-level exports include:

  • SkrApi
  • Account
  • Transaction
  • JournalEntry
  • ChartOfAccounts
  • Ledger
  • Reports
  • SkrExport
  • LedgerExporter
  • AccountsExporter
  • BalancesExporter
  • PdfReportGenerator
  • SecurityManager
  • SKR03_ACCOUNTS, SKR04_ACCOUNTS
  • SKR03_ACCOUNT_CLASSES, SKR04_ACCOUNT_CLASSES

This makes the package usable as both an application-facing API and a toolkit for custom accounting workflows.

Development

Build:

pnpm build

Test:

pnpm test

Current project checks include:

  • runtime tests for SKR03 and SKR04 flows
  • transaction and journal validation
  • report generation
  • DATEV export
  • published type consumption through test/fixtures/strict-consumer

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
a module implementing german SKR03/SKR04 account structures.
Readme 1.7 MiB
Languages
TypeScript 100%