Vismaz for Craft CMS

Usage

Previewing an order

Every completed order gets a Vismaz panel on Commerce's own order screen, showing how the order is taxed, why it was taxed that way, and which accounts it books to.

Preview builds the document without sending it. It runs the same builder the real send runs with remote lookups switched off, so it creates nothing in Visma. What you see is what would be posted — not an approximation of it.

Sending

Send to Visma posts the document now. In voucher mode a single order is swept into its period's journal instead, since that is where it belongs.

Turn on Send automatically when an order completes and Vismaz queues a job on completion. It is queued rather than inline, deliberately: a Visma outage, an expired token or a slow VIES lookup must never be able to stop a customer paying.

Summary vouchers

From Vismaz → Documents, pick a date range and press Send summary voucher. Or from the console, where you can rehearse first:

php craft vismaz/sync/voucher --from=2026-08-01 --to=2026-08-31 --dryRun

The dry run prints the journal as debits and credits and tells you whether it balances:

Webshop sales 2026-08-01 – 2026-08-31 (412 orders)

  Account                                            Debit         Credit
  1580     Settlement                            512 340,90
  6570     Payment fees                            7 685,10
  1580     Payment fees                                          7 685,10
  3001     Försäljning varor inom Sverige, 25 %                409 872,00
  2610     Utgående moms, 25 %                                 102 468,00
  3740     Öresavrundning                                            0,90

  Balances.

An unbalanced voucher is refused rather than sent, with the difference named. Visma rejects one too, but its error does not say which line is wrong.

Refunds

Credit refunds raises a credit note for the amount actually refunded. It is keyed on the refund transactions rather than the order, so a second partial refund produces a second credit note while a retry of the first does not.

What happens after a send

Vismaz records the invoice or verification number Visma assigns, then compares the total Visma booked with the total that was sent. If they disagree — Visma's own invoice rounding, a mapping that is subtly wrong — the document is marked mismatched rather than reported as sent.

This matters because a bad mapping does not throw. It books the wrong number, quietly, and nobody finds out until a bank reconciliation months later.

A mismatched document is already in Visma, so Vismaz does not offer to retry it — retrying would post it a second time. Reconcile it by hand.

Retrying failures

php craft vismaz/sync/retry

Failed documents are rebuilt from the order as it is now, not replayed from the stored payload. An order corrected after a failure pushes as corrected.

The OSS report

Vismaz → OSS report breaks a quarter down by destination country and VAT rate — the shape the declaration asks for. It reads the same tax decisions the ledger was built from, so the report and the books cannot disagree with each other.

SIE 4 export

For merchants on desktop Visma Administration, which has no public API. The same journal that would have been posted as a voucher is written to a .se file the accountant imports by hand.

php craft vismaz/sie/export --from=2026-08-01 --to=2026-08-31 --path=./august.se

Or download it from Vismaz → Documents. Orders already sent to Visma are excluded by default — including them as well is how a merchant books a month twice.

Console reference

php craft vismaz/sync/orders --dryRun --from=2026-08-01 --to=2026-08-31
php craft vismaz/sync/orders --from=2026-08-01 --to=2026-08-31
php craft vismaz/sync/voucher --from=2026-08-01 --to=2026-08-31 [--dryRun]
php craft vismaz/sync/retry
php craft vismaz/sie/export --from=… --to=… [--path=…] [--includeSynced]
php craft vismaz/auth/status
php craft vismaz/auth/refresh
php craft vismaz/log/prune [--days=30]

A dry run needs no Visma connection at all. That is rather the point of it.

Twig

{% if craft.vismaz.isConnected() %}
  {% set treatment = craft.vismaz.treatment(order) %}
  {{ treatment.getLabel() }} — {{ treatment.reason }}

  {% for document in craft.vismaz.documentsForOrder(order) %}
    {{ document.type }}: {{ document.vismaNumber ?? 'pending' }}
  {% endfor %}
{% endif %}