2

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.

1 Answers1

1

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.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
  • I found the native WooCommerce function that generate a username… So I have updated my code. Untested, I hop it will work this time. Waiting for your feedback. – LoicTheAztec Jun 27 '23 at 18:29
  • Perfect! The code works like a dream. Thank you so much. Je t'enverrai plus du popcorn! – AbandonedHope Jun 28 '23 at 15:26
  • I forgot that a key requirement was that customer code appear on each order. I've amended (I think) my original post above to include the customer code on an order. Presumably this would be sufficient? – AbandonedHope Jun 29 '23 at 12:40
  • 1
    @AbandonedHope Ask that in a new question please… Once done, notify me here and I will answer it. – LoicTheAztec Jun 29 '23 at 14:38
  • 1
    I've asked a new question. The link is https://stackoverflow.com/q/76583303/14648861 – AbandonedHope Jun 29 '23 at 17:22