I have a product with variations, that is being displayed by the templates/single-product/add-to-cart/variation.php template, which uses JavaScript-based templates {{{ data.variation.display_price }}}. When I have price that end with a zero, for example, € 12.50, the price on the front-end will be displayed as € 12.5 (without the zero). I want to have the price include the trailing zero.
I've tried the following filter, but it does not work.
add_filter( 'woocommerce_price_trim_zeros', 'wc_hide_trailing_zeros', 10, 1 );
function wc_hide_trailing_zeros( $trim ) {
// set to false to show trailing zeros
return false;
}
I've fixed it by checking that when the price has one decimal, add a zero.
// https://stackoverflow.com/a/2430214/3689325
function numberOfDecimals( $value ) {
if ( (int) $value == $value ) {
return 0;
}
else if ( ! is_numeric( $value ) ) {
return false;
}
return strlen( $value ) - strrpos( $value, '.' ) - 1;
}
/**
* Make sure prices have two decimals.
*/
add_filter( 'woocommerce_get_price_including_tax', 'price_two_decimals', 10, 1 );
add_filter( 'woocommerce_get_price_excluding_tax', 'price_two_decimals', 10, 1 );
function price_two_decimals( $price ) {
if ( numberOfDecimals( $price ) === 1 ) {
$price = number_format( $price, 2 );
return $price;
}
return $price;
}
Related
I am trying to add a random string when the order number is created as the default sequential number can be very easily guessed.
I tried this snippet:
function generate_random_string( $length = 16 ) {
return substr( str_shuffle( str_repeat( $x = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil( $length / strlen( $x ) ) ) ), 1, $length );
}
add_filter( 'woocommerce_order_number', 'ct_change_woocommerce_order_number', 1, 2);
function ct_change_woocommerce_order_number( $order_id, $order ) {
$random_string1 = generate_random_string(5);
return $random_string1 . $order->get_id();
}
The problem is that this change the order number every time the order number is requested somewhere.
This would work if I will use a constant prefix and suffix, but in the actual way a different order number is shown each time for the same order. Any advice?
To prevent this you can save the result as meta data, once this exists return the meta data instead of the result of the function
So you get:
function generate_random_string( $length = 16 ) {
return substr( str_shuffle( str_repeat( $x = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil( $length / strlen( $x ) ) ) ), 1, $length );
}
function filter_woocommerce_order_number( $order_number, $order ) {
// Get meta
$random_meta_string = $order->get_meta( '_random_meta_string' );
// When meta empty
if ( empty ( $random_meta_string ) ) {
// Call function
$generate_random_string = generate_random_string( 5 );
// Append
$random_string = $generate_random_string . $order->get_id();
// Add the meta data
$order->update_meta_data( '_random_meta_string', $random_string );
// Return random string
$order_number = $random_string;
} else {
// Return meta
$order_number = $random_meta_string;
}
return $order_number;
}
add_filter( 'woocommerce_order_number', 'filter_woocommerce_order_number', 10, 2 );
the following code: Set product sale price programmatically in WooCommerce 3 works perfectly.
Also its continuation: Set programmatically product sale price and cart item prices in Woocommerce 3.
However I'd like to exclude an user role from this function altogether, how can I do that?
I added the following to the code above to no avail:
if ( ! wc_current_user_has_role( 'trader' ) ) return $product->get_regular_price();
Thanks
After asking around and trying it out, the following code manages to achieve what I need. For future reference if someone else needs it:
// Generating dynamically the product "regular price"
add_filter( 'woocommerce_product_get_regular_price', 'custom_dynamic_regular_price', 10, 2 );
add_filter( 'woocommerce_product_variation_get_regular_price', 'custom_dynamic_regular_price', 10, 2 );
function custom_dynamic_regular_price( $regular_price, $product ) {
if( empty($regular_price) || $regular_price == 0 )
return $product->get_price();
else
return $regular_price;
}
// Generating dynamically the product "sale price"
add_filter( 'woocommerce_product_get_sale_price', 'custom_dynamic_sale_price', 10, 2 );
add_filter( 'woocommerce_product_variation_get_sale_price', 'custom_dynamic_sale_price', 10, 2 );
function custom_dynamic_sale_price( $sale_price, $product ) {
$user = wp_get_current_user();
$rate = 0.9;
if( in_array( 'trader', (array) $user->roles ) ) {
return $product->get_regular_price() * $rate;
}
else
{
if( empty($sale_price) || $sale_price == 0 )
return $product->get_regular_price() * $rate;
else
return $sale_price;
}
};
// Displayed formatted regular price + sale price
add_filter( 'woocommerce_get_price_html', 'custom_dynamic_sale_price_html', 20, 2 );
function custom_dynamic_sale_price_html( $price_html, $product ) {
if( $product->is_type('variable') ) return $price_html;
$user = wp_get_current_user();
if( in_array( 'trader', (array) $user->roles ) ) {
$price_html = wc_price($product->get_regular_price() );
return $price_html;
}
else
{
$price_html = wc_format_sale_price( wc_get_price_to_display( $product, array( 'price' => $product->get_regular_price() ) ), wc_get_price_to_display( $product, array( 'price' => $product->get_sale_price() ) ) ) . $product->get_price_suffix();
return $price_html;
}
}
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 ) {
$price = $cart_item['data']->get_sale_price(); // get sale price
$cart_item['data']->set_price( $price ); // Set the sale price
}
}
I'm trying to drop the VAT on my cart items when the subtotal is over a specific amount. But when I use WC()->cart->get_subtotal() to get the subtotal price, it returns 0 and my if statement fails.
add_action( 'woocommerce_product_get_tax_class', 'wp_check_uk_vat', 1, 2 );
function wp_check_uk_vat( $tax_class, $product ) {
$cart_total = WC()->cart->get_subtotal();
// echo $cart_total;
if( $cart_total >= 100 ) {
$tax_class = "Zero rate";
}
return $tax_class;
}
Similar code is used here: https://docs.woocommerce.com/document/setting-up-taxes-in-woocommerce/#section-18
The goal of the code is to drop the VAT on the cart (not shipping) when the order amount is over 135 GBP (to comply with the new Brexit rules for merchants).
Because WC()->cart->get_subtotal() returns 0, the if statement fails, and the VAT is not being dropped.
Try this
add_action( 'woocommerce_product_get_tax_class', 'wp_check_uk_vat', 1, 2 );
function wp_check_uk_vat( $tax_class, $product ) {
if(WC()->cart){
$subtotal = 0;
foreach ( WC()->cart->get_cart() as $cart_item ) {
$subtotal += $cart_item[ 'data' ]->get_price( 'edit' ) * $cart_item[ 'quantity' ];
}
if ( $subtotal >= 100 ) {
$tax_class = "Zero rate";
}
}
return $tax_class;
}
I have been working on the cart value roundup to the nearest value and created a function but it roundups the complete value.
What am I doing wrong?
//round cart total up to nearest dollar
add_filter( 'woocommerce_calculated_total', 'custom_calculated_total' );
function custom_calculated_total( $total ) {
$total = round( $total );
return ceil($total / 5) * 5;
}
If I have the values 44.24 I want to output as 44.25 and if I have the value as 44.28 then the output should be as 44.30
First of all you will need to use the woocommerce_cart_subtotal filter hook in addition to the woocommerce_calculated_total filter hook.
Otherwise your subtotal will deviate from the total, which seems weird
Then you can use 1 of the answers from: Rounding Mechanism to nearest 0.05
So you get:
function rnd_func( $x ) {
return round( $x * 2, 1 ) / 2;
}
function filter_woocommerce_cart_subtotal( $subtotal, $compound, $cart ) {
// Get cart subtotal
$round = rnd_func( $cart->subtotal );
// Use wc_price(), for the correct HTML output
$subtotal = wc_price( $round );
return $subtotal;
}
add_filter( 'woocommerce_cart_subtotal', 'filter_woocommerce_cart_subtotal', 10, 3 );
// Allow plugins to filter the grand total, and sum the cart totals in case of modifications.
function filter_woocommerce_calculated_total( $total, $cart ) {
return rnd_func( $total );
}
add_filter( 'woocommerce_calculated_total', 'filter_woocommerce_calculated_total', 10, 2 );
I wish to set up a specific discount on a particular variable products but for certain selected variations not for all variation:
eg : my varible id - 1571
variation id - 1572
variation id - 1573
So if customer buys one product they get the another (the same) on 50% discount (Buy one get another for 50% off).
I've tried many discount plugins and the closest that I have found are:
Pricing Deals for WooCommerce,
Conditional Discounts for WooCommerce
WooCommerce Extended Coupon Features FREE
With some of them, I was able to setup discount on subtotal or discount on a each product but not exactly what I am looking for (Buy 1 get 1 off). There are other pro plugins I don't want to go for it.
The nearest code that I found is WooCommerce discount: buy one get one 50% off with a notice.
Is it possible to make a discount on the 2nd item for specific product variations of a variable product (only for each product variation)?
mak
To get a 50% Off on the 2nd item for some specific product variations of a variable product, you will use the following instead:
add_action('woocommerce_cart_calculate_fees', 'add_custom_discount_2nd_at_50', 10, 1 );
function add_custom_discount_2nd_at_50( $cart ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// YOUR SETTINGS:
$product_variations_ids = array(1572, 1573); // <== HERE your targeted product variations
// Initializing variables
$discount = 0;
$product_names = array();
// Loop through cart items
foreach ( $cart->get_cart() as $key => $cart_item ) {
if ( in_array( $cart_item['variation_id'], $product_variations_ids ) ) {
$qty = (int) $cart_item['quantity'];
$price = (float) $cart_item['data']->get_price();
$name = (string) $cart_item['data']->get_name();
if ( $qty > 1 ) {
$discount -= number_format( $price / 2, 2 );
}
elseif( $qty = 1 ) {
$product_names[] = $name;
}
}
}
// Applying the discount
if( $discount != 0 ){
$cart->add_fee('Buy one get one 50% off', $discount );
}
// Display a custom reminder notice on cart page (otional)
if( ! empty($product_names) ){
wc_clear_notices(); // clear other notices on checkout page.
if( ! is_checkout() ){
wc_add_notice( sprintf(
__( "Add one more to get 50%% off on the 2nd item for %s" ),
'"<strong>' . implode(', ', $product_names) . '</strong>"'
), 'notice' );
}
}
}
Code goes in functions.php file of your active child theme (or active theme) Tested and works.
To get 1 item at 50% OFF for each item purchased, instead of 50% OFF on the 2nd item, replace:
$discount -= number_format( $price / 2, 2 );
by:
$multiplier = ( $qty % 2 ) === 0 ? $qty / 2 : ( $qty - 1 ) / 2;
$discount -= number_format( $price / 2 * $multiplier, 2 );
To get the 2nd one Free instead of 50% OFF on the 2nd item, replace the code line:
$discount -= number_format( $price / 2, 2 );
by:
$discount -= $price;
To get 1 item free for each item purchased, instead of 50% OFF on the 2nd item, replace:
$discount -= number_format( $price / 2, 2 );
by:
$multiplier = ( $qty % 2 ) === 0 ? $qty / 2 : ( $qty - 1 ) / 2;
$discount -= $price * $multiplier;
You were really close!
The discount amount calculation were wrong
add_action('woocommerce_cart_calculate_fees', 'add_custom_discount_2nd_at_50', 10, 1 );
function add_custom_discount_2nd_at_50( $wc_cart ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
$discount = 0;
$items_prices = array();
$qty_notice = 0; // <== Added HERE
// Set HERE your targeted variable product ID
$targeted_product_id = 1571 ;
foreach ( $wc_cart->get_cart() as $key => $cart_item ) {
if( $cart_item['product_id'] == $targeted_product_id ){
$qty = intval( $cart_item['quantity'] );
$qty_notice += intval( $cart_item['quantity'] ); // <== Added HERE
for( $i = 0; $i < $qty; $i++ )
$items_prices[] = floatval( $cart_item['data']->get_price());
}
}
$count_items_prices = count($items_prices);
//to get the discount of lowest price sorting in descending order
rsort($items_prices);
if( $count_items_prices > 1 ) foreach( $items_prices as $key => $price )
if( $key % 2 == 1 ) $discount -= number_format($price / 2, 2 );
if( $discount != 0 ){
// The discount
# Note: Last argument in add_fee() method is related to applying the tax or not to the discount (true or false)
$wc_cart->add_fee('50% off de segunda almohada' , (($price/2)*($discount))^⁻1, true ); //EDITED
// Displaying a custom notice (optional)
wc_clear_notices();
if(!is_checkout()){
wc_add_notice( __("Hurrah!! You got 50% off discount on the 2nd item"), 'notice');
}}
// Display a custom notice on cart page when quantity is equal to 1.
elseif( $qty_notice == 1){
wc_clear_notices();
if(!is_checkout()){
wc_add_notice( __( "Add one more to get 50% off on 2nd item" ), 'notice');
}}
}