I have three subscription products, yearly, half-yearly and monthly. By default, prices are shown per year, per every 6 months and per month on my Shop page.
Based on "Change product prices via a hook in WooCommerce 3" answer code, I am trying to display all the prices per month, so to change the price display based on the subscription term:
// Utility function to change the prices with a multiplier (number)
function get_price_multiplier($var_product) {
switch($var_product) {
case 111:
// Annual
return 12;
break;
case 222:
// Semiannual
return 6;
break;
case 333:
// Month
return 1;
break;
default:
return 1;
break;
}
}
add_filter('woocommerce_product_variation_get_price', 'custom_price', 99, 2 );
function custom_price( $price, $product ) {
$var_product = $product->get_id();
return $price / get_price_multiplier($var_product);
}
This worked but when a product is added to the cart, the price is not the regular price but the modified price from the function above.
Based on "Set programmatically product sale price and cart item prices in Woocommerce 3" answer code, I have been able to fix that:
add_action( 'woocommerce_before_calculate_totals', 'set_cart_item_sale_price', 20, 1 );
function set_cart_item_sale_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Iterate through each cart item
foreach( $cart->get_cart() as $cart_item ) {
$regular_price = $cart_item['data']->get_regular_price();
$variation_id = $cart_item['variation_id'];
$cart_item['data']->set_price( $regular_price * get_price_multiplier($variation_id) );
}
}
This reset the cart price and it seems to work although one of the prices is off by a penny.
Is this all convoluted and prone to problems or is it a solid way to achieve what I am after: to show the price per subscription term on the shop page?
You are doing it in the right way… There is 2 ways (the last is the best one):
Note: The calculation and the price change is not needed when the multiplier is equal to 1.
1) First alternative: Improved code version that is very similar to yours.
Here is my commented code:
// Utility function that increase conditionally the variation price with a multiplier (int)
function get_variation_calculated_price( $variation_id, $price, $multiplier = true ) {
switch( $variation_id ) {
case 111: // Annual
$rate = 12;
break;
case 222: // Semi-annual
$rate = 6;
break;
default: // Month (and others)
$rate = 1;
break;
}
// Return calculated price (or false when multiplier is 1, as calculation is not needed)
return $rate !== 1 ? ( $multiplier ? $price * $rate : $price / $rate ) : false;
}
// Change variations calculated prices
add_filter('woocommerce_product_variation_get_price', 'custom_price', 99, 2 );
function custom_price( $price, $variation ) {
if( $new_price = get_variation_calculated_price( $variation->get_id(), $price, false ) )
return $new_price;
return $price;
}
// Customizing cart item prices
add_action( 'woocommerce_before_calculate_totals', 'set_cart_item_sale_price', 20, 1 );
function set_cart_item_sale_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Required since Woocommerce version 3.2 for cart items properties changes
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach( $cart->get_cart() as $cart_item ) {
// Only for variations
if( $cart_item['variation_id'] > 0 ) {
if( $new_price = get_variation_calculated_price( $cart_item['variation_id'], $cart_item['data']->get_price() ) ) {
$cart_item['data']->set_price( $new_price );
}
}
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
2) Second Alternative: much more accurate and lighter, restricting the product price avoiding price change on cart, checkout and backend.
Note: You will need to avoid displaying Cross-sells products in cart page (as the prices will not be changed like in shop page).
So the code will be more light, compact and efficient:
// Utility function that increase conditionally the variation price with a multiplier (int)
function get_variation_calculated_price( $variation_id, $price ) {
switch( $variation_id ) {
case 111: // Annual
$rate = 12;
break;
case 939: // Semi-annual
$rate = 6;
break;
default: // Month (and others)
$rate = 1;
break;
}
// Return calculated price (or false when multiplier is 1, as calculation is not needed)
return $rate !== 1 ? $price / $rate : false;
}
// Change variations calculated prices
add_filter('woocommerce_product_variation_get_price', 'custom_price', 99, 2 );
function custom_price( $price, $variation ) {
// Not in cart, checkout and admin
if( is_cart() || is_checkout() || is_admin() )
return $price;
if( $new_price = get_variation_calculated_price( $variation->get_id(), $price ) )
return $new_price;
return $price;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.