Set in WooCommerce a sequential order number with country and year prefix

114 views Asked by At

I need some advice on how to proceed with the following implementation. For my site in multiple languages ​​that uses Polylang as a plugin, I would need to have separate and consecutive numbering for the orders. For example:

  • Italian: IT-2024-01, IT-2024-02, IT-2024-03
  • German: DE-2024-01, DE-2024-02, DE-2024-03.

Right now, I just used this snippet for the Italian:

/**
 * Orders Prefix
 */

add_filter( 'woocommerce_order_number', 'change_woocommerce_order_number' );

function change_woocommerce_order_number( $order_id ) {
    $order = wc_get_order( $order_id );
    
    if ( $order && $order->get_date_created() ) {
        $order_year = $order->get_date_created()->format( 'Y' );
        $prefix = 'IT-' . $order_year . '-';
        $new_order_id = $prefix . $order_id;
        return $new_order_id;
    }
    
    return $order_id;
}

2

There are 2 answers

2
LoicTheAztec On BEST ANSWER

This requires to be done when the order is created after checkout, setting the order number as custom metadata, to keep a lightweight process each time the order number is read from the database.

The following code will set as order metadata the order number, sequentially by country code, year, and incremental number (on 2 digits, or you can define the desired number of digits).

// Set and save the order number as metadata
add_filter( 'woocommerce_checkout_create_order', 'save_order_number_metadata' );
function save_order_number_metadata( $order ) {
    $digits  = 2; // Number of desired digits for the number part
    $year    = date('Y', time()); // Get the current year
    $country = $order->get_billing_country(); // Get the billing country
    $data    = get_option('wc_sequential_order_number'); // Get order number sequencial helper registered data

    $number = isset($data[$year][$country]) ? intval($data[$year][$country]) + 1 : 1;
    $data[$year][$country] = $number;

    // Update order number sequencial helper registered data
    update_option('wc_sequential_order_number', $data);

    // Add order number as custom metadata
    $order->add_meta_data('_order_number', sprintf('%s-%s-%s', $country, $year, str_pad($number, $digits, '0', STR_PAD_LEFT)), true);
}

// Read the order number from metadata
add_filter( 'woocommerce_order_number', 'define_order_number', 10, 2 );
function define_order_number( $order_id, $order ) {
    if ( $order_number = $order->get_meta('_order_number') ) {
        $order_id = $order_number;
    }
    return $order_id;
}

Code goes in functions.php file of your child theme (or in a plugin). Tested and works.


For orders created manually, it requires something different, as the language code is not yet defined.

0
Dishant On

I have fixed the issue in two parts.

  1. First I get the current language using polylang function

  2. Then I pass it two custom function, which populates the desired sequence

    //Orders Prefix
    add_filter( 'woocommerce_order_number', 'change_woocommerce_order_number' );
    
    function change_woocommerce_order_number( $order_id ) {
        $order = wc_get_order( $order_id );
        if ( $order && $order->get_date_created() ) {
            $language_code = pll_current_language(); // Detect current language
            $new_order_id = generate_woo_order_sequence($language_code);
    
            return $new_order_id;
        }
    
        return $order_id;
    }
    
    // Generate the sequence with language code passed
    function generate_woo_order_sequence($language_code) {
        $year = date('Y');
        $option_name = "wc_custom_series_{$language_code}";
    
        // Reset if year has changed
        $stored_year = get_option($option_name . "_year", -1);
        if ($year > $stored_year) {
            update_option($option_name, 1);
            update_option($option_name . "_year", $year);
        }
    
        $current_number = get_option($option_name, 1);
        update_option($option_name, $current_number + 1);
    
        return strtoupper($language_code) . '-' . $year . '-' . str_pad($current_number, 3, '0', STR_PAD_LEFT);
    }
    

Note: I have considered prefix with leading zeros upto 3 digits.

For e.g., EN-2024-001 will be the first order number for new order placed after this code integration.