Search code examples
phpwordpresswoocommerceuser-rolesproduct-variations

Different Product Variation Price Based On User Role In WooCommerce


Scenario

7 custom roles in 2 categories are built to serve B2C clients with subscriptions and B2B distributors, which are:

  • B2C subcription_tier1 / subcription_tier2
  • B2B distributor_tier1 / distributor_tier2 / distributor_tier3 / distributor_tier4 / distributor_tier5

When the user is logged in, the user can see the different discounts for their tiers:

  • Users in the B2C category(subcription_tier) are able to see their discounted prices for their roles
  • Users in the B2B category(distributor_tier) are able to see their discounted prices for their roles but only on those products that we may offer competitive pricing for them, or texts telling them the product isn't available for distributions because we're not eligible to provide quality pricings

What Has Been Achieved

I've achieved the goal with simple products by adding the snippet from this tutorial. The solution only enables me the solution to accomplish the needs of simple ones by using ACF and some snippets together but not variable ones.

The Code

After some days of research, a great solution by LoicTheAztec came insight from this post. However, I found myself stuck in building the connection between the custom prices and the roles(I'm NOT an engineer and still learning how to code to reduce the dependences for plugins).

// Add custom fields to variations option pricing
add_action( 'woocommerce_variation_options_pricing', 'add_variation_wholesale_options_pricing', 20, 3 );
function add_variation_wholesale_options_pricing( $loop, $variation_data, $post_variation )
{
    $value1  = get_post_meta( $post_variation->ID, '_sp_price_1', true ); // subscription tier 1
    $value2  = get_post_meta( $post_variation->ID, '_sp_price_2', true ); // subscription tier 2
    $value3  = get_post_meta( $post_variation->ID, '_sp_price_3', true ); // distributor tier 1
    $value4  = get_post_meta( $post_variation->ID, '_sp_price_4', true ); // distributor tier 2
    $value5  = get_post_meta( $post_variation->ID, '_sp_price_5', true ); // distributor tier 3
    $value6  = get_post_meta( $post_variation->ID, '_sp_price_6', true ); // distributor tier 4
    $value7  = get_post_meta( $post_variation->ID, '_sp_price_7', true ); // distributor tier 5

    $symbol = ' (' . get_woocommerce_currency_symbol() . ')';

    $key_1 = '_sp_price_1[' . $loop . ']';
    $key_2 = '_sp_price_2[' . $loop . ']';
    $key_3 = '_sp_price_3[' . $loop . ']';
    $key_4 = '_sp_price_4[' . $loop . ']';
    $key_5 = '_sp_price_5[' . $loop . ']';
    $key_6 = '_sp_price_6[' . $loop . ']';
    $key_7 = '_sp_price_7[' . $loop . ']';

    // Field Label For Subscription Tier 1
    echo '<div><p class="form-row form-row-first">
        <label>' . __( "Subscription Tier 1", "woocommerce" ) . $symbol . '</label>
        <input type="text" size="5" name="' . $key_1 .'" value="' . esc_attr( $value1 ) . '" />
    </p></div>';

    // Field Label For Subscription Tier 2
    echo '<div><p class="form-row form-row-first">
        <label>' . __( "Subscription Tier 2", "woocommerce" ) . $symbol . '</label>
        <input type="text" size="5" name="' . $key_2 .'" value="' . esc_attr( $value2 ) . '" />
    </p></div>';

    // Field Label For Distributor Tier 1
    echo '<div><p class="form-row form-row-first">
        <label>' . __( "Distributor Tier ", "woocommerce" ) . $symbol . '</label>
        <input type="text" size="5" name="' . $key_3 .'" value="' . esc_attr( $value3 ) . '" />
    </p></div>';

    // Field Label For Distributor Tier 2
    echo '<div class="variable_wholesale-price"><p class="form-row form-row-first">
        <label>' . __( "Distributor Tier 2", "woocommerce" ) . $symbol . '</label>
        <input type="text" size="5" name="' . $key_4 .'" value="' . esc_attr( $value4 ) . '" />
    </p></div>';

    // Field Label For Distributor Tier 3
    echo '<div class="variable_wholesale-price"><p class="form-row form-row-first">
        <label>' . __( "Distributor Tier 3", "woocommerce" ) . $symbol . '</label>
        <input type="text" size="5" name="' . $key_5 .'" value="' . esc_attr( $value5 ) . '" />
    </p></div>';

    // Field Label For Distributor Tier 4
    echo '<div class="variable_wholesale-price"><p class="form-row form-row-first">
        <label>' . __( "Distributor Tier 4", "woocommerce" ) . $symbol . '</label>
        <input type="text" size="5" name="' . $key_6 .'" value="' . esc_attr( $value6 ) . '" />
    </p></div>';

    // Field Label For Distributor Tier 5
    echo '<div class="variable_wholesale-price"><p class="form-row form-row-first">
        <label>' . __( "Distributor Tier 5", "woocommerce" ) . $symbol . '</label>
        <input type="text" size="5" name="' . $key_7 .'" value="' . esc_attr( $value7 ) . '" />
    </p></div>';
}


// Save variations wholesale prices custom fields values
add_action( 'woocommerce_save_product_variation', 'save_product_variation_wholesale_price', 20, 2 );
function save_product_variation_wholesale_price( $variation_id, $i )
{
    if ( isset( $_POST['_sp_price_1'][$i] ) )
    {
        update_post_meta( $variation_id, '_sp_price_1', floatval( $_POST['_sp_price_1'][$i] ) );
    }
}

// Variable product price range
add_filter('woocommerce_variation_prices_price', 'wholesale_variation_price', 900, 2 );
add_filter('woocommerce_variation_prices_sale_price', 'wholesale_variation_price', 900, 2 );

// Product variations (of a variable product)
add_filter('woocommerce_product_variation_get_price', 'wholesale_variation_price', 900, 2 );
add_filter('woocommerce_product_variation_get_sale_price', 'wholesale_variation_price', 900, 2 );
function wholesale_variation_price( $price, $object )
{
    if ( is_user_logged_in() ) {

        // For Wholesale user levels 1 and 2
        if( in_array($level, [1]) ) {
            $new_price = (float) get_post_meta( $object->get_id(), '_sp_price_1', true );
            $price     = empty($new_price) ? $price : $new_price;
        }

    }
    return $price;
}

// Handling custom variable price range caching
add_filter( 'woocommerce_get_variation_prices_hash', 'wholesale_variation_performances_caching_prices', 99, 1 );
function wholesale_variation_performances_caching_prices( $hash ) {
    if ( is_user_logged_in() ) {
        $level = (int) get_user_meta( get_current_user_id(), 'mokeeper_keen', true );
        // For Wholesale user levels 1 and 2
        if( in_array($level, [1]) ) {
            $hash[] = 'level_' . $level;
        }
        // For customers
        else {
            $hash[] = 'level_0'; // Set the user level to zero here
        }
    }
    return $hash;
}

The custom blank fields are set in the backend but they're not seeable to the logged-in user with the desired tiered role on the frontend.

I've tried my best to build up the correct logic and this is the furthest spot I could get to with my limited knowledge of coding. I know I MUST have missed something(or some things) or did something wrong, but I really couldn't figure out where to audit to make it correct.

Appreciate you for responding in advance. Looking forward to your help.


Solution

  • Try the following optimized and complete code version that handle your user roles too:

    // Field settings (field key / label name)
    function custom_field_settings() {
        return array(
            '_st_price_1' => __( "Subscription Tier 1", "woocommerce" ),
            '_st_price_2' => __( "Subscription Tier 2", "woocommerce" ),
            '_dt_price_1' => __( "Distributor Tier 1", "woocommerce" ),
            '_dt_price_2' => __( "Distributor Tier 2", "woocommerce" ),
            '_dt_price_3' => __( "Distributor Tier 3", "woocommerce" ),
            '_dt_price_4' => __( "Distributor Tier 4", "woocommerce" ),
            '_dt_price_5' => __( "Distributor Tier 5", "woocommerce" ),
        );
    }
    
    // Settings: custom field keys by user role
    function custom_field_key_by_user_role() {
        return array(
            '_st_price_1' => 'subcription_tier1',
            '_st_price_2' => 'subcription_tier2',
            '_dt_price_1' => 'distributor_tier1',
            '_dt_price_2' => 'distributor_tier2',
            '_dt_price_3' => 'distributor_tier3',
            '_dt_price_4' => 'distributor_tier4',
            '_dt_price_5' => 'distributor_tier5',
        );
    }
    
    // Add custom fields to variations option pricing
    add_action( 'woocommerce_variation_options_pricing', 'add_variation_custom_options_pricing', 20, 3 );
    function add_variation_custom_options_pricing( $loop, $variation_data, $post_variation ) {
        $symbol  = ' (' . get_woocommerce_currency_symbol() . ')';
    
        // Loop through field key / label name pairs
        foreach ( custom_field_settings() as $key => $label ) {
            printf( '<div><p class="form-row form-row-first"> <label>%s</label>
            <input type="text" size="5" name="%s[%s]" value="%s" /></p></div>', 
            $label.$symbol, $key, $loop, floatval(get_post_meta($post_variation->ID, $key, true)) );
        }
    }
    
    // Save variations prices custom fields values
    add_action( 'woocommerce_admin_process_variation_object', 'save_variation_custom_options_pricing', 20, 2 );
    function save_variation_custom_options_pricing( $variation, $i ) {
        // Loop through field key / label name pairs
        foreach ( custom_field_settings() as $key => $label ) {
            if ( isset( $_POST[$key][$i] ) ){
                $variation->update_meta_data( $key, floatval($_POST[$key][$i]) );
            }
        }
    }
    
    // Variable product price range
    add_filter('woocommerce_variation_prices_price', 'wholesale_variation_price', 900, 2 );
    add_filter('woocommerce_variation_prices_sale_price', 'wholesale_variation_price', 900, 2 );
    // Product variations (of a variable product)
    add_filter('woocommerce_product_variation_get_price', 'wholesale_variation_price', 900, 2 );
    add_filter('woocommerce_product_variation_get_sale_price', 'wholesale_variation_price', 900, 2 );
    function wholesale_variation_price( $price, $object ) {
        global $current_user;
    
        if ( ! is_user_logged_in() ) return $price;
    
        // Loop through field key / user role pairs
        foreach ( custom_field_key_by_user_role() as $key => $role ) {
            if( in_array($role, $current_user->roles) ) {
                $new_price = (float) $object->get_meta($key);
                $price     = empty($new_price) ? $price : $new_price;
            }
        }
        return $price;
    }
    
    // Handling custom variable price range caching
    add_filter( 'woocommerce_get_variation_prices_hash', 'wholesale_variation_performances_caching_prices', 99, 1 );
    function wholesale_variation_performances_caching_prices( $hash ) {
        global $current_user;
    
        if ( ! is_user_logged_in() ) return $hash;
    
        // Loop through field key / user role pairs
        foreach ( custom_field_key_by_user_role() as $key => $role ) {
            if( in_array($role, $current_user->roles) ) {
                $hash[] = $key;
            }
        }
        return $hash;
    }
    

    It should work.