Numbering
Sequential Numbering
Tax-compliant invoice numbering is the most common reason store owners come to us from other plugins. Some plugins skip proper concurrency handling and produce duplicates under load. Others handle concurrency well but don't show a live preview of your number format. Easy PDF Invoices does both: atomic database-locked increment with a live preview of the format in settings.
How it works
When an order needs an invoice number, the plugin:
- Opens a database transaction.
- Issues
SELECT ... FOR UPDATEon the counter row, blocking other requests until the transaction completes. - Reads the current number, increments it, writes it back.
- Commits the transaction. Other waiting requests can now proceed in order.
For hosts that don't support FOR UPDATE (rare, but some shared hosts disable transactions), the plugin falls back to optimistic compare-and-set with bounded retries. If a write conflict happens, the plugin re-reads, re-increments, and tries again up to three times before giving up.
Settings
Go to WooCommerce > Settings > Invoices > Numbering.
Format
A live preview shows what your next invoice number will look like as you change settings. For example, with prefix INV- + date {YYYY}- + 6-digit padding starting at 1, the preview shows INV-2026-000001.
Prefix
Free text that appears before the number. Default: INV-.
Date placeholders are supported:
{YYYY}— full year, e.g.,2026{YY}— short year, e.g.,26{MM}— month with leading zero, e.g.,04{DD}— day with leading zero, e.g.,09
INV-{YYYY}-{MM}- produces INV-2026-04-000001.
Suffix
Free text that appears after the number. Default: empty.
Date placeholders work here too. Some EU stores prefer /{YYYY} as the suffix instead of in the prefix, e.g., 000042/2026.
Padding
Number of digits to pad the sequential number with leading zeros. Default: 6 (so 000001, 000002, ...).
Range: 1–10. Pick what fits your tax authority's requirements. Six digits supports up to 999,999 invoices per year, which covers most stores forever.
Start number
The first number to use. Default: 1.
Useful when migrating from another system: set start to 5001 if your old plugin reached 5000 and you want to continue without overlap.
Annual reset
Toggle on if your accounting period requires the counter to reset every year (most EU and Latin American jurisdictions).
When enabled, the plugin stores both the current number and the year in the counter row. On the first order of a new calendar year, the year-change is detected inside the same database lock, the number resets to your Start number (or 1), and the year is updated. No skipping, no duplication, no manual intervention.
Combined with the {YYYY} placeholder, this produces clean year-scoped sequences:
INV-2025-001234...INV-2025-001235... (2025 ends)INV-2026-000001...INV-2026-000002... (2026 begins)
Next number display + manual override
The settings screen shows the next number that will be used. If you need to change it (for example, you set the start number wrong, or you manually issued an invoice outside the system), click Override.
A confirmation dialog asks you to type the new value. The plugin verifies the value isn't already in use by an existing order, then writes it to the counter atomically.
Use this carefully. Setting the next number lower than already-issued numbers can break tax compliance. The plugin warns you, but it doesn't prevent you (sometimes you legitimately need to override after data restoration or a migration).
Credit note numbering
Credit notes have their own counter so credit note numbers don't collide with invoice numbers.
The Numbering tab includes the same fields again under Credit notes:
- Prefix (default:
CN-) - Suffix (default: empty)
- Padding (default: 6)
- Start number (default: 1)
- Annual reset (independent toggle)
- Next number display + manual override
CN-2026-000001.
Persistence rules
Across status changes
Once a number is assigned, it persists. If the order moves from Processing (Invoice) to Completed (Receipt), the same number stays. If it moves further to Refunded, a Credit Note is generated with its own credit-note number — but it references the original invoice number on the document.
Across plugin deactivation
By default, deactivating the plugin keeps:
- The current next-number counter.
- All existing invoice numbers on orders.
- Generated PDF files.
- Order meta.
Across uninstall
Settings > Invoices > Advanced > Uninstall behaviour controls what happens when the plugin is deleted from the Plugins screen:
- Keep data (default) — same as deactivation.
- Delete everything — counters, settings, order meta, and PDF files are removed.
Working with the number programmatically
Read the assigned number
$order = wc_get_order( 123 );
$raw_number = (int) $order->get_meta( '_epdi_invoice_number' );
$display_number = $order->get_meta( '_epdi_invoice_display_number' );
echo $display_number; // INV-2026-000042
Format your own number
The epdi_display_number filter lets you change how the formatted display number is rendered without altering the underlying counter:
add_filter( 'epdi_display_number', function( $display, $raw_number, $order ) {
if ( $order->get_meta( '_b2b_order' ) === 'yes' ) {
return 'B2B-' . $display;
}
return $display;
}, 10, 3 );
Listen for number assignment
add_action( 'epdi_number_allocated', function( $order, $invoice_number, $display_number ) {
// Sync to your accounting tool, log to an audit trail, etc.
your_accounting_sync( $order->get_id(), $display_number );
}, 10, 3 );
Common scenarios
"Two orders just got the same number"
This shouldn't happen with Easy PDF Invoices. If it does, check:
- Is your database server transactional? InnoDB and most modern engines support
FOR UPDATE. MyISAM doesn't. RunSHOW TABLE STATUSand confirm the engine. - Are you running multiple WordPress instances against the same database? The lock works at the database level so this should still be safe, but verify there's no caching layer (Redis, memcached) intercepting the counter read.
- Did you manually override the counter? Check the Numbering tab and the order meta for
_epdi_invoice_number. The override flow has a uniqueness check, but a forced direct database write would bypass it.
"I need to skip a number"
Numbers are issued atomically and you can't skip without manually advancing the counter. Use the Manual override in the Numbering tab to jump the counter forward. The skipped numbers won't be assigned to anything.
Some tax authorities require contiguous numbering — check your local regulations before skipping.
"I migrated from another plugin and need to keep my old numbers"
Two options:
- Continue the sequence. Set the start number to
last_number + 1from your old system. New orders get fresh numbers, old orders keep what they had. - Backfill old orders with their original numbers. Use the REST API or a one-time script to set
_epdi_invoice_numberand_epdi_invoice_display_numberon each existing order. The plugin's Backfill tool handles this if your old plugin stored numbers in compatible meta keys; otherwise a custom script is needed.
CLI access
The plugin exposes WP-CLI commands for testing and diagnostics:
# Check plugin and counter health
wp epdi diagnose
# Generate a test PDF for an order (uses the next available number)
wp epdi test-pdf 123
# Force a specific document type
wp epdi test-pdf 123 --type=receipt
# Force regeneration even if a PDF already exists
wp epdi test-pdf 123 --force
See Developer Guide for the complete CLI reference.