> For the complete documentation index, see [llms.txt](https://developer.vario-software.de/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.vario-software.de/documentation/apps/app-backends/financial-accounting.md).

# Financial Accounting

Export bookings and debtor/creditor master data to an accounting system.

A financial accounting app exports the ERP's **bookings** and **debtor/creditor master data** to an accounting system. DATEV is the reference implementation.

{% hint style="info" %}
Read [App Backends](/documentation/apps/app-backends.md) first; this page only covers what is specific to accounting.
{% endhint %}

## Registering the finance backend

```javascript
await methods.createFinanceBackend('DATEV');
```

Registering it makes your app appear as an export target in the ERP's finance export.

{% hint style="info" %}
`createFinanceBackend` takes **only a label**. Passing a description as a second argument does nothing — it is silently ignored. Set the description afterwards with `changeFinanceBackend(label, description)`.
{% endhint %}

{% hint style="info" %}
Note that `changeFinanceBackend` looks the backend up by your **app identifier**, not by the label — the label argument only appears in its log message. It returns `null` instead of throwing when nothing is found.
{% endhint %}

## The export is two-phase

Do not collapse these. The ERP model separates producing the files from transmitting them:

```
1. Build    ERP data ──► formatted files ──► DMS
2. Transmit DMS files ──► accounting system ──► job status
```

Phase 1 is triggered by the user, reads the booking run, and writes finished files into the DMS. Phase 2 picks those files up and uploads them. Keeping them apart means a transmission can be retried without regenerating, and the exported files stay auditable.

Both phases are long-running. The reference app answers the HTTP request **immediately** and continues in the background, writing progress into a status record the UI polls.

{% hint style="warning" %}
If you do that, remember a `200` then means *"accepted"*, not *"finished"*. Clients that treat it as completion will read stale files. Give them a status endpoint.
{% endhint %}

## Reading the data

Bookings come from computed queries — the booking run, its bookings, and the individual records:

```
POST /cmn/computed-queries/finance-export/bookings   (filtered by bookingRun.id)
POST /cmn/computed-queries/finance-export/records    (filtered by booking.bookingRun.id)
```

Within a booking, the **first record is the header** and the remaining records are the lines. A booking with only one record therefore produces no output.

The accounts come straight from the data — there is no mapping table in the app:

| Export field                  | Source                            |
| ----------------------------- | --------------------------------- |
| Account (`Konto`)             | the **header** record's ledger    |
| Contra account (`Gegenkonto`) | the **line** record's ledger      |
| Tax key (`BU-Schlüssel`)      | the line record's transaction key |

Skip lines whose amount is zero or whose booking state is not `OK`.

{% hint style="warning" %}
**Always sort a paged query by a unique column.** Offset paging over an unsorted result set is undefined: pages overlap, some rows arrive twice and others never arrive at all — differently on every run. The backend adds no default order. Sort by `id`, and deduplicate afterwards as a safety net.
{% endhint %}

## Format details are unforgiving

Accounting formats are validated strictly on import. The things that actually break exports:

**Encoding.** Write UTF-8 **with a byte-order mark**. Strip it again when reading your own files back.

**Numbers.** German decimal comma, exactly two decimals, no thousands separator, and the **absolute value** — the sign is carried by the debit/credit indicator. If that indicator is lost, the booking silently inverts.

**Dates are partial.** In the DATEV booking format the document date is only day and month; the year comes from the file header. That is precisely why the export must be split per business year — a booking outside the header's year would be filed under the wrong one. Walk backwards and forwards from the main business year and write additional files as needed.

**Field widths and quoting.** Fields have maximum lengths and per-field quoting rules. Truncation is silent, and an embedded quote character is usually removed rather than escaped — so validate values before writing rather than trusting the writer.

**Timezones.** Date formatters typically use local time while a date-only configuration value parses as UTC midnight. On a server west of UTC that shifts the business year by a day. Normalise before comparing.

## Configuration

An accounting integration needs client identification — for DATEV the consultant number and client number, which combine into the client id used on every API call. Along with the business-year start and the ledger account length, these are **required before an export can run**: gate the export button on them rather than failing mid-run.

Store them where both your export code and your API layer can read them, and remember that values round-trip as strings.

## What to watch out for

**Make re-exports distinguishable.** A regenerated file must not silently overwrite the previous one — add a suffix. Auditors need to see what was sent and when.

**Deduplicate transmissions.** Track which files and documents already succeeded and skip them on a re-run, or you create duplicate jobs in the accounting system. Treat the accounting system's own "duplicate" response as success, not as an error.

**Guard against concurrent runs**, with a staleness timeout so a crashed run doesn't block the export forever.

**Respect rate limits and retry properly.** Honour `Retry-After` on `429`/`503`, then back off exponentially with a cap. Distinguish fatal errors (missing rights, expired authorisation) — which should abort the whole run — from per-document failures, which should be logged and skipped.

**Check the export rights explicitly.** An authorised session is not the same as an authorised *service*. Verify the required services are enabled for the client and surface a clear message; otherwise a missing entitlement looks like a generic authorisation failure.

**Attachments have size limits.** Document upload limits are enforced on both sides — check the size before uploading and report oversized documents as warnings rather than aborting the run.

**Long-lived authorisation expires.** Refresh tokens for accounting APIs can last months or years, but they do expire; handle the expiry as a normal state with a re-authorisation prompt.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developer.vario-software.de/documentation/apps/app-backends/financial-accounting.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
