DATEV Integration
NestJS services for DATEV export and BWA/SuSa reconciliation upload. Part of the Commerce Bounded Context.
IMPORTANT
DATEV-Schnittstellen-Klarstellung:
- DATEV XML online (Belegbilderservice / Rechnungsdatenservice) = Einzelbelege (Rechnungen als PDF + strukturierte XML-Metadaten). Nicht für aggregierte Buchungssätze.
- DATEV-Format CSV (ehemals KNE/OBE) = Aggregierte Buchungsstapel (Journal Entries) pro Periode.
- DATEV Buchungsdatenservice (REST API) = Alternative zu CSV für automatisierten Buchungsdaten-Transfer [future Phase 2].
Busflow braucht beide Wege: Einzelbelege (Quittungsfotos) + Buchungsstapel (Journal Entries).
Export-Strategie: Dual-Format
1. Einzelbelege → DATEV XML online
Für die Übermittlung einzelner Rechnungen und OCR-gescannter Belege (Tankquittungen, Mautbelege) an den Steuerberater.
Key Feature: <BelegLink> — Bindet die URL/GUID der abfotografierten Quittung (ExpenseReceipt) direkt in die XML-Struktur ein. Der Steuerberater hat in DATEV Unternehmen online sofort das Belegbild zum Buchungssatz. → Zero-Touch Accounting.
Workflow
flowchart LR
A[Invoice / ExpenseReceipt] --> B[Generate PDF + XML metadata]
B --> C[Embed BelegLink to receipt image]
C --> D[Package as DATEV XML online]
D --> E[Upload to DATEV Unternehmen online]Data Mapping
| Busflow Entity | DATEV XML Element |
|---|---|
Invoice (PDF) | <Beleg> with <BelegLink> |
ExpenseReceipt (Foto) | <Beleg> with <BelegLink> to storage URL |
Invoice.invoice_number | <Belegfeld1> |
Invoice.issue_date | <Belegdatum> |
2. Buchungsstapel → DATEV-Format CSV
Für die Übermittlung aggregierter, steuerlich aufgelöster Buchungssätze (Journal Entries) einer Periode.
Workflow
flowchart LR
A[Operator triggers export] --> B[Validate period lock]
B -->|Not locked| C[Auto-lock period]
B -->|Already locked| D[Aggregate TaxLedgerEntries]
C --> D
D --> E[Generate DATEV-Format CSV]
E --> F[Write GOBD audit event]
F --> G[Return CSV file]Data Mapping
| Busflow Field | DATEV CSV Column | Notes |
|---|---|---|
TaxLedgerEntry.tax_base_amount | Umsatz | |
TaxLedgerEntry.tax_amount | Steuerbetrag | |
TaxLedgerEntry.tax_strategy | BU-Schlüssel (40 = § 25 UStG) | |
Invoice.invoice_number | Belegfeld 1 | |
Invoice.issue_date | Belegdatum | |
FinancialLedger.tenant_id | Mandantennummer | |
| FREMD procurement costs | Konto 3220 (SKR 03) | § 25 Abs. 4: Kein Vorsteuerabzug! Wareneingang Margenbesteuerung |
margin_exempt_net (Drittland) | Steuerfrei | § 25 Abs. 2 |
API Surface (Hasura Action)
mutation GenerateDatevExport(
$tenantId: uuid!
$periodStart: date!
$periodEnd: date!
$format: DatevExportFormat! # XML_ONLINE | CSV_BUCHUNGSSTAPEL
) {
generateDatevExport(
tenant_id: $tenantId
period_start: $periodStart
period_end: $periodEnd
format: $format
) {
file_url
record_count
period_locked
}
}Reconciliation: BWA/SuSa Upload
Purpose
Accept exports from the operator's Steuerkanzlei and compare against Busflow's internal FinancialLedger actuals.
Accepted Formats
| Format | Parser Strategy |
|---|---|
| CSV | Flexible parser: configurable delimiters (;, ,, \t), header detection, encoding detection (UTF-8, ISO-8859-1). Supports SKR03 and SKR04 column orderings. |
| XLSX | Parsing via xlsx npm package. First sheet, auto-detect header row. |
API Surface (Hasura Action)
mutation UploadReconciliation($tenantId: uuid!, $period: String!, $file: Upload!) {
uploadReconciliation(tenant_id: $tenantId, period: $period, file: $file) {
upload_id
status
entry_count
}
}
query GetReconciliationDiff($uploadId: uuid!) {
reconciliation_entries(where: { reconciliation_upload_id: { _eq: $uploadId } }) {
account_code
busflow_amount
datev_amount
delta
}
}Delta Interpretation
| Scenario | delta | Action |
|---|---|---|
| Perfect match | 0 | ✅ No action |
| Busflow > DATEV | Positive | Investigate: revenue/expense not yet booked in DATEV |
| DATEV > Busflow | Negative | Investigate: manual booking in DATEV not reflected in Busflow |
| Account missing in Busflow | busflow_amount = null | New account in DATEV, no Busflow mapping |