@fin.cx/compatibility

Exchange formats of third-party accounting systems: the files a German tax advisor's system imports. The package's name carries no vendor's name; the formats and files keep the names the importing system expects (EXTF, DATEV_Belege_<label>.zip, the Datev field names in @fin.cx/skr's rows). Two formats, each on its own entry point:

  • @fin.cx/compatibility/extf: the EXTF Buchungsstapel. The rows and the text come from @fin.cx/skr (journalDraftsToDatevRows, writeExtfBuchungsstapel); this module checks the drafts, writes the file in Windows-1252 and says which characters the encoding could not hold.
  • @fin.cx/compatibility/belegbilder: the document-image archive, a DATEV Document-Package: document.xml after DATEV's Document_v060.xsd plus one <GUID>.<extension> per document (a PDF or a picture), as one ZIP, linked to the bookings through the GUID in their Beleglink (field 20, BEDI "GUID").

It reads no clock, keeps no state and writes nothing anywhere: files go back to the caller as bytes.

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.

Install

pnpm install @fin.cx/compatibility

Usage

import * as skrCore from '@fin.cx/skr/core';
import { extf } from '@fin.cx/compatibility';
// or: import * as extf from '@fin.cx/compatibility/extf';

const file = extf.writeBuchungsstapel({
  drafts, // skrCore.IJournalDraft[] of the period
  policy: skrCore.SKR03_DEFAULT_POLICY,
  header: {
    createdAt: new Date(),
    consultantNumber: 1001,
    clientNumber: 1,
    fiscalYearStart: new Date('2026-01-01T00:00:00Z'),
    dateFrom: new Date('2026-03-01T00:00:00Z'),
    dateTo: new Date('2026-03-31T00:00:00Z'),
    label: 'Buchungsstapel 2026-03',
    festschreibung: false,
  },
  filename: extf.extfFilename('2026-03'), // EXTF_Buchungsstapel_2026-03.csv
});
file.bytes; // Uint8Array in Windows-1252, served as extf.EXTF_CONTENT_TYPE
file.bookings; // what each row says, for keeping and comparing
file.replacedCharacters; // [{ offset, character: 'ő', row: 1, field: 14 }, …]: written as "?"
  • Where a character that became ? stands. Each entry of replacedCharacters names its offset in the text and, for a booking row, the index of that row in rows (from 0) and the number of its DATEV field (from 1; 14 is the Buchungstext). A character of the header line (the label) has neither.

  • The same without a file. extf.replacedCharactersOfRows(rows) names the characters of each row the file could not hold ({ row, field, character }), without a header and without writing anything: for a preview, before the consultant and client numbers of the header are known. It reads each row's line as the writer writes it (@fin.cx/skr datevRowFields), so a character a field is cut off before (the Buchungstext keeps 60 characters) is not named.

  • A draft the account policy refuses throws CompatibilityError with code invalid_bookings and a message naming the draft.

  • A failed conversion throws code export_failed.

  • A document-image archive DATEV would not take (no document, a GUID of another form, a format it does not take) throws code invalid_documents.

  • differingBookingCount(left, right) counts the rows two Stapel of one period do not share. It compares each booking's day, amount, side, accounts, BU key and Belegfeld 1, as a multiset, and ignores wording and document links.

import { belegbilder } from '@fin.cx/compatibility';

const zip = await belegbilder.createBelegbilderZip(
  [
    { guid, description: 'Rechnung 7', bytes: pdfBytes },
    { guid: photoGuid, description: 'Kassenbon', bytes: jpegBytes, format: 'jpeg' },
  ],
  { createdAt: new Date(), generatingSystem: 'My app' },
);
// served as belegbilder.BELEGBILDER_CONTENT_TYPE under belegbilder.belegbilderFilename('2026-03')

The archive is what DATEV's XML-Schnittstelle online takes as a Document-Package (developer.datev.de, "DATEV XML interface online", format specification and XSD files, read 2026-09-28):

  • document.xml in the namespace http://xml.datev.de/bedi/tps/document/v06.0, version="6.0", with the header's date (createdAt's UTC clock time without a zone, as the Buchungsstapel header writes its own; the package reads no clock) and, when given, generatingSystem (up to 40 characters).
  • One <document guid="…"> per entry, its description cut to DATEV's 40 characters (none when empty), and one <extension xsi:type="File" name="<GUID>.<extension>"/>. The GUID is what DATEV Unternehmen online files the document under and what the Beleglink names; it must have the RFC 4122 form 8-4-4-4-12 hexadecimal digits.
  • format is pdf (the default), jpeg, png, gif, tiff or bmp (BELEGBILD_FORMATS), written as .pdf, .jpg, .png, .gif, .tif, .bmp (belegbildFileName). DATEV takes these among its File types (PDF, XML, TIF, TIFF, BMP, CSV, DOC, DOCX, GIF, JPEG, JPG, ODS, ODT, PKCS7, PNG, RTF, TXT, XLS, XLSX); it does not take WebP, AVIF or HEIC, so a picture in one of them must be converted by the caller first.
  • One file per GUID: a document listed twice (the same document linked by two bookings: the same GUID, format and bytes) is in the archive once, under its first description.
  • Characters XML 1.0 does not allow (C0 controls other than tab, line feed and carriage return, a surrogate not in a pair, U+FFFE, U+FFFF) are left out of a description and of generatingSystem before they are cut.
  • Refused with CompatibilityError invalid_documents: no document, more than 4999 (the schema's bound), a GUID of another form, a format outside BELEGBILD_FORMATS, one GUID for two different files (another format, or other bytes).

Windows-1252

encodeWindows1252(text) follows the WHATWG Encoding Standard. Bytes 0x00–0x7F and 0xA0–0xFF are the code points of the same number, and 0x80–0x9F carry the 27 characters € ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š › œ ž Ÿ.

A character outside the encoding becomes one ? and is listed in replaced, so nothing is lost unreported. This includes C1 controls, U+FFFD, and anything outside the Basic Latin and Latin-1 ranges other than those 27.

Compared with iconv-lite 0.7.3 over every code point of the Basic Multilingual Plane, the output is the same except in two cases:

  • U+FFFD: iconv-lite writes byte 0x9D, a C1 control; this module writes ?.
  • A character outside the BMP: iconv-lite writes ??, one per UTF-16 unit; this module writes one ?.

Open

  • The document.xml layout (<archive><content><document guid>…, no namespace) is the one this module took over from its first user. No primary description of the importing system's schema was available when it was written. The layout should be confirmed by one import at a tax advisor before it is relied on.

This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the license.md 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
324 KiB
Languages
TypeScript 100%