Description
AMS Studio ERP turns WordPress into the back office of a small company. Quotes, invoices,
purchase orders, stock, deals, documents, treasury and accounting all live in your
own database, on your own server. There is no service to subscribe to and no second
password to remember.
The document chain
Nothing is retyped from one step to the next. A quote becomes an invoice, the invoice
calls for its payment, the payment produces its ledger entry, and every document keeps
the link back to the one it came from.
- Quotes sent by email with their PDF, then signed online by the customer
- Invoices numbered in a continuous, chronological series
- Payments entered by hand or matched to an imported bank statement
- Ledger entries posted to the right journal and financial year
What holds up under audit
These rules live in the engine, and the plugin refuses whatever breaks them.
- Numbering stays continuous: a prefix already in use cannot change, and a reopened
document keeps its number - An approved document is sealed by a fingerprint over its amounts, its party and its
number; a mistake is corrected by a credit note, never by editing - VAT is broken down by rate, as the law requires on the document itself
- Factur-X (PDF/A-3) and Peppol BIS Billing 3.0; a non-compliant invoice refuses to go
out, in print, by email and on the public page - Attachments are encrypted with AES-256-GCM, outside the public media library
- Exact decimal arithmetic throughout, never floating point
What this plugin does
- Customers: records, addresses, contacts, categories
- Catalogue: products and services, purchase and sale prices, margins, VAT rates
- Quotes: drafted, sent by email with their PDF, signed online by the customer
- Customer invoices and credit notes: continuous numbering, deposits, payment terms,
what is paid and what remains due - Factur-X (PDF/A-3) and Peppol BIS Billing 3.0 on every invoice
- Encrypted attachments, exact decimal arithmetic, an audit trail on every document
What it does not do
Purchasing, stock, deals, treasury, accounting, the document library and the
WooCommerce bridge are not part of this plugin. They come with AMS Studio Pro, a
separate add-on installed alongside it and distributed from https://www.ams-studio.lu/
Nothing here is capped, time-limited or waiting for a key: what you install is
complete. The modules above are simply not in this package — the menu shows them so
you know they exist, and says where to get them.
Languages
French and English. The plugin follows your site’s language, or its own if you ask it to —
an English site can run a French ERP.
Third-party libraries
Producing an invoice as a PDF needs an HTML-to-PDF engine, and WordPress core
has none. Four libraries are bundled, unmodified except where noted, each with
its own licence file in its directory — all GPL-compatible:
- dompdf/dompdf 3.1.6 — LGPL-2.1 — https://github.com/dompdf/dompdf
- masterminds/html5 — MIT — https://github.com/Masterminds/html5-php
- sabberworm/php-css-parser — MIT — https://github.com/MyIntervals/PHP-CSS-Parser
- php-font-lib — LGPL-2.1 — https://github.com/dompdf/php-font-lib
They are bundled rather than pulled by Composer because the plugin must install
from the WordPress admin, on shared hosting, with no command line. Their
namespaces are prefixed under AMSBM\Vendor\ with PHP-Scoper, so that two
plugins bundling dompdf in different versions cannot break each other; the
prefixing is reproducible with bin/scoper.sh. Every other deviation from the
upstream code is marked in place and listed in lib/pdf/ECARTS.md: a direct-access guard on the two files that carry code
outside a class, embedding a Factur-X XML attachment into a PDF/A-3 file which
dompdf does not support upstream, heredoc blocks rewritten as concatenation,
and the removal of SVG rendering.
dompdf’s fifth dependency, php-svg-lib, is deliberately not bundled. It
exists only to draw SVG images inside a PDF, and drawing one means parsing XML
that a site user supplied. The plugin does not need it — logos are PNG, JPEG,
GIF or WebP, and an SVG is refused at upload — so the smaller attack surface is
worth more than the feature. The three places where dompdf reached for it are
neutralised in place, and an SVG is simply not a recognised image type any
more.
One more deviation is replayed automatically rather than by hand: in
php-font-lib, glyf::toHTML() — a font-debugging method that builds an HTML
page with inline scripts — is emptied. Neither dompdf nor the plugin calls it.
bin/pdf-patches.php applies that change after every copy of the libraries
(bin/vendor-pdf.sh) and again after prefixing (bin/scoper.sh), and the
package build (build-zip.sh) refuses to proceed if it is missing.
Compiled admin interface
The admin interface is a React/TypeScript application, compiled with Vite. The
compiled, minified bundle is in app/build/; its unminified sources ship with
the plugin, in app/src/, along with everything needed to rebuild it:
app/package.json, app/package-lock.json, app/tsconfig.json,
app/tsconfig.node.json, app/vite.config.ts, app/tailwind.config.js and
app/postcss.config.js.
The same sources are publicly available at https://github.com/SanMat11/wp-erpx
To rebuild the bundle — Node.js 18 or 20 and later, verified with Node.js 22:
cd app && npm ci && npm run build
npm ci installs the exact dependency versions recorded in
app/package-lock.json. `npm run build` type-checks the sources with TypeScript
5.9, then builds them with Vite 5.4 into app/build/. The file
app/build/morceaux/vendor-[hash].js is the npm dependencies listed in
app/package.json — React, Ant Design and the others — bundled together by
Vite; each package’s name and exact version are in app/package-lock.json.
Vite writes the entry point as amsbm-app-[hash].js and amsbm-app-[hash].css,
with the code split into chunks under app/build/morceaux/. The hash in the
filename is the version: a new build produces a new name, so a cached file is
never a stale one. app/build/manifeste.json records the current pair, and the
plugin reads it rather than guessing.
Nothing about the plugin requires npm at runtime. The bundle is committed and
shipped because the target audience installs from the WordPress admin, on shared
hosting, with no command line.
External services
VIES — EU VAT number validation (European Commission)
Used only when you click “check” beside an EU VAT number, to confirm that the number
exists. What is sent: the country code and the VAT number, in the request URL. Nothing
else — no company name, no site address, no identifier.
Service: https://ec.europa.eu/taxation_customs/vies/
Terms and privacy: https://ec.europa.eu/taxation_customs/vies/#/help
Stripe — card payment of your invoices (Stripe, Inc.)
Used only if you enter your own Stripe API keys in the settings. It lets a customer of
yours pay a shared invoice by card. What is sent, when your customer clicks “pay”: the
amount, the currency, the invoice reference and the return address. The plugin never
sends your customer list or your documents.
Service: https://stripe.com/
Terms: https://stripe.com/legal/ssa
Privacy: https://stripe.com/privacy
AMS Studio licence server (AMS Studio, Luxembourg)
Only reached if you click “Upgrade to Pro” in the application header. That button is a
link: your browser opens it, the plugin itself opens no connection and sends nothing on
its own. What travels in that link: an installation identifier drawn at random on your
site, a one-way hash of a secret drawn with it, your site address and your admin email
address — so that the licence you buy is tied to this site and fills itself in when you
install the paid package, with nothing to copy by hand. The hash cannot be reversed, and
the secret itself never appears in the link.
Nothing is sent if you never click the button, and nothing is sent on a schedule.
Service: https://licences.ams-studio.lu/
Terms and privacy: https://www.ams-studio.lu/
The plugin contacts nothing else. No telemetry, no update server, no licence check.
Screenshots






