im trying to figure out how to show all variation prices as text in single product page.
ex if product has 5 diffrent prices, 1 - 500, 2 - 900, 3 - 2100, 4 - 2500, 5 - 3000
i want it to show in text on all product pages.
as this:
VARIATIONS
1 - 500
2 - 900.
right now i do this manually, write them all up so ppl dont have to scroll down in the roll down menu to se what prices are but i want this done automaticly when i change the prices so i dont have to change this text everythime. i tryed diffrent hooks but couldnt find right one.
i tried this one but cant the hook to work and show it in product page
add_filter( 'woocommerce_get_price_html', 'custom_price_format', 10, 2 );
add_filter( 'woocommerce_variable_price_html', 'custom_price_format', 10, 2 );
function custom_price_format( $price, $product ) {
// 1. Variable products
if( $product->is_type('variable') ){
// Searching for the default variation
$default_attributes = $product->get_default_attributes();
// Loop through available variations
foreach($product->get_available_variations() as $variation){
$found = true; // Initializing
// Loop through variation attributes
foreach( $variation['attributes'] as $key => $value ){
$taxonomy = str_replace( 'attribute_', '', $key );
// Searching for a matching variation as default
if( isset($default_attributes[$taxonomy]) && $default_attributes[$taxonomy] != $value ){
$found = false;
break;
}
}
// When it's found we set it and we stop the main loop
if( $found ) {
$default_variaton = $variation;
break;
} // If not we continue
else {
continue;
}
}
// Get the default variation prices or if not set the variable product min prices
$regular_price = isset($default_variaton) ? $default_variaton['display_price']: $product->get_variation_regular_price( 'min', true );
$sale_price = isset($default_variaton) ? $default_variaton['display_regular_price']: $product->get_variation_sale_price( 'min', true );
}
// 2. Other products types
else {
$regular_price = $product->get_regular_price();
$sale_price = $product->get_sale_price();
}
// Formatting the price
if ( $regular_price !== $sale_price && $product->is_on_sale()) {
// Percentage calculation and text
$percentage = round( ( $regular_price - $sale_price ) / $regular_price * 100 ).'%';
$percentage_txt = __(' Save', 'woocommerce' ).' '.$percentage;
$price = '<del>' . wc_price($regular_price) . '</del> <ins>' . wc_price($sale_price) . $percentage_txt . '</ins>';
}
return $price;
}
Place the following function in your functions.php . You can replace get_price_html() with $product->get_price(); or $product->get_regular_price(); $product->get_sale_price(); depending on how you want to show your data.
function list_variation_prices() {
global $product;
if($product->get_type() === 'variable'):
$variations = $product->get_children();
if($variations):
echo '<ul>';
foreach($variations as $variation):
$vproduct = wc_get_product($variation);
echo '<li>'.$vproduct->get_price_html().'</li>';
endforeach;
echo '</ul>';
endif;
endif;
}
/** Change priority to change where you want to show it. Default priorities
* #hooked woocommerce_template_single_title - 5
* #hooked woocommerce_template_single_rating - 10
* #hooked woocommerce_template_single_price - 10
* #hooked woocommerce_template_single_excerpt - 20
* #hooked woocommerce_template_single_add_to_cart - 30
* #hooked woocommerce_template_single_meta - 40
* #hooked woocommerce_template_single_sharing - 50
* #hooked WC_Structured_Data::generate_product_data() - 60
*/
add_action('woocommerce_single_product_summary','list_variation_prices',25);
Related
When the product subtotal is more than 3000, it has to be another price.
I added it as a meta field to products. On Stack Overflow, a long time ago someone advised me the filter for a special price called woocommerce_cart_item_name
I modified it a little for my purpose:
function kia_add_subtitle_to_cart_product( $title, $cart_item ){
$threshold = 3000; // Change price if > 3000
$_product = $cart_item['data'];
$meta = $_product->get_meta( '_opt_price_var');
$price = $_product->get_regular_price();
$subtotal = $cart_item['quantity'] * $price;
if ( $subtotal > $threshold && ( $meta )) {
$_product->set_price( $meta );
}
echo $title;
}
add_filter( 'woocommerce_cart_item_name', 'kia_add_subtitle_to_cart_product', 10, 2 );
The special price is showing only in the cart form, but not in cart totals and not in the checkout, in totals and in checkout the price is still regular, the main calculator doesn't take it from this filter.
How can I fix it? How to include it in the main calculator too?
I tried different hooks that can be responsive for the calculator - both add_action and add_filter, with and without 10, 2 - it isn't it.
add_action('woocommerce_before_shipping_calculator', 'kia_add_subtitle_to_cart_product',10,2);
add_action('woocommerce_before_cart_totals', 'kia_add_subtitle_to_cart_product', 10, 2 );
The woocommerce_cart_item_name hook is being misused in your code attempt. A filter hook should always return something compared to the use of echo.
You should use the woocommerce_before_calculate_totals hook instead
So you get:
function action_woocommerce_before_calculate_totals( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 ) return;
// Change price if > 3000
$threshold = 3000;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ) {
// Get regular price
$price = $cart_item['data']->get_regular_price();
// Subtotal = Get quantity * price
$subtotal = $cart_item['quantity'] * $price;
// Greater than
if ( $subtotal > $threshold ) {
// Get meta
$meta = $cart_item['data']->get_meta( '_opt_price_var', true );
// NOT empty
if ( ! empty ( $meta ) ) {
// Set new price
$cart_item['data']->set_price( $meta );
}
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'action_woocommerce_before_calculate_totals', 10, 1 );
add_filter('woocommerce_calculated_total', 'custom_calculated_total', 10, 2);
function custom_calculated_total($total, $cart_object) {
if (is_admin() && !defined('DOING_AJAX'))
return;
if (!WC()->cart->is_empty()):
foreach ($cart_object->cart_contents as $key => $cart_item) {
$meta = $cart_item['data']->get_meta('_opt_price_var');
if ($total >= 3000 && ( $meta )) {
$total = $meta;
}
}
endif;
return $total;
}
I'm working on this code and while I've done my best to make it work, I cannot fix the "divided by zero" error that occurs on the archive (shop page) for grouped products.
This is the error message:
Warning: Division by zero in
That makes the percentage text show up like this: Save: -$18 (-INF%)
The error refers to this line:
$saving_percentage = round( 100 - ( $sale_price / $regular_price * 100 ), 1 ) . '%';
Here's the full code:
add_filter( 'woocommerce_get_price_html', 'display_sale_price_and_percentage_html', 10, 2 );
function display_sale_price_and_percentage_html( $price, $product ) {
// sale products on frontend excluding variable products
if( $product->is_on_sale() && ! is_admin() && ! $product->is_type('variable')) {
// product prices
$regular_price = (float) $product->get_regular_price(); // Regular price
$sale_price = (float) $product->get_price();
// price calculation and formatting
$saving_price = wc_price( $regular_price - $sale_price );
// percentage calculation and formatting
$precision = 1; // decimals
$saving_percentage = round( 100 - ( $sale_price / $regular_price * 100 ), 1 ) . '%';
// display the formatted html price including amount and precentage using a span tag which means displaying it on the same row, if you want this on a new row, change the tag into a paragraph
$price .= sprintf( __('<span class="saved-sale"> Save: %s <em>(%s)</em></span>', 'woocommerce' ), $saving_price, $saving_percentage );
}
return $price;
}
The error is shown on grouped products. It works fine on simple products. My goal is to make this work for all product types (simple, grouped, external and variable).
I need all the help I can get.
I believe it's because grouped products don't have a $regular_price. You should add some conditions to check if $sale_price and $regular_price are non-zero. You could also check that you aren't on a grouped product, but checking for 0 will also prevent divide by zero errors anywhere you have a free product.
add_filter( 'woocommerce_get_price_html', 'display_sale_price_and_percentage_html', 10, 2 );
function display_sale_price_and_percentage_html( $price, $product ) {
// sale products on frontend excluding variable products
if( $product->is_on_sale() && ! is_admin() && ! $product->is_type('variable')) {
// product prices
$regular_price = (float) $product->get_regular_price();
$sale_price = (float) $product->get_price();
if( $regular_price > 0 && $sale_price > 0 ) {
// price calculation and formatting
$saving_price = wc_price( $regular_price - $sale_price );
// percentage calculation and formatting
$precision = 1; // decimals
$saving_percentage = round( 100 - ( $sale_price / $regular_price * 100 ), 1 ) . '%';
// display the formatted html price including amount and precentage using a span tag which means displaying it on the same row, if you want this on a new row, change the tag into a paragraph
$price .= sprintf( __('<span class="saved-sale"> Save: %s <em>(%s)</em></span>', 'your-textdomain' ), $saving_price, $saving_percentage );
}
}
return $price;
}
I have some code in a function that displays a message to the end user when the total added value of a custom field exceeds a certain value. Basically its a message telling the customer that they have ordered too much furniture to fit into a 68m3 container.
I also have code which is suppose to disable the add to cart button on products - this doesn't seem to work.
So is there a visible issue in the code below to disable the add to cart button when my custom field total value exceeds 68 and is there a way to totally disable the submit order so it's not possible to submit the order until the value of the custom field total is below 68?
The code to display the message is as follows and works fine.
// display message when cart volume exceeds 68mcube
add_action('woocommerce_before_calculate_totals', 'display_custom_notice',
50, 1);
function display_custom_notice( $cart ) {
if ( is_admin() && !defined('DOING_AJAX') )
return;
$total_volume = 0;
// Loop through cart items and calculate total volume
foreach( WC()->cart->get_cart() as $cart_item ){
$product_volume = (float) get_post_meta( $cart_item['product_id'],
'_item_volume', true );
$total_volume += $product_volume * $cart_item['quantity'];
}
if( $total_volume > 68 && $total_volume != 0 ){
// Display a custom notice
wc_add_notice( __("Note: Your order total volume has reached a full 40ft
container - 68 m3 - Please submit order with volume no greater than 68m3",
"woocommerce"), 'notice' );
}
}
The function below are not appearing to work
// function to disable add to cart when volume exceeds 68m3
function get_total_volume(){
$total_volume = 0;
// Loop through cart items and calculate total volume
foreach( WC()->cart->get_cart() as $cart_item ){
$product_volume = (float) get_post_meta( $cart_item['product_id'],
'_item_volume', true );
$total_volume += $product_volume * $cart_item['quantity'];
}
return $total_volume;
}
// Replacing the button add to cart by a link to the product in Shop and
archives pages
add_filter( 'woocommerce_loop_add_to_cart_link',
'replace_loop_add_to_cart_button', 10, 2 );
function replace_loop_add_to_cart_button( $button, $product ) {
if( get_total_volume() > 68 ){
$button_text = __( "View product", "woocommerce" );
$button = '<a class="button" href="' . $product->get_permalink() . '">'
. $button_text . '</a>';
}
return $button;
}
add_action( 'woocommerce_single_product_summary',
'remove_add_to_cart_button', 1 );
function remove_add_to_cart_button() {
// Only when total volume is up to 68
if( get_total_volume() <= 68 ) return;
global $product;
// For variable product types (keeping attribute select fields)
if( $product->is_type( 'variable' ) ) {
remove_action( 'woocommerce_single_variation',
'woocommerce_single_variation_add_to_cart_button', 20 );
add_action( 'woocommerce_single_product_summary',
'innactive_add_to_cart_button', 20 );
}
// For all other product types
else {
remove_action( 'woocommerce_single_product_summary',
'woocommerce_template_single_add_to_cart', 30 );
add_action( 'woocommerce_single_product_summary',
'innactive_add_to_cart_button', 30 );
}
}
To replace "Place Order" button in checkout page when total cart volume exceed 68 m3 we will need to use your utility function get_total_volume() in the following code:
// Utility function to disable add to cart when volume exceeds 68m3
function get_total_volume(){
$total_volume = 0;
// Loop through cart items and calculate total volume
foreach( WC()->cart->get_cart() as $cart_item ){
$product_volume = (float) get_post_meta( $cart_item['product_id'], '_item_volume', true );
$total_volume += $product_volume * $cart_item['quantity'];
}
return $total_volume;
}
// Replacing the Place order button when total volume exceed 68 m3
add_filter( 'woocommerce_order_button_html', 'replace_order_button_html', 10, 2 );
function replace_order_button_html( $order_button ) {
// Only when total volume is up to 68
if( get_total_volume() <= 68 ) return $order_button;
$order_button_text = __( "Max volume reached", "woocommerce" );
$style = ' style="color:#fff;cursor:not-allowed;background-color:#999;"';
return '<a class="button alt"'.$style.' name="woocommerce_checkout_place_order" id="place_order" >' . esc_html( $order_button_text ) . '</a>';
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
When total volume exceed 68 m3, you will get an inactive custom button instead:
Check that the utility function get_total_volume() is defined only once, to avoid errors…
Now, to disable add to cart buttons, there is some missing things in your code like the function inactive_add_to_cart_button() and other little things… Try this:
// display message when cart volume exceeds 68mcube
add_action('woocommerce_before_calculate_totals', 'display_custom_notice', 50, 1);
function display_custom_notice( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
$total_volume = 0;
// Loop through cart items and calculate total volume
foreach( WC()->cart->get_cart() as $cart_item ){
$product_volume = (float) get_post_meta( $cart_item['product_id'], '_item_volume', true );
$total_volume += $product_volume * $cart_item['quantity'];
}
if( $total_volume > 68 && $total_volume != 0 ){
// Display a custom notice
wc_clear_notices();
wc_add_notice( __("Note: Your order total volume has reached a full 40ft container - 68 m3 - Please submit order with volume no greater than 68m3", "woocommerce"), 'notice' );
}
}
// Utility function to disable add to cart when volume exceeds 68m3
function get_total_volume(){
$total_volume = 0;
// Loop through cart items and calculate total volume
foreach( WC()->cart->get_cart() as $cart_item ){
$product_volume = (float) get_post_meta( $cart_item['product_id'], '_item_volume', true );
$total_volume += $product_volume * $cart_item['quantity'];
}
return $total_volume;
}
// Replacing the button add to cart by a link to the product in Shop and archives pages
add_filter( 'woocommerce_loop_add_to_cart_link', 'replace_loop_add_to_cart_button', 10, 2 );
function replace_loop_add_to_cart_button( $button, $product ) {
// Only when total volume is up to 68
if( get_total_volume() <= 68 ) return $button;
$small_text = '<br><em style="font-size:85%;">(' . __( "Max volume reached", "woocommerce" ) . ')</em>';
$button_text = __( "View product", "woocommerce" ) . $small_text;
return '<a class="button" href="' . $product->get_permalink() . '">' . $button_text . '</a>';
}
// Replacing the button add to cart by an inactive button on single product pages
add_action( 'woocommerce_single_product_summary', 'remove_add_to_cart_button', 1 );
function remove_add_to_cart_button() {
// Only when total volume is up to 68
if( get_total_volume() <= 68 ) return;
global $product;
// For variable product types (keeping attribute select fields)
if( $product->is_type( 'variable' ) ) {
remove_action( 'woocommerce_single_variation', 'woocommerce_single_variation_add_to_cart_button', 20 );
add_action( 'woocommerce_single_product_summary', 'inactive_add_to_cart_button', 20 );
}
// For all other product types
else {
remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart', 30 );
add_action( 'woocommerce_single_product_summary', 'inactive_add_to_cart_button', 30 );
}
}
// Utility function: displays a custom innactive add to cart button replacement
function inactive_add_to_cart_button(){
global $product;
$style = 'style="color:#fff;cursor:not-allowed;background-color:#999;"';
echo '<a class="button" '.$style.'>' . __ ( 'Max volume reached', 'woocommerce' ) . '</a>';
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Check that the utility function get_total_volume() is defined only once, to avoid errors…
When the total cart volume is up to 68m3 you will get:
1) On shop and archives pages:
2) On single product pages:
In our woocommerce shop , customer can enter the custom width and height of the product and product price calculated based on this details .
For example if the initial price for a product is 50 . And customer add width =2, height=3 , then the price for this product is going to 50*2*3=300
for this we are using following code
// Save custom field value in cart item as custom data
add_filter( 'woocommerce_add_cart_item', 'calculate_custom_cart_item_prices', 30, 3 );
function calculate_custom_cart_item_prices( $cart_item_data, $product_id, $variation_id ) {
if ( isset($_POST['width']) && isset($_POST['height']) ) {
// Get the correct Id to be used (compatible with product variations)
$the_id = $variation_id > 0 ? $variation_id : $product_id;
$product = wc_get_product( $the_id ); // Get the WC_Product object
$product_price = (float) $product->get_price(); // Get the product price
// Get the posted data
$width = (float) sanitize_text_field( $_POST['width'] );
$height = (float) sanitize_text_field( $_POST['height'] );
$new_price = $width * $height * $product_price; // Calculated price
$cart_item_data['calculated-price'] = $new_price; // Save this price as custom data
}
return $cart_item_data;
}
// Set custom calculated price in cart item price
add_action( 'woocommerce_before_calculate_totals', 'set_calculated_cart_item_price', 20, 1 );
function set_calculated_cart_item_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ){
if( ! empty( $cart_item['calculated-price'] ) ){
// Set the calculated item price (if there is one)
$cart_item['data']->set_price( $cart_item['calculated-price'] );
}
}
And it is wormking , but the problem is:
when customer apply a 50% coupon code for this product then the
discount is coming as 25 , because it calculating based on 50*(50/100)=25;
But actually product new price is 300, so the discount should be
300*(50/100)=150;
Try updating your 'calculate_custom_cart_item_prices' function to something like this and see if that helps.
add_filter( 'woocommerce_add_cart_item', 'calculate_custom_cart_item_prices', 30, 2 );
function calculate_custom_cart_item_prices( $cart_item_data, $cart_item_key ) {
if ( isset($_POST['width']) && isset($_POST['height']) ) {
// Get the correct Id to be used (compatible with product variations)
$the_id = $cart_item_data['variation_id'] > 0 ? $cart_item_data['variation_id'] : $cart_item_data['product_id'];
$product = wc_get_product( $the_id ); // Get the WC_Product object
$product_price = (float) $product->get_price(); // Get the product price
// Get the posted data
$width = (float) sanitize_text_field( $_POST['width'] );
$height = (float) sanitize_text_field( $_POST['height'] );
$new_price = $width * $height * $product_price; // Calculated price
$cart_item_data['calculated-price'] = $new_price; // Save this price as custom data
}
return $cart_item_data;
}
My guess as to what is happening is a change in Woocommerce has changed the way the 'woocommerce_add_cart_item' filter works and so you need to update this function.
Iv setup my Woocommerce Site such that the prices are entered excluding tax.
However id like to show Prices Inclusive of tax on the cart and Product Pages but excluding on the Checkout Page.
The Issue is, Woocommerce by default allows you to select Includes/Excludes tax For Cart & Checkout Together. but I need them to display individually.
Any ideas?
Use the filter woocommerce_get_price_including_tax on the single product pages. I copied the code out of the function get_price_including_tax. I haven't tested this code but this is the basic idea of what you need to do.
function modify_woocommerce_get_price_including_tax( $price, $qty, $product ) {
if( ! is_checkout() ) {
$tax_rates = WC_Tax::get_rates( $product->get_tax_class() );
$base_tax_rates = WC_Tax::get_base_tax_rates( $product->tax_class );
if ( ! empty( WC()->customer ) && WC()->customer->is_vat_exempt() ) {
$base_taxes = WC_Tax::calc_tax( $price * $qty, $base_tax_rates, true );
$base_tax_amount = array_sum( $base_taxes );
$price = round( $price * $qty - $base_tax_amount, wc_get_price_decimals() );
/**
* The woocommerce_adjust_non_base_location_prices filter can stop base taxes being taken off when dealing with out of base locations.
* e.g. If a product costs 10 including tax, all users will pay 10 regardless of location and taxes.
* product feature is experimental #since 2.4.7 and may change in the future. Use at your risk.
*/
} elseif ( $tax_rates !== $base_tax_rates && apply_filters( 'woocommerce_adjust_non_base_location_prices', true ) ) {
$base_taxes = WC_Tax::calc_tax( $price * $qty, $base_tax_rates, true );
$modded_taxes = WC_Tax::calc_tax( ( $price * $qty ) - array_sum( $base_taxes ), $tax_rates, false );
$price = round( ( $price * $qty ) - array_sum( $base_taxes ) + array_sum( $modded_taxes ), wc_get_price_decimals() );
} else {
$price = $price * $qty;
}
}
return $price;
}
add_filter( 'woocommerce_get_price_including_tax', 'modify_woocommerce_get_price_including_tax', 10, 3 );
If you want to modify the prices on the cart page then you'll need to override the template cart-totals.php in your child theme.