EMS PDF Invoice
Source |
First-party — auto-fetched from its GitHub release (private repo) |
Purpose |
Per-customer branding, billing-field labels and EFT details on the PDF invoice |
Special infra |
None (no writable filesystem paths) |
This plugin does not generate PDFs. It is a customisation layer over WooCommerce PDF Invoices, hooking that extension’s filters to swap in a branded template, re-colour it, relabel the billing fields, and add an EFT payment card. Every customer-specific value is configuration, so one artefact serves every site.
Install — auto-fetched release
Ours, so no hand-staging. The build fetches the pinned GitHub release asset
(ems-pdf-invoice-<ver>.zip, root folder ems-pdf-invoice/):
# scripts/build-image.sh — before the docker build
gh release download "${EMS_PDF_INVOICE_VERSION}" \
-R <org>/ems-pdf-invoice --pattern 'ems-pdf-invoice-*.zip' --dir packages --clobber
unzip -q packages/ems-pdf-invoice-*.zip -d web/app/plugins # -> web/app/plugins/ems-pdf-invoice
Activated in bootstrap after both WooCommerce and the PDF Invoices extension, which it depends on. If either is missing it registers no hooks and raises an admin notice.
Configuration
Everything lives in one option, ems_pdf_invoice_settings. Nothing in it is a secret —
bank details are printed on every invoice — so unlike most plugin config it can be declared in
git in full. Defaults reproduce the original single-customer output, except the bank fields,
which default empty.
Because the plugin owns this option end to end, it is declared whole in the chart’s
bootstrap.options, not patched key by key. That is the opposite of the vendor’s option, which
must be merged — see the warning there.
Behaviour
| Key | Default | Meaning |
|---|---|---|
|
|
Master switch. Off leaves the vendor’s stock template and branding in place, which is the quickest way to rule this plugin out when diagnosing a rendering problem. |
|
|
Off suppresses the generated palette block, for a customer supplying wholly custom CSS. |
|
|
Off keeps the vendor’s template but still expands the custom placeholders. |
Brand palette
| Key | Default | Meaning |
|---|---|---|
|
|
Headings, card rules, the line-item heading row. |
|
derived |
EFT card background. Left empty it is derived by mixing |
|
|
Text on the brand-filled heading row. |
|
greys |
Body text, secondary labels, rules, panel fills, alternating rows. |
Layout, labels and custom fields
| Key | Default | Meaning |
|---|---|---|
|
|
Logo box; the originals were tuned to one customer’s asset. |
|
|
Absolute filesystem path to a logo, bypassing the media library — so branding can be baked into the image and versioned with it rather than uploaded by hand. |
|
|
Empty uses the vendor’s own translated "Invoice". |
|
|
Billing-field labels. An empty label renders the value unlabelled. |
|
|
Candidate order-meta keys; first non-empty wins. |
|
registration / PO number |
Two configurable order-meta slots — |
EFT payment card
| Key | Default | Meaning |
|---|---|---|
|
|
Off suppresses the card regardless of gateway or bank details. |
|
|
Card heading. |
|
|
Gateway IDs that trigger the card. Add the site’s own ( |
|
"…use Invoice No {reference}…" |
|
|
empty |
Bank details. Empty rows are omitted, and when all are empty the whole card is suppressed — so an unconfigured site prints nothing rather than an empty box. |
How the palette is injected
The colours are not templated. The vendor exposes a
pdf_template_additional_template_css filter whose return lands last inside <style>, and
which nothing else uses. The plugin generates an override block there, so equal-specificity
rules win by cascade order without !important, and the template keeps its original literals
as a fallback if the filter never fires.
|
CSS custom properties ( |
The failure worth knowing about
The template override works by handing the vendor a relative path that climbs out of its templates directory — the only route available to a plugin, since the vendor’s other override path is inside the read-only theme (see the override contract).
If that traversal breaks — a renamed vendor directory, a restructured image — the invoice still renders, just with the vendor’s stock design. Nothing errors. Two guards exist:
wp ems-pdf-invoice doctor
reports whether the override resolves, alongside dependency and palette checks; and every
rendered invoice carries an <!-- ems-pdf-invoice v<version> -→ marker in <head>.
The complementary symptom is the opposite one: if a raw or
appears on an invoice, the plugin is not running at all — deactivated, or
one of its two dependencies missing — rather than misconfigured. When it is running it clears
its own tokens even for an order it cannot resolve.
Re-run the check after any upgrade of the vendor extension.
Sites not on the EMS chart
For a WordPress site provisioned some other way, wp ems-pdf-invoice seed reads the same
settings from EPI_* environment variables (brand_colour → EPI_BRAND_COLOUR).
Updates
Self-updating is opt-in via EMS_PDF_INVOICE_UPDATE_REPO / EMS_PDF_INVOICE_UPDATE_TOKEN, and
is skipped entirely where DISALLOW_FILE_MODS is set or under WP-CLI. On an immutable image the
check cannot lead to an install, so enabling it would only make an outbound call to GitHub and
raise a nag nobody can action. Upgrades follow the normal path: bump the pinned version and
rebuild the image.