Arbictus Sync for e-Arveldaja
WooCommerce e-Financials integration for easy bookkeeping (E-arveldaja WooCommerce liidestus).
by Arbictus OÜ · github.com/aanndryyyy/arbictus-sync-for-e-financials-woocommerce-integration · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/aanndryyyy/arbictus-sync-for-e-financials-woocommerce-integration/archive/refs/heads/main.zipWooCommerce → e-Arveldaja / e-Financials bookkeeping sync.
Workflow map (WooCommerce hooks → e-Financials OpenAPI → aanndryyyy/e-financials-php-client): see docs/accounting-workflow.md.
API traffic goes through aanndryyyy/e-financials-php-client, installed from Packagist (^0.1).
Requirements
- PHP 8.2+
- WordPress 6.5+ (tested up to 7.0)
- WooCommerce 8.2+ (tested up to 11.0), with or without High-Performance Order Storage
- e-Financials API credentials (live or demo environment)
What this plugin does
Background (Action Scheduler / WP-Cron) sync so checkout stays fast:
- Client upsert → e-Financials
clients - Products ensure →
products(requiredproducts_idon invoice rows) - Sale invoice create + register
- Payment recording — cash fields and/or
transactions(gateway-agnostic; uses WC payment method ids) - Optional deliver — email PDF / e-invoice
- Credit invoices on full/partial refunds
- Admin UX — settings, order column, metabox PDF, order actions, email note
Configure under WooCommerce → Settings → Integrations → e-Financials:
- API connection — key id, public key, password, and the live or test (demo) environment.
- Invoicing — invoice series, template, default sale article (required), VAT rate → sale article map, payment term, and whether the WooCommerce order number becomes the invoice suffix.
- Payment recording — default payment mode, default cash account and accounts dimension, and a per-gateway payment map. The series, template, sale article, cash account and dimension fields are dropdowns loaded from the e-Financials API once credentials are saved, so nobody has to look up internal ids (or mistake a ledger account number for one).
- Delivery & products — auto-deliver the invoice email, also send an e-invoice, and auto-sync products on save.
What triggers a sync
An order is queued when WooCommerce fires woocommerce_payment_complete, with the order moving
to Completed as a fallback for gateways that never report payment. Neither trigger is
configurable. Nothing is queued until API credentials are set, and an order that already has a
sale invoice is skipped. Full and partial refunds queue a credit invoice. Sync and deliver can
also be run by hand from the order actions.
Sequences
Main goal of the integration is to be invisible for the end user. Processing and sending data to e-Financials happens in the background.
New Order
sequenceDiagram
autonumber
actor C as Client
participant WC as WooCommerce
participant Q as Background queue
participant eF as e-Financials
C->>+WC: New Order / payment
WC-->>C: Immediate response
WC->>Q: Enqueue SyncOrder
deactivate WC
Q->>eF: Upsert client
Q->>eF: Ensure products
Q->>eF: Create + register sale invoice
opt Payment
Q->>eF: Cash fields or transactions
end
opt Deliver
Q->>eF: deliver (email / e-invoice)
end
Products
Products are synchronised using product meta _ef_products_id and e-Financials products_id. Opt-in auto-sync on product save is available in settings. Shipping/fees use shared generic products (WC-SHIP, WC-FEE).
Invoicing
Sale invoices are created via the OpenAPI client, then registered. System PDFs can be downloaded from the order screen. Optional auto-deliver emails the customer after register.
Invoice Series
Choose invoice series + template in settings before the first sync. The series' number prefix is sent as number_prefix, so invoices follow the accountant's numbering. Optionally push the WooCommerce order number as number_suffix — it is reduced to digits, because the API rejects non-numeric invoice numbers.
VAT
Line VAT rates are read from WooCommerce's own tax rows; nothing is inferred from the tax/net ratio. e-Financials books VAT by sale article, not by the row's vat_rate, so a mixed-rate catalogue needs the VAT rate → sale article map. A sync fails loudly rather than posting tax into the wrong VAT-return bucket when a line's rate does not match its article.
The default sale article is required: products/create is rejected with "Please select sales account or purchases account" without it.
Refund credit invoices
A refund posts a credit sale invoice linked to the original. Three undocumented API rules govern it, all verified end-to-end against the demo tenant (2026-08-03):
sale_invoice_typecomes fromEFinancialsClient\Enums\SaleInvoiceType, which spells the value the server actually branches on — hyphenatedCREDIT-INVOICE. The field is neither documented nor validated, so any other spelling skips the credit branch, the credit number is never derived, and the request dies with HTTP 500 (null value in column "number"). An earlier revision of this plugin sentCREDIT_INVOICE, which is what that 500 was; it is not a server bug.- The credit repeats the original's
number_suffix. The server derives the number itself by appendingK— andK2,K3, … for further partial credits against the same original, so multiple partial refunds are safe. - The credited quantity carries the sign:
amountis negative,unit_net_pricepositive. The server recomputes the row and invoice totals as negative.
Over-crediting is refused per row with a 409, and voiding a credit does not give the capacity back. API error text is sanitised and truncated before it reaches an order note, so the raw server traceback is never shown to customers.
Development
The repository ships a devcontainer (PHP 8.2) — run PHP and Composer commands inside it.
The local WordPress runs on wp-env, which
needs Docker.
composer install # the plugin loads vendor/autoload.php, so this is required
npm install
npm start # wp-env: http://localhost:8888 (admin / password)
npm run import:demo # optional WooCommerce sample products
Tests and static analysis:
composer test # unit tests + PHPCS, PHPStan and Psalm
composer test:unit # PHPUnit only (tests/unit)
composer test:static # PHPCS, PHPStan, Psalm
npm run test:e2e # Playwright against wp-env (excludes @live)
npm run test:e2e:live # real calls to the e-Financials demo API; needs credentials
PHPStan and Psalm live in vendor-bin/ via bamarni/composer-bin-plugin. See
tests/e2e/README.md for the e2e conventions and the environment
variables the live suite reads.
Demo in WordPress Playground
Every pull request gets a one-click, throwaway demo of that branch in WordPress Playground: WordPress runs in the browser via WebAssembly, with WooCommerce, this plugin, demo products, a customer and three orders in different states. The store is seeded as an Estonian shop (EUR, 22% VAT) and the WooCommerce setup wizard is skipped, so the demo lands directly on the e-Financials integration settings.
blueprints/blueprint.json— the Playground blueprint (site setup + demo data). Edited by hand; the trunk build is pinned toplayground-builds/main.zip..github/workflows/playground.yml— builds the plugin zip (withvendor/, without dev files), publishes it to the orphanplayground-buildsbranch, and comments the demo link on the pull request.
The zip is served from raw.githubusercontent.com because Playground fetches it from the
browser and that host sends access-control-allow-origin: *. playground.wordpress.net's
own plugin-proxy is not an option here — it only allowlists the wordpress, automattic
and woocommerce organisations.
No e-Financials API calls happen in Playground. The plugin talks to the API over Guzzle, which does not reach the network from WebAssembly, so the demo covers the admin UI, settings and order screens — not live syncing.
Releasing to WordPress.org
The plugin is published as arbictus-sync-for-e-arveldaja. That slug is also the text
domain and the main file name, and it must stay in sync with all three.
composer build # dist/arbictus-sync-for-e-arveldaja.zip, built from .distignore
The script installs production-only dependencies, refuses to build when the plugin header
Version and the readme.txt Stable tag disagree, and fails if the zip would exceed the
10 MB submission limit. Verify a build the way the review team does, using
Plugin Check inside wp-env:
npm start
npx wp-env run cli wp plugin install <path-to-zip> --force
npx wp-env run cli wp plugin check arbictus-sync-for-e-arveldaja \
--categories=general,plugin_repo,security,performance,accessibility --include-experimental
Releases are cut by tagging and publishing a GitHub release whose tag matches both version
fields; .github/workflows/deploy-wordpress-org.yml then pushes trunk, the tag and the
/assets directory to SVN. Copy-only changes (readme wording, screenshots, a "Tested up to"
bump) go out through .github/workflows/update-wordpress-org-assets.yml on push to main.
Both need the SVN_USERNAME / SVN_PASSWORD repository secrets.
Plugin page artwork lives in .wordpress-org/ and never ships inside the zip. It is
generated, not hand-drawn:
node bin/assets/render.mjs # icon + banner PNGs from bin/assets/mark.svg
npx wp-env run cli wp eval-file \
wp-content/plugins/<dir>/bin/assets/seed-screenshots.php
node bin/assets/screenshots.mjs # screenshot-1..3 from the running admin
# The seed wipes orders and fills in the integration settings. Reset it before
# running e2e again — the suite expects an unconfigured settings screen.
npx wp-env run cli wp option delete woocommerce_efinancials_integration_settings
License
Copyright (c) 2026 Arbictus OÜ.
Free software under GPL-2.0-or-later. The plugin runs inside WordPress (GPL-2.0-or-later) and WooCommerce (GPL-3.0-or-later), so it is a derivative work and is distributed under GPL-compatible terms — as it must be.
There is no restriction on production use, company size, or revenue, and no non-profit versus commercial distinction. You may run it on any number of sites, modify it, and redistribute it under the same terms, free of charge and forever.
Releases are distributed free through the WordPress.org plugin directory, so every install updates at no cost and there is no licence key or premium build. Paid subscriptions cover support only — a service, never permission to use the code, and never access to functionality. See COMMERCIAL.md. When a subscription lapses the plugin keeps working and keeps updating; you simply stop receiving support.
The "Arbictus" name and logo, and the product name "Arbictus Sync for e-Arveldaja", are trademarks and are not licensed by the GPL. Forks are welcome under a different name.
Disclaimer
Independent, unofficial integration built against a public REST API. Not affiliated with, endorsed by, or supported by Registrite ja Infosüsteemide Keskus (RIK), or by Automattic, Inc.