PHPackages                             justinholtweb/craft-datevz - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. justinholtweb/craft-datevz

ActiveCraft-plugin

justinholtweb/craft-datevz
==========================

DATEV bookkeeping export for Craft Commerce — turn orders, payments and refunds into an importable DATEV-Format Buchungsstapel with German VAT, OSS and reverse-charge handled.

5.0.0(today)11↑2900%proprietaryPHPPHP ^8.2

Since Aug 24Pushed today1 watchersCompare

[ Source](https://github.com/justinholtweb/craft-datevz)[ Packagist](https://packagist.org/packages/justinholtweb/craft-datevz)[ RSS](/packages/justinholtweb-craft-datevz/feed)WikiDiscussions main Synced today

READMEChangelog (1)Dependencies (2)Versions (2)Used By (0)

Datevz
======

[](#datevz)

DATEV bookkeeping export for Craft Commerce 5. Your orders come out as a **Buchungsstapel** your Steuerberater can import, with German VAT, OSS and reverse charge already worked out — and a preview that shows every posting before a file exists.

Datevz never guesses quietly. If an order cannot be booked — no account mapped for a product, a batch that straddles two fiscal years, postings that do not add up to the order total — it says so, names the order, and refuses to write the file. A DATEV import is not a good place to find that out.

Requirements
------------

[](#requirements)

- Craft CMS 5.3+
- Craft Commerce 5.0+
- PHP 8.2+

Installation
------------

[](#installation)

```
composer require justinholtweb/craft-datevz
php craft plugin/install datevz
```

Then open **Settings → Plugins → Datevz** and fill in the four things that come from the tax practice: the **Beraternummer**, the **Mandantennummer**, the **Sachkontenlänge**, and which chart of accounts (SKR03 or SKR04) the client is on. Everything else has a working default.

What it costs
-------------

[](#what-it-costs)

**$99**, and $79 a year to keep receiving updates. One edition — everything below is in it. There is no feature you discover you needed at month-end and cannot have.

Buchungsstapel export (DATEV-Format, v12/v13)✅SKR03 / SKR04 account presets✅Revenue split per tax rate, per account✅Reverse charge, export and **One Stop Shop** treatment✅Account mapping rules — by SKU, product type, category, shipping method✅Per-customer debtor accounts + Debitoren master data export✅Payment postings per gateway, with fees✅Refunds as credit notes✅Preview before exporting, with per-order problems✅Reconciliation against the order total✅Batch archive with checksums✅Log of every export, download and refusal✅Panel on Commerce's order screen✅Console commands and `craft.datevz.*` Twig API✅What a posting looks like
-------------------------

[](#what-a-posting-looks-like)

One order, €100 of goods at 19%:

```
119,00;"S";"EUR";;;;1400;8400;;1503;"RE-1042";"8556";;"Shop RE-1042";…

```

Gross against the debtor in Soll, revenue in Gegenkonto, Belegdatum as `TTMM`, invoice number in Belegfeld 1. The revenue account is an **Automatikkonto**, so it carries its own tax rate and the BU-Schlüssel column stays empty — which is what DATEV expects, and why the presets choose those accounts.

The direction is never in the amount. `Umsatz` is always positive and unsigned; a credit is a posting with `H` in the Soll/Haben column. A negative Umsatz is a rejected import, not a refund.

VAT treatment
-------------

[](#vat-treatment)

Datevz decides how an order is treated from where it is going and who the customer is:

SituationTreatmentBooks to (SKR03)Ships within GermanyDomestic8400 / 8300 by rateEU consumer, OSS offDomestic8400 / 8300EU consumer, OSS onDistance salethe account you map for that countryEU business with a valid VAT ID for its own countryReverse charge8125Outside the EUExport8120The VAT ID comes from the address's **Organization Tax ID**, which is where Craft 5 keeps it. It is checked for shape, not with VIES: a checkout must not depend on a foreign service being up, and by the time an order is being booked the validity of the number at the time of sale is a matter for the invoice.

What Datevz will **not** do is override the treatment because of how much tax Commerce charged. An export carrying 19%, or a domestic sale carrying none, is a problem in the shop's tax configuration — so it is reported, not papered over.

Accounts
--------

[](#accounts)

The presets are a starting point, not advice. A chart of accounts belongs to the practice, and two shops on the same SKR can legitimately book to different accounts, so every number is editable and the preset buttons never overwrite one you have already set.

SKR03SKR04Revenue, standard rate84004400Revenue, reduced rate83004300Intra-community supply81254125Export81204120Discounts granted87004700Gateway fees49706855Money (Geldtransit)13601460Collective debtor14001200**Mapping rules** beat all of it: match a SKU (`BOOK-*` works), a product type, a category or a shipping method, and send it to an account of its own. First match wins.

Debtors
-------

[](#debtors)

Two modes. **Collective** books every receivable to one account — right for a shop with thousands of one-off buyers. **Per customer** allocates a personal account from a range you choose, keyed to the customer and falling back to their email address, so somebody who ordered as a guest and later registered keeps one number. Those accounts can be exported as a **Debitoren/Kreditoren** master-data file for the practice to import.

Debtor accounts are always **one digit longer** than the Sachkonten. Datevz checks that before it writes anything; getting it wrong is the most common reason an otherwise fine batch is refused.

Payments and refunds
--------------------

[](#payments-and-refunds)

- Each successful transaction books the money in against the debtor, on the account mapped for its gateway (a Geldtransit account is the usual choice).
- A gateway fee is booked **only when the gateway recorded one** on the transaction. Nothing is estimated.
- A refund is two postings, not one: the money goes back, and — unless you turn it off — the revenue is reversed the way a credit note reverses it, in the same proportions and off the same accounts. Posting only the money movement leaves the debtor account permanently out of balance.

Exporting
---------

[](#exporting)

**Datevz → Export**: pick a period, preview, then create the batch. The preview and the export run the same code, so what you read is what the file contains.

An order that has been exported is left out of the next batch. Delete a batch and its orders are released again — the archive is the record of what went, so deleting it is the only honest way to say "that never happened".

Every batch is stored whole, with a **sha256 of the delivered file**, so months later you can prove that the copy the practice holds is the copy Datevz produced.

### From the command line

[](#from-the-command-line)

```
php craft datevz/export/run --from=2026-01-01 --to=2026-01-31
php craft datevz/export/run --from=2026-01-01 --to=2026-01-31 --dryRun
php craft datevz/export/run --from=2026-01-01 --to=2026-01-31 --path=/tmp/january.csv
php craft datevz/export/list
php craft datevz/debtors/export
php craft datevz/log/prune
```

A dry run exits non-zero when it finds errors, so a monthly cron job can fail loudly instead of mailing a broken batch.

Twig
----

[](#twig)

```
{% set state = craft.datevz.orderState(order) %}
{% if craft.datevz.isExported(order) %}
    Booked on {{ state.dateExported|date('d.m.Y') }} in batch {{ state.batchId }}
{% endif %}

{% set set = craft.datevz.postings(order) %}
{{ craft.datevz.treatmentLabel(set.treatment) }}
{% for posting in set.postings %}
    {{ posting.account }} / {{ posting.contraAccount }} {{ posting.debitCredit }} {{ posting.amount }}
{% endfor %}
```

Nothing in the Twig API writes: rendering a template is not a place to allocate a debtor number or mark an order exported.

What Datevz checks before it writes
-----------------------------------

[](#what-datevz-checks-before-it-writes)

- The practice numbers are present, and the accounts are the right length.
- Every bucket of revenue has an account and, where it needs one, a BU-Schlüssel.
- The postings add up to the order total. A cent or two of inclusive-tax rounding can go to a rounding account if you configure one; anything more is an error.
- The batch stays inside one fiscal year. `Belegdatum` carries only a day and a month — the year comes from the header — so a batch that straddles a year-end books half its postings into the wrong one, silently, with every row individually valid.
- No two orders share a document number.
- Every order is in the currency the batch declares.
- Every row is exactly as wide as the format version in the header.

Encoding
--------

[](#encoding)

Files are written **Windows-1252**, which is what DATEV's own exports use. If your customers have names that do not survive that trip, switch to **UTF-8 with BOM**, which current DATEV releases accept. The stored document is always UTF-8; the conversion happens when the file is delivered.

A note on the format
--------------------

[](#a-note-on-the-format)

The column layout lives in one place — `src/format/Fields.php` — as an ordered list, because everything about a DATEV file is positional. Datevz writes **Buchungsstapel** format version 13 (125 columns) by default and can write version 12 (121 columns). If a practice's DATEV expects a different release, switch the version in the settings; if a column has moved, that array is the only thing to change.

License
-------

[](#license)

Proprietary. See `LICENSE.md`.

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance100

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/035cb655c55af0e9e5b96754b80fd9703e195c32dbdfc49ae9a43ab9cf8db560?d=identicon)[justinholtweb](/maintainers/justinholtweb)

---

Top Contributors

[![justinholtweb](https://avatars.githubusercontent.com/u/295903?v=4)](https://github.com/justinholtweb "justinholtweb (4 commits)")

---

Tags

craftcmscraft-pluginvatossAccountingcommercegermanybuchhaltungcraft-commercedatevskr03skr04

### Embed Badge

![Health badge](/badges/justinholtweb-craft-datevz/health.svg)

```
[![Health](https://phpackages.com/badges/justinholtweb-craft-datevz/health.svg)](https://phpackages.com/packages/justinholtweb-craft-datevz)
```

###  Alternatives

[verbb/events

A full-featured plugin for event management and ticketing.

2312.1k](/packages/verbb-events)[verbb/formie

The most user-friendly forms plugin for Craft.

101400.6k79](/packages/verbb-formie)[verbb/hyper

A user-friendly links field for Craft.

24153.5k15](/packages/verbb-hyper)[verbb/vizy

A flexible visual editor field for Craft.

4251.5k1](/packages/verbb-vizy)[verbb/gift-voucher

Sell and redeem digital gift vouchers for Craft Commerce.

1323.1k1](/packages/verbb-gift-voucher)[verbb/postie

Get shipping rates, tracking status and print labels with Australia Post, UPS, USPS, FedEx, DHL Express, and more.

1219.4k](/packages/verbb-postie)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