Installation
- Install and activate the plugin as usual.
- Open AMS ERP in the admin menu and fill in your company details.
- Optionally install the demo data set to look around before entering your own.
No command line is required, and no account anywhere: the plugin ships its own autoloader and its own PDF engine. Two optional features do contact an outside service when you use them — see “External services” below.
FAQ
-
Is my data sent anywhere?
-
Your business data stays put. Documents, customers, articles and ledger entries live
in your own database and are never sent anywhere.Two features do reach outside, and only when you ask them to:
- VAT number validation. When you click “check” next to an EU VAT number, that
number and its country code are sent to VIES, the European Commission’s service.
Nothing else. If you never click, nothing is ever sent. - Card payment of an invoice. If — and only if — you enter your own Stripe keys,
your customer can pay a shared invoice by card. Stripe then receives the amount, the
currency and the invoice reference, at the moment your customer clicks “pay”.
Both are documented in full under “External services” below.
- VAT number validation. When you click “check” next to an EU VAT number, that
-
Is my data deleted when I uninstall?
-
No. Dropping the tables requires adding
define( 'AMSBM_REMOVE_ALL_DATA', true );to
yourwp-config.php. Accounting retention obligations — six years in France, up to ten
elsewhere in Europe — outlast the lifetime of any plugin. -
Does it need InnoDB?
-
Yes. Without a transactional engine, operations cannot be made atomic. The built-in
health check reports it. -
Are any features locked or limited?
-
No. Every feature in this package works, with no cap on the number of customers,
quotes, invoices or articles, and no trial period.Some modules — purchasing, stock, deals, treasury, accounting, the document library
and the WooCommerce bridge — are not in this package at all. They ship with a separate
add-on. The menu lists them so you know they exist; they are not disabled code waiting
for a key. -
Do I need Composer or npm to install this?
-
No. The plugin ships its own autoloader, its own PDF engine and its compiled
admin interface.composer.jsonand the JavaScript sources are included so that
the build is reproducible and the dependencies are auditable, not because
anything has to be run. -
What happens to my data if I deactivate the plugin?
-
Nothing. Deactivating stops the code from running; the tables, the documents and
the ledger stay exactly as they are, and reactivating picks up where you left
off. -
Which chart of accounts is supported?
-
The French plan comptable général and the Luxembourg plan comptable normalisé 2020.
Accounts, journals and financial years are all editable.
Reviews
There are no reviews for this plugin.
Contributors & Developers
“AMS Studio ERP — Invoicing & Accounting” is open source software. The following people have contributed to this plugin.
ContributorsTranslate “AMS Studio ERP — Invoicing & Accounting” into your language.
Interested in development?
Browse the code, check out the SVN repository, or subscribe to the development log by RSS.
Changelog
0.5.25
- On a site without the stock, WooCommerce or accounting modules, the admin
interface no longer requests routes that do not exist there: the dashboard
only asks for stock alerts when stock is installed, the VAT settings only list
accounting accounts when accounting is, and the Warehouses and Store settings
tabs only appear when their module does. - Demo data no longer writes purchase orders and supplier invoices on a site
that has no purchasing module.
0.5.24
- Plugin Check reports nothing left in the plugin’s own code. The hook fired when
the installation fingerprint is renewed is now fired by its literal, prefixed
name. The one call it discourages,load_plugin_textdomain(), says why it
stays: the plugin ships its own English catalogue, which WordPress only finds
through that call.
0.5.23
- The print page of a document is no longer echoed as one string. Its template
is included directly in the response, and escapes every value at the exact
place where it is output. - No inline JavaScript left: the print button, the confirmation before a status
change, the setup fields to copy and the “copy backup codes” button now use a
script enqueued withwp_enqueue_script(), or printed with
wp_print_inline_script_tag() on the standalone print page. The “copy backup
codes” button, which copied nothing, now copies. - PDF downloads, accounting exports and stored attachments are sent with
X-Content-Type-Options: nosniff. - Bundled php-font-lib: a font-debugging method that built HTML with inline
scripts, and that nothing calls, is emptied — see “Third-party libraries”.
0.5.22
- The sources of the admin interface shipped in
app/src/can be rebuilt from
the plugin package alone again. A screen that is not part of this package was
imported unconditionally, sonpm run buildstopped on a missing file; it is
now loaded only where its file exists. - The same sources are published at https://github.com/SanMat11/wp-erpx, and the
readme gives the Node.js version, the exact command and where the output goes.
0.5.21
- English is translated again. The English catalogue was deleted during the
0.5.7 rename and never rebuilt: since then every string written by the server
— API refusals shown on screen, e-mails, PDFs, meta boxes — came out in French
on English sites, while the interface itself switched. All 971 strings the
code asks for are now translated and shipped as
languages/ams-studio-erp-invoicing-accounting-en_US.mo. - A quote could not be accepted in English. The wording a customer copies
out to accept a quote was compared against the Frenchbon pour accord, while
the refusal message told English customers to type “Agreed and accepted”. They
typed exactly what was asked and were turned away. The expected wording now
follows the language of the site, and French stays accepted so agreements
given before a language change remain valid. - Four screen strings still quoted the French wording inside English sentences,
and two were left in French outright. The screen, the refusal message and the
check now say the same thing.
0.5.20
- The text domain is now
ams-studio-erp-invoicing-accounting, matching the
slug registered on WordPress.org. It wasams-studio-business-managementon
1155 i18n calls, which prevented the plugin from being translated through
translate.wordpress.org. The PHP prefixamsbm_, the option, table, post
type and REST namespace names are unchanged, as is the main file name. - Internal: the translation catalogue built by
bin/i18n/was named
amsbm-.po, which matched no text domain — WordPress would never
have loaded it. It now carries the domain, as the format requires.
0.5.19
- Internal: the Peppol reader no longer emits a PHP warning when an incoming
invoice omits optional elements — payment terms, legal entity, trading name
or tax scheme. Absent elements read back as an empty string, as intended. - Internal: the test asserting that every document type is reachable by a role
performed no assertion at all: it returned on the first role covering
everything. It now checks the business roles, and was seen to fail when a
document type is unwired from them.
0.5.18
- Internal: the bundled PDF libraries — dompdf, php-font-lib, masterminds/html5
and sabberworm/php-css-parser — now live under theAMSBM\Vendor\namespace,
prefixed with PHP-Scoper. Two plugins bundling dompdf in different versions
can no longer break each other. A document renders byte-for-byte as before.
0.5.17
- Internal: the document editor’s article catalogue no longer writes its
tag by hand. It goes throughwp_print_inline_script_tag(), which
carries the page’s Content-Security-Policy nonce where there is one. It stays
a JSON data block, not executable code. Nothing changes on screen.
0.5.16
- Security: SVG rendering has been removed from the PDF engine, and the
php-svg-lib library with it. Drawing an SVG meant parsing user-supplied XML
inside the printing chain, for a format the plugin never needed: logos are
PNG, JPEG, GIF or WebP. An SVG is now refused when the logo is uploaded, and
refused again before it reaches the PDF. Documents without an SVG render
byte-for-byte as before.
0.5.15
- Internal: every SQL query now goes through
wpdb::prepare(), with no exception.
Table and column names are bound with the%iplaceholder instead of being
interpolated into the query string, and the plugin’s own table names are checked
against a hard-coded list before any query is built. Nothing changes on screen:
the 132 read routes return byte-for-byte identical answers.
0.5.14
- Internal: the public quote and invoice page no longer writes its script tags by hand.
They go through the WordPress loader, which now also carries the page’s
Content-Security-Policy nonce. Nothing changes on screen.
0.5.13
- Internal: four blocks in the bundled PDF libraries were rewritten from heredoc to
string concatenation, so that the whole package passes the directory’s automated
check. The generated PDFs are byte-for-byte identical. Every deviation from the
upstream libraries is now listed inlib/pdf/ECARTS.md.
0.5.12
- The “Upgrade to Pro” link in the header now offers to subscribe, in so many words:
the dialog it opens says “Subscribe” instead of “Learn more” and states the price —
the same one the website quotes. It no longer names an empty module when it was not
opened from a particular screen.
0.5.11
- Internal: this package now refuses any update whose archive does not come from where
it says it does. Nothing changes for you here — the free edition is updated by
WordPress.org as it should be.
0.5.10
- Removing the demo dataset and installing it again left you with no customers and no
products at all — while reporting it had created twenty. Removing deleted the
WordPress posts but not the plugin’s own rows, and the next install reused a post id
that pointed at nothing. - A company named “Roussel & Fils” read “Roussel & Fils” in the demo dataset.
- An IBAN broke into six stacked pieces in the treasury list, and an IBAN entered with
spaces was regrouped wrongly. A cash account shows a dash instead of an empty box.
0.5.9
- This installation now has an identity of its own — an identifier drawn at random on
your site, and a secret drawn with it. Nothing is sent anywhere: they exist so that a
licence bought later belongs to this site and fills itself in, instead of being copied
by hand. See “External services” for the one moment anything leaves, and what it is. - The activate button no longer sits flush against the licence field.
0.5.8
- Screens that spoke French on an English site. The public invoice page a customer
opens to pay, the goods receipt screen and the online payment settings carried their
wording inside the code instead of in the catalogues: 87 sentences that no
translation could reach. They are now in both catalogues, English included. - The subscription screen showed its translation keys instead of its labels — its
whole set of 80 keys was in neither catalogue. It also named the four licence states
it had never been given a word for, starting with a site that simply has no key.
0.5.7
- This package now contains exactly what it runs. Purchasing, stock, deals, treasury,
accounting, the document library and the WooCommerce bridge moved to a separate
add-on — their code is no longer shipped here. Nothing is capped or locked: what is
present works, in full. - New name. The plugin was called ERP-X; it is now AMS Studio ERP.
Everything it declares was renamed with it — options, tables, custom post types,
capabilities, hooks and the REST namespace. Nothing carries over from the old
names, so this version is an installation, not an upgrade. - Nothing in this package is capped, time-limited or tied to a key. What is here
is installed and open; what is not here is not shipped at all. - Electronic invoicing follows. Factur-X (PDF/A-3) and Peppol BIS Billing 3.0
used to be conditional; they now apply to every invoice, which is what a legal
obligation ought to be. - The plugin no longer looks anywhere but here for its updates.
0.5.6
- A document the WooCommerce bridge found wrong now says so before you click. The
bridge marks it, approval refuses it — but nothing showed the reason, so the refusal
arrived out of nowhere. - The supplier invoice screens stop offering what the server will refuse: five write
actions in the list, and approving a credit note, are now hidden from roles that do
not have the right — and the reason is stated, with who to ask. - A supplier credit note table headed two different columns “Amount excl. VAT”.
- The credit note route names the capability it needs instead of relying on the global
write right.
0.5.5
- The plugin’s own header is translated at last. Its name and description were in no
catalogue, so an English site read the description in French on its plugins screen —
the one place never reread. The extractor now covers the header, as xgettext does. - One header dropped: the plugin address and the author address may not be the same,
and the site at ams-studio.lu presents the plugin.
0.5.4
- Every PHP file the plugin ships now refuses a direct HTTP call. On shared hosting
wp-content is often served without going through WordPress, and a file reached that
way ran outside it — printing the server’s absolute path in the fatal error that
followed. A test counts them all and fails on the next file shipped without a guard. - The two-factor QR script goes through the WordPress script queue.
- Repository compliance: exemptions declared for the directory’s own database sniff,
which is stricter than the WordPress coding standard, and one core function dropped
in favour of a plain expression.
0.5.3
- The plugin is named ERP-X throughout, with a new mark drawn in one place and used on
the admin menu, the sidebar and the sign-in screen.
0.5.2
- fail2ban on the API, installation blocking, and an allow list on the administration.
0.5.1
- Sealing: an invoice or credit note the WooCommerce bridge found wrong can no longer be
approved by any path. - Electronic invoicing: a non-compliant invoice no longer goes out by email or on the
public page either. - Purchasing: only what has been received is invoiced; invoicing ahead must be asked for.
0.5.0
- Two-factor authentication and the per-module permission matrix.
0.2.0
- The sales cycle end to end: items, parties, quotes, invoices.
- Multi-rate VAT rounded per rate at the foot of the document.
- Quote to invoice transformation, with a line-by-line trace.
0.1.0
- Exact decimal arithmetic, with no silent overflow.
- Numbering by atomic UPDATE, refusing documents backdated below the last period used.
- Migration lock through a MySQL GET_LOCK, genuinely atomic.
