# DATEV exporter for Java — full documentation Release 0.2.0. Source: https://github.com/mrtyldr/datev-exporter Java 17 library that generates, validates and streams DATEV Buchungsstapel / EXTF v13 and v12 files. It is a format exporter, not an accounting engine, DATEV API client, SKR03/SKR04 mapper or import certification service. Generated files are import candidates; acceptance must be verified in the licensed, configured target environment. This project is independent and is not affiliated with, endorsed by or supported by DATEV. DATEV is a trademark of DATEV eG. --- Source: https://mrtyldr.github.io/datev-exporter/en/ Java 17 · Apache-2.0 · release 0.2.0 # Own your booking data. Reuse the format layer. Turn already-mapped booking data into deterministic DATEV Buchungsstapel / EXTF files—with fixed v13/v12 schemas, typed metadata, technical validation and forward-only streaming. The contract ## Format exporter, not accounting engine. Your application supplies mapped accounts, amounts, dates and business decisions. The library supplies the EXTF management record, official column order, CSV encoding and deterministic technical checks. Read the exact limitations → Why it exists ## The hard part is not joining values with semicolons. A complete Buchungsstapel file combines a differently shaped management record, an exact versioned heading and tightly formatted booking rows. Small drift in width, order, quoting, line endings or encoding can make an otherwise valid export unusable. ### Keep the schema canonical Use one ordered definition for all 125 v13 fields and the 124-field legacy v12 shape instead of maintaining a private spreadsheet or array. ### Fail before bytes drift Reject structural, Windows-1252 and optional semantic violations before a booking row is committed to the destination. ### Choose the memory model Retain rows when they must be inspected or replayed; stream them forward once when export volume makes retention unnecessary. What ships ## A narrow API around a precise output contract. The common path stays dependency-light. Optional modules are explicit, so a Univocity dependency or semantic validator appears only when you choose it. Recommended default ### Plain fixed-schema exporter Complete v13/v12 EXTF output, retained DatevFile and the forward-only DatevStreamWriter. Choose this unless the receiving contract intentionally differs from the official heading. Start with plain → Custom downstream CSV ### Advanced exporter Rename, reorder or replace headings and select validation mode per file. Custom headings cannot be combined with EXTF metadata because they are no longer the fixed DATEV schema. Compare exporters → Existing pipeline only ### Univocity adapter Route heading-and-row output through an existing Univocity CsvWriter. It cannot emit the differently shaped management record and quotes official text headings differently. Understand the boundary → A deliberate boundary ## Your domain in. An import candidate out. ### Map in your application Resolve chart of accounts, tax treatment, master data and client-specific rules before calling the exporter. ### Assemble and validate Attach typed metadata, write named or typed columns, and select metadata-aware technical validation. ### Verify in the target Move the generated file through your controlled transfer process and verify acceptance in the licensed, configured DATEV environment. Passing library validation—or matching the checked v13 schema and sample contract—does not prove accounting correctness or acceptance by a specific DATEV product, tenant or configuration. Evidence, not adjectives ## Compatibility claims are graded. For v13, an opt-in contract test pins DATEV’s official checker schema and official sample archive, compares the 125 field definitions and validates all 54 sample booking rows. It does not turn a Windows GUI launch into a pass/fail import result. For v12, the library supports the 124-column prefix and matching management record, but currently has no independent official v12 fixture or recorded real-import acceptance evidence. See the compatibility matrix Continue exploring ### Use evidence at the right layer - Complete v13 example - v12/v13 evidence matrix - Validation layers - Reproducible benchmarks - Versioned Java API --- Source: https://mrtyldr.github.io/datev-exporter/en/getting-started.html Getting started · 0.2.0 # From mapped bookings to a complete EXTF file. Start with the fixed-schema plain exporter. Add typed metadata and metadata-aware validation, then choose retained or forward-only output based on your lifecycle. Step 0 ## Requirements and module choice - Java 17 or newer. - datev-exporter-plain for the fixed v13/v12 schemas and output. - datev-exporter-field-validator when deterministic semantic checks should run before each row is accepted. The root datev-exporter artifact is a Bill of Materials (BOM), not a runtime JAR. Import it to keep module versions aligned, then depend on the modules you use. The examples use placeholder adviser/client numbers and account assignments. Replace every business value with data approved for your target ledger; the library does not map SKR03/SKR04 or determine tax treatment. Step 1A ## Run with Gradle Create a Java application, place the Java example below in src/main/java/Example.java, then use this build.gradle: ``` plugins { id 'application' } repositories { mavenCentral() } dependencies { implementation platform('io.github.mrtyldr:datev-exporter:0.2.0') implementation 'io.github.mrtyldr:datev-exporter-plain' implementation 'io.github.mrtyldr:datev-exporter-field-validator' } java { toolchain { languageVersion = JavaLanguageVersion.of(17) } } application { mainClass = 'Example' } ``` Run it with your Gradle wrapper: ``` ./gradlew run ``` The repository also contains a deterministic, CI-executed application: ``` git clone https://github.com/mrtyldr/datev-exporter.git cd datev-exporter ./gradlew :examples:quickstart-gradle:run ``` Step 1B ## Build with Maven The same Example.java works with this minimal pom.xml. The BOM owns the module versions: ``` 4.0.0 example datev-quickstart 1.0.0 17 UTF-8 io.github.mrtyldr datev-exporter 0.2.0 pom import io.github.mrtyldr datev-exporter-plain io.github.mrtyldr datev-exporter-field-validator org.apache.maven.plugins maven-compiler-plugin 3.15.0 org.codehaus.mojo exec-maven-plugin 3.6.3 Example ``` Compile and run with: ``` mvn verify exec:java ``` Step 2 ## Create a complete v13 file This application writes all three required record layers: EXTF management record, the exact 125-column v13 heading, and one booking row. DatevFile retains accepted rows so they can be inspected or written again. ``` import io.github.mrtyldr.datev.core.DatevColumn; import io.github.mrtyldr.datev.core.DatevField; import io.github.mrtyldr.datev.core.DatevMetadata; import io.github.mrtyldr.datev.core.DatevValidationContext; import io.github.mrtyldr.datev.plain.DatevFile; import io.github.mrtyldr.datev.validation.DatevValidator; import java.io.OutputStream; import java.math.BigDecimal; import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDate; import java.time.LocalDateTime; public final class Example { public static void main(String[] args) throws Exception { LocalDate fiscalStart = LocalDate.of(2026, 1, 1); LocalDate periodStart = LocalDate.of(2026, 8, 1); LocalDate periodEnd = LocalDate.of(2026, 8, 31); DatevMetadata metadata = DatevMetadata.bookingBatchV13() .createdAt(LocalDateTime.now()) .origin("RE") .exportedBy("my_application") .advisorNumber(1001) .clientNumber(1) .fiscalYearStart(fiscalStart) .accountLength(4) .period(periodStart, periodEnd) .description("August 2026") .applicationInformation("my-application") .build(); DatevValidationContext context = DatevValidationContext.builder() .accountLength(metadata.accountLength()) .fiscalYearStart(metadata.fiscalYearStart()) .period(metadata.periodStart(), metadata.periodEnd()) .build(); DatevValidator validator = DatevValidator.builder() .context(context) .build(); DatevFile file = DatevFile.builder() .metadata(metadata) .validator(validator) .build(); file.append( DatevColumn.amount(DatevField.AMOUNT, new BigDecimal("1250.00")), DatevColumn.of(DatevField.DEBIT_CREDIT_FLAG, "S"), DatevColumn.of(DatevField.CURRENCY, "EUR"), DatevColumn.account(DatevField.ACCOUNT, 1000), DatevColumn.account(DatevField.CONTRA_ACCOUNT, 8400), DatevColumn.documentDate(LocalDate.of(2026, 8, 10)), DatevColumn.of(DatevField.DOCUMENT_FIELD_1, "RE-42"), DatevColumn.of(DatevField.POSTING_TEXT, "Invoice 42") ); try (OutputStream output = Files.newOutputStream( Path.of("EXTF_Buchungsstapel.csv"))) { file.writeTo(output); } } } ``` Its readable English constants carry the exact German output headings. A misspelled field becomes a compile-time error while the emitted heading remains canonical. Large exports ## Stream through a buffered destination DatevStreamWriter writes the management record and heading when it is built, then prepares and hands off one booking row per append without retaining successful rows. Wrap an otherwise unbuffered file or network destination in BufferedOutputStream to reduce small operating-system writes. ``` import io.github.mrtyldr.datev.plain.DatevStreamWriter; import java.io.BufferedOutputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; try (OutputStream file = Files.newOutputStream( Path.of("EXTF_Buchungsstapel.csv")); OutputStream output = new BufferedOutputStream(file); DatevStreamWriter writer = DatevStreamWriter.builder() .metadata(metadata) .validator(validator) .build(output)) { for (Map row : bookingRows) { writer.append(row); } } ``` - The default JDK buffer size is a sound starting point; tune only from measurements. - Do not add another buffer around ByteArrayOutputStream, StringWriter or an already buffered destination. - The writer flushes but does not close the caller-owned destination. Declaring it last makes try-with-resources close the writer first, then the buffer and file. - A validation failure leaves the current output unchanged. An I/O failure can occur after a destination accepted part of a row; physical rollback is impossible, and the writer becomes terminal. Library-managed working memory follows the row being assembled rather than the total row count. A validator or destination such as ByteArrayOutputStream may still retain data, so choose a genuinely streaming destination when heap use matters. Legacy contract ## Select v12 deliberately Use matching v12 metadata and schema. The builder rejects a management-record version that does not match its 124-column heading. ``` import io.github.mrtyldr.datev.core.DatevSchema; DatevMetadata legacyMetadata = DatevMetadata.bookingBatchV12() .createdAt(LocalDateTime.now()) .origin("RE") .exportedBy("my_application") .advisorNumber(1001) .clientNumber(1) .fiscalYearStart(fiscalStart) .accountLength(4) .period(periodStart, periodEnd) .build(); DatevFile legacy = DatevFile.builder(DatevSchema.LEGACY_V12) .metadata(legacyMetadata) .build(); ``` v12 support is structurally derived from the first 124 v13 fields and covered by library tests. Release 0.2.0 has no independently pinned official v12 fixture and no recorded real-target acceptance result. See the version matrix. Final step ## Verify beyond the library ### Test your mapping Assert the approved accounts, tax choices, dates and source-to-field mapping in your application. ### Inspect the file boundary Keep generated bytes as Windows-1252 with CRLF records; do not let a later UTF-8 text step rewrite them. ### Use DATEV tooling and the real target Run the applicable checker workflow and a controlled import in the licensed, configured target environment. Record the product/version and result for your own compatibility evidence. Review claims and limitations Open API 0.2.0 --- Source: https://mrtyldr.github.io/datev-exporter/en/compatibility.html Evidence statement · release 0.2.0 # Know what is verified—and what remains yours. Compatibility here means a documented file-format contract with graded test evidence. It does not mean DATEV certification, partnership or guaranteed acceptance by a product installation. Version matrix ## v13 has pinned official-contract evidence. v12 is structurally derived. Dimension v13 Legacy v12 Library schema implementedCURRENT_V13, exactly 125 booking columns. implementedLEGACY_V12, exactly 124 booking columns. Difference Includes field 125, Abw. Skontokonto. The exact first 124 fields of the library’s v13 table; field 125 is absent. EXTF management record implementedDatevMetadata.bookingBatchV13(), with schema/version mismatch rejected. implementedDatevMetadata.bookingBatchV12(), with schema/version mismatch rejected. Built-in complete-file output implementedManagement record, official heading and booking rows. implementedManagement record, official heading and booking rows. Pinned official checker-schema comparison contract-testedAll 125 field types, lengths, decimal places, required flags and applicable labels/aliases compared with the schema shipped in DATEV Prüfprogramm DATEV-Format 2.2.3.0. not independentNo separate official v12 checker schema or fixture is pinned in this release. Official sample contract contract-testedExact heading compared and all 54 booking rows in the pinned official v13 sample processed by strict semantic validation. derived onlyLibrary unit tests prove the 124-column prefix, widths and serialization; no independent official v12 sample is tested. Real target import acceptance not evidencedNo recorded licensed real-environment acceptance test in this release. not evidencedNo recorded licensed real-environment acceptance test in this release. Claim level Official-contract-tested v13 file generation; still an import candidate. Derived structural v12 generation; treat as a lower-evidence legacy path. The opt-in test reads pinned official artifacts and validates generated structure. The supplied checker is an interactive Windows GUI with no documented headless pass/fail exit-code contract. The test does not claim a successful GUI report or downstream product import. How the claim is built ## The evidence ladder ### Library invariants Default tests pin the two schema widths, prove v12 is the first 124 fields of v13, enforce version-matched metadata, and exercise Windows-1252/CRLF serialization and the 99,999-row boundary. ### Pinned official v13 artifacts The opt-in datevCheckerTest downloads the official checker and official sample archives, verifies their fixed SHA-256 values, then compares the in-memory schema and sample content. External copyrighted artifacts are not copied into the repository. ### Generated fixture inspection A deterministic complete v13 fixture is decoded strictly as Windows-1252 and inspected for its management record, exact unquoted heading, 125-cell booking row and CRLF record boundaries. ### Target acceptance remains open A licensed operator must inspect the checker report and import into the actual configured target. Client settings, product versions, master data and accounting decisions lie beyond a portable Java test. Review the versioned official-schema compatibility test and its pinned download/checksum setup. Exact file boundary ## What complete built-in output guarantees Property Contract Record sequence EXTF management record, fixed official heading, then zero to 99,999 booking rows. Delimiter Semicolon (;). Line ending CRLF (\r\n) for every emitted record. Byte encoding Strict Windows-1252 on built-in OutputStream paths; unmappable metadata or booking values are rejected rather than replaced. Heading Exact fixed v13 or v12 order, emitted unquoted by the built-in writer. Booking-row quoting DATEV text-column quoting and CSV escaping through the shared DatevCsv codec. Atomic validation Formatting, structural, encoding and configured semantic checks complete before the booking row is handed to the destination. An I/O error cannot be rolled back physically. A caller-supplied character Writer controls its eventual byte encoding. Metadata-free advanced output may deliberately select a different charset for a custom downstream CSV contract; that is not a complete canonical EXTF byte profile. Product scope ## Capabilities and explicit non-goals Area Status Meaning Fixed v13/v12 assembly yes Canonical widths/order, typed EXTF metadata and complete built-in file output. Technical validation yes Structure, format, lengths, dependencies and optional metadata-aware account/date/period checks. Forward-only writing yes DatevStreamWriter does not retain successfully written rows; destination buffering remains caller-controlled. Custom CSV contracts optional Advanced can rename/reorder headings; such output is deliberately not compatible with EXTF metadata. DATEV GUI/server/API integration no No authentication, upload, remote API client, desktop automation or server integration. Accounting or tax logic no No debit/credit decision, tax treatment, period close policy or legal advice. Chart-of-accounts mapping no No SKR03/SKR04 or source-system account mapping. The caller supplies approved accounts. Master-data validation no No check that advisers, clients, accounts, tax keys, cost centers or business partners exist in the target. Import guarantee/certification no No promise that a generated file will be accepted by a particular DATEV product or configuration. Unlimited rows no One file is limited to 99,999 booking rows. Split larger exports at an application-approved boundary. Thread-safe mutable exporters no Exporter instances are mutable and not thread-safe; confine each file/writer to one thread. Primary references ## Sources used for the format contract - DATEV Developer Portal: technical structure / getting started. - DATEV Developer Portal: booking-batch format description. - DATEV Developer Portal: management-record/header description. - DATEV Developer Portal: character-set description. - DATEV Developer Portal: Prüfprogramm DATEV-Format and sample data. Those links describe DATEV’s format. They do not imply affiliation. This site paraphrases only the portions required to state the library contract and does not redistribute DATEV documentation, checker binaries or sample files. Build an export Choose an API --- Source: https://mrtyldr.github.io/datev-exporter/en/reference.html Decision guide · API 0.2.0 # Use the smallest module that owns your contract. The fixed official format, custom CSV needs and existing Univocity pipelines are separate use cases. Choosing explicitly keeps dependencies and output claims honest. ## Deep-dive references Four pages cover the parts of the DATEV contract that generate the most questions. Each is generated from the library, so the tables match what the exporters actually write. ### Field reference All 125 Buchungsstapel columns in output order, with official headings, checker types, lengths and version-12 availability. ### Validation errors The six stable error codes, what raises each one, the paired fields and the three validation depths. ### EXTF header The 31-field management record: fixed identifiers, date and timestamp formats, quoting rules and the coded fields. ### Encoding and umlauts Windows-1252, CRLF, semicolons and quoting — which characters survive and how exports get corrupted afterwards. Artifacts ## Module map Artifact Runtime dependencies Responsibility datev-exporter None (platform POM) BOM that aligns every module on one version. It contains no runtime API. datev-exporter-core None Canonical schemas, field definitions, metadata, headers, CSV codec and validation model. datev-exporter-plain core Fixed v13/v12 retained file and forward-only writer. Recommended output module. datev-exporter-field-validator core Optional semantic validator callback for the plain exporter. datev-exporter-advanced core Retained files with custom/renamed/reordered headings and built-in validation modes. datev-exporter-advanced-univocity advanced + Univocity Adapter for applications already committed to a Univocity CsvWriter pipeline. datev-exporter-verification and datev-exporter-benchmarks are internal build modules; they are not in the BOM or Maven Central publication. Decision table ## Plain, advanced or Univocity? Need Plain Advanced Univocity adapter Complete fixed v13/v12 EXTF recommended yes with exact official header, strict mode and compatible metadata no management record Forward-only rows yes DatevStreamWriter no rows retained Writes advanced retained rows through a third-party writer Rename/reorder headings no yes yes via advanced file Custom charset no byte path is Windows-1252 Only for metadata-free custom downstream contracts Uses advanced file charset; strict encoder wrapper provided Third-party runtime dependency None beyond core None beyond core Univocity Official heading byte shape Canonical unquoted heading Canonical on built-in writer with official header Text headings are quoted differently Use plain unless you can state a concrete custom-heading requirement. Use retained DatevFile for inspection/replay and DatevStreamWriter for one-pass production. Layered checks ## Validation catches technical defects, not business truth Always on in built-in output ### Structural and encoding safety Column width/order, known headings, CSV syntax/control characters, row limit and strict Windows-1252 encodability on byte output. Optional plain dependency ### DatevValidator A format-version/immutable-row callback. Build it with account length, fiscal-year start and period to validate context-dependent account and date rules. Advanced configuration ### DatevValidationMode STRICT adds mandatory fields and dependencies; FIELD_LEVEL validates supplied known fields; NONE keeps structural CSV/header checks. Always application-owned ### Accounting and master data Account selection, tax treatment, validity in the target ledger and client-specific requirements must be validated outside this library. Adding datev-exporter-field-validator alone changes nothing. Pass the validator to the plain builder/factory. Official advanced schemas default to STRICT; custom headers default to NONE because their domain semantics are unknown. Interop, not replacement ## The Univocity adapter solves one narrow problem Choose datev-exporter-advanced-univocity only when the surrounding application already centralizes CSV emission in Univocity and heading-plus-booking-row output is the intended boundary. ``` CsvWriter writer = DatevUnivocityWriters.newCsvWriter(file, outputStream); DatevUnivocityWriters.writeTo(file, writer); ``` - A CsvWriter emits uniformly shaped records and therefore cannot emit the differently shaped 31-field EXTF management record. - writeTo rejects a metadata-backed file. Use the advanced built-in DatevFile.writeTo(OutputStream) for a complete file. - writeDataTo explicitly writes only heading and rows, even if metadata exists. - With unmodified official v12/v13 settings, booking rows match built-in output, but text headings are quoted differently. Whole-file byte parity is not claimed. - The supplied newCsvWriter reports unmappable characters and leaves the caller stream open. Avoid raw Univocity constructors that may replace unsupported characters with ?. Ownership rules ## Output stays caller-owned - Built-in writers flush but do not close a caller-supplied OutputStream or Writer. - Use the OutputStream overload for canonical Windows-1252 bytes. A character Writer is only a character contract; its final encoder is yours. - Wrap otherwise unbuffered file/network output once. The library intentionally does not choose a persistent buffer size. - Plain and advanced DatevFile retain accepted rows. DatevStreamWriter hands each accepted row forward and discards its assembly storage. - All mutable exporter instances are single-threaded by design. See the buffered streaming example and benchmark report before choosing based on volume. Symbol-level reference ## Use versioned Javadoc for exact signatures This guide explains contracts and decisions. The generated API site is the source for public classes, methods and their lifecycle details: - Javadoc index for release 0.2.0 - Versioned runnable quickstart source - Published BOM on Maven Central The project follows Semantic Versioning, but public APIs may change between minor versions until 1.0.0. Pin the BOM version and read release notes when upgrading. --- Source: https://mrtyldr.github.io/datev-exporter/en/fields.html Field reference · schema v13 and v12 · 0.2.0 # All 125 Buchungsstapel columns, in official output order. Field numbers, exact German headings, checker types and lengths for format version 13, with the four differences that matter when you target version 12. ## What this table is A DATEV Buchungsstapel booking row has a fixed number of columns in a fixed order. Format version 13 defines 125 columns; version 12 defines the first 124 and omits Abw. Skontokonto. Column order carries meaning: a row is positional, so field 7 is Konto whether or not you supplied field 6. The table below is generated from DatevFieldSpecs, the single canonical copy of the schema that every module in this library derives from. It is not a hand-maintained transcription, so it cannot drift away from what the exporters actually write. Knowing that field 9 is BU-Schlüssel does not tell you which posting key your case needs. Account mapping, tax treatment and posting logic stay with your application and your tax adviser. ## Shape of the schema Type Checker name Columns Meaning TEXT Text 84 Quoted textual value; the maximum length counts characters. NUMBER Zahl 27 Unquoted numeric value with an optional comma decimal separator. DATE Datum 7 DATEV-formatted date. ACCOUNT Konto 4 Numeric account identifier, narrowed further by the account length. AMOUNT Betrag 3 Positive monetary value; the sign lives in Soll/Haben-Kennzeichen. ## The five required fields Strict validation reports an empty value in these columns as REQUIRED_FIELD. Every other column may be left empty. # Official heading DatevField constant Type 1 Umsatz (ohne Soll/Haben-Kz) AMOUNT Betrag 2 Soll/Haben-Kennzeichen DEBIT_CREDIT_FLAG Text 7 Konto ACCOUNT Konto 8 Gegenkonto (ohne BU-Schlüssel) CONTRA_ACCOUNT Konto 10 Belegdatum DOCUMENT_DATE Datum ## Repeating groups and one spelling trap Two column families repeat as type/content pairs. Supplying one half without the other is reported as DEPENDENT_FIELD_MISSING in strict mode. - Beleginfo - Art 1 … Beleginfo - Inhalt 8 — eight pairs, fields 21–36. - Zusatzinformation - Art 1 … Zusatzinformation- Inhalt 20 — twenty pairs, fields 48–87. DATEV spells the pair inconsistently: Zusatzinformation - Art 1 has spaces around the dash, but Zusatzinformation- Inhalt 1 has none. Both spellings are reproduced exactly. Use the DatevField constants and a typo becomes a compile error instead of a rejected file. ## Full field table # Official heading DatevField constant Type Max. length Decimals Required In v12 1 Umsatz (ohne Soll/Haben-Kz) AMOUNT Betrag 10 2 yes yes 2 Soll/Haben-Kennzeichen DEBIT_CREDIT_FLAG Text 1 — yes yes 3 WKZ Umsatz CURRENCY Text 3 — no yes 4 Kurs EXCHANGE_RATE Zahl 5 6 no yes 5 Basis-Umsatz BASE_AMOUNT Betrag 10 2 no yes 6 WKZ Basis-Umsatz BASE_CURRENCY Text 3 — no yes 7 Konto ACCOUNT Konto 9 — yes yes 8 Gegenkonto (ohne BU-Schlüssel) CONTRA_ACCOUNT Konto 9 — yes yes 9 BU-Schlüssel POSTING_KEY Text 4 — no yes 10 Belegdatum DOCUMENT_DATE Datum 8 — yes yes 11 Belegfeld 1 DOCUMENT_FIELD_1 Text 36 — no yes 12 Belegfeld 2 DOCUMENT_FIELD_2 Text 12 — no yes 13 Skonto CASH_DISCOUNT Betrag 8 2 no yes 14 Buchungstext POSTING_TEXT Text 60 — no yes 15 Postensperre ITEM_BLOCK Zahl 1 — no yes 16 Diverse Adressnummer MISC_ADDRESS_NUMBER Text 9 — no yes 17 Geschäftspartnerbank PARTNER_BANK Zahl 3 — no yes 18 Sachverhalt MATTER_CODE Zahl 2 — no yes 19 Zinssperre INTEREST_BLOCK Zahl 1 — no yes 20 Beleglink DOCUMENT_LINK Text 210 — no yes 21 Beleginfo - Art 1 DOCUMENT_INFO_TYPE_1 Text 20 — no yes 22 Beleginfo - Inhalt 1 DOCUMENT_INFO_CONTENT_1 Text 210 — no yes 23 Beleginfo - Art 2 DOCUMENT_INFO_TYPE_2 Text 20 — no yes 24 Beleginfo - Inhalt 2 DOCUMENT_INFO_CONTENT_2 Text 210 — no yes 25 Beleginfo - Art 3 DOCUMENT_INFO_TYPE_3 Text 20 — no yes 26 Beleginfo - Inhalt 3 DOCUMENT_INFO_CONTENT_3 Text 210 — no yes 27 Beleginfo - Art 4 DOCUMENT_INFO_TYPE_4 Text 20 — no yes 28 Beleginfo - Inhalt 4 DOCUMENT_INFO_CONTENT_4 Text 210 — no yes 29 Beleginfo - Art 5 DOCUMENT_INFO_TYPE_5 Text 20 — no yes 30 Beleginfo - Inhalt 5 DOCUMENT_INFO_CONTENT_5 Text 210 — no yes 31 Beleginfo - Art 6 DOCUMENT_INFO_TYPE_6 Text 20 — no yes 32 Beleginfo - Inhalt 6 DOCUMENT_INFO_CONTENT_6 Text 210 — no yes 33 Beleginfo - Art 7 DOCUMENT_INFO_TYPE_7 Text 20 — no yes 34 Beleginfo - Inhalt 7 DOCUMENT_INFO_CONTENT_7 Text 210 — no yes 35 Beleginfo - Art 8 DOCUMENT_INFO_TYPE_8 Text 20 — no yes 36 Beleginfo - Inhalt 8 DOCUMENT_INFO_CONTENT_8 Text 210 — no yes 37 KOST1 - Kostenstelle COST_CENTER_1 Text 36 — no yes 38 KOST2 - Kostenstelle COST_CENTER_2 Text 36 — no yes 39 Kost-Menge COST_QUANTITY Zahl 12 4 no yes 40 EU-Land u. UStID (Bestimmung) EU_COUNTRY_VAT_ID_DESTINATION Text 15 — no yes 41 EU-Steuersatz (Bestimmung) EU_TAX_RATE_DESTINATION Zahl 2 2 no yes 42 Abw. Versteuerungsart DIFFERING_TAXATION_TYPE Text 1 — no yes 43 Sachverhalt L+L MATTER_CODE_LL Zahl 3 — no yes 44 Funktionsergänzung L+L FUNCTION_SUPPLEMENT_LL Zahl 3 — no yes 45 BU 49 Hauptfunktionstyp BU49_MAIN_FUNCTION_TYPE Zahl 1 — no yes 46 BU 49 Hauptfunktionsnummer BU49_MAIN_FUNCTION_NUMBER Zahl 2 — no yes 47 BU 49 Funktionsergänzung BU49_FUNCTION_SUPPLEMENT Zahl 3 — no yes 48 Zusatzinformation - Art 1 ADDITIONAL_INFO_TYPE_1 Text 20 — no yes 49 Zusatzinformation- Inhalt 1 ADDITIONAL_INFO_CONTENT_1 Text 210 — no yes 50 Zusatzinformation - Art 2 ADDITIONAL_INFO_TYPE_2 Text 20 — no yes 51 Zusatzinformation- Inhalt 2 ADDITIONAL_INFO_CONTENT_2 Text 210 — no yes 52 Zusatzinformation - Art 3 ADDITIONAL_INFO_TYPE_3 Text 20 — no yes 53 Zusatzinformation- Inhalt 3 ADDITIONAL_INFO_CONTENT_3 Text 210 — no yes 54 Zusatzinformation - Art 4 ADDITIONAL_INFO_TYPE_4 Text 20 — no yes 55 Zusatzinformation- Inhalt 4 ADDITIONAL_INFO_CONTENT_4 Text 210 — no yes 56 Zusatzinformation - Art 5 ADDITIONAL_INFO_TYPE_5 Text 20 — no yes 57 Zusatzinformation- Inhalt 5 ADDITIONAL_INFO_CONTENT_5 Text 210 — no yes 58 Zusatzinformation - Art 6 ADDITIONAL_INFO_TYPE_6 Text 20 — no yes 59 Zusatzinformation- Inhalt 6 ADDITIONAL_INFO_CONTENT_6 Text 210 — no yes 60 Zusatzinformation - Art 7 ADDITIONAL_INFO_TYPE_7 Text 20 — no yes 61 Zusatzinformation- Inhalt 7 ADDITIONAL_INFO_CONTENT_7 Text 210 — no yes 62 Zusatzinformation - Art 8 ADDITIONAL_INFO_TYPE_8 Text 20 — no yes 63 Zusatzinformation- Inhalt 8 ADDITIONAL_INFO_CONTENT_8 Text 210 — no yes 64 Zusatzinformation - Art 9 ADDITIONAL_INFO_TYPE_9 Text 20 — no yes 65 Zusatzinformation- Inhalt 9 ADDITIONAL_INFO_CONTENT_9 Text 210 — no yes 66 Zusatzinformation - Art 10 ADDITIONAL_INFO_TYPE_10 Text 20 — no yes 67 Zusatzinformation- Inhalt 10 ADDITIONAL_INFO_CONTENT_10 Text 210 — no yes 68 Zusatzinformation - Art 11 ADDITIONAL_INFO_TYPE_11 Text 20 — no yes 69 Zusatzinformation- Inhalt 11 ADDITIONAL_INFO_CONTENT_11 Text 210 — no yes 70 Zusatzinformation - Art 12 ADDITIONAL_INFO_TYPE_12 Text 20 — no yes 71 Zusatzinformation- Inhalt 12 ADDITIONAL_INFO_CONTENT_12 Text 210 — no yes 72 Zusatzinformation - Art 13 ADDITIONAL_INFO_TYPE_13 Text 20 — no yes 73 Zusatzinformation- Inhalt 13 ADDITIONAL_INFO_CONTENT_13 Text 210 — no yes 74 Zusatzinformation - Art 14 ADDITIONAL_INFO_TYPE_14 Text 20 — no yes 75 Zusatzinformation- Inhalt 14 ADDITIONAL_INFO_CONTENT_14 Text 210 — no yes 76 Zusatzinformation - Art 15 ADDITIONAL_INFO_TYPE_15 Text 20 — no yes 77 Zusatzinformation- Inhalt 15 ADDITIONAL_INFO_CONTENT_15 Text 210 — no yes 78 Zusatzinformation - Art 16 ADDITIONAL_INFO_TYPE_16 Text 20 — no yes 79 Zusatzinformation- Inhalt 16 ADDITIONAL_INFO_CONTENT_16 Text 210 — no yes 80 Zusatzinformation - Art 17 ADDITIONAL_INFO_TYPE_17 Text 20 — no yes 81 Zusatzinformation- Inhalt 17 ADDITIONAL_INFO_CONTENT_17 Text 210 — no yes 82 Zusatzinformation - Art 18 ADDITIONAL_INFO_TYPE_18 Text 20 — no yes 83 Zusatzinformation- Inhalt 18 ADDITIONAL_INFO_CONTENT_18 Text 210 — no yes 84 Zusatzinformation - Art 19 ADDITIONAL_INFO_TYPE_19 Text 20 — no yes 85 Zusatzinformation- Inhalt 19 ADDITIONAL_INFO_CONTENT_19 Text 210 — no yes 86 Zusatzinformation - Art 20 ADDITIONAL_INFO_TYPE_20 Text 20 — no yes 87 Zusatzinformation- Inhalt 20 ADDITIONAL_INFO_CONTENT_20 Text 210 — no yes 88 Stück PIECES Zahl 8 — no yes 89 Gewicht WEIGHT Zahl 8 2 no yes 90 Zahlweise PAYMENT_METHOD Zahl 2 — no yes 91 Forderungsart RECEIVABLE_TYPE Text 10 — no yes 92 Veranlagungsjahr ASSESSMENT_YEAR Zahl 4 — no yes 93 Zugeordnete Fälligkeit ASSIGNED_DUE_DATE Datum 8 — no yes 94 Skontotyp CASH_DISCOUNT_TYPE Zahl 1 — no yes 95 Auftragsnummer ORDER_NUMBER Text 30 — no yes 96 Buchungstyp POSTING_TYPE Text 2 — no yes 97 USt-Schlüssel (Anzahlungen) VAT_KEY_PREPAYMENT Zahl 2 — no yes 98 EU-Land (Anzahlungen) EU_COUNTRY_PREPAYMENT Text 2 — no yes 99 Sachverhalt L+L (Anzahlungen) MATTER_CODE_LL_PREPAYMENT Zahl 3 — no yes 100 EU-Steuersatz (Anzahlungen) EU_TAX_RATE_PREPAYMENT Zahl 2 2 no yes 101 Erlöskonto (Anzahlungen) REVENUE_ACCOUNT_PREPAYMENT Konto 9 — no yes 102 Herkunft-Kz ORIGIN_CODE Text 2 — no yes 103 Buchungs GUID POSTING_GUID Text 36 — no yes 104 KOST-Datum COST_DATE Datum 8 — no yes 105 SEPA-Mandatsreferenz SEPA_MANDATE_REFERENCE Text 35 — no yes 106 Skontosperre CASH_DISCOUNT_BLOCK Zahl 1 — no yes 107 Gesellschaftername SHAREHOLDER_NAME Text 76 — no yes 108 Beteiligtennummer PARTICIPANT_NUMBER Zahl 4 — no yes 109 Identifikationsnummer IDENTIFICATION_NUMBER Text 11 — no yes 110 Zeichnernummer SUBSCRIBER_NUMBER Text 20 — no yes 111 Postensperre bis ITEM_BLOCK_UNTIL Datum 8 — no yes 112 Bezeichnung SoBil-Sachverhalt SOBIL_MATTER_LABEL Text 30 — no yes 113 Kennzeichen SoBil-Buchung SOBIL_POSTING_FLAG Zahl 2 — no yes 114 Festschreibung FINAL_POSTING_FLAG Zahl 1 — no yes 115 Leistungsdatum SERVICE_DATE Datum 8 — no yes 116 Datum Zuord. Steuerperiode TAX_PERIOD_DATE Datum 8 — no yes 117 Fälligkeit DUE_DATE Datum 8 — no yes 118 Generalumkehr (GU) GENERAL_REVERSAL Text 1 — no yes 119 Steuersatz TAX_RATE Zahl 2 2 no yes 120 Land COUNTRY Text 2 — no yes 121 Abrechnungsreferenz SETTLEMENT_REFERENCE Text 50 — no yes 122 BVV-Position BVV_POSITION Zahl 1 — no yes 123 EU-Land u. UStID (Ursprung) EU_COUNTRY_VAT_ID_ORIGIN Text 15 — no yes 124 EU-Steuersatz (Ursprung) EU_TAX_RATE_ORIGIN Zahl 2 2 no yes 125 Abw. Skontokonto DIFFERING_CASH_DISCOUNT_ACCOUNT Konto 8 — no no ## Addressing a field from Java Every DatevColumn factory accepts either the enum constant or the raw heading string, and both produce identical output. The constant is checked at compile time. ``` import io.github.mrtyldr.datev.core.DatevColumn; import io.github.mrtyldr.datev.core.DatevField; import io.github.mrtyldr.datev.core.DatevSchema; // Field 7, "Konto" — compile-checked. DatevColumn account = DatevColumn.account(DatevField.ACCOUNT, 1000); // Identical output, but a typo would only surface at runtime. DatevColumn same = DatevColumn.account("Konto", 1000); int number = DatevField.ACCOUNT.fieldNumber(); // 7 String heading = DatevField.ACCOUNT.heading(); // "Konto" boolean inLegacy = DatevField.DIFFERING_CASH_DISCOUNT_ACCOUNT .isPresentIn(DatevSchema.LEGACY_V12); // false ``` DatevField constants are declared in output order, so ordinal() + 1 is the DATEV field number, and isPresentIn(DatevSchema.LEGACY_V12) answers the version-12 question for any field. ## Related references - Validation errors — the six error codes explained - EXTF header — the 31-field management record - Encoding — Windows-1252, CRLF, quoting and umlauts --- Source: https://mrtyldr.github.io/datev-exporter/en/validation-errors.html Validation reference · 0.2.0 # Six error codes, and what each one is telling you. Validation failures carry a stable machine-readable code, the DATEV field number and the official column name. Here is what triggers each code and what to change. ## Three validation depths Depth is a deliberate choice per file. A stricter mode never changes the bytes that are written; it only changes what is rejected before writing. Mode What it checks Possible codes NONE Nothing semantic. The exporter's structural checks — row width, control characters, encodability — still apply. — FIELD_LEVEL Each supplied non-empty cell against its official field definition. INVALID_FORMAT, VALUE_OUT_OF_RANGE, TEXT_TOO_LONG, UNMAPPABLE_CHARACTER STRICT Everything above, plus required fields and cross-field dependencies. all six ## The six codes Code Raised when Usual fix REQUIRED_FIELD A field the official checker marks as necessary is empty, in strict mode on an official schema. Populate the field. Only five columns are affected: Umsatz, Soll/Haben-Kennzeichen, Konto, Gegenkonto and Belegdatum. INVALID_FORMAT A value does not use DATEV's required representation — a malformed number, a date that is not a real calendar date, a flag outside its allowed set, or a control or line-separator character inside a cell. Format the value the way DATEV expects rather than the way your locale prints it. Strip newlines and tabs from free text. VALUE_OUT_OF_RANGE The value is syntactically valid but exceeds its range — too many integral digits, too many decimals, or an account number wider than the configured account length. Check the field's maximum length and decimal places in the field reference, and check that the account length in your metadata matches the client. TEXT_TOO_LONG A text value exceeds the field's maximum character count. Truncate deliberately in your own mapping. Silent truncation is not performed for you, because losing part of a Buchungstext is a business decision. UNMAPPABLE_CHARACTER The value contains a code point that Windows-1252 cannot represent. Transliterate or replace the character before export. See the encoding reference for which characters survive. DEPENDENT_FIELD_MISSING One half of a paired field group was supplied without the other, in strict mode. Supply both halves, or neither. ## The paired fields Strict mode enforces these pairs. Supplying either side alone produces DEPENDENT_FIELD_MISSING on the missing side. - Basis-Umsatz ↔ WKZ Basis-Umsatz (fields 5 and 6). - Beleginfo - Art n ↔ Beleginfo - Inhalt n for n = 1…8. - Zusatzinformation - Art n ↔ Zusatzinformation- Inhalt n for n = 1…20. ## Context sharpens the checks Some rules cannot be evaluated from the schema alone. A validation context carries the facts that make them decidable; without it, those specific checks are simply skipped rather than guessed. - Account length narrows how wide Konto and Gegenkonto may be. - Fiscal year start and posting period let the four-digit Belegdatum be resolved against real calendar dates. These checks confirm the file matches the technical schema. They say nothing about whether the bookings are correct, or whether a specific DATEV product and configuration will accept the file. ## Reading errors in code A rejected row raises DatevValidationException, which carries the full list of errors. Each error keeps its code, field number and official column name, so failures can be logged or mapped without parsing message text. ``` import io.github.mrtyldr.datev.core.DatevValidationError; import io.github.mrtyldr.datev.core.DatevValidationException; try { file.append(columns); } catch (DatevValidationException failure) { for (DatevValidationError error : failure.errors()) { log.warn("field {} ({}): {} — {}", error.fieldNumber(), error.canonicalKey(), error.code(), error.message()); } } ``` ## Related references - Field reference — all 125 Buchungsstapel columns - EXTF header — the 31-field management record - Encoding — Windows-1252, CRLF, quoting and umlauts --- Source: https://mrtyldr.github.io/datev-exporter/en/extf-header.html Format reference · EXTF · 0.2.0 # The line before the headings. A Buchungsstapel file opens with a management record whose shape has nothing to do with the booking rows beneath it. Getting it wrong is the most common reason an otherwise correct export is rejected. ## Three records, two shapes A complete file is three layers: the EXTF management record, the exact versioned column heading, and the booking rows. Only the last two share a shape. The management record always has 31 fields, whichever format version you target. The first five fields are fixed identifiers. They are what a reader uses to recognise the file at all: Field Value Meaning 1 EXTF Identifies an EXTF export file. 2 700 Header version. 3 21 Format category for Buchungsstapel. 4 Buchungsstapel Format name. 5 13 / 12 Data format version: 13 or 12. ## A real management record These lines are produced by the library itself, from metadata with a fixed timestamp so the output is reproducible. Format version 13 ``` "EXTF";700;21;"Buchungsstapel";13;20260812093000000;;"RE";"my_application";"";1001;1;20260101;4;20260801;20260831;"August 2026";"";1;0;1;"EUR";;"";;;"";;;"";"my-application" ``` Format version 12 — identical except field 5 ``` "EXTF";700;21;"Buchungsstapel";12;20260812093000000;;"RE";"my_application";"";1001;1;20260101;4;20260801;20260831;"August 2026";"";1;0;1;"EUR";;"";;;"";;;"";"my-application" ``` Reserved fields are written as empty cells, and text fields stay quoted even when empty. Dropping a field instead of emptying it shifts every later field by one position. ## Formats that trip people up - Created-at timestamp uses yyyyMMddHHmmssSSS — 17 digits including milliseconds, unquoted. - Fiscal year start and posting period use yyyyMMdd, unquoted. - Text fields are quoted, including designated empty ones. A quote inside application information is escaped by doubling it. - Numeric fields are unquoted, including the adviser and client numbers. ## The coded fields Four fields take values from a closed set rather than free input. Constant DATEV value FINANCIAL_ACCOUNTING 1 ANNUAL_FINANCIAL_STATEMENTS 2 Constant DATEV value INDEPENDENT 0 TAX_LAW 30 CALCULATION 40 COMMERCIAL_LAW 50 IFRS 64 ## Building it from Java Metadata is built once per file and validated on construction, so an impossible combination fails before any row is written. The builder chooses the format version, and the exporter refuses a management record whose version does not match its heading. ``` import io.github.mrtyldr.datev.core.DatevMetadata; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Currency; DatevMetadata metadata = DatevMetadata.bookingBatchV13() .createdAt(LocalDateTime.now()) .origin("RE") .exportedBy("my_application") .advisorNumber(1001) .clientNumber(1) .fiscalYearStart(LocalDate.of(2026, 1, 1)) .accountLength(4) .period(LocalDate.of(2026, 8, 1), LocalDate.of(2026, 8, 31)) .description("August 2026") .currency(Currency.getInstance("EUR")) .applicationInformation("my-application") .build(); String record = metadata.toCsvLine(); // the 31-field management record ``` A v12 management record cannot be combined with the 125-column v13 heading. The builder rejects the mismatch rather than writing a file that no importer can interpret. ## Related references - Field reference — all 125 Buchungsstapel columns - Validation errors — the six error codes explained - Encoding — Windows-1252, CRLF, quoting and umlauts --- Source: https://mrtyldr.github.io/datev-exporter/en/encoding.html Codec reference · 0.2.0 # Windows-1252 is not a detail you can postpone. A Buchungsstapel file is bytes, not text. Umlauts survive; a Turkish dotless i does not. One careless UTF-8 step after export undoes everything. ## The byte contract Property Value Character encoding windows-1252 Record separator \r\n (CRLF) Field delimiter ; Quote character " Escaped quote "" ## When a value is quoted Two independent rules decide quoting, and both are applied. - Official text columns are always quoted, even when empty. 84 of the 125 version 13 columns are text columns. - Any other value is quoted only when it must be — that is, when it contains a semicolon or a quote character. - An embedded quote is doubled, never backslash-escaped. ## Which characters survive Windows-1252 is a single-byte encoding, so it can represent at most 256 code points. German text is comfortably inside that range; a lot of other text is not. Encodable — written unchanged ``` ä ö ü ß Ä Ö Ü € § µ ° á é í ó ú ñ ç å ø æ š ž ``` Not encodable — rejected as UNMAPPABLE_CHARACTER ``` ı ğ ş ł ą č ř ő ū 日 😀 ``` A value containing an unencodable character is refused rather than silently replaced with a question mark, because a corrupted Buchungstext is harder to notice later than a failed export now. Transliterate deliberately in your own mapping if your source data can contain such characters. ## Control characters Control, line-separator and paragraph-separator characters are rejected anywhere in a cell, reported as INVALID_FORMAT. A newline inside a Buchungstext would otherwise split one booking row into two unusable records. ## How exports get corrupted after they are written The library controls the bytes it writes. Everything downstream of that is yours to protect. - A text editor that opens the file and saves it back as UTF-8 — every umlaut becomes two bytes and the file is no longer valid. - A transfer or archiving step configured for text mode that rewrites CRLF to LF. - Reading the file back with the platform default charset instead of Windows-1252. - A templating or logging layer that normalises the output to Unicode NFD, splitting umlauts into base letter plus combining mark. A file that looks right in an editor may already be broken. Check the byte length and the encoding, and keep the generated file untouched between export and import. ## Related references - Field reference — all 125 Buchungsstapel columns - Validation errors — the six error codes explained - EXTF header — the 31-field management record --- Source: https://mrtyldr.github.io/datev-exporter/en/benchmarks.html Measured locally · v0.2.0 · Java 17 # A complete 99,999-row EXTF file. A reproducible JMH run compares three strictly validated exporter paths, records cumulative allocation, and preserves the uncertainty instead of turning one laptop run into a universal claim. Observed data ## This run did not resolve a timing difference between the two plain paths. One operation creates a complete EXTF v13 file containing the management record, the 125-column heading and 99,999 booking rows. Lower is better in the time and allocation columns. The ± values are JMH 99.9% confidence-interval half-widths across ten measurement samples, under JMH’s normality assumption. Exporter path Time / file Derived time / row Derived rows / second Allocated bytes / file Derived allocated bytes / row Plain forward-onlyDatevStreamWriter 670.193 ± 34.910 ms 6.702 µs 149,209 980,975,803 ± 10,710,036 B 9,809.86 B Plain retainedDatevFile 670.000 ± 15.068 ms 6.700 µs 149,252 1,011,067,254 ± 10,710,046 B 10,110.77 B Advanced retainedadvanced.DatevFile 742.976 ± 51.878 ms 7.430 µs 134,593 966,056,224 ± 10,710,041 B 9,660.66 B The two plain means differ by only 0.03%, far below their reported uncertainty, so this run supports no timing ranking between them. Forward-only allocated about 3.0% fewer cumulative bytes than plain retained in this workload. Advanced retained had the lowest allocation score and the highest observed mean time, but this single run is not evidence of a universal ranking. Product decision ## Choose the lifecycle first, not the smallest benchmark number. ### Use forward-only for one-pass, high-volume export DatevStreamWriter validates, serializes and hands off each completed row without retaining it in a library-owned collection. Its live retained row state is therefore proportional to one row rather than the total row count. That design property—not a speed claim—is the strongest reason to prefer it for a 99,999-row one-pass export. ### Use retained output when rows must remain available The retained APIs support inspection, iteration and delayed writing. That lifecycle is useful when the caller must review or replay rows; this run did not resolve a timing difference versus forward-only. It necessarily keeps aligned rows alive until the file object can be released. ### Allocation is not retained heap JMH’s gc.alloc.rate.norm reports all bytes allocated during an operation, including short-lived validation and serialization objects. It does not report peak live heap. The advanced retained result demonstrates the distinction: it allocated the fewest cumulative bytes here even though it still retains the aligned rows. A separate live-set or heap-occupancy study would be required to quantify peak memory. What was measured ## A sparse but non-trivial fixed row. - Format: complete DATEV Buchungsstapel / EXTF v13, Windows-1252 and CRLF. - Size: 125 columns; 99,999 booking rows, the supported per-file maximum. - Density: six non-empty fields per row: amount, debit/credit marker, account, contra account, document date and booking text. - Escaping: booking text is Müller; Beleg "42" €, exercising Windows-1252, delimiter and quote escaping. - Validation: metadata-aware strict semantic validation on every append. Plain paths use DatevValidator; advanced uses built-in STRICT mode. - Output: all paths are checked byte-for-byte and emit exactly 33,602,459 bytes: 2,795 fixed management/heading bytes plus 336 bytes per booking row. The same immutable six-entry map is submitted for each row. Row alignment, validation and serialization remain inside the timed operation; upstream record creation and accounting-data mapping do not. Measurement contract ## Environment and JMH configuration. Library under test v0.2.0 · commit 06fa7ae Measured 12 August 2026 Machine Apple M1 Pro, 10 logical CPUs, 32 GiB RAM, aarch64 Operating system macOS 26.5.2 (build 25F84) JVM Eclipse Temurin 17.0.19+10; -Xms1g -Xmx1g -XX:+AlwaysPreTouch Harness JMH 1.37, average-time mode, one thread, two forks Iterations Per fork: three × 1 s warmup, then five × 1 s measurement Profiler JMH gc; normalized allocation in bytes per operation Destination A pre-sized, reusable ByteArrayOutputStream, reset outside each measured invocation I/O Byte serialization and destination writes included; filesystem, network and destination allocation excluded Each measured call constructs its exporter and performs all 99,999 appends plus final output. Immutable fixture construction, output capacity allocation and initial byte-equivalence checks happen at trial setup. The final invocation is checked again at trial teardown. Run it yourself ## The maximum-row task pins the important inputs. ``` git clone https://github.com/mrtyldr/datev-exporter.git cd datev-exporter git checkout cb91f5dd8b174bb4e10d98a8a7cc93f007483042 ./gradlew --no-daemon :datev-exporter-benchmarks:jmhMaxRows ``` The task selects the Adoptium Java 17 toolchain and expands to the following JMH settings: ``` -p rowCount=99999 -wi 3 -w 1s -i 5 -r 1s -f 2 -t 1 -jvmArgs "-Xms1g -Xmx1g -XX:+AlwaysPreTouch" -prof gc -rf json ``` Generated JSON is written to datev-exporter-benchmarks/build/results/jmh/max-rows-gc.json. The published summary retains every raw timing and allocation sample: - Machine-readable result and raw samples - Benchmark source - Versioned Java API Do not over-generalize ## What this run cannot establish. - It is one run on one developer laptop, without dedicated-host isolation, CPU pinning or thermal controls. - The ten samples estimate steady-state behavior for this fixture; they are not production latency percentiles. - Derived rows/second is arithmetic from average file time, not a multi-threaded throughput test. - The sparse fixed row does not model every field density, value length, validation failure or upstream object-allocation pattern. - The in-memory destination excludes filesystem, network, encryption, compression and caller-selected buffering costs. - The profiler measures cumulative allocation, not maximum RSS, peak live heap or retained object size. - No Univocity path is included: the adapter has a different output boundary and cannot produce the complete management-record-plus-bookings file measured here. Use these figures to understand this implementation and to reproduce a comparison on your deployment hardware—not as an SLA. Review the compatibility evidence and limits separately; speed does not prove DATEV import acceptance. --- Source: https://mrtyldr.github.io/datev-exporter/de/ Java 17 · Apache-2.0 · Release 0.2.0 # Buchungsdaten besitzen. Formatschicht wiederverwenden. Bereits zugeordnete Buchungsdaten werden zu deterministischen DATEV-Buchungsstapel-/EXTF-Dateien – mit festen v13/v12-Schemata, typisierten Metadaten, technischer Validierung und Forward-only-Streaming. Der Vertrag ## Formatexporter, keine Buchhaltungslogik. Die Anwendung liefert zugeordnete Konten, Beträge, Daten und fachliche Entscheidungen. Die Bibliothek liefert den EXTF-Verwaltungssatz, die offizielle Spaltenreihenfolge, CSV-Kodierung und deterministische technische Prüfungen. Exakte Grenzen nachlesen → Warum es die Bibliothek gibt ## Die Schwierigkeit ist nicht, Werte mit Semikolons zu verbinden. Ein vollständiger Buchungsstapel kombiniert einen anders aufgebauten Verwaltungssatz, eine exakte versionsgebundene Überschrift und streng formatierte Buchungszeilen. Schon kleine Abweichungen bei Breite, Reihenfolge, Maskierung, Zeilenenden oder Kodierung können einen ansonsten gültigen Export unbrauchbar machen. ### Schema kanonisch halten Eine geordnete Definition für alle 125 v13-Felder und die 124 Felder der älteren v12-Struktur verwenden – statt eigene Tabellen oder Arrays zu pflegen. ### Vor der Byte-Abweichung scheitern Strukturelle, Windows-1252- und optionale semantische Verstöße ablehnen, bevor eine Buchungszeile an das Ziel übergeben wird. ### Speichermodell auswählen Zeilen behalten, wenn sie geprüft oder erneut ausgegeben werden müssen; sie einmalig vorwärts streamen, wenn das Exportvolumen keine Speicherung rechtfertigt. Lieferumfang ## Eine schmale API für einen präzisen Ausgabevertrag. Der Standardpfad bleibt abhängigkeitsarm. Optionale Module sind explizit – eine Univocity-Abhängigkeit oder der semantische Validator kommt nur hinzu, wenn er gewählt wird. Empfohlener Standard ### Plain-Exporter mit festem Schema Vollständige v13/v12-EXTF-Ausgabe, speichernde DatevFile und der Forward-only-DatevStreamWriter. Die richtige Wahl, solange der Empfängervertrag nicht bewusst von der offiziellen Überschrift abweicht. Mit Plain beginnen → Individuelles Folgeformat ### Advanced-Exporter Überschriften umbenennen, umordnen oder ersetzen und den Validierungsmodus je Datei wählen. Individuelle Überschriften sind nicht mit EXTF-Metadaten kombinierbar, weil sie nicht mehr dem festen DATEV-Schema entsprechen. Exporter vergleichen → Nur für vorhandene Pipeline ### Univocity-Adapter Überschrift und Buchungszeilen über einen vorhandenen Univocity-CsvWriter ausgeben. Er kann den anders geformten Verwaltungssatz nicht erzeugen und maskiert offizielle Textüberschriften abweichend. Grenze verstehen → Bewusste Abgrenzung ## Fachliche Daten hinein. Importkandidat heraus. ### In der Anwendung zuordnen Kontenrahmen, Steuerbehandlung, Stammdaten und mandantenspezifische Regeln vor dem Aufruf des Exporters bestimmen. ### Zusammenstellen und prüfen Typisierte Metadaten anhängen, benannte oder typisierte Spalten schreiben und metadatenbezogene technische Validierung auswählen. ### Im Zielsystem verifizieren Die erzeugte Datei über den kontrollierten Übertragungsweg bewegen und die Annahme in der lizenzierten, konfigurierten DATEV-Zielumgebung prüfen. Eine bestandene Bibliotheksvalidierung – oder die Übereinstimmung mit dem geprüften v13-Schema- und Beispielvertrag – beweist weder die buchhalterische Richtigkeit noch die Annahme durch ein bestimmtes DATEV-Produkt, einen Mandanten oder dessen Konfiguration. Nachweise statt Adjektive ## Kompatibilitätsaussagen sind abgestuft. Für v13 fixiert ein optionaler Vertragstest das offizielle DATEV-Prüfprogrammschema und das offizielle Beispielarchiv, vergleicht die 125 Felddefinitionen und validiert alle 54 Beispielbuchungen. Er macht aus dem Start einer Windows-GUI kein bestandenes oder fehlgeschlagenes Importergebnis. Für v12 unterstützt die Bibliothek das Präfix mit 124 Spalten und den passenden Verwaltungssatz. Aktuell fehlt jedoch ein unabhängiges offizielles v12-Testobjekt oder ein dokumentierter realer Importnachweis. Kompatibilitätsmatrix öffnen Weiterlesen ### Nachweise auf der richtigen Ebene nutzen - Vollständiges v13-Beispiel - v12/v13-Nachweismatrix - Validierungsebenen - Reproduzierbare Benchmarks - Versionierte Java-API --- Source: https://mrtyldr.github.io/datev-exporter/de/getting-started.html Erste Schritte · 0.2.0 # Von zugeordneten Buchungen zur vollständigen EXTF-Datei. Mit dem Plain-Exporter für feste Schemata beginnen. Typisierte Metadaten und kontextbezogene Validierung ergänzen, dann je nach Lebenszyklus zwischen Speicherung und Forward-only-Ausgabe wählen. Schritt 0 ## Voraussetzungen und Modulauswahl - Java 17 oder neuer. - datev-exporter-plain für feste v13/v12-Schemata und Ausgabe. - datev-exporter-field-validator, wenn deterministische semantische Prüfungen vor der Annahme jeder Zeile ausgeführt werden sollen. Das Root-Artefakt datev-exporter ist eine Bill of Materials (BOM), kein Runtime-JAR. Es richtet die Modulversionen aus; anschließend werden nur die tatsächlich verwendeten Module eingebunden. Die Beispiele verwenden Platzhalter für Berater-/Mandantennummern und Kontenzuordnungen. Alle fachlichen Werte müssen durch für das Zielsystem freigegebene Daten ersetzt werden; die Bibliothek ordnet weder SKR03/SKR04 zu noch bestimmt sie die Steuerbehandlung. Schritt 1A ## Mit Gradle ausführen Eine Java-Anwendung anlegen, das Java-Beispiel unten als src/main/java/Example.java speichern und dieses build.gradle verwenden: ``` plugins { id 'application' } repositories { mavenCentral() } dependencies { implementation platform('io.github.mrtyldr:datev-exporter:0.2.0') implementation 'io.github.mrtyldr:datev-exporter-plain' implementation 'io.github.mrtyldr:datev-exporter-field-validator' } java { toolchain { languageVersion = JavaLanguageVersion.of(17) } } application { mainClass = 'Example' } ``` Mit dem Gradle Wrapper starten: ``` ./gradlew run ``` Das Repository enthält außerdem eine deterministische, durch CI ausgeführte Anwendung: ``` git clone https://github.com/mrtyldr/datev-exporter.git cd datev-exporter ./gradlew :examples:quickstart-gradle:run ``` Schritt 1B ## Mit Maven bauen Dieselbe Example.java funktioniert mit diesem minimalen pom.xml. Die BOM verwaltet die Modulversionen: ``` 4.0.0 example datev-quickstart 1.0.0 17 UTF-8 io.github.mrtyldr datev-exporter 0.2.0 pom import io.github.mrtyldr datev-exporter-plain io.github.mrtyldr datev-exporter-field-validator org.apache.maven.plugins maven-compiler-plugin 3.15.0 org.codehaus.mojo exec-maven-plugin 3.6.3 Example ``` Kompilieren und ausführen: ``` mvn verify exec:java ``` Schritt 2 ## Vollständige v13-Datei erstellen Diese Anwendung schreibt alle drei notwendigen Satzebenen: EXTF-Verwaltungssatz, die exakte v13-Überschrift mit 125 Spalten und eine Buchungszeile. DatevFile behält angenommene Zeilen, damit sie geprüft oder erneut ausgegeben werden können. ``` import io.github.mrtyldr.datev.core.DatevColumn; import io.github.mrtyldr.datev.core.DatevField; import io.github.mrtyldr.datev.core.DatevMetadata; import io.github.mrtyldr.datev.core.DatevValidationContext; import io.github.mrtyldr.datev.plain.DatevFile; import io.github.mrtyldr.datev.validation.DatevValidator; import java.io.OutputStream; import java.math.BigDecimal; import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDate; import java.time.LocalDateTime; public final class Example { public static void main(String[] args) throws Exception { LocalDate fiscalStart = LocalDate.of(2026, 1, 1); LocalDate periodStart = LocalDate.of(2026, 8, 1); LocalDate periodEnd = LocalDate.of(2026, 8, 31); DatevMetadata metadata = DatevMetadata.bookingBatchV13() .createdAt(LocalDateTime.now()) .origin("RE") .exportedBy("my_application") .advisorNumber(1001) .clientNumber(1) .fiscalYearStart(fiscalStart) .accountLength(4) .period(periodStart, periodEnd) .description("August 2026") .applicationInformation("my-application") .build(); DatevValidationContext context = DatevValidationContext.builder() .accountLength(metadata.accountLength()) .fiscalYearStart(metadata.fiscalYearStart()) .period(metadata.periodStart(), metadata.periodEnd()) .build(); DatevValidator validator = DatevValidator.builder() .context(context) .build(); DatevFile file = DatevFile.builder() .metadata(metadata) .validator(validator) .build(); file.append( DatevColumn.amount(DatevField.AMOUNT, new BigDecimal("1250.00")), DatevColumn.of(DatevField.DEBIT_CREDIT_FLAG, "S"), DatevColumn.of(DatevField.CURRENCY, "EUR"), DatevColumn.account(DatevField.ACCOUNT, 1000), DatevColumn.account(DatevField.CONTRA_ACCOUNT, 8400), DatevColumn.documentDate(LocalDate.of(2026, 8, 10)), DatevColumn.of(DatevField.DOCUMENT_FIELD_1, "RE-42"), DatevColumn.of(DatevField.POSTING_TEXT, "Rechnung 42") ); try (OutputStream output = Files.newOutputStream( Path.of("EXTF_Buchungsstapel.csv"))) { file.writeTo(output); } } } ``` Lesbare englische Konstanten tragen die exakten deutschen Ausgabeüberschriften. Ein falsch geschriebener Feldname wird zum Kompilierfehler, während die Ausgabe kanonisch bleibt. Große Exporte ## Über ein gepuffertes Ziel streamen DatevStreamWriter schreibt beim Erstellen Verwaltungssatz und Überschrift. Danach wird je append genau eine Buchungszeile vorbereitet und übergeben, ohne erfolgreiche Zeilen zu behalten. Ein ansonsten ungepuffertes Datei- oder Netzwerkziel in BufferedOutputStream einbetten, um kleine Betriebssystem-Schreibvorgänge zusammenzufassen. ``` import io.github.mrtyldr.datev.plain.DatevStreamWriter; import java.io.BufferedOutputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; try (OutputStream file = Files.newOutputStream( Path.of("EXTF_Buchungsstapel.csv")); OutputStream output = new BufferedOutputStream(file); DatevStreamWriter writer = DatevStreamWriter.builder() .metadata(metadata) .validator(validator) .build(output)) { for (Map row : bookingRows) { writer.append(row); } } ``` - Die JDK-Standardpuffergröße ist ein sinnvoller Ausgangspunkt; Anpassungen nur auf Basis von Messungen. - Keinen zusätzlichen Puffer um ByteArrayOutputStream, StringWriter oder ein bereits gepuffertes Ziel legen. - Der Writer leert den Puffer, schließt aber das vom Aufrufer verwaltete Ziel nicht. Durch die letzte Deklaration wird zuerst der Writer und danach Puffer und Datei geschlossen. - Ein Validierungsfehler verändert die bestehende Ausgabe nicht. Ein E/A-Fehler kann auftreten, nachdem das Ziel einen Teil der Zeile angenommen hat; ein physisches Rollback ist unmöglich und der Writer wird terminal. Der von der Bibliothek verwaltete Arbeitsspeicher folgt der gerade zusammengesetzten Zeile, nicht der Gesamtzahl. Validator oder Ziel – etwa ByteArrayOutputStream – können Daten trotzdem behalten. Für begrenzten Heap ist daher ein tatsächlich streamendes Ziel nötig. Älterer Vertrag ## v12 bewusst auswählen Passende v12-Metadaten und das v12-Schema verwenden. Der Builder lehnt einen Verwaltungssatz ab, dessen Version nicht zur Überschrift mit 124 Spalten passt. ``` import io.github.mrtyldr.datev.core.DatevSchema; DatevMetadata legacyMetadata = DatevMetadata.bookingBatchV12() .createdAt(LocalDateTime.now()) .origin("RE") .exportedBy("my_application") .advisorNumber(1001) .clientNumber(1) .fiscalYearStart(fiscalStart) .accountLength(4) .period(periodStart, periodEnd) .build(); DatevFile legacy = DatevFile.builder(DatevSchema.LEGACY_V12) .metadata(legacyMetadata) .build(); ``` v12 wird strukturell aus den ersten 124 v13-Feldern abgeleitet und durch Bibliothekstests abgedeckt. Release 0.2.0 besitzt weder ein unabhängig fixiertes offizielles v12-Testobjekt noch ein dokumentiertes Annahmeergebnis aus einem realen Zielsystem. Siehe Versionsmatrix. Letzter Schritt ## Über die Bibliothek hinaus prüfen ### Zuordnung testen Freigegebene Konten, Steuerentscheidungen, Daten und Quellfeldzuordnungen in der Anwendung absichern. ### Dateigrenze kontrollieren Erzeugte Bytes als Windows-1252 mit CRLF-Sätzen erhalten; kein späterer UTF-8-Textschritt darf sie umschreiben. ### DATEV-Werkzeuge und reales Ziel nutzen Den passenden Prüfablauf und einen kontrollierten Import in der lizenzierten, konfigurierten Zielumgebung durchführen. Produkt/Version und Ergebnis als eigene Kompatibilitätsnachweise dokumentieren. Aussagen und Grenzen prüfen API 0.2.0 öffnen --- Source: https://mrtyldr.github.io/datev-exporter/de/compatibility.html Nachweisstand · Release 0.2.0 # Wissen, was geprüft ist – und was in eigener Verantwortung bleibt. Kompatibilität bezeichnet hier einen dokumentierten Dateiformatvertrag mit abgestuften Testnachweisen. Sie bedeutet keine DATEV-Zertifizierung, Partnerschaft oder garantierte Annahme durch eine Produktinstallation. Versionsmatrix ## v13 besitzt fixierte offizielle Vertragsnachweise. v12 ist strukturell abgeleitet. Dimension v13 Ältere v12 Bibliotheksschema implementiertCURRENT_V13, exakt 125 Buchungsspalten. implementiertLEGACY_V12, exakt 124 Buchungsspalten. Unterschied Enthält Feld 125, Abw. Skontokonto. Exakt die ersten 124 Felder der bibliotheksinternen v13-Tabelle; Feld 125 fehlt. EXTF-Verwaltungssatz implementiertDatevMetadata.bookingBatchV13(); unpassende Schema-/Metadatenversion wird abgelehnt. implementiertDatevMetadata.bookingBatchV12(); unpassende Schema-/Metadatenversion wird abgelehnt. Vollständige integrierte Ausgabe implementiertVerwaltungssatz, offizielle Überschrift und Buchungszeilen. implementiertVerwaltungssatz, offizielle Überschrift und Buchungszeilen. Fixierter Vergleich mit offiziellem Prüfprogrammschema vertraglich getestetAlle 125 Feldtypen, Längen, Dezimalstellen, Pflichtkennzeichen sowie relevante Bezeichnungen/Aliase werden mit dem Schema aus Prüfprogramm DATEV-Format 2.2.3.0 verglichen. nicht unabhängigIn diesem Release ist kein separates offizielles v12-Prüfschema oder -Testobjekt fixiert. Offizieller Beispielvertrag vertraglich getestetExakte Überschrift verglichen und alle 54 Buchungszeilen des fixierten offiziellen v13-Beispiels durch strikte semantische Validierung verarbeitet. nur abgeleitetBibliothekstests belegen Präfix, Breite und Serialisierung mit 124 Spalten; kein unabhängiges offizielles v12-Beispiel wird getestet. Annahme im realen Zielsystem nicht belegtKein dokumentierter Annahmetest in einer lizenzierten realen Umgebung für dieses Release. nicht belegtKein dokumentierter Annahmetest in einer lizenzierten realen Umgebung für dieses Release. Aussagestufe Gegen den offiziellen Vertrag getestete v13-Dateierzeugung; weiterhin ein Importkandidat. Strukturell abgeleitete v12-Dateierzeugung; als älteren Pfad mit niedrigerem Nachweisniveau behandeln. Der optionale Test liest fixierte offizielle Artefakte und prüft die erzeugte Struktur. Das bereitgestellte Prüfprogramm ist eine interaktive Windows-GUI ohne dokumentierten Headless-Vertrag für Ergebnis oder Exit-Code. Der Test behauptet weder einen erfolgreichen GUI-Bericht noch einen Produktimport. Aufbau der Aussage ## Die Nachweisleiter ### Bibliotheksinvarianten Standardtests fixieren beide Schemabreiten, beweisen v12 als die ersten 124 Felder von v13, erzwingen passende Metadatenversionen und testen Windows-1252-/CRLF-Serialisierung sowie die Grenze von 99.999 Zeilen. ### Fixierte offizielle v13-Artefakte Der optionale datevCheckerTest lädt das offizielle Prüfprogramm- und Beispielarchiv, verifiziert festgelegte SHA-256-Werte und vergleicht anschließend In-Memory-Schema und Beispielinhalt. Externe urheberrechtlich geschützte Artefakte werden nicht ins Repository kopiert. ### Prüfung eines erzeugten Testobjekts Eine deterministische vollständige v13-Datei wird strikt als Windows-1252 dekodiert und auf Verwaltungssatz, exakte nicht maskierte Überschrift, Buchungszeile mit 125 Zellen und CRLF-Satzgrenzen geprüft. ### Zielannahme bleibt offen Eine lizenzierte Bedienperson muss den Prüfbericht kontrollieren und in das tatsächlich konfigurierte Ziel importieren. Mandanteneinstellungen, Produktversionen, Stammdaten und Buchungsentscheidungen liegen außerhalb eines portablen Java-Tests. Einsehbar sind der versionierte Kompatibilitätstest zum offiziellen Schema und die fixierte Download-/Prüfsummenkonfiguration. Exakte Dateigrenze ## Was die vollständige integrierte Ausgabe zusichert Eigenschaft Vertrag Satzfolge EXTF-Verwaltungssatz, feste offizielle Überschrift, danach null bis 99.999 Buchungszeilen. Trennzeichen Semikolon (;). Zeilenende CRLF (\r\n) für jeden ausgegebenen Satz. Byte-Kodierung Striktes Windows-1252 auf integrierten OutputStream-Pfaden; nicht darstellbare Metadaten oder Buchungswerte werden abgelehnt, nicht ersetzt. Überschrift Exakte feste v13- oder v12-Reihenfolge, durch den integrierten Writer unmaskiert ausgegeben. Maskierung der Buchungszeile DATEV-Textspaltenmaskierung und CSV-Escaping über den gemeinsamen DatevCsv-Codec. Atomare Validierung Formatierung, Struktur-, Kodierungs- und konfigurierte semantische Prüfungen enden, bevor die Buchungszeile an das Ziel übergeben wird. Ein E/A-Fehler kann physisch nicht zurückgerollt werden. Ein vom Aufrufer gelieferter Zeichen-Writer kontrolliert seine spätere Byte-Kodierung. Eine metadatenfreie Advanced-Ausgabe darf bewusst einen anderen Zeichensatz für einen individuellen CSV-Folgevertrag wählen; das ist kein vollständiges kanonisches EXTF-Byteprofil. Produktumfang ## Fähigkeiten und explizite Nicht-Ziele Bereich Status Bedeutung Feste v13/v12-Erzeugung ja Kanonische Breite/Reihenfolge, typisierte EXTF-Metadaten und vollständige integrierte Dateiausgabe. Technische Validierung ja Struktur, Format, Längen, Abhängigkeiten sowie optionale kontextbezogene Konto-/Datums-/Periodenprüfungen. Forward-only-Ausgabe ja DatevStreamWriter behält erfolgreich geschriebene Zeilen nicht; Zielpufferung bleibt beim Aufrufer. Individuelle CSV-Verträge optional Advanced kann Überschriften umbenennen/umordnen; diese Ausgabe ist bewusst nicht mit EXTF-Metadaten kombinierbar. DATEV-GUI-/Server-/API-Integration nein Keine Authentifizierung, Uploads, Remote-API-Clients, Desktop-Automation oder Serverintegration. Buchhaltungs- oder Steuerlogik nein Keine Soll-/Haben-Entscheidung, Steuerbehandlung, Periodenabschlusspolitik oder Rechtsberatung. Kontenrahmenzuordnung nein Keine SKR03-/SKR04- oder Quellsystemkontenzuordnung. Der Aufrufer liefert freigegebene Konten. Stammdatenprüfung nein Keine Prüfung, ob Berater, Mandanten, Konten, Steuerschlüssel, Kostenstellen oder Geschäftspartner im Ziel existieren. Importgarantie/Zertifizierung nein Keine Zusage, dass eine erzeugte Datei von einem bestimmten DATEV-Produkt oder einer Konfiguration angenommen wird. Unbegrenzte Zeilen nein Eine Datei ist auf 99.999 Buchungszeilen begrenzt. Größere Exporte an einer fachlich freigegebenen Grenze aufteilen. Thread-sichere mutable Exporter nein Exporter-Instanzen sind mutabel und nicht thread-sicher; jede Datei/jeden Writer auf einen Thread begrenzen. Primärreferenzen ## Quellen des Formatvertrags - DATEV Developer Portal: technischer Aufbau / Einstieg. - DATEV Developer Portal: Formatbeschreibung Buchungsstapel. - DATEV Developer Portal: Beschreibung des Verwaltungssatzes. - DATEV Developer Portal: Zeichensatzbeschreibung. - DATEV Developer Portal: Prüfprogramm DATEV-Format und Beispieldaten. Diese Links beschreiben das DATEV-Format; sie implizieren keine Verbindung. Die Website paraphrasiert nur die für den Bibliotheksvertrag nötigen Teile und verteilt weder DATEV-Dokumentation noch Prüfprogrammbinärdateien oder Beispieldateien. Export erstellen API auswählen --- Source: https://mrtyldr.github.io/datev-exporter/de/reference.html Entscheidungshilfe · API 0.2.0 # Das kleinste Modul verwenden, das den eigenen Vertrag erfüllt. Das feste offizielle Format, individuelle CSV-Anforderungen und vorhandene Univocity-Pipelines sind unterschiedliche Anwendungsfälle. Eine bewusste Auswahl hält Abhängigkeiten und Ausgabeaussagen nachvollziehbar. ## Vertiefende Referenzen Vier Seiten behandeln die Teile des DATEV-Vertrags, die die meisten Fragen auslösen. Jede wird aus der Bibliothek erzeugt, sodass die Tabellen zu dem passen, was die Exporter tatsächlich schreiben. ### Feldreferenz Alle 125 Buchungsstapel-Spalten in Ausgabereihenfolge, mit amtlichen Überschriften, Prüfprogramm-Typen, Längen und Verfügbarkeit in Version 12. ### Validierungsfehler Die sechs stabilen Fehlercodes, ihre Auslöser, die Feldpaare und die drei Prüftiefen. ### EXTF-Header Der Verwaltungssatz mit 31 Feldern: feste Kennungen, Datums- und Zeitformate, Quoting-Regeln und die kodierten Felder. ### Kodierung und Umlaute Windows-1252, CRLF, Semikolon und Quoting — welche Zeichen überstehen und wie Exporte nachträglich beschädigt werden. Artefakte ## Modulübersicht Artefakt Runtime-Abhängigkeiten Verantwortung datev-exporter Keine (Plattform-POM) BOM zur Ausrichtung aller Module auf eine Version. Enthält keine Runtime-API. datev-exporter-core Keine Kanonische Schemata, Felddefinitionen, Metadaten, Überschriften, CSV-Codec und Validierungsmodell. datev-exporter-plain core Feste v13/v12-Datei mit Speicherung und Forward-only-Writer. Empfohlenes Ausgabemodul. datev-exporter-field-validator core Optionaler semantischer Validator-Callback für den Plain-Exporter. datev-exporter-advanced core Speichernde Dateien mit individuellen/umbenannten/umgeordneten Überschriften und integrierten Validierungsmodi. datev-exporter-advanced-univocity advanced + Univocity Adapter für Anwendungen mit bereits festgelegter Univocity-CsvWriter-Pipeline. datev-exporter-verification und datev-exporter-benchmarks sind interne Build-Module; sie gehören weder zur BOM noch zur Maven-Central-Veröffentlichung. Entscheidungstabelle ## Plain, Advanced oder Univocity? Bedarf Plain Advanced Univocity-Adapter Vollständige feste v13/v12-EXTF empfohlen ja mit exakter offizieller Überschrift, Strict-Modus und passenden Metadaten kein Verwaltungssatz Forward-only-Zeilen ja DatevStreamWriter nein Zeilen werden behalten Schreibt gespeicherte Advanced-Zeilen über Drittanbieter-Writer Überschriften umbenennen/-ordnen nein ja ja über Advanced-Datei Eigener Zeichensatz nein Byte-Pfad ist Windows-1252 Nur für metadatenfreie individuelle Folgeverträge Zeichensatz der Advanced-Datei; strikte Encoder-Hülle bereitgestellt Drittanbieter-Runtime-Abhängigkeit Keine außer Core Keine außer Core Univocity Byte-Form der offiziellen Überschrift Kanonische unmaskierte Überschrift Kanonisch im integrierten Writer mit offizieller Überschrift Textüberschriften werden abweichend maskiert Plain verwenden, solange kein konkreter Bedarf für individuelle Überschriften benannt werden kann. Speichernde DatevFile für Prüfung/Wiederholung, DatevStreamWriter für einmalige Produktion. Prüfung in Schichten ## Validierung findet technische Fehler, nicht fachliche Wahrheit In integrierter Ausgabe immer aktiv ### Struktur- und Kodierungssicherheit Spaltenbreite/-reihenfolge, bekannte Überschriften, CSV-Syntax/Steuerzeichen, Zeilenlimit und strikte Windows-1252-Darstellbarkeit auf Byte-Ausgabe. Optionale Plain-Abhängigkeit ### DatevValidator Callback mit Formatversion und unveränderlicher Zeile. Mit Kontenlänge, Wirtschaftsjahresbeginn und Periode bauen, um kontextbezogene Konto-/Datumsregeln zu prüfen. Advanced-Konfiguration ### DatevValidationMode STRICT ergänzt Pflichtfelder und Abhängigkeiten; FIELD_LEVEL prüft gelieferte bekannte Felder; NONE behält strukturelle CSV-/Überschriftsprüfungen. Immer Sache der Anwendung ### Buchhaltung und Stammdaten Kontenauswahl, Steuerbehandlung, Gültigkeit im Ziel und mandantenspezifische Anforderungen müssen außerhalb der Bibliothek validiert werden. Das bloße Einbinden von datev-exporter-field-validator verändert nichts. Der Validator wird an Plain-Builder/-Factory übergeben. Offizielle Advanced-Schemata nutzen standardmäßig STRICT; individuelle Überschriften NONE, weil ihre Fachsemantik unbekannt ist. Interoperabilität, kein Ersatz ## Der Univocity-Adapter löst genau ein enges Problem datev-exporter-advanced-univocity nur wählen, wenn die umgebende Anwendung CSV-Ausgabe bereits in Univocity zentralisiert und Überschrift plus Buchungszeilen die beabsichtigte Grenze sind. ``` CsvWriter writer = DatevUnivocityWriters.newCsvWriter(file, outputStream); DatevUnivocityWriters.writeTo(file, writer); ``` - Ein CsvWriter gibt gleichförmige Sätze aus und kann daher den anders aufgebauten EXTF-Verwaltungssatz mit 31 Feldern nicht erzeugen. - writeTo lehnt eine Datei mit Metadaten ab. Für eine vollständige Datei den integrierten Advanced-Pfad DatevFile.writeTo(OutputStream) verwenden. - writeDataTo schreibt ausdrücklich nur Überschrift und Zeilen – selbst wenn Metadaten vorhanden sind. - Mit unveränderten offiziellen v12/v13-Einstellungen entsprechen Buchungszeilen der integrierten Ausgabe, Textüberschriften werden aber anders maskiert. Keine Aussage über Byte-Gleichheit der Gesamtdatei. - Das bereitgestellte newCsvWriter meldet nicht darstellbare Zeichen und lässt den Stream offen. Rohe Univocity-Konstruktoren vermeiden, die unzulässige Zeichen durch ? ersetzen können. Verantwortungsregeln ## Das Ausgabeziel bleibt beim Aufrufer - Integrierte Writer leeren, aber schließen einen gelieferten OutputStream oder Writer nicht. - Für kanonische Windows-1252-Bytes den OutputStream-Pfad verwenden. Ein Zeichen-Writer ist nur ein Zeichenvertrag; sein finaler Encoder bleibt Aufgabe des Aufrufers. - Ungepufferte Datei-/Netzwerkausgabe einmalig puffern. Die Bibliothek wählt bewusst keine dauerhafte Puffergröße. - Plain und Advanced DatevFile behalten angenommene Zeilen. DatevStreamWriter übergibt jede angenommene Zeile und verwirft ihren Zusammenstellungsspeicher. - Alle mutablen Exporter-Instanzen sind bewusst single-threaded. Vor einer volumenbasierten Auswahl das Beispiel für gepuffertes Streaming und den Benchmarkbericht lesen. Referenz auf Symbolebene ## Versionierte Javadocs für exakte Signaturen Dieser Leitfaden erklärt Verträge und Entscheidungen. Die generierte API-Seite ist die Quelle für öffentliche Klassen, Methoden und Lebenszyklusdetails: - Javadoc-Index für Release 0.2.0 - Versionierter Quellcode des ausführbaren Einstiegsbeispiels - Veröffentlichte BOM auf Maven Central Das Projekt verwendet Semantic Versioning, öffentliche APIs können sich bis 1.0.0 jedoch zwischen Minor-Versionen ändern. BOM-Version fixieren und beim Upgrade die Release Notes lesen. --- Source: https://mrtyldr.github.io/datev-exporter/de/fields.html Feldreferenz · Schema v13 und v12 · 0.2.0 # Alle 125 Buchungsstapel-Spalten in amtlicher Reihenfolge. Feldnummern, exakte Überschriften, Prüfprogramm-Typen und Längen für Formatversion 13 — mit den Unterschieden, die bei Formatversion 12 zählen. ## Was diese Tabelle ist Eine Buchungszeile im DATEV-Buchungsstapel hat eine feste Anzahl Spalten in fester Reihenfolge. Formatversion 13 definiert 125 Spalten, Version 12 die ersten 124 und lässt Abw. Skontokonto weg. Die Reihenfolge trägt Bedeutung: eine Zeile ist positionsbasiert, Feld 7 ist Konto — unabhängig davon, ob Feld 6 gefüllt wurde. Die Tabelle wird aus DatevFieldSpecs erzeugt, der einzigen kanonischen Schemakopie, aus der sich jedes Modul dieser Bibliothek ableitet. Sie ist keine handgepflegte Abschrift und kann daher nicht von dem abweichen, was die Exporter tatsächlich schreiben. Zu wissen, dass Feld 9 BU-Schlüssel heißt, sagt nichts darüber, welcher Buchungsschlüssel im konkreten Fall richtig ist. Kontenzuordnung, steuerliche Behandlung und Buchungslogik bleiben bei der aufrufenden Anwendung und der steuerlichen Beratung. ## Aufbau des Schemas Typ Name im Prüfprogramm Spalten Bedeutung TEXT Text 84 Gequoteter Textwert; die maximale Länge zählt Zeichen. NUMBER Zahl 27 Ungequoteter numerischer Wert mit optionalem Komma als Dezimaltrenner. DATE Datum 7 Datum im DATEV-Format. ACCOUNT Konto 4 Numerische Kontonummer, zusätzlich durch die Sachkontenlänge eingegrenzt. AMOUNT Betrag 3 Positiver Betrag; das Vorzeichen steht im Soll/Haben-Kennzeichen. ## Die fünf Pflichtfelder Die strikte Validierung meldet einen leeren Wert in diesen Spalten als REQUIRED_FIELD. Jede andere Spalte darf leer bleiben. # Amtliche Überschrift DatevField-Konstante Typ 1 Umsatz (ohne Soll/Haben-Kz) AMOUNT Betrag 2 Soll/Haben-Kennzeichen DEBIT_CREDIT_FLAG Text 7 Konto ACCOUNT Konto 8 Gegenkonto (ohne BU-Schlüssel) CONTRA_ACCOUNT Konto 10 Belegdatum DOCUMENT_DATE Datum ## Wiederholende Gruppen und eine Schreibweisen-Falle Zwei Spaltenfamilien wiederholen sich als Art-/Inhalt-Paare. Wird nur eine Hälfte gefüllt, meldet der strikte Modus DEPENDENT_FIELD_MISSING. - Beleginfo - Art 1 … Beleginfo - Inhalt 8 — acht Paare, Felder 21–36. - Zusatzinformation - Art 1 … Zusatzinformation- Inhalt 20 — zwanzig Paare, Felder 48–87. DATEV schreibt das Paar uneinheitlich: Zusatzinformation - Art 1 mit Leerzeichen um den Bindestrich, Zusatzinformation- Inhalt 1 ohne. Beide Schreibweisen werden exakt reproduziert. Mit den DatevField-Konstanten wird ein Tippfehler zum Compile-Fehler statt zu einer abgelehnten Datei. ## Vollständige Feldtabelle # Amtliche Überschrift DatevField-Konstante Typ Max. Länge Nachkommastellen Pflicht In v12 1 Umsatz (ohne Soll/Haben-Kz) AMOUNT Betrag 10 2 ja ja 2 Soll/Haben-Kennzeichen DEBIT_CREDIT_FLAG Text 1 — ja ja 3 WKZ Umsatz CURRENCY Text 3 — nein ja 4 Kurs EXCHANGE_RATE Zahl 5 6 nein ja 5 Basis-Umsatz BASE_AMOUNT Betrag 10 2 nein ja 6 WKZ Basis-Umsatz BASE_CURRENCY Text 3 — nein ja 7 Konto ACCOUNT Konto 9 — ja ja 8 Gegenkonto (ohne BU-Schlüssel) CONTRA_ACCOUNT Konto 9 — ja ja 9 BU-Schlüssel POSTING_KEY Text 4 — nein ja 10 Belegdatum DOCUMENT_DATE Datum 8 — ja ja 11 Belegfeld 1 DOCUMENT_FIELD_1 Text 36 — nein ja 12 Belegfeld 2 DOCUMENT_FIELD_2 Text 12 — nein ja 13 Skonto CASH_DISCOUNT Betrag 8 2 nein ja 14 Buchungstext POSTING_TEXT Text 60 — nein ja 15 Postensperre ITEM_BLOCK Zahl 1 — nein ja 16 Diverse Adressnummer MISC_ADDRESS_NUMBER Text 9 — nein ja 17 Geschäftspartnerbank PARTNER_BANK Zahl 3 — nein ja 18 Sachverhalt MATTER_CODE Zahl 2 — nein ja 19 Zinssperre INTEREST_BLOCK Zahl 1 — nein ja 20 Beleglink DOCUMENT_LINK Text 210 — nein ja 21 Beleginfo - Art 1 DOCUMENT_INFO_TYPE_1 Text 20 — nein ja 22 Beleginfo - Inhalt 1 DOCUMENT_INFO_CONTENT_1 Text 210 — nein ja 23 Beleginfo - Art 2 DOCUMENT_INFO_TYPE_2 Text 20 — nein ja 24 Beleginfo - Inhalt 2 DOCUMENT_INFO_CONTENT_2 Text 210 — nein ja 25 Beleginfo - Art 3 DOCUMENT_INFO_TYPE_3 Text 20 — nein ja 26 Beleginfo - Inhalt 3 DOCUMENT_INFO_CONTENT_3 Text 210 — nein ja 27 Beleginfo - Art 4 DOCUMENT_INFO_TYPE_4 Text 20 — nein ja 28 Beleginfo - Inhalt 4 DOCUMENT_INFO_CONTENT_4 Text 210 — nein ja 29 Beleginfo - Art 5 DOCUMENT_INFO_TYPE_5 Text 20 — nein ja 30 Beleginfo - Inhalt 5 DOCUMENT_INFO_CONTENT_5 Text 210 — nein ja 31 Beleginfo - Art 6 DOCUMENT_INFO_TYPE_6 Text 20 — nein ja 32 Beleginfo - Inhalt 6 DOCUMENT_INFO_CONTENT_6 Text 210 — nein ja 33 Beleginfo - Art 7 DOCUMENT_INFO_TYPE_7 Text 20 — nein ja 34 Beleginfo - Inhalt 7 DOCUMENT_INFO_CONTENT_7 Text 210 — nein ja 35 Beleginfo - Art 8 DOCUMENT_INFO_TYPE_8 Text 20 — nein ja 36 Beleginfo - Inhalt 8 DOCUMENT_INFO_CONTENT_8 Text 210 — nein ja 37 KOST1 - Kostenstelle COST_CENTER_1 Text 36 — nein ja 38 KOST2 - Kostenstelle COST_CENTER_2 Text 36 — nein ja 39 Kost-Menge COST_QUANTITY Zahl 12 4 nein ja 40 EU-Land u. UStID (Bestimmung) EU_COUNTRY_VAT_ID_DESTINATION Text 15 — nein ja 41 EU-Steuersatz (Bestimmung) EU_TAX_RATE_DESTINATION Zahl 2 2 nein ja 42 Abw. Versteuerungsart DIFFERING_TAXATION_TYPE Text 1 — nein ja 43 Sachverhalt L+L MATTER_CODE_LL Zahl 3 — nein ja 44 Funktionsergänzung L+L FUNCTION_SUPPLEMENT_LL Zahl 3 — nein ja 45 BU 49 Hauptfunktionstyp BU49_MAIN_FUNCTION_TYPE Zahl 1 — nein ja 46 BU 49 Hauptfunktionsnummer BU49_MAIN_FUNCTION_NUMBER Zahl 2 — nein ja 47 BU 49 Funktionsergänzung BU49_FUNCTION_SUPPLEMENT Zahl 3 — nein ja 48 Zusatzinformation - Art 1 ADDITIONAL_INFO_TYPE_1 Text 20 — nein ja 49 Zusatzinformation- Inhalt 1 ADDITIONAL_INFO_CONTENT_1 Text 210 — nein ja 50 Zusatzinformation - Art 2 ADDITIONAL_INFO_TYPE_2 Text 20 — nein ja 51 Zusatzinformation- Inhalt 2 ADDITIONAL_INFO_CONTENT_2 Text 210 — nein ja 52 Zusatzinformation - Art 3 ADDITIONAL_INFO_TYPE_3 Text 20 — nein ja 53 Zusatzinformation- Inhalt 3 ADDITIONAL_INFO_CONTENT_3 Text 210 — nein ja 54 Zusatzinformation - Art 4 ADDITIONAL_INFO_TYPE_4 Text 20 — nein ja 55 Zusatzinformation- Inhalt 4 ADDITIONAL_INFO_CONTENT_4 Text 210 — nein ja 56 Zusatzinformation - Art 5 ADDITIONAL_INFO_TYPE_5 Text 20 — nein ja 57 Zusatzinformation- Inhalt 5 ADDITIONAL_INFO_CONTENT_5 Text 210 — nein ja 58 Zusatzinformation - Art 6 ADDITIONAL_INFO_TYPE_6 Text 20 — nein ja 59 Zusatzinformation- Inhalt 6 ADDITIONAL_INFO_CONTENT_6 Text 210 — nein ja 60 Zusatzinformation - Art 7 ADDITIONAL_INFO_TYPE_7 Text 20 — nein ja 61 Zusatzinformation- Inhalt 7 ADDITIONAL_INFO_CONTENT_7 Text 210 — nein ja 62 Zusatzinformation - Art 8 ADDITIONAL_INFO_TYPE_8 Text 20 — nein ja 63 Zusatzinformation- Inhalt 8 ADDITIONAL_INFO_CONTENT_8 Text 210 — nein ja 64 Zusatzinformation - Art 9 ADDITIONAL_INFO_TYPE_9 Text 20 — nein ja 65 Zusatzinformation- Inhalt 9 ADDITIONAL_INFO_CONTENT_9 Text 210 — nein ja 66 Zusatzinformation - Art 10 ADDITIONAL_INFO_TYPE_10 Text 20 — nein ja 67 Zusatzinformation- Inhalt 10 ADDITIONAL_INFO_CONTENT_10 Text 210 — nein ja 68 Zusatzinformation - Art 11 ADDITIONAL_INFO_TYPE_11 Text 20 — nein ja 69 Zusatzinformation- Inhalt 11 ADDITIONAL_INFO_CONTENT_11 Text 210 — nein ja 70 Zusatzinformation - Art 12 ADDITIONAL_INFO_TYPE_12 Text 20 — nein ja 71 Zusatzinformation- Inhalt 12 ADDITIONAL_INFO_CONTENT_12 Text 210 — nein ja 72 Zusatzinformation - Art 13 ADDITIONAL_INFO_TYPE_13 Text 20 — nein ja 73 Zusatzinformation- Inhalt 13 ADDITIONAL_INFO_CONTENT_13 Text 210 — nein ja 74 Zusatzinformation - Art 14 ADDITIONAL_INFO_TYPE_14 Text 20 — nein ja 75 Zusatzinformation- Inhalt 14 ADDITIONAL_INFO_CONTENT_14 Text 210 — nein ja 76 Zusatzinformation - Art 15 ADDITIONAL_INFO_TYPE_15 Text 20 — nein ja 77 Zusatzinformation- Inhalt 15 ADDITIONAL_INFO_CONTENT_15 Text 210 — nein ja 78 Zusatzinformation - Art 16 ADDITIONAL_INFO_TYPE_16 Text 20 — nein ja 79 Zusatzinformation- Inhalt 16 ADDITIONAL_INFO_CONTENT_16 Text 210 — nein ja 80 Zusatzinformation - Art 17 ADDITIONAL_INFO_TYPE_17 Text 20 — nein ja 81 Zusatzinformation- Inhalt 17 ADDITIONAL_INFO_CONTENT_17 Text 210 — nein ja 82 Zusatzinformation - Art 18 ADDITIONAL_INFO_TYPE_18 Text 20 — nein ja 83 Zusatzinformation- Inhalt 18 ADDITIONAL_INFO_CONTENT_18 Text 210 — nein ja 84 Zusatzinformation - Art 19 ADDITIONAL_INFO_TYPE_19 Text 20 — nein ja 85 Zusatzinformation- Inhalt 19 ADDITIONAL_INFO_CONTENT_19 Text 210 — nein ja 86 Zusatzinformation - Art 20 ADDITIONAL_INFO_TYPE_20 Text 20 — nein ja 87 Zusatzinformation- Inhalt 20 ADDITIONAL_INFO_CONTENT_20 Text 210 — nein ja 88 Stück PIECES Zahl 8 — nein ja 89 Gewicht WEIGHT Zahl 8 2 nein ja 90 Zahlweise PAYMENT_METHOD Zahl 2 — nein ja 91 Forderungsart RECEIVABLE_TYPE Text 10 — nein ja 92 Veranlagungsjahr ASSESSMENT_YEAR Zahl 4 — nein ja 93 Zugeordnete Fälligkeit ASSIGNED_DUE_DATE Datum 8 — nein ja 94 Skontotyp CASH_DISCOUNT_TYPE Zahl 1 — nein ja 95 Auftragsnummer ORDER_NUMBER Text 30 — nein ja 96 Buchungstyp POSTING_TYPE Text 2 — nein ja 97 USt-Schlüssel (Anzahlungen) VAT_KEY_PREPAYMENT Zahl 2 — nein ja 98 EU-Land (Anzahlungen) EU_COUNTRY_PREPAYMENT Text 2 — nein ja 99 Sachverhalt L+L (Anzahlungen) MATTER_CODE_LL_PREPAYMENT Zahl 3 — nein ja 100 EU-Steuersatz (Anzahlungen) EU_TAX_RATE_PREPAYMENT Zahl 2 2 nein ja 101 Erlöskonto (Anzahlungen) REVENUE_ACCOUNT_PREPAYMENT Konto 9 — nein ja 102 Herkunft-Kz ORIGIN_CODE Text 2 — nein ja 103 Buchungs GUID POSTING_GUID Text 36 — nein ja 104 KOST-Datum COST_DATE Datum 8 — nein ja 105 SEPA-Mandatsreferenz SEPA_MANDATE_REFERENCE Text 35 — nein ja 106 Skontosperre CASH_DISCOUNT_BLOCK Zahl 1 — nein ja 107 Gesellschaftername SHAREHOLDER_NAME Text 76 — nein ja 108 Beteiligtennummer PARTICIPANT_NUMBER Zahl 4 — nein ja 109 Identifikationsnummer IDENTIFICATION_NUMBER Text 11 — nein ja 110 Zeichnernummer SUBSCRIBER_NUMBER Text 20 — nein ja 111 Postensperre bis ITEM_BLOCK_UNTIL Datum 8 — nein ja 112 Bezeichnung SoBil-Sachverhalt SOBIL_MATTER_LABEL Text 30 — nein ja 113 Kennzeichen SoBil-Buchung SOBIL_POSTING_FLAG Zahl 2 — nein ja 114 Festschreibung FINAL_POSTING_FLAG Zahl 1 — nein ja 115 Leistungsdatum SERVICE_DATE Datum 8 — nein ja 116 Datum Zuord. Steuerperiode TAX_PERIOD_DATE Datum 8 — nein ja 117 Fälligkeit DUE_DATE Datum 8 — nein ja 118 Generalumkehr (GU) GENERAL_REVERSAL Text 1 — nein ja 119 Steuersatz TAX_RATE Zahl 2 2 nein ja 120 Land COUNTRY Text 2 — nein ja 121 Abrechnungsreferenz SETTLEMENT_REFERENCE Text 50 — nein ja 122 BVV-Position BVV_POSITION Zahl 1 — nein ja 123 EU-Land u. UStID (Ursprung) EU_COUNTRY_VAT_ID_ORIGIN Text 15 — nein ja 124 EU-Steuersatz (Ursprung) EU_TAX_RATE_ORIGIN Zahl 2 2 nein ja 125 Abw. Skontokonto DIFFERING_CASH_DISCOUNT_ACCOUNT Konto 8 — nein nein ## Ein Feld aus Java ansprechen Jede DatevColumn-Factory akzeptiert entweder die Enum-Konstante oder die Überschrift als Zeichenkette; beide erzeugen identische Ausgabe. Die Konstante wird zur Übersetzungszeit geprüft. ``` import io.github.mrtyldr.datev.core.DatevColumn; import io.github.mrtyldr.datev.core.DatevField; import io.github.mrtyldr.datev.core.DatevSchema; // Field 7, "Konto" — compile-checked. DatevColumn account = DatevColumn.account(DatevField.ACCOUNT, 1000); // Identical output, but a typo would only surface at runtime. DatevColumn same = DatevColumn.account("Konto", 1000); int number = DatevField.ACCOUNT.fieldNumber(); // 7 String heading = DatevField.ACCOUNT.heading(); // "Konto" boolean inLegacy = DatevField.DIFFERING_CASH_DISCOUNT_ACCOUNT .isPresentIn(DatevSchema.LEGACY_V12); // false ``` Die DatevField-Konstanten sind in Ausgabereihenfolge deklariert, daher ist ordinal() + 1 die DATEV-Feldnummer. isPresentIn(DatevSchema.LEGACY_V12) beantwortet die Version-12-Frage für jedes Feld. ## Verwandte Referenzen - Validierungsfehler — die sechs Fehlercodes erklärt - EXTF-Header — der Verwaltungssatz mit 31 Feldern - Kodierung — Windows-1252, CRLF, Quoting und Umlaute --- Source: https://mrtyldr.github.io/datev-exporter/de/validation-errors.html Validierungsreferenz · 0.2.0 # Sechs Fehlercodes und was sie jeweils aussagen. Jeder Validierungsfehler trägt einen stabilen maschinenlesbaren Code, die DATEV-Feldnummer und die amtliche Spaltenbezeichnung. Hier steht, was den Code auslöst und was zu ändern ist. ## Drei Prüftiefen Die Tiefe ist eine bewusste Entscheidung je Datei. Ein strikterer Modus ändert nie die geschriebenen Bytes, sondern nur, was vorher abgelehnt wird. Modus Was geprüft wird Mögliche Codes NONE Keine semantische Prüfung. Die strukturellen Prüfungen des Exporters — Zeilenbreite, Steuerzeichen, Kodierbarkeit — gelten weiterhin. — FIELD_LEVEL Jede gefüllte Zelle gegen ihre amtliche Felddefinition. INVALID_FORMAT, VALUE_OUT_OF_RANGE, TEXT_TOO_LONG, UNMAPPABLE_CHARACTER STRICT Alles davon, zusätzlich Pflichtfelder und Feldabhängigkeiten. alle sechs ## Die sechs Codes Code Wird ausgelöst, wenn Übliche Behebung REQUIRED_FIELD Ein vom amtlichen Prüfprogramm als notwendig markiertes Feld ist leer — im strikten Modus auf einem amtlichen Schema. Feld füllen. Betroffen sind nur fünf Spalten: Umsatz, Soll/Haben-Kennzeichen, Konto, Gegenkonto und Belegdatum. INVALID_FORMAT Ein Wert entspricht nicht der von DATEV geforderten Darstellung — eine fehlerhafte Zahl, ein Datum ohne realen Kalenderbezug, ein Kennzeichen außerhalb der erlaubten Menge oder ein Steuer- bzw. Zeilentrennzeichen innerhalb einer Zelle. Den Wert so formatieren, wie DATEV ihn erwartet, nicht wie das eigene Locale ihn ausgibt. Zeilenumbrüche und Tabulatoren aus Freitext entfernen. VALUE_OUT_OF_RANGE Der Wert ist syntaktisch gültig, überschreitet aber seinen Bereich — zu viele Vorkommastellen, zu viele Nachkommastellen oder eine Kontonummer breiter als die konfigurierte Sachkontenlänge. Maximale Länge und Nachkommastellen in der Feldreferenz prüfen und sicherstellen, dass die Sachkontenlänge in den Metadaten zum Mandanten passt. TEXT_TOO_LONG Ein Textwert überschreitet die maximale Zeichenzahl des Feldes. Bewusst in der eigenen Zuordnung kürzen. Es wird nicht still gekürzt, weil der Verlust eines Teils des Buchungstexts eine fachliche Entscheidung ist. UNMAPPABLE_CHARACTER Der Wert enthält ein Zeichen, das Windows-1252 nicht darstellen kann. Das Zeichen vor dem Export transliterieren oder ersetzen. Die Kodierungsreferenz zeigt, welche Zeichen erhalten bleiben. DEPENDENT_FIELD_MISSING Im strikten Modus wurde eine Hälfte eines Feldpaares ohne die andere gefüllt. Beide Hälften füllen — oder keine. ## Die Feldpaare Der strikte Modus erzwingt diese Paare. Wird nur eine Seite gefüllt, entsteht DEPENDENT_FIELD_MISSING auf der fehlenden Seite. - Basis-Umsatz ↔ WKZ Basis-Umsatz (Felder 5 und 6). - Beleginfo - Art n ↔ Beleginfo - Inhalt n für n = 1…8. - Zusatzinformation - Art n ↔ Zusatzinformation- Inhalt n für n = 1…20. ## Kontext schärft die Prüfungen Manche Regeln lassen sich nicht allein aus dem Schema entscheiden. Ein Validierungskontext trägt die Angaben, die sie entscheidbar machen; ohne ihn werden genau diese Prüfungen übersprungen statt geraten. - Die Sachkontenlänge begrenzt die Breite von Konto und Gegenkonto. - Wirtschaftsjahresbeginn und Buchungszeitraum erlauben es, das vierstellige Belegdatum gegen reale Kalenderdaten aufzulösen. Diese Prüfungen bestätigen, dass die Datei dem technischen Schema entspricht. Sie sagen nichts darüber aus, ob die Buchungen fachlich richtig sind oder ob ein bestimmtes DATEV-Produkt in einer bestimmten Konfiguration die Datei annimmt. ## Fehler im Code auswerten Eine abgelehnte Zeile löst eine DatevValidationException aus, die die vollständige Fehlerliste trägt. Jeder Fehler behält Code, Feldnummer und amtliche Spaltenbezeichnung, sodass sich Fehler protokollieren oder abbilden lassen, ohne Meldungstexte zu parsen. ``` import io.github.mrtyldr.datev.core.DatevValidationError; import io.github.mrtyldr.datev.core.DatevValidationException; try { file.append(columns); } catch (DatevValidationException failure) { for (DatevValidationError error : failure.errors()) { log.warn("field {} ({}): {} — {}", error.fieldNumber(), error.canonicalKey(), error.code(), error.message()); } } ``` ## Verwandte Referenzen - Feldreferenz — alle 125 Buchungsstapel-Spalten - EXTF-Header — der Verwaltungssatz mit 31 Feldern - Kodierung — Windows-1252, CRLF, Quoting und Umlaute --- Source: https://mrtyldr.github.io/datev-exporter/de/extf-header.html Formatreferenz · EXTF · 0.2.0 # Die Zeile vor den Überschriften. Eine Buchungsstapel-Datei beginnt mit einem Verwaltungssatz, dessen Aufbau nichts mit den darunter liegenden Buchungszeilen zu tun hat. Fehler hier sind der häufigste Grund, warum ein sonst korrekter Export abgelehnt wird. ## Drei Sätze, zwei Formen Eine vollständige Datei besteht aus drei Schichten: dem EXTF-Verwaltungssatz, der exakten versionierten Spaltenüberschrift und den Buchungszeilen. Nur die letzten beiden teilen ihre Form. Der Verwaltungssatz hat immer 31 Felder, unabhängig von der Formatversion. Die ersten fünf Felder sind feste Kennungen. An ihnen erkennt ein Leser die Datei überhaupt erst: Feld Wert Bedeutung 1 EXTF Kennzeichnet eine EXTF-Exportdatei. 2 700 Header-Version. 3 21 Formatkategorie für Buchungsstapel. 4 Buchungsstapel Formatname. 5 13 / 12 Datenformatversion: 13 oder 12. ## Ein echter Verwaltungssatz Diese Zeilen erzeugt die Bibliothek selbst, aus Metadaten mit festem Zeitstempel, damit die Ausgabe reproduzierbar ist. Formatversion 13 ``` "EXTF";700;21;"Buchungsstapel";13;20260812093000000;;"RE";"my_application";"";1001;1;20260101;4;20260801;20260831;"August 2026";"";1;0;1;"EUR";;"";;;"";;;"";"my-application" ``` Formatversion 12 — identisch bis auf Feld 5 ``` "EXTF";700;21;"Buchungsstapel";12;20260812093000000;;"RE";"my_application";"";1001;1;20260101;4;20260801;20260831;"August 2026";"";1;0;1;"EUR";;"";;;"";;;"";"my-application" ``` Reservierte Felder werden als leere Zellen geschrieben, und Textfelder bleiben auch leer gequotet. Ein Feld wegzulassen statt es zu leeren verschiebt jedes folgende Feld um eine Position. ## Formate, über die man stolpert - Der Erstellungszeitstempel nutzt yyyyMMddHHmmssSSS — 17 Ziffern inklusive Millisekunden, ungequotet. - Wirtschaftsjahresbeginn und Buchungszeitraum nutzen yyyyMMdd, ungequotet. - Textfelder werden gequotet, auch die vorgesehenen leeren. Ein Anführungszeichen in der Anwendungsinformation wird durch Verdopplung maskiert. - Numerische Felder bleiben ungequotet, einschließlich Berater- und Mandantennummer. ## Die kodierten Felder Vier Felder nehmen Werte aus einer geschlossenen Menge statt freier Eingabe. Konstante DATEV-Wert FINANCIAL_ACCOUNTING 1 ANNUAL_FINANCIAL_STATEMENTS 2 Konstante DATEV-Wert INDEPENDENT 0 TAX_LAW 30 CALCULATION 40 COMMERCIAL_LAW 50 IFRS 64 ## Aufbau aus Java Die Metadaten werden einmal je Datei gebaut und bei der Konstruktion validiert, sodass eine unmögliche Kombination scheitert, bevor eine Zeile geschrieben wird. Der Builder wählt die Formatversion, und der Exporter lehnt einen Verwaltungssatz ab, dessen Version nicht zur Überschrift passt. ``` import io.github.mrtyldr.datev.core.DatevMetadata; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Currency; DatevMetadata metadata = DatevMetadata.bookingBatchV13() .createdAt(LocalDateTime.now()) .origin("RE") .exportedBy("my_application") .advisorNumber(1001) .clientNumber(1) .fiscalYearStart(LocalDate.of(2026, 1, 1)) .accountLength(4) .period(LocalDate.of(2026, 8, 1), LocalDate.of(2026, 8, 31)) .description("August 2026") .currency(Currency.getInstance("EUR")) .applicationInformation("my-application") .build(); String record = metadata.toCsvLine(); // the 31-field management record ``` Ein v12-Verwaltungssatz lässt sich nicht mit der 125-spaltigen v13-Überschrift kombinieren. Der Builder weist die Abweichung zurück, statt eine Datei zu schreiben, die kein Importeur deuten kann. ## Verwandte Referenzen - Feldreferenz — alle 125 Buchungsstapel-Spalten - Validierungsfehler — die sechs Fehlercodes erklärt - Kodierung — Windows-1252, CRLF, Quoting und Umlaute --- Source: https://mrtyldr.github.io/datev-exporter/de/encoding.html Codec-Referenz · 0.2.0 # Windows-1252 ist kein Detail, das warten kann. Eine Buchungsstapel-Datei besteht aus Bytes, nicht aus Text. Umlaute überstehen das; ein türkisches punktloses i nicht. Ein unbedachter UTF-8-Schritt nach dem Export macht alles zunichte. ## Der Byte-Vertrag Eigenschaft Wert Zeichenkodierung windows-1252 Satztrenner \r\n (CRLF) Feldtrenner ; Anführungszeichen " Maskiertes Anführungszeichen "" ## Wann ein Wert gequotet wird Zwei unabhängige Regeln entscheiden über das Quoting, beide werden angewendet. - Amtliche Textspalten werden immer gequotet, auch wenn sie leer sind. 84 der 125 Spalten in Version 13 sind Textspalten. - Jeder andere Wert wird nur gequotet, wenn es nötig ist — also wenn er ein Semikolon oder ein Anführungszeichen enthält. - Ein enthaltenes Anführungszeichen wird verdoppelt, nie mit Backslash maskiert. ## Welche Zeichen überstehen Windows-1252 ist eine Ein-Byte-Kodierung und kann höchstens 256 Zeichen darstellen. Deutscher Text liegt bequem darin, viel anderer Text nicht. Kodierbar — werden unverändert geschrieben ``` ä ö ü ß Ä Ö Ü € § µ ° á é í ó ú ñ ç å ø æ š ž ``` Nicht kodierbar — abgelehnt als UNMAPPABLE_CHARACTER ``` ı ğ ş ł ą č ř ő ū 日 😀 ``` Ein Wert mit einem nicht kodierbaren Zeichen wird abgelehnt statt still durch ein Fragezeichen ersetzt, weil ein beschädigter Buchungstext später schwerer auffällt als ein jetzt fehlgeschlagener Export. Wenn die Quelldaten solche Zeichen enthalten können, sollte in der eigenen Zuordnung bewusst transliteriert werden. ## Steuerzeichen Steuer-, Zeilen- und Absatztrennzeichen werden an jeder Stelle einer Zelle abgelehnt und als INVALID_FORMAT gemeldet. Ein Zeilenumbruch in einem Buchungstext würde sonst eine Buchungszeile in zwei unbrauchbare Sätze zerlegen. ## Wie Exporte nach dem Schreiben beschädigt werden Die Bibliothek kontrolliert die Bytes, die sie schreibt. Alles danach liegt in eigener Verantwortung. - Ein Texteditor, der die Datei öffnet und als UTF-8 zurückspeichert — jeder Umlaut wird zu zwei Bytes und die Datei ist ungültig. - Ein Übertragungs- oder Archivierungsschritt im Textmodus, der CRLF zu LF umschreibt. - Das Zurücklesen der Datei mit dem Plattform-Standardzeichensatz statt Windows-1252. - Eine Template- oder Logging-Schicht, die die Ausgabe auf Unicode NFD normalisiert und Umlaute in Grundbuchstabe plus kombinierendes Zeichen zerlegt. Eine Datei, die im Editor richtig aussieht, kann bereits defekt sein. Bytelänge und Kodierung prüfen und die erzeugte Datei zwischen Export und Import unangetastet lassen. ## Verwandte Referenzen - Feldreferenz — alle 125 Buchungsstapel-Spalten - Validierungsfehler — die sechs Fehlercodes erklärt - EXTF-Header — der Verwaltungssatz mit 31 Feldern --- Source: https://mrtyldr.github.io/datev-exporter/de/benchmarks.html Lokal gemessen · v0.2.0 · Java 17 # Eine vollständige EXTF-Datei mit 99.999 Zeilen. Ein reproduzierbarer JMH-Lauf vergleicht drei streng validierte Exportpfade, erfasst die kumulative Allokation und bewahrt die Messunsicherheit, statt aus einem Laptop-Lauf eine allgemeingültige Aussage zu machen. Beobachtete Daten ## Dieser Lauf löste keinen Zeitunterschied zwischen den beiden Plain-Pfaden auf. Eine Operation erzeugt eine vollständige EXTF-v13-Datei aus Verwaltungssatz, 125-spaltiger Überschrift und 99.999 Buchungszeilen. In den Zeit- und Allokationsspalten ist weniger besser. Die ±-Werte sind die Halbbreiten der JMH-99,9-%-Konfidenzintervalle über zehn Messstichproben unter der Normalverteilungsannahme von JMH. Exportpfad Zeit / Datei Abgeleitete Zeit / Zeile Abgeleitete Zeilen / Sekunde Allokierte Bytes / Datei Abgeleitete allokierte Bytes / Zeile Plain Forward-onlyDatevStreamWriter 670,193 ± 34,910 ms 6,702 µs 149.209 980.975.803 ± 10.710.036 B 9.809,86 B Plain RetainedDatevFile 670,000 ± 15,068 ms 6,700 µs 149.252 1.011.067.254 ± 10.710.046 B 10.110,77 B Advanced Retainedadvanced.DatevFile 742,976 ± 51,878 ms 7,430 µs 134.593 966.056.224 ± 10.710.041 B 9.660,66 B Die Mittelwerte der beiden Plain-Pfade unterscheiden sich nur um 0,03 % und damit weit weniger als ihre ausgewiesene Unsicherheit; dieser Lauf trägt daher keine zeitliche Rangfolge. Forward-only allokierte in dieser Arbeitslast rund 3,0 % weniger kumulative Bytes als Plain Retained. Advanced Retained zeigte den niedrigsten Allokationswert und den höchsten beobachteten Zeitmittelwert; dieser einzelne Lauf begründet jedoch keine allgemeingültige Rangfolge. Produktentscheidung ## Zuerst den Lebenszyklus wählen, nicht die kleinste Benchmark-Zahl. ### Forward-only für einmalige Exporte mit hohem Volumen DatevStreamWriter validiert, serialisiert und übergibt jede vollständige Zeile, ohne sie in einer bibliothekseigenen Sammlung zu speichern. Der lebende, zurückgehaltene Zeilenzustand ist damit proportional zu einer Zeile statt zur Gesamtzahl. Diese Entwurfseigenschaft – keine Geschwindigkeitsbehauptung – ist der stärkste Grund für Forward-only bei einem einmaligen Export mit 99.999 Zeilen. ### Retained verwenden, wenn Zeilen verfügbar bleiben müssen Die speichernden APIs erlauben Prüfung, Iteration und verzögerte Ausgabe. Dieser Lebenszyklus ist sinnvoll, wenn der Aufrufer Zeilen erneut prüfen oder ausgeben muss; dieser Lauf löste gegenüber Forward-only keinen Zeitunterschied auf. Die ausgerichteten Zeilen bleiben zwangsläufig bis zur Freigabe des Dateiobjekts im Speicher. ### Allokation ist nicht gleich belegter Heap JMH gc.alloc.rate.norm erfasst alle während einer Operation allokierten Bytes, einschließlich kurzlebiger Validierungs- und Serialisierungsobjekte. Es misst nicht den maximal lebenden Heap. Das Advanced-Retained-Ergebnis zeigt den Unterschied: Es allokierte hier kumulativ am wenigsten, obwohl es ausgerichtete Zeilen speichert. Zur Quantifizierung des Spitzenverbrauchs wäre eine separate Live-Set- oder Heap-Occupancy-Untersuchung erforderlich. Gemessene Arbeitslast ## Eine dünn belegte, aber nicht triviale feste Zeile. - Format: vollständiger DATEV-Buchungsstapel / EXTF v13, Windows-1252 und CRLF. - Größe: 125 Spalten; 99.999 Buchungszeilen als unterstütztes Dateimaximum. - Belegung: sechs nicht leere Felder je Zeile: Betrag, Soll/Haben-Kennzeichen, Konto, Gegenkonto, Belegdatum und Buchungstext. - Maskierung: Der Buchungstext lautet Müller; Beleg "42" € und beansprucht Windows-1252-, Trennzeichen- und Anführungszeichenbehandlung. - Validierung: metadatenbezogene strenge semantische Validierung bei jedem Append. Plain verwendet DatevValidator, Advanced den eingebauten Modus STRICT. - Ausgabe: Alle Pfade werden bytegenau verglichen und erzeugen exakt 33.602.459 Bytes: 2.795 feste Verwaltungs-/Überschriftsbytes plus 336 Bytes je Buchungszeile. Für jede Zeile wird dieselbe unveränderliche Map mit sechs Einträgen übergeben. Zeilenausrichtung, Validierung und Serialisierung liegen im Messintervall; die vorgelagerte Datensatzerzeugung und fachliche Kontierungslogik nicht. Messvertrag ## Umgebung und JMH-Konfiguration. Gemessene Bibliothek v0.2.0 · Commit 06fa7ae Messdatum 12. August 2026 Rechner Apple M1 Pro, 10 logische CPUs, 32 GiB RAM, aarch64 Betriebssystem macOS 26.5.2 (Build 25F84) JVM Eclipse Temurin 17.0.19+10; -Xms1g -Xmx1g -XX:+AlwaysPreTouch Messgerüst JMH 1.37, Average-Time-Modus, ein Thread, zwei Forks Iterationen Je Fork: drei × 1 s Warmup, danach fünf × 1 s Messung Profiler JMH gc; normalisierte Allokation in Bytes je Operation Ziel Ein vorab dimensionierter, wiederverwendeter ByteArrayOutputStream, außerhalb jeder Messoperation zurückgesetzt I/O Byte-Serialisierung und Schreiben ins Ziel enthalten; Dateisystem, Netzwerk und Zielallokation ausgeschlossen Jeder gemessene Aufruf erstellt seinen Exporter und führt alle 99.999 Appends sowie die abschließende Ausgabe aus. Unveränderliche Testdaten, Zielkapazität und der erste Bytegleichheitsvergleich werden vor der Messung vorbereitet. Die letzte Ausgabe wird nach dem Lauf erneut geprüft. Selbst ausführen ## Die Maximalzeilen-Task fixiert die wichtigen Eingaben. ``` git clone https://github.com/mrtyldr/datev-exporter.git cd datev-exporter git checkout cb91f5dd8b174bb4e10d98a8a7cc93f007483042 ./gradlew --no-daemon :datev-exporter-benchmarks:jmhMaxRows ``` Die Task wählt die Adoptium-Java-17-Toolchain und verwendet diese JMH-Einstellungen: ``` -p rowCount=99999 -wi 3 -w 1s -i 5 -r 1s -f 2 -t 1 -jvmArgs "-Xms1g -Xmx1g -XX:+AlwaysPreTouch" -prof gc -rf json ``` Das erzeugte JSON liegt unter datev-exporter-benchmarks/build/results/jmh/max-rows-gc.json. Die veröffentlichte Zusammenfassung bewahrt jede rohe Zeit- und Allokationsstichprobe: - Maschinenlesbares Ergebnis und Rohwerte - Benchmark-Quellcode - Versionierte Java-API Nicht verallgemeinern ## Was dieser Lauf nicht belegt. - Es ist ein Lauf auf einem Entwickler-Laptop ohne dedizierte Host-Isolation, CPU-Pinning oder thermische Kontrolle. - Die zehn Stichproben schätzen den eingeschwungenen Zustand dieser Arbeitslast; sie sind keine Produktions-Latenzperzentile. - Zeilen pro Sekunde ist aus der durchschnittlichen Dateizeit berechnet und kein mehrthreadiger Durchsatztest. - Die dünn belegte feste Zeile bildet nicht jede Felddichte, Wertlänge, Validierungsverletzung oder vorgelagerte Objektallokation ab. - Das In-Memory-Ziel schließt Dateisystem, Netzwerk, Verschlüsselung, Kompression und aufruferseitige Pufferung aus. - Der Profiler misst kumulative Allokation, nicht maximalen RSS, maximal lebenden Heap oder Retained Size. - Ein Univocity-Pfad ist nicht enthalten: Der Adapter hat eine andere Ausgabegrenze und kann die hier gemessene vollständige Datei aus Verwaltungssatz und Buchungen nicht erzeugen. Diese Zahlen dienen zum Verständnis der Implementierung und als reproduzierbare Vergleichsbasis auf der eigenen Zielhardware – nicht als SLA. Die Kompatibilitätsnachweise und Grenzen sind separat zu bewerten; Geschwindigkeit belegt keine DATEV-Importannahme.