Basic / Advanced Order Numbers for WooCommerce

IMPORTANT ANNOUNCEMENT: Plugin development ceased, all plugins made available freely (GPL)

With great sadness we have to announce that we are ceasing development of all our VirtueMart, WooCommerce and Joomla plugins. Effective immediately, all our plugins -- even those that were paid downloads -- are made available for free from our homepage (GPL license still applies), but we cannot and will not provide any support anymore.

It has been a great pleasure to be part of the thriving development communities of VirtueMart as well as WooCommerce. However, during the last year it became painstakingly clear that in addition to a full-time job, a young family and several other time-consuming hobbies at professional level (like being a professional singer) the plugin development and the support that it requires is not sustainable and is taking its toll. It has been an honor, but it is now time to say good bye!

Advanced Order Numbers for WooCommerce

Advanced Order Numbers for WooCommerce




Introduction / About the plugin

The most flexible and complete solution for your WooCommerce webshop to customize your order numbers and the invoice numbers of select invoicing plugins!

Documentation - Demo server - Support Forum

Endless possibilities to have order/invoice numbers the way YOU like them

Whether you want simple order/invoice numbers with a prefix, or whether you want the invoice number to match the order number, or whether you even want separate number counters for each country or even customer, OpenTools Advanced Order Numbers for WooCommerce is the solution you need!

By default, WooCommerce uses the WordPress post ID of the order, which result in gaps between the order numbers. With this plugin you can configure the order numbers to have consecutive counters, also including order-specific information like the date, address etc.

The number format is a simple string, where # indicates the counter and [...] indicates an order-specific variable.
To get order numbers like "WC-2015-1", "WC-2015-2", etc., simply set the format to "WC-[year]-#".

The plugin comes in two flavors:

  • The free basic version, which provides sequential numbers and allows arbitrary, fixed text (prefix/postfix) in the order number
  • The paid advanced version, with lots of additional features:


  Basic Advanced
Sequential order numbers with prefix/postfix/arbitrary format X X
Numbers can include the year - X
Counter formatting: initial value, counter increments, number padding - X
Lots of variables to be used in the formats
   - date/time: year, month, day, hour, etc.
   - address: customer country, zip, name, etc.
   - order-specific: Number of articles, products, order total etc.
   - product categories, shipping method
- X
Custom variable definitions (with conditions on available variables) - X
Multiple concurrent counters (e.g. numbering per country, per day, per ZIP, ...) - X
Different order numbers for free orders (e.g. "FREE-01" for free orders) - X
Different number format for e.g. certain IP addresses (for testing) - X
Different number format depending on products, product categories, shipping classes - X
Customize invoice numbers (only for the "WooCommerce PDF Invoices and Package Slips" plugin and for the PayPal payment method)  - X
Support for vendor plugins ("WC Vendors", "WooThemes Product Vendors" and "YITH WooCommerce Multi Vendors") - X


Advanced Ordernumbers examples

Advanced Ordernumbers Examples 


Easy configuration, yet powerful features

Key features of this module:

  • Freely customize order numbers and invoice numbers to your desires!
  • Number format is given as an ordinary string that can contain a vast number of variables as [variable] (date/time, order properties, customer properties, address, and even random numbers or letters) and a counter #
  • Customizable counter resets, multiple parallel counters: Counter can be global (one counter for all orders or invoices) with no reset, or yearly/monthly/daily counters, or even more general counter resets. You can even have multiple counters running concurrently (e.g. one counter per country, or different counters and thus different order/invoice numbers for wholesale and retail customers).
  • Alternatively, you can re-use the order number as invoice numbers, too (in some jurisdictions this might be problematic, because the order numbers can have gaps!)
  • Counter increments and formatting: In addition to the number format, you can choose your custom counter increment (to prevent your competitors from seeing how many orders you actually get) and the padding of the counters with zeros on the left (e.g. a counter with value 35 can be displayed as 000035 inside the number format)
  • Manually set/change your counters in the module configuration
  • Define your own custom variables for use in the number formats.
  • Use different number formats e.g. for free orders, for different products or categories, for different IP addresses (so test orders have their own numbers), or for different shipping methods


