Invoice numbers explained

We often receive questions about the sequential character of PDF invoice numbers from our WooCommerce PDF Invoices plugin:

  • Why is the PDF invoice number different from the woocommerce order number?
  • When is the PDF invoice number assigned?
  • How do I reset the invoice numbers?

This article explains exactly how invoice numbers work, and why.

The answer is simple: because many countries require invoice numbers to be sequential (gaps are not allowed). WooCommerce or WordPress don’t automatically do this for you.

By default, WooCommerce orders are numbered in the main WordPress post numbering system. This includes everything in WordPress: posts, pages, images, orders, coupons, products, etc. So if you create a new product between when two orders are placed, and upload an image for that product too, the order number is not sequential (if the first order was #100, the product would be #101, the image #102 and the next order #103). This is not ideal for invoices.

There’s a plugin that fixes this behavior: WooCommerce Sequental Order Numbers. This will make sure that in the above example, the order sequence would not be broken. However, for invoice numbers, this is still not accurate enough. What about fake orders, or orders that are never finished because there was an issue with the payment and the customer places a new order? This is where a separate invoice numbering system comes in! Which leads us to the next question:

Invoice numbers are assigned at the moment a PDF invoice is created, either manually (by pressing the buttons or executing the bulk action), or automatically (by means of email attachment). This depends completely on the plugin settings. If you attach the invoice to the Admin New Order email, this means the invoice (and invoice number!) will be created immediately after the order is placed – for most setups, this is not recommended! The ‘safest’ way is to attach the PDF invoice only to the Customer Completed Order email and the Customer Invoice email. This will make sure an invoice number will not be created before the order is completed.

If you want to send the customer an invoice without affecting the invoice numbers, you can use the WooCommerce PDF Invoices & Packing Slips Professional extension: this will allow you to create Proforma Invoices, which have their own, separate (sequential) numbering system.

We can’t change invoice numbers from invoices that have already been issued (that would be fraud!), but with the WooCommerce PDF Invoices & Packing Slips Professional extension you can create Credit Notes. These are negative invoices that can either have a number that is in the same sequence as the invoice numbers, or a completely separate (again, sequential) numbering system.

You can reset the invoice numbering in the plugin settings, under Template “Next invoice number”. Enter the number that you want to use for the first invoice that will be created here. Note that this will not change or remove any existing invoice numbers!

If you don’t need the invoice numbers to be sequential, and you want the invoice numbers to match the order numbers, you can do this by adding the code below to your themes functions.php. If you have never edited your themes functions.php before, be careful and make sure to read this page: how to use filters.

Simply use the order number as invoice number:

/**
 * Use order number as invoice number
 */
add_filter( 'wpo_wcpdf_invoice_number', 'wpo_wcpdf_format_invoice_number', 20, 4 );
function wpo_wcpdf_format_invoice_number( $invoice_number, $order_number, $order_id, $order_date ) {
    return $order_number;
}

A more complex version of the above filter, this will format the order number with all the settings you have configured for the invoice number, and show it instead of the invoice number:

/**
 * Format order number with invoice number settings
 */
add_filter( 'wpo_wcpdf_invoice_number', 'wpo_wcpdf_format_invoice_number', 20, 4 );
function wpo_wcpdf_format_invoice_number( $invoice_number, $order_number, $order_id, $order_date ) {
	// We want to use the order number as invoice number!
	$invoice_number = ltrim($order_number, '#');

	// get format settings
	$template_settings = get_option('wpo_wcpdf_template_settings');

	$formats['prefix'] = isset($template_settings['invoice_number_formatting_prefix'])?$template_settings['invoice_number_formatting_prefix']:'';
	$formats['suffix'] = isset($template_settings['invoice_number_formatting_suffix'])?$template_settings['invoice_number_formatting_suffix']:'';
	$formats['padding'] = isset($template_settings['invoice_number_formatting_padding'])?$template_settings['invoice_number_formatting_padding']:'';

	// Replacements
	$order_year = date_i18n( 'Y', strtotime( $order_date ) );
	$order_month = date_i18n( 'm', strtotime( $order_date ) );

	foreach ($formats as $key => $value) {
		$value = str_replace('[order_year]', $order_year, $value);
		$value = str_replace('[order_month]', $order_month, $value);
		$formats[$key] = $value;
	}

	// Padding
	if ( ctype_digit( (string)$formats['padding'] ) ) {
		$invoice_number = sprintf('%0'.$formats['padding'].'d', $invoice_number);
	}

	$formatted_invoice_number = $formats['prefix'] . $invoice_number . $formats['suffix'] ;

	return $formatted_invoice_number;
}

Internally, the invoice number is created like this:​

  • Whenever an invoice is requested (could be automatic email attachment or manual creation via the backend), the plugin checks if an the order already has an invoice number (stored as order meta data under ‘_wcpdf_invoice_number’)
  • When no invoice number is present in the order, it
    1. fetches the ‘next invoice number’ from the database (as defined in your plugin settings)
    2. Assigns that number to the order
    3. Adds 1 to the ‘next invoice number’ and save to the database.
  • When an invoice number is present in the order, it uses that that without going through the ‘next invoice number’ loop.