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.

Zugeordnete Buchhaltungsdaten bereitstellen

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:

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

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);
        }
    }
}
Warum DatevField?

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<String, ?> 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();
Niedrigere Nachweisstufe für v12

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

  1. Zuordnung testen

    Freigegebene Konten, Steuerentscheidungen, Daten und Quellfeldzuordnungen in der Anwendung absichern.

  2. Dateigrenze kontrollieren

    Erzeugte Bytes als Windows-1252 mit CRLF-Sätzen erhalten; kein späterer UTF-8-Textschritt darf sie umschreiben.

  3. 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