Fully configurable

Advanced Ordernumbers Settings

 You can even configure your own (conditional) variables for use inside the number formats:

 OpenTools WooCommerce AdvancedOrdernumbers CustomVariables

As you can see in the fifth line, you can even override the order number format for e.g. free orders to a format different from other orders (in this example, free orders will get order numbers FREE-2015-0100, FREE-2015-0101, etc).

The exotic...

The incredible flexibility of this WooCommerce plugin means that you do not have to stop at the usual numbering schemes involving a counter. You can even use only the date/time of the order or even random numbers as order or invoice numbers:

Advanced Ordernumbers exotic examles

Please note that in many jurisdictions there are certain restrictions on invoice numberings (mostly that they have to be / include an increasing number, and that they have to be unique). So the random invoice numbering scheme shown above would not be legal e.g. in the European Union.

Just because this module gives you the possibility to choose such invoice numberings, does not mean they are legal everywhere. Please check with your tax advisor and/or company lawyer.

To our knowledge, there are no legal restrictions on order, shipment or credit memo numberings.


All Available Variables


# Running counter (either global or per format-value); Not applicable to the order password! X X
Date and Time:
[year], [year2] Current year (4 and 2 digits) X X
[month] Current month (2 digits); leading zeros if necessary X X
[monthname], [monthname3] English month names (full and 3-letter abbreviations) X X


ISO week number
Year the ISO week belongs to (usually equal to [year], except around new year)
[day] Current day (2 digits); leading zeros if necessary X X
[dayofyear] Current day of year (1-366); no leading zeros X X
[weekday] Current weekday (1=Monday, ..., 7=Sunday) X X
[weekdayName], [weekdayName3] Current weekday name (full and 3-letter abbreviation, English) X X
[hour] Current hour in 24-hour format; leading zeros if necessary X X
[hour12] Current hour in 12-hour format; leading zeros if necessary X X
[ampm] Current am-pm (for 12-hour format) in lower-case X X
[minute] Current minute; leading zeros if necessary X X
[second] Current second; leading zeros if necessary X X
[decisecond] Current tenth of a second (1 digit) X X
[centisecond] Current hundreth of a second (2 digits, padded with 0 if neccessary) X X
[millisecond] Current millisecond (3 digits, padded with 0 if neccessary) X X
Random Numbers and Strings:
[randomDigit[n]] Random sequences of n decimal digits (n=1 if not given). X X
[randomHex[n]] Random sequences of n hexadecimal digits (n=1 if not given). X X
[randomLetter[n]] Random sequences of n (upper- and lowercase) letters (n=1 if not given). X X
[randomULetter[n]] Random sequences of n uppercase letters (n=1 if not given). X X
[randomLLetter[n]] Random sequences of n lowercase letters (n=1 if not given). X X
[randomAlphanum[n]] Random sequences of n general alphanumeric characters (A-Z, a-z, 0-9) (n=1 if not given). X X
Address information
Lastname Last name of the shopper X X
Firstname First name of the shopper X X
Company Company of the shopper X X
City City of the shopper X X
zip, PostCode ZIP of the shopper X X
Country 2-letter ISO country code X X
CountryName Full country name X X
StateName Full state name X X
State Short code for the state X X
Store, Order and Invoice details:
orderID (internal) unique ID of the order X X
orderNumber Order number, for which the invoice is created - X
orderStatus Status of the order (abbreviations: S, R, X, C, U, P) X X
Currency Currency of the order X X
Articles Total number of items (quantity) of the order X X
OrderTotal, Amount The order total / total amount of the order X X
OrderSubtotal The order subtotal before shipping and payment fees X X
TotalTax The tax amount of the order X X
TotalShipping, Shipping The shipping costs of the order X X
ShippingMethods Human-readable shipping method title (as shown during checkout) X X
IPAddress The IP address of the computer from which the order was submitted (to be used in conditions for custom variable definitions) X X
UserID (internal) ID of the user, who submitted the order    
issuborder Whether the order (for ordernumbers) is a suborder created by a multi-vendor plugin or not. If the order is a suborder, the value of this variable is larger than 0 (the parent order ID). If the order is a self-contained order, then this variable has value 0. X  
List variables (can be used only in conditions for custom variable definitions)
SKUs A list of all SKUs of all products in the cart X X
Categories A list of all Categories of all products in the cart (category SLUGs, not their human-readable names!) X X
Tags A list of all Tags of all products in the cart (SLUGs, not their human-readable names!) X X
UserRoles A list of all WordPress user roles of the customer (strings like "administrator", etc.). Empty if customer is not logged in. X X
ShippingClasses A list of all Shipping classes of all products in the cart (SLUGs, not their human-readable names!) X X

