I am using the following code in WooCommerce to add a discount based on user roles:
//DISCOUNT
add_filter('woocommerce_add_cart_item_data', 'add_items_default_price_as_custom_data', 20, 3 );
function add_items_default_price_as_custom_data( $cart_item_data, $product_id, $variation_id ){
if ( !is_user_logged_in() ) { return;}
$product_id = $variation_id > 0 ? $variation_id : $product_id;
//get discount from role
$user = wp_get_current_user();
$user_role = $user->roles[0];
$page = jet_engine()->options_pages->registered_pages['shop-opt'];
// give a discount depending on the role
switch ($user_role) {
case 'customer':
$discount_percentage = 0;
break;
case 'discount1':
$discount_percentage = $page->get( 'diskont-riven-1' );
break;
case 'discount2':
$discount_percentage = $page->get( 'diskont-riven-2' );
break;
case 'discount3':
$discount_percentage = $page->get( 'diskont-riven-3' );
break;
}
// The WC_Product Object
$product = wc_get_product($product_id);
$price = (float) $product->get_price();
// Set the Product default base price as custom cart item data
$cart_item_data['base_price'] = $price;
// Only for non on sale products
if( ! $product->is_on_sale() ){
// Set the Product discounted price as custom cart item data
$cart_item_data['new_price'] = $price * (100 - $discount_percentage) / 100;
// Set the percentage as custom cart item data
$cart_item_data['percentage'] = $discount_percentage;
}
if( $product->is_on_sale() ){
// Set the Product discounted price as custom cart item data
$cart_item_data['new_price'] = $price;
}
return $cart_item_data;
}
// Display the product name with the discount percentage
add_filter( 'woocommerce_cart_item_name', 'append_percetage_to_item_name', 20, 3 );
function append_percetage_to_item_name( $product_name, $cart_item, $cart_item_key ){
if ( !is_user_logged_in() ) {
return;
}
if( isset($cart_item['percentage']) && isset($cart_item['base_price']) ) {
if( $cart_item['data']->get_price() != $cart_item['base_price'] )
$product_name .= ' <em>(' . $cart_item['percentage'] . '% discount)</em>';
}
return $product_name;
}
add_action( 'woocommerce_before_calculate_totals', 'custom_discounted_cart_item_price', 20, 1 );
function custom_discounted_cart_item_price( $cart ) {
if ( !is_user_logged_in() ) {
return;
}
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ) {
// Set cart item discounted price
$cart_item['data']->set_price($cart_item['new_price']);
}
}
// CHECK/ADD ROLE after buuing
add_action( 'woocommerce_order_status_completed', 'order_completed', 1);
function order_completed($order_id) {
$order = wc_get_order( $order_id );
$user_id = $order->get_customer_id();
if ( $user_id == ''){
return ;
}
$user = new WP_User( $user_id);
$user_role = $user->roles[0];
switch ($user_role) {
case 'customer':
$user->set_role( 'discount1' );
break;
case 'discount1':
$user->set_role( 'discount2' );
break;
case 'discount2':
$user->set_role( 'discount3' );
break;
}
}
All is mostly fine.
The issue I am facing:
Sometime, I get empty order, so I setup a logger:
It looks like the issue happen when a guest adds to cart some products, go to the checkout and see that he can get discount after first order but need to be logged.
Then the guest register on shop, go to the cart and his cart is empty.
And I receive an empty order, without email, name and product.
First, you should enable registration on checkout page, to avoid this kind of issue. This issue could belong to some other custom code you added or some 3rd Party plugin that is interfering.
Now, there are some mistakes and complications in your current code.
Try the following revised and optimized code replacement (commented):
// Add discounted price as custom cart item data conditionally
add_filter( 'woocommerce_add_cart_item_data', 'add_discounted_price_as_custom_cart_item_data', 20, 3 );
function add_discounted_price_as_custom_cart_item_data( $cart_item_data, $product_id, $variation_id ) {
global $current_user;
if ( !$current_user ) {
return; // Exit if not logged
}
$product = wc_get_product($variation_id > 0 ? $variation_id : $product_id); // The WC_Product Object
if ( ! $product->is_on_sale() ) {
return; // Exit if product is on sale
}
$page = jet_engine()->options_pages->registered_pages['shop-opt'];
// Define each option for a user role in an array
$discount_for_role = array(
'discount1' => $page->get('diskont-riven-1'),
'discount2' => $page->get('diskont-riven-2'),
'discount3' => $page->get('diskont-riven-3'),
);
$discount_percentage = 0; // Initialize
// Loop through options for user roles array
foreach ( $discount_for_role as $user_role => $option_percentage ) {
// Check user role
if ( wc_current_user_has_role($user_role) ) {
$discount_percentage = (float) $option_percentage; // Define discount percentage
break;
}
}
$price = (float) $product->get_price(); // Get product active price
if ( $discount_percentage > 0 && $price > 0 ) {
$cart_item_data['base_price'] = $price;
$cart_item_data['percentage'] = $discount_percentage;
$cart_item_data['new_price'] = $price * (100 - $discount_percentage) / 100; // New Price
}
return $cart_item_data;
}
// Display the discount percentage conditionally
add_filter( 'woocommerce_cart_item_name', 'append_discount_percentage_to_cart_item_name', 20, 3 );
function append_discount_percentage_to_cart_item_name($product_name, $cart_item, $cart_item_key) {
if (is_user_logged_in() && isset($cart_item['percentage']) ) {
$product_name .= ' <em>(' . $cart_item['percentage'] . '% discount)</em>';
}
return $product_name;
}
// Discount cart item price conditionally
add_action( 'woocommerce_before_calculate_totals', 'custom_discounted_cart_item_price', 20 );
function custom_discounted_cart_item_price( $cart ) {
if ( !is_user_logged_in() ) {
return;
}
if ( is_admin() && ! defined('DOING_AJAX') ) {
return;
}
if (did_action('woocommerce_before_calculate_totals') >= 2) {
return;
}
// Loop through cart items
foreach ($cart->get_cart() as $item) {
// Check for discounted price
if ( isset($item['new_price']) ) {
$item['data']->set_price($item['new_price']); // Set discounted price
}
}
}
// Check and change user role after a purchase
add_action( 'woocommerce_order_status_completed', 'alter_user_role_on_order_completed', 10, 2 );
function alter_user_role_on_order_completed( $order_id, $order ) {
if ( !$order->get_user_id() ) {
return;
}
// Array of user roles transitions
$role_transition = array(
'customer' => 'discount1',
'discount1' => 'discount2',
'discount2' => 'discount3',
);
$user = $order->get_user(); // Get The WP_User object
// Loop through role transition array
foreach ( $role_transition as $role_from => $role_to ) {
// Check user role
if ( wc_user_has_role( $order->get_user(), $role_from ) ) {
$user->set_role( $role_to ); // set new user role
break; // Stop the loop
}
}
}
It should work.
You should not remove the "customer" user role, as WooCommerce requires a valid user role. Instead, you should add the discount role as an additional role.
The ideal solution would have been to add that allowed discount as custom user metadata, instead of changing the user role.
Here is a modified code version, that add the "discount user role" instead of replacing "customer" role.
Replace the last function with the following:
// Check and add/update user discount role after a purchase
add_action( 'woocommerce_order_status_completed', 'define_discount_user_role_on_order_completed', 10, 2 );
function define_discount_user_role_on_order_completed( $order_id, $order ) {
if ( !$order->get_user_id() ) {
return;
}
// Array of user roles transitions
$discount_roles = array( 'discount1', 'discount2', 'discount3' );
$user = $order->get_user(); // Get The WP_User object
// If user has already 'discount3' user role we exit
if ( wc_user_has_role( $order->get_user(), end($discount_roles) ) ) {
return; // Exit
}
// No discount role has been added
if ( ! array_intersect( $discount_roles, $user->roles) ) {
$user->add_role( reset($discount_roles) ); // add the first role
}
// A discount role exist already
else {
// Loop through discount roles
foreach ( $discount_roles as $key => $role ) {
// Check role
if ( wc_user_has_role( $order->get_user(), $role ) ) {
$user->remove_role( $role ); // Remove current discount role
$user->add_role( $discount_roles[$key+1] ); // Add the next discount role
break; // Stop the loop
}
}
}
}
It should work.