Sequential Invoice Numbers for Orders
Gives each paid order its own gap-free invoice number, such as INV-2026-0001, and shows it on the order screen, in emails and in the customer's order details.
Opens WPIDE on
Code
$wpide_invoice = array(
'prefix' => 'INV-{year}-', // {year} is replaced with the current year.
'start' => 1, // First number used.
'digits' => 4, // Zero-padded length.
);
$wpide_invoice_assign = function ( $order_id, $order = null ) use ( $wpide_invoice ) {
global $wpdb;
$order = $order instanceof WC_Order ? $order : wc_get_order( $order_id );
if ( ! $order || $order->get_meta( '_wpide_invoice_number' ) ) {
return;
}
add_option( 'wpide_invoice_counter', (string) ( (int) $wpide_invoice['start'] - 1 ), '', false );
// Atomic increment: LAST_INSERT_ID(expr) returns the new value to this connection only.
$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->options} SET option_value = LAST_INSERT_ID(option_value + 1) WHERE option_name = %s", 'wpide_invoice_counter' ) );
$number = (int) $wpdb->get_var( 'SELECT LAST_INSERT_ID()' );
wp_cache_delete( 'wpide_invoice_counter', 'options' );
if ( $number < 1 ) {
return;
}
$invoice = str_replace( '{year}', wp_date( 'Y' ), $wpide_invoice['prefix'] ) . str_pad( (string) $number, $wpide_invoice['digits'], '0', STR_PAD_LEFT );
$order->update_meta_data( '_wpide_invoice_number', $invoice );
$order->update_meta_data( '_wpide_invoice_date', time() );
$order->save_meta_data();
};
add_action( 'woocommerce_order_status_processing', $wpide_invoice_assign, 5, 2 );
add_action( 'woocommerce_order_status_completed', $wpide_invoice_assign, 5, 2 );
add_action( 'woocommerce_admin_order_data_after_order_details', function ( $order ) {
$invoice = $order->get_meta( '_wpide_invoice_number' );
if ( $invoice ) {
printf( '<p class="form-field form-field-wide"><strong>Invoice:</strong> %s</p>', esc_html( $invoice ) );
}
} );
add_filter( 'woocommerce_email_order_meta_fields', function ( $fields, $sent_to_admin, $order ) {
$invoice = $order->get_meta( '_wpide_invoice_number' );
if ( $invoice ) {
$fields['wpide_invoice'] = array( 'label' => 'Invoice number', 'value' => $invoice );
}
return $fields;
}, 10, 3 );
add_filter( 'woocommerce_get_order_item_totals', function ( $rows, $order ) {
$invoice = $order->get_meta( '_wpide_invoice_number' );
if ( $invoice ) {
$rows = array( 'wpide_invoice' => array( 'label' => 'Invoice number:', 'value' => esc_html( $invoice ) ) ) + $rows;
}
return $rows;
}, 10, 2 );
Notes
Numbers are assigned when an order reaches Processing or Completed, never change, and use an atomic counter so two orders cannot share one.
How to add this snippet
- Install WPIDE on your WordPress site (it is free) and open Code Snippets.
- Use snippet above adds it from the library in one click. Or copy the code into a new snippet with the same code type.
- It is added inactive: review the code and settings, then switch it on.