Step 0
Requirements and module choice
- Java 17 or newer.
datev-exporter-plainfor the fixed v13/v12 schemas and output.datev-exporter-field-validatorwhen 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:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>example</groupId>
<artifactId>datev-quickstart</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.github.mrtyldr</groupId>
<artifactId>datev-exporter</artifactId>
<version>0.2.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.github.mrtyldr</groupId>
<artifactId>datev-exporter-plain</artifactId>
</dependency>
<dependency>
<groupId>io.github.mrtyldr</groupId>
<artifactId>datev-exporter-field-validator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.15.0</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.6.3</version>
<configuration>
<mainClass>Example</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
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);
}
}
}
DatevField?
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<String, ?> 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,StringWriteror 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.