A list of all Shipping method IDs (an order can have more than one shipping method if multiple packages are shipped!). Shipping method types are the (internal) names of the shipping plugins, the shipping method ID contains the plugin name and potentially the instance id (for shipping zones) and the instance IDs are used only when Shipping zones are used. Example: method ID is "shipping_by_rules:31", type is "shipping_by_rules" and instance ID is 31.

Vendors A list of all vendors (SLUG for "WooThemes Product Vendors" or User login for "WC Vendors") as string and their numeric vendor IDs. X X


Defining custom variables

OpenTools WooCommerce AdvancedOrdernumbers CustomVariables

In this configuration section, you can define your own custom variables that can be used in the number format (e.g. you can use the prefix variable defined above like like any other pre-defined variable: "[prefix]-[year]-#". Whenever the condition on the left is fulfilled, the variable will be set to the value on the right (no further evaluation is done).

One can also overwrite built-in variables, like the country variable in the example above. This modification applies ONLY to uses of that variable inside the number format and will not modify the values stored for the order and/or user in the database or shown on the invoice.

If there are multiple assignments for a variable, the last one with matching condition will be applied.

As a special case, one can use the odernumber_format and invoice_format to use a completely different order/invoice number format if the condition is fulfilled. In the example above, free orders (i.e. the order total = 0) will use an order number format "FREE-[year]-[####:100]", regardless of the order number format configured in the general setup of this plugin.

Ignoring sub-orders with Multi-vendor plugins

Some multi-vendor extensions (like the YITH WC Multi Vendor plugin) create a sub-order for each vendor in the cart. This sub-order by default is handled just like any other order and is thus assigned a new number. The problem with this is that the top-level orders (i.e. the actual orders) will appear to have gaps in the numbering.

In the latest version, our numbering plugin adds the variable issuborder that can be used to assign a different order number format to suborders, so that top-level orders will have subsequent numbers without gaps. In the advanced version, you can use the issuborder variable like this:

OpenTools WooCommerce AdvancedOrdernumbers Suborders 

Examples of Numbering Schemes and their Settings

We have collected several basic as well as lots of innovative numbering schemes on a separate page.

 Format-specific counters

As a general rule of thumb for format-specific counters, whenever any of the variables included in the format changes, the counter will start from 1.

For example, with a format "[year]-[month]-[day]/#", the counter will start at 1 for each day. With a format "[year][month][day][hour][minute]-#", the counter will be reset each minute.  

The details

In fact, it's actually a bit more complicated: The plugin does not have one counter, that is reset at the beginning of a new year/month.
Rather is has multiple concurrent counters. When generating the order number, the plugin will first replace all [...] parts with the variable values. Then the resulting string is used as the name for the counter. The counter is looked up in the database (if not found, a value of 0 is used) and then incremented, stored back to the database and inserted for the # in the format.


This approach is best understood at an example:

Number format [year][month][day]-#:

FIRST ORDER on Aug 18, 2014:

  1. The plugin will insert all variables except for the counter.
    Result today: "20140818-#"
  2. Then it checks in the database if a counter with this name already exists.
    This is not the case, so it uses a counter of 1:
    Result today: "20140818-1"
  3. The counter value 1 for "20140818-#" is stored in the database.

NEXT ORDER on Aug 18, 2014:

  1. First, the plugin will insert all variables except for the counter.
    Result today: "20140818-#" (same as above)
  2. Now this counter already exists and has a value of 1 in the database. The plugin uses this and increases it to 2. Then it inserts this into the order number:
    Result today: "20140818-2"
  3. The counter value 2 for "20140818-#" is stored in the database.

FIRST ORDER  on Aug 19, 2014:

  1. The plugin will insert all variables except for the counter.
    Result tomorrow: "20140819-#" (different than above!)
  2. Then it checks in the database if a counter with this name already exists.
    This is not the case, so it uses a counter of 1:
    Result today: "20140819-1"
  3. The counter value 1 for "20140819-#" is stored in the database.

As you can see, with a format-specific counter, whenever any of the variables in the format changes, a new counter is created and it starts from 1.

If you want one global counter that does not reset, simply select "Global counter" in the plugin configuration. 



So, in effect it appears as if the counter is reset daily, while in fact a new counter is used for each day. This approach has the advantage that you can have e.g. different counters for orders to different countries or even for orders containing different products. If you use the format "[year]-[countrycode2]-#", each country will have its own counter and you have order numbers like "2014-AT-1", "2014-DE-1", "2014-AT-2", "2014-AT-3", "2014-CH-1", "2014-DE-2", etc. (Notice that the orders for AT have one counter, while the DE have another independent countet).

This approach is very generic (and very simple, yet really powerful), but unfortunately it also means that having a format of "[year][month]#" where the counter is reset only each year is NOT possible by default, since the format value will change each month and the format value will be used as the counter name...


Advanced Topic: Separate counter format

To solve the problem described in the previous paragraph (the counter resets if and only if any of the variables used in the format changes, which is not always what is desired), you can even give your own custom counter name. Notice that this is needed only in exceptional cases and certainly not intended for a normal installation. If you think that you really need this feature in your shop, it is probably best to ask in the support forum, because this feature has a lot of potential to cause havoc.

A typical example is a number format like "[year][month]/#" where the shop owner wants the counter to be running inside the year. By default, a new counter will be used each month (since the month variable changes each month...). Starting with plugin version 1.12, the shop owner can now use a number format "[year][month]/#", but let the plugin use a counter name of "[year]/#", which will cause the counter to be reset only at the beginning of each year. The proper format string in this case is:


This gives the shop owner even more flexibility, but is quite hard to understand with all consequences. In particular, a wrong counter format can cause orders with duplicate numbers, in which case VirtueMart will append the current timestamp to the number (leading to ugly numbers like "201410/1_2014-10-16T15:10:12". To prevent this, the counter format should ONLY use variables that are also in the number format string. Everything else will lead to duplicate numbers.

Changing counter values (or "How can I start the counter from 1234?")

For each counter, its current value is stored in the database and read from there when the plugin creates a new number. Directly after installation, each counter will start from 1 (its current value is 0). If your shop has been running for a while, you might want it to start from some other value.

The easiest way to achieve this is to set up the plugin, make one test order so that the plugin creates the counter, and then adjust its name in the plugin configuration in the backend.

If you are using a global counter, modify the "Global counter" row, otherwise find the corresponding counter and modify that. 

If you don't want to create a test order and the counter you want to modify does not exist yet, you can manually create a counter with the proper button in the table. Your browser will ask you for the counter name and then send the request to your server to create a counter with that name and initial value 0. To create the global counter, simply leave the field completely empty.
Please read the section on format-specific counters to determine the correct counter name for your format. In particular, the counter name will be the format string where all variables like [year] etc. have been replaced with their corresponding value at the time an order is made. Only the counter (the #) is left unchanged.


Known Incompatibilities

  • The iDEAL payment method does not work if the order number contains SPACES!
  • Similarly, an order number that contains - (or any character other than letters and digits!), the iDEAL payment server will return an error and the payment will fail.

The only solution is to use an order number format that only contains letters and digits, but no other characters like -, /, etc.

How can I change the order and invoice numbers of existing orders?

In general, there is no way (except directly modifying the database) to change the order number to a value of your choice. There is, however, an action to automatically assign a new order number as if the order was submitted at that moment.

Simply go to the order view in the admin backend. On the top right of that page there is a combobox providing among others an action "Assign a new ordernumber", which will create an order number as if the order was submitted at the moment you click "Save order". Please note that in this case, all date variables will use the CURRENT time and not the time when the order was initially submitted.

OpenTools WooCommerce AdvancedOrdernumbers NewNumber

Further information

Demo server - Support Forum

Frequently Asked Questions

Q: Will this plugin also change the order numbers in the database or will it just modify the display on the dashboard?

Answer: The "order numbers" in the database are by definition the IDs of the WordPress post representing the order, so the database entry itself is not (and cannot be) modified.
WooCommerce however provides a layer (through the woocommerce_order_number filter) to let third partie supply order numbers. Whenever any part or plugin of WooCommerce accesses the order number, it should/needs to call $order->get_order_number(), which in turn triggers that filter and lets our plugin return the order number.

So, technically, our plugin neither changes the database itself, but it also does not just alter the display on the dashboard. Rather, it hooks into the way WooCommerce determines the order number, so to all plugins and other extensions properly using the get_order_number() function it appears as if the order numbers are coming directly from WooCommerce. Third-party extensions should not need any modifications if they use get_order_number() to get the order number instead of directly accessing the database. The only issues are with extensions that either (a) query the database directly or (b) assume that the order number is equal to the post ID.


This plugin is licensed under the GPL v3+. When you purchase the plugin, you get the right to use the plugin on all your stores. Updates and support are included for at least 12 months.

Version History

2018-01-29: Version 1.4.4 (Added Coupon variable; fix PHP warnings)
2017-09-02: Version 1.4.3 (Fix manual order generation in the backend)
2017-08-08: Version 1.4.2 (Allow checking for empty lists in custom variable definitions)
2017-05-26: Version 1.4.1 (Fix searching and sorting by order number)
2017-04-09: Version 1.4.0 (Compatibility with WC 3.0, new varaible issuborder)
2017-03-04: Version 1.3.9 (Fix issues with WooCommerce PDF Invoices & Package slips)
2016-12-17: Version 1.3.8 (Add debug messages to the update system)
2016-12-09: Version 1.3.7 (Add PayPal support for invoice numbers; Add [UserRoles] variable; Add shipping method list variables; Fix update credentials with multiple OpenTools plugins installed
2016-07-23: Version 1.3.6 (Fix for old orders; Fix JS for update credentials to work when multiple plugins are installed)
2016-07-10: Version 1.3.5 (New variables [MonthName], [MonthName3], [Week], [WeekNumberYear], [DayOfYear], [Weekday], [WeekdayName], [WeekdayName3])
2016-05-26: Version 1.3.4 (Fix issues with PHP 5.3; Fix timezone issues)
2016-04-22: Version 1.3.3 (Fix order tracking; Add woocommerce_order_id_from_number($ordernumber) filter that returns the order ID for the given order number)
2016-04-17: Version 1.3.2 (Fix invoice numbers for PDF invoices and packaging slips)
2016-02-29: Version 1.3, 1.3.1 (Support for vendor plugins)
2016-02-21: Version 1.2.2 (Fix counter modification table in the advanced version of the plugin)
2016-01-04: Version 1.2.1 (Implement automatic updates for the advanced version of the plugin)
2015-12-13: Version 1.2 (Fix for case-sensitive random number indicators; Fix race conditions with counters for concurrent orders)
2015-05-06: Version 1.0 (Initial public release)
2015-05-02: Version 0.1.0 (Initial beta release)