Craft Commerce 5 · Holded
Every order in Holded, with the right IVA
Holded is where most Spanish shops keep their books. Getting Craft Commerce into it is not the hard part — the hard part is that one checkout produces domestic sales, intra‑community reverse charges, exports and OSS sales, and Holded will file whichever one it is told to without complaint. Holding works out which, records why, and checks the invoice against the order before it calls the job done.
Rehearse it first
Before a single document exists, ask what one would look like. `holding/sync/preview` prints the VAT treatment, the reasoning behind it, every line with its rate and Holded tax key, and the document total against the order total — without touching Holded.
$ php craft holding/sync/preview 1042
Order #1042
Treatment: Reverse charge
· B2B sale to Germany with VAT number DE123456789 — reverse charge.
Note: Operación exenta de IVA por inversión del sujeto pasivo (art. 84.Uno.2º LIVA).
Lines
Ceramic tile, 30×30 12.00 × 14.50 VAT 0.00% s_iva_0
Gastos de envío 1.00 × 9.90 VAT 0.00% s_iva_0
Document total: 183.90 Order total: 183.90
Features
An accounting integration is judged on the documents it leaves behind, not on the API calls it makes.
VAT that knows which kind of sale it is
Domestic, intra‑community reverse charge, export, One Stop Shop and the territories that are inside a member state and outside the VAT area — each with the legal wording it requires printed on the document.
- Canarias, Ceuta and Melilla are checked before the country, because they are in Spain and not in Spanish VAT
- Büsingen, Helgoland, Livigno, Campione, Åland and Mount Athos as well
- Every classification shows its reasoning on the order screen
The invoice says what was charged
Commerce has already taken the customer's money, and an invoice that disagrees with the payment is worse than none. Holding classifies the sale; it never quietly changes a number.
- A rate that disagrees with the classification is a warning on the document
- An adjustment from another plugin becomes a line, not a discrepancy
- Tax‑inclusive pricing is unwound properly
Checks its own work
Every document is read back out of Holded and its total compared with the order's. Anything that disagrees is flagged and counted, so a bad quarter is something you find in August rather than in January.
- One extra API call per document, and worth it
- Filtered on the Documents screen under “With warnings”
- The payload and the response are kept for every call
Spanish tax identifiers, validated
NIF, NIE and CIF check characters computed properly, and the VAT number formats of all 27 member states checked before VIES is ever asked.
- A customer who pastes ES‑12.345.678‑Z gets it read correctly
- Optional VIES validation — and you decide what happens when VIES will not answer
- A bad tax id is a warning, or a refusal, whichever you prefer
Paid invoices and proper rectificativas
Successful Commerce transactions are registered against the document as they happen, so nobody chases a customer who has already paid. Refunds become credit notes, because an issued invoice is locked the moment it is issued.
- One transaction is registered once, ever — the unique index is the guarantee
- Holded's bank account is attached in a follow‑up call, because /pay will not take one
- Two partial refunds are two credit notes, not one overwriting the other
Never in the way of a checkout
Everything happens in the queue. A checkout does not wait on Holded and does not fail because Holded is down — the order completes, and the document follows.
- Retries with a ceiling, then it waits for a human
- A per‑minute cap so a backfill cannot spend the month's API allowance
- Console commands for the backfill, the retry and the housekeeping
Frequently Asked Questions
The questions worth answering before you install it.
No, and that is the design. Commerce charges; Holding records. If the two disagree you get a warning on the document, not a silently different invoice.
No. The VAT engine is built around Spanish and EU rules because that is who Holded's customers are, but the home country is a setting — reverse charge, OSS, exports and the excluded‑territory list all work from whichever member state you invoice from. The Spanish specifics, like NIF check characters and recargo de equivalencia, are simply unused elsewhere.
No. Everything happens in the queue. A checkout never waits on Holded and never fails because Holded is down: the order completes, and the document follows.
No. A unique index on the order and document type is the guarantee — not a check that could be raced. Re‑sending an order that already has a document returns the existing one.
Not through Holding, because not through Holded: an approved document is locked permanently. Issue a credit note, or delete it in Holded and use Forget so the order can be sent afresh. Holding refuses to attempt an edit rather than half‑applying one.
Handled, and checked before the country test, because they are in Spain and not in Spanish VAT. Matched on postcode — 35 and 38 for Canarias, 51 for Ceuta, 52 for Melilla — along with the other EU territories in the same position.
Nothing, until you ask. holding/sync/all --since=… backfills them, and holding/sync/preview shows you what one would look like first. Holding also installs with automatic syncing switched off, so it will not start filing documents in your live books the moment it arrives.
No. There is one edition at $149 with everything in it. A shop that needs the VAT engine needs the credit notes and the OSS rates too, and a plugin that files an accountant's documents is a poor place to discover which half you bought.
Put the books on rails
$149, one edition, everything in it. Install it, send one order by hand, and look at what arrives in Holded before you let it loose on the rest.