Search code examples
phpdatabasewordpresswoocommerceuser-roles

How to assign a custom Customer Code to Guest purchasers in WooCommerce


I'm working on an e-commerce project. When a customer purchases a product, they are assigned a Customer Code based on their first name, last name and sequential numbers. It's used by the accounting software.

Here's my code:

add_action( 'show_user_profile', 'ipx_user_profile_fields' );
add_action( 'edit_user_profile', 'ipx_user_profile_fields' );

function ipx_user_profile_fields( $user ) { ?>
    <h3><?php _e("IPX", "blank"); ?></h3>

    <table class="form-table">
    <tr>
        <th><label for="ipx_customer_code"><?php _e("Customer Code"); ?></label></th>
        <td>
            <input type="text" name="ipx_customer_code" id="ipx_customer_code" value="<?php echo esc_attr( get_the_author_meta( 'ipx_customer_code', $user->ID ) ); ?>" class="regular-text" /><br />
            <span class="description"><?php _e("This field should contain only the IPX Customer Code."); ?></span>
        </td>
    </tr>
    </table>
<?php }

add_action( 'personal_options_update', 'save_ipx_user_profile_fields' );
add_action( 'edit_user_profile_update', 'save_ipx_user_profile_fields' );

function save_ipx_user_profile_fields( $user_id ) {
    if ( !current_user_can( 'edit_user', $user_id ) ) { 
        return false; 
    }
    update_user_meta( $user_id, 'ipx_customer_code', $_POST['ipx_customer_code'] );
}

/**
 * Generate customer code based on user's first name, last name and a sequential number
 */
function generate_customer_code($user_id) {
    $user = get_userdata($user_id);
    $first_name = $user->first_name;
    $last_name = $user->last_name;

    // Get the current sequential number for the customer code
    $sequential_number = get_user_meta($user_id, 'sequential_customer_code', true);
    if (!$sequential_number) {
        $sequential_number = 0;
    }

    // Loop until a unique customer code is generated
    do {
        $sequential_number++;
        // Generate the customer code
        $customer_code = strtoupper(substr($first_name, 0, 4) . substr($last_name, 0, 4) . sprintf('%02d', $sequential_number));
        // Check if the customer code already exists
        $user_query = new WP_User_Query(array(
            'meta_key' => 'ipx_customer_code',
            'meta_value' => $customer_code
        ));
    } while (!empty($user_query->results));

    // Save the customer code and sequential number as separate custom fields
    update_user_meta($user_id, 'ipx_customer_code', $customer_code);
    update_user_meta($user_id, 'sequential_customer_code', $sequential_number);
}
add_action('user_register', 'generate_customer_code');

It checks to see if the Customer Code is already used and moves onto the next (sequentially) if it is.

To include the customer code on an order, I've used the following the code:

add_action( 'woocommerce_checkout_update_order_meta', 'include_customer_code_on_order', 10, 2 );

function include_customer_code_on_order( $order_id, $posted_data ) {
    $order = wc_get_order( $order_id );
    $user_id = $order->get_user_id();

    if ( $user_id ) {
        $customer_code = get_user_meta( $user_id, 'ipx_customer_code', true );
        if ( $customer_code ) {
            $order->update_meta_data( 'ipx_customer_code', $customer_code );
            $order->save();
        }
    }
}

I've been testing this out and come up against a problem. It only works if the customer creates an account. My client is adamant that Guest access should remain switched on, which means that a Customer Code is never generated.

Is there a way to store this information in the wp_wc_customer_lookup table instead? That records all customers, irrespective of whether they've registered or not. I think my client would still want to see the Customer Code somewhere physically (on the WooCommerce Customers tab?) but it needs to be for every customer, not just registered customers.

Any thoughts would be appreciated. And apologies in advance for any dodgy coding - my PHP is rough, but I'd rather attempt it than not.


Solution

  • Here, the idea is to register Guest users when they make a purchase on your store.

    • First, we add to WordPress a "guest" user role (the first function).
    • Then, once the order is processed (meaning that the order is created and saved to the database, just before payment), we hook a function where we register the user as "guest" using the WordPress function wp_insert_user() that WooCommerce uses too.

    Now as your last function that generates a "Customer Code" is hooked in user_register WordPress hook that is triggered precisely by wp_insert_user(), so that function will be executed, and that solves your problem.

    The code (untested):

    // Add "guest" user role
    add_action( 'init', 'add_role_guest' );
    function add_role_guest() {
        add_role(
            'guest',
            __( 'Guest', 'woocommerce' ),
            array( 'read' => true )
        );
    }
    
    // Registering unregistered Guest users
    add_action( 'woocommerce_checkout_order_processed', 'process_checkout_guest', 10, 3 );
    function process_checkout_guest( $order_id, $posted_data, $order ) {
        $user_id = $order->get_user_id(); // Get user Id (for guest the value is 0)
        $b_email = $order->get_billing_email(); // Get email from order
    
       // Only Guest
        if ( ! $user_id ) {
            // check with th email if WP_User exist
            $user = get_user_by( 'email', $b_email );
    
            // WP_User don't exist
            if ( $b_email && ! $user ) {
                // Generating the username
                $username = wc_create_new_customer_username( $b_email, array(
                    'first_name' => $order->get_billing_first_name(),
                    'last_name' => $order->get_billing_last_name(),
                ) );
    
                // Creating user
                $user_id = wp_insert_user( array(
                    'user_login' => $username,
                    'user_pass'  => wp_generate_password(),
                    "first_name" => $order->get_billing_first_name(),
                    'last_name'  => $order->get_billing_last_name(),
                    'user_email' => sanitize_email($b_email),
                    'role'       => 'guest', // Custom user role
                ) );
                // For testing
                if ( is_wp_error( $user_id ) ) {
                    error_log( print_r($user_id, true) );
                }
            } 
        }
    }
    

    It should work.

    This required testing, to be sure that there are no bad interactions with WooCommerce.

    I think that it's the best and simplest way.