1

for various reasons a webshop needs to allow guest orders, but then saves the data to an existing user if the email is already registered. However, a product (parent variable product) should not be bought twice. In that case, the customer should be returned to the checkout page and get a notice that this product is already bought.

So far i have the following code (not complete):

add_action('woocommerce_checkout_process', function () {
    if (!is_user_logged_in() && $_POST['billing_email'] && $_POST['createaccount'] == 1) {
        $user = get_user_by('email', $_POST['billing_email']);
        $order = wc_get_order( $order_id );
        $items = $order->get_items();
        foreach ( $items as $item ) {
    $product_id = $item->get_product_id();
        if (!empty($user)){
            if (wc_customer_bought_product($user->user_email,$user->ID,$product_id))
            return false;
        wp_set_current_user($user->ID);
        }
        if (!empty($user)) {
        if (wc_customer_bought_product($user->user_email,$user->ID,$product_id)) 
            return true;
        exit;
    }
    else{
        ///guest, continue
    }
}}});

But that gives only an internal server error.

Anyone?

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
Webdever
  • 485
  • 5
  • 17

1 Answers1

1

First, instead of using wc_customer_bought_product(), you can use the following enhanced conditional function from this existing answer (based on the same source code than wc_customer_bought_product()). This function will handle your variable products case.

// Conditional function( stackOverFlow answer: https://stackoverflow.com/a/46217461/3730754 )
function has_bought_items( $user_var = 0,  $product_ids = 0 ) {
    global $wpdb;
    
    // Based on user ID (registered users)
    if ( is_numeric( $user_var) ) { 
        $meta_key     = '_customer_user';
        $meta_value   = $user_var == 0 ? (int) get_current_user_id() : (int) $user_var;
    } 
    // Based on billing email (Guest users)
    else { 
        $meta_key     = '_billing_email';
        $meta_value   = sanitize_email( $user_var );
    }
    
    $paid_statuses    = array_map( 'esc_sql', wc_get_is_paid_statuses() );
    $product_ids      = is_array( $product_ids ) ? implode(',', $product_ids) : $product_ids;

    $line_meta_value  = $product_ids !=  ( 0 || '' ) ? 'AND woim.meta_value IN ('.$product_ids.')' : 'AND woim.meta_value != 0';

    // Count the number of products
    $count = $wpdb->get_var( "
        SELECT COUNT(p.ID) FROM {$wpdb->prefix}posts AS p
        INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
        INNER JOIN {$wpdb->prefix}woocommerce_order_items AS woi ON p.ID = woi.order_id
        INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS woim ON woi.order_item_id = woim.order_item_id
        WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $paid_statuses ) . "' )
        AND pm.meta_key = '$meta_key'
        AND pm.meta_value = '$meta_value'
        AND woim.meta_key IN ( '_product_id', '_variation_id' ) $line_meta_value 
    " );

    // Return true if count is higher than 0 (or false)
    return $count > 0 ? true : false;
}

You can't get yet the order or order items… Instead you will check cart items.

Then here is your revisited code that handle all kind of customers and variable products as asked:

add_action('woocommerce_checkout_process', 'wc_action_checkout_process' );
function wc_action_checkout_process() {
    $user_id = get_current_user_id();

    // 1. - For logged in users
    if( $user_id > 0 ) {
        $user_var = $user_id;
    }
    // 2. - For others
    else {
        if ( isset($_POST['billing_email']) && ! empty($_POST['billing_email']) ) {
            $email = sanitize_email( $_POST['billing_email'] );
            $user  = get_user_by( 'email', $email );

            // 2.a - When a user exist for the billing email return an error notice
            if ( is_a($user, 'WP_User') ) {
                wc_add_notice( sprintf( __('The email "%s" is already registered for "%s" user name. Please login.'), $email, $user->user_login ), 'error' );
            }
            // 2.b - User doesn't exits (real "guest")
            else {
                $user_var = $email;
            }
        }
    }

    // Loop through cart items to check products
    foreach ( WC()->cart->get_cart() as $cart_item ) {
        if ( has_bought_items( $user_var,  $cart_item['product_id'] ) ) {
            wc_add_notice( sprintf( __('You have already purchased "%s" product before. You can only purchase it once.'), $cart_item['data']->get_name() ), 'error' );
        }
    }
}

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

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
  • Almost correct, in my case it did work with wc_customer_bought_product(). And that you do all this work for nothing... respect! Thank you! – Webdever Jul 25 '20 at 19:53