I need to remove the tax from the signup fee, I can't see a way of having a tax on the subscription and not the signup fee.
I've tried using the filter: woocommerce_subscriptions_product_price_string. I can only manage to turn on or off the signup fee.
How can I remove tax from the signup fee?
This is the right hook to use, but it's not easy to get it working for that. So Here it's a custom function hooked in woocommerce_subscriptions_product_price_string that will replace the displayed signup fee price "including taxes" by the signup fee price "excluding taxes".
I have separated in a function that you can reuse, the prices signup fee. It will return an array of prices (the price with / without taxes and the tax ).
Here is that code:
function get_price_sign_up_fee( $product ){
$price_incl_tax = wcs_get_price_including_tax( $product, array( 'price' => WC_Subscriptions_Product::get_sign_up_fee( $product ) ) );
$price_excl_tax = wcs_get_price_excluding_tax( $product, array( 'price' => WC_Subscriptions_Product::get_sign_up_fee( $product ) ) );
$price_tax = $price_incl_tax - $price_excl_tax;
return array( 'incl_tax' => $price_incl_tax, 'excl_tax' => $price_excl_tax, 'tax' => $price_tax );
}
add_filter( 'woocommerce_subscriptions_product_price_string', 'subscr_product_price_string', 10, 3 );
function subscr_product_price_string( $subscription_string, $product, $include ){
//print_pr($subscription_string);
if ( $include['sign_up_fee'] && WC_Subscriptions_Product::get_sign_up_fee( $product ) > 0 ) {
$sign_up_fee = get_price_sign_up_fee( $product );
// Replacing price to excluding taxes one
$subscription_string = str_replace( wc_price($sign_up_fee['incl_tax']), wc_price($sign_up_fee['excl_tax']), $subscription_string );
}
return $subscription_string;
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Tested and works.
But in cart items the signup fee remain with taxes… but this is another thing (or another question)… So you will need to find the way to make changes on it too…
Related
Is there any way to remove a fee from an Woocommerce order before saving it to the database?
I have tried the following hooks, to no success
woocommerce_before_save_order_items
woocommerce_calculate_totals
wp_insert_post_data
I also tried to edit the fee total as below, but the fee still gets saved to the database
add_action( 'woocommerce_review_order_before_payment', 'cst_my_hook_1', 10, 3);
function cst_my_hook_1() {
WC()->cart->set_fee_total(0);
}
I am sharing a screenshot to make my requirements more clear. Woocommerce cart class (class-wc-cart.php) contains a public function to add fees, so I think there should be ways to remove it too.
I used the hook "woocommerce_cart_calculate_fees" to add the fee shown in the screenshot. Now I want to remove it before saving to DB.
I am using Wordpress 5.7.1, Woocommerce 5.2.1
To disable fees from order once checking out, use this simple following hooked function, that will clean all fees from orders and email notifications:
add_action( 'woocommerce_checkout_order_created', 'order_created_disable_fees' );
function order_created_disable_fees( $order ) {
$targeted_item_name = __( "Total Tax Payment", "woocommerce" );
foreach( $order->get_items( 'fee' ) as $item_id => $item ) {
if( $targeted_item_name === $item['name'] ) {
$order->remove_item($item_id);
}
}
$order->calculate_totals();
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Now if you want to remove the fees, but keep original total order amount, use the following:
add_action( 'woocommerce_checkout_order_created', 'order_created_disable_fees' );
function order_created_disable_fees( $order ) {
$targeted_item_name = __( "Total Tax Payment", "woocommerce" );
foreach( $order->get_items( 'fee' ) as $item_id => $item ) {
if( $targeted_item_name === $item['name'] ) {
$order->remove_item($item_id);
}
}
$order->save();
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
If you want to remove a fee from an order before saving it in the database you can use the woocommerce_create_order hook that fires before the order is created within the create_order method of the WC_Checkout class.
Based on this answer:
Remove applied specific fee from Cart programmatically
you will be able to remove the fee from the cart.
However, you will also have to recalculate the totals and taxes (it works whether the fee is taxable or not, and even with multiple tax classes).
REMOVE A FEE FROM CART AND RECALCULATE TOTALS
// removes a specific fee by name before creating the order and recalculates the totals
add_filter( 'woocommerce_create_order', 'remove_specific_fee_from_cart_before_creating_order', 10, 2 );
function remove_specific_fee_from_cart_before_creating_order( $order_id = null, $order ) {
// get the fees added to the cart
$fees = WC()->cart->get_fees();
// initialize taxes (if the fee is not taxable)
$fee_tax = 0;
$fee_tax_data = array();
foreach ( $fees as $key => $fee ) {
// replace "Total Tax Payment" with the name of the fee you added to the cart
if ( $fee->name == 'Total Tax Payment' ) {
// gets the data to recalculate the cart total
$fee_amount = $fee->amount;
if ( $fee->taxable ) {
$fee_tax = $fee->tax;
$fee_tax_data = $fee->tax_data;
}
// removes the fee
unset( $fees[$key] );
break;
}
}
// updates the cart fees
WC()->cart->fees_api()->set_fees( $fees );
// gets the current values of the cart to be recalculated
$cart_total = WC()->cart->get_total(''); // returns the float value instead of HTML code
$cart_total_tax = WC()->cart->get_total_tax();
$cart_fee_taxes = WC()->cart->get_fee_taxes();
// if the fee is taxable
// recalculates the taxes by removing the taxes of the removed fee
if ( ! empty( $fee_tax_data ) ) {
foreach ( $cart_fee_taxes as $fee_tax_key => $fee_tax ) {
if ( array_key_exists( $fee_tax_key, $fee_tax_data ) ) {
$cart_fee_taxes[$fee_tax_key] = $fee_tax - $fee_tax_data[$fee_tax_key];
}
}
}
// updates the cart totals
WC()->cart->set_total( $cart_total - $fee_amount - $fee_tax );
WC()->cart->set_total_tax( $cart_total_tax - $fee_tax );
WC()->cart->set_fee_taxes( $cart_fee_taxes );
return $order_id;
}
REMOVES A FEE FROM CART WITHOUT RECALCULATING THE TOTALS
// removes a specific fee by name before creating the order
add_filter( 'woocommerce_create_order', 'remove_specific_fee_from_cart_before_creating_order', 10, 2 );
function remove_specific_fee_from_cart_before_creating_order( $order_id = null, $order ) {
// get the fees added to the cart
$fees = WC()->cart->get_fees();
foreach ( $fees as $key => $fee ) {
// replace "Total Tax Payment" with the name of the fee you added to the cart
if ( $fee->name == 'Total Tax Payment' ) {
// removes the fee
unset( $fees[$key] );
break;
}
}
// updates the cart fees
WC()->cart->fees_api()->set_fees( $fees );
return $order_id;
}
The code has been tested and works. Add it to your active theme's functions.php.
Based on Make coupon field mandatory for a product category in WooCommerce answer, I am trying to implement a mandatory coupons for specific products in woocommerce.
Here is my code attempt:
add_action( 'woocommerce_check_cart_items', 'mandatory_coupon_for_specific_items' );
function mandatory_coupon_for_specific_items() {
$targeted_ids = array(40, 41, 42, 43, 44); // The targeted product ids (in this array)
$coupon_code = 'summer1, summer2, summer3, summer4, summer5'; // The required coupon code
$coupon_applied = in_array( strtolower($coupon_code), WC()->cart->get_applied_coupons() );
// Loop through cart items
foreach(WC()->cart->get_cart() as $cart_item ) {
// Check cart item for defined product Ids and applied coupon
if( in_array( $cart_item['product_id'], $targeted_ids ) && ! $coupon_applied ) {
wc_clear_notices(); // Clear all other notices
// Avoid checkout displaying an error notice
wc_add_notice( sprintf( 'The product"%s" requires a coupon for checkout.', $cart_item['data']-
>get_name() ), 'error' );
break; // stop the loop
}
}
}
The code works perfectly to make a coupon mandatory but I need that ONE fo the products can be checkedout using only ONE of the coupons.
With the actual code if I add product ID 40 to the cart, is obligatory to add the 5 coupons, summer1, summer2, summer3, summer 4 and summer5, otherwise the checkout will be impossible. I would like the customer can make the checkout using any of the coupons for any of the products listed in the targeted_id array.
In other words, for all the products the use of coupon is mandatory but not necessarily a specific coupon, could be anyone of the listed in the code.
For example purposes I listed 5 products and 5 coupons, but in reality there are 100 products and 100 coupons
Thank you in advance
There are some mistakes in your code, use the following instead:
add_action( 'woocommerce_check_cart_items', 'mandatory_coupon_for_specific_items' );
function mandatory_coupon_for_specific_items() {
$targeted_ids = array(40, 41, 42, 43, 44); // The targeted product ids (in this array)
$coupon_codes = array('summer1', 'summer2', 'summer3', 'summer4', 'summer5'); // Array of required coupon codes
$coupons_found = array_intersect( array_filter( array_map( 'sanitize_title', $coupon_codes) ), WC()->cart->get_applied_coupons() );
// Loop through cart items
foreach(WC()->cart->get_cart() as $item ) {
// Check cart item for defined product Ids and applied coupon
if( in_array( $item['product_id'], $targeted_ids ) && empty($coupons_found) ) {
wc_clear_notices(); // Clear all other notices
// Avoid checkout displaying an error notice
wc_add_notice( sprintf( 'The product"%s" requires a coupon for checkout.', $item['data']->get_name() ), 'error' );
break; // stop the loop
}
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
I have created a product on WooCommerce, and added two options on product detail page using the hook woocommerce_before_add_to_cart_button. Now when customers add product to cart from product detail page they have two options their. They can choose one option from these two options.
Then I have stored the user selected value in cart meta using the woocommerce hook woocommerce_add_cart_item_data.
I am using the code from this answer: Save product custom field radio button value in cart and display it on Cart page
This is my code:
// single Product Page options
add_action("woocommerce_before_add_to_cart_button", "options_on_single_product");
function options_on_single_product(){
$dp_product_id = get_the_ID();
$product_url = get_permalink($dp_product_id);
?>
<input type="radio" name="custom_options" checked="checked" value="option1"> option1<br />
<input type="radio" name="custom_options" value="option2"> option2
<?php
}
//Store the custom field
add_filter( 'woocommerce_add_cart_item_data', 'save_custom_data_with_add_to_cart', 10, 2 );
function save_custom_data_with_add_to_cart( $cart_item_meta, $product_id ) {
global $woocommerce;
$cart_item_meta['custom_options'] = $_POST['custom_options'];
return $cart_item_meta;
}
And this is what I have tried:
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price', 10, 1);
function add_custom_price( $cart_obj ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
foreach ( $cart_obj->get_cart() as $key => $value ) {
$product_id = $value['product_id'];
$custom_options = $value['custom_options'];
$coupon_code = $value['coupon_code'];
if($custom_options == 'option2')
{
if($coupon_code !='')
{
global $woocommerce;
if ( WC()->cart->has_discount( $coupon_code ) ) return;
(WC()->cart->add_discount( $coupon_code ))
//code for second discount
}
else{
$percentage = get_post_meta( $product_id , 'percentage', true );
//print_r($value);
$old_price = $value['data']->regular_price;
$new_price = ($percentage / 100) * $old_price;
$value['data']->set_price( $new_price );
}
}
}
}
Now what I am trying to get with that last snippet is:
If Option1 is selected by the customer then woocommerce regular process is run.
If Option2 is selected then firstly coupon code applied to cart (if code entered by the customer) and then the price is divide by some percentage (stored in product meta) is applied afterward.
But it’s not working as expected because the changed product price is maid before and coupon discount is applied after on this changed price.
What I would like is that the coupon discount will be applied first on the product regular price and then after change this price with my custom product discount.
Is this possible? How can I achieve that?
Thanks.
This is not really possible … Why? … Because (the logic):
You have the product price
Then the coupon discount is applied to that price (afterwards)
==> if you change the product price, the coupon is will be applied to that changed price
What you can do instead:
You don't change product price
if entered the coupon is applied and …
If "option2" product is added to cart:
Apply a custom discount (a negative fee) based on the product price added after using WC_cart add_fee() method…
For this last case you will have to fine tune your additional discount.
If the coupon has not been applied or it's removed there is no additional discount.
Your custom function will be hooked in woocommerce_cart_calculate_fees action hook instead:
add_action( 'woocommerce_cart_calculate_fees', 'option2_additional_discount', 10, 1 );
function option2_additional_discount( $cart_obj ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$discount = 0;
$applied_coupons = $cart_obj->get_applied_coupons();
foreach ( $cart_obj->get_cart() as $item_values ) {
if( 'option2' == $item_values['custom_options'] && !empty($applied_coupons) ){
$product_id = $item_values['product_id'];
$percentage = get_post_meta( $product_id , 'percentage', true );
$quantity = $item_values['quantity'];
$product_reg_price = $item_values['data']->regular_price;
$line_total = $item_values['line_total'];
$line_subtotal = $item_values['line_subtotal'];
$percentage = 90;
## ----- CALCULATIONS (To Fine tune) ----- ##
$item_discounted_price = ($percentage / 100) * $product_reg_price * $item_values['quantity'];
// Or Besed on line item subtotal
$discounted_price = ($percentage / 100) * $line_subtotal;
$discount += $product_reg_price - $item_discounted_price;
}
}
if($discount != 0)
$cart_obj->add_fee( __( 'Option2 discount', 'woocommerce' ) , - $discount );
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and works.
Adding a negative fee using the WC_Cart->add_fee() method wasn't working for me. When i check the WC Cart class, it even states you are not allowed to use a negative ammount.
See the docs.
I did the following:
create a placeholder coupon with a 'secure' code, e.g. custom_discount_fjgndfl28. Set a discount ammount of 0, so when somebody (somehow) uses this coupon outside your program the discount is still 0.
Use filter woocommerce_get_shop_coupon_data, and set all the coupon data you want for that coupon/session.
Hook into woocommerce_before_calculate_totals and set your custom coupon to the cart.
At this point the Cart should calculate everything correctly. And when it becomes an order, it also has the correct discount ammount.
Note: the coupon code is also used as a label in some templates. Use filter woocommerce_cart_totals_coupon_label to change it.
Example functions:
/**
* NOTE: All the hooks and filters below have to be called from your own
* does_it_need_custom_discount() function. I used the 'wp' hook for mine.
* Do not copy/paste this to your functions.php.
**/
add_filter('woocommerce_get_shop_coupon_data', 'addVirtualCoupon', 10, 2);
function addVirtualCoupon($unknown_param, $curr_coupon_code) {
if($curr_coupon_code == 'custom_discount_fjgndfl28') {
// possible types are: 'fixed_cart', 'percent', 'fixed_product' or 'percent_product.
$discount_type = 'fixed_cart';
// how you calculate the ammount and where you get the data from is totally up to you.
$amount = $get_or_calculate_the_coupon_ammount;
if(!$discount_type || !$amount) return false;
$coupon = array(
'id' => 9999999999 . rand(2,9),
'amount' => $amount,
'individual_use' => false,
'product_ids' => array(),
'exclude_product_ids' => array(),
'usage_limit' => '',
'usage_limit_per_user' => '',
'limit_usage_to_x_items' => '',
'usage_count' => '',
'expiry_date' => '',
'apply_before_tax' => 'yes',
'free_shipping' => false,
'product_categories' => array(),
'exclude_product_categories' => array(),
'exclude_sale_items' => false,
'minimum_amount' => '',
'maximum_amount' => '',
'customer_email' => '',
'discount_type' => $discount_type,
);
return $coupon;
}
}
add_action('woocommerce_before_calculate_totals', 'applyFakeCoupons');
function applyFakeCoupons() {
global $woocommerce;
// $woocommerce->cart->remove_coupons(); remove existing coupons if needed.
$woocommerce->cart->applied_coupons[] = $this->coupon_code;
}
add_filter( 'woocommerce_cart_totals_coupon_label', 'cart_totals_coupon_label', 100, 2 );
function cart_totals_coupon_label($label, $coupon) {
if($coupon) {
$code = $coupon->get_code();
if($code == 'custom_discount_fjgndfl28') {
return 'Your custom coupon label';
}
}
return $label;
}
Please Note: i copied these functions out of a class that handles much more, it's only to help you get going.
I am trying to change product price in cart using the following function:
add_action( 'woocommerce_before_shipping_calculator', 'add_custom_price'
);
function add_custom_price( $cart_object ) {
foreach ( $cart_object->cart_contents as $key => $value ) {
$value['data']->price = 400;
}
}
It was working correctly in WooCommerce version 2.6.x but not working anymore in version 3.0+
How can I make it work in WooCommerce Version 3.0+?
Thanks.
Update 2021 (Handling mini cart custom item price)
With WooCommerce version 3.0+ you need:
To use woocommerce_before_calculate_totals hook instead.
To use WC_Cart get_cart() method instead
To use WC_product set_price() method instead
Here is the code:
// Set custom cart item price
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price', 1000, 1);
function add_custom_price( $cart ) {
// This is necessary for WC 3.0+
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Avoiding hook repetition (when using price calculations for example | optional)
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ) {
$cart_item['data']->set_price( 40 );
}
}
And for mini cart (update):
// Mini cart: Display custom price
add_filter( 'woocommerce_cart_item_price', 'filter_cart_item_price', 10, 3 );
function filter_cart_item_price( $price_html, $cart_item, $cart_item_key ) {
if( isset( $cart_item['custom_price'] ) ) {
$args = array( 'price' => 40 );
if ( WC()->cart->display_prices_including_tax() ) {
$product_price = wc_get_price_including_tax( $cart_item['data'], $args );
} else {
$product_price = wc_get_price_excluding_tax( $cart_item['data'], $args );
}
return wc_price( $product_price );
}
return $price_html;
}
Code goes in functions.php file of your active child theme (or active theme).
This code is tested and works (still works on WooCommerce 5.1.x).
Note: you can increase the hook priority from 20 to 1000 (or even 2000) when using some few specific plugins or others customizations.
Related:
Set cart item price from a hidden input field custom price in Woocommerce 3
Change cart item prices based on custom cart item data in Woocommerce
Set a specific product price conditionally on Woocommerce single product page & cart
Add a select field that will change price in Woocommerce simple products
With WooCommerce version 3.2.6, #LoicTheAztec's answer works for me if I increase the priority to 1000.
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price', 1000, 1);
I tried priority values of 10,99 and 999 but the price and total in my cart did not change (even though I was able to confirm with get_price() that set_price() had actually set the price of the item.
I have a custom hook that adds a fee to my cart and I'm using a 3rd party plugin that adds product attributes. I suspect that these WooCommerce "add-ons" introduce delays that require me to delay my custom action.
I am trying to change product price in cart using the following function:
add_action( 'woocommerce_before_shipping_calculator', 'add_custom_price'
);
function add_custom_price( $cart_object ) {
foreach ( $cart_object->cart_contents as $key => $value ) {
$value['data']->price = 400;
}
}
It was working correctly in WooCommerce version 2.6.x but not working anymore in version 3.0+
How can I make it work in WooCommerce Version 3.0+?
Thanks.
Update 2021 (Handling mini cart custom item price)
With WooCommerce version 3.0+ you need:
To use woocommerce_before_calculate_totals hook instead.
To use WC_Cart get_cart() method instead
To use WC_product set_price() method instead
Here is the code:
// Set custom cart item price
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price', 1000, 1);
function add_custom_price( $cart ) {
// This is necessary for WC 3.0+
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Avoiding hook repetition (when using price calculations for example | optional)
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ) {
$cart_item['data']->set_price( 40 );
}
}
And for mini cart (update):
// Mini cart: Display custom price
add_filter( 'woocommerce_cart_item_price', 'filter_cart_item_price', 10, 3 );
function filter_cart_item_price( $price_html, $cart_item, $cart_item_key ) {
if( isset( $cart_item['custom_price'] ) ) {
$args = array( 'price' => 40 );
if ( WC()->cart->display_prices_including_tax() ) {
$product_price = wc_get_price_including_tax( $cart_item['data'], $args );
} else {
$product_price = wc_get_price_excluding_tax( $cart_item['data'], $args );
}
return wc_price( $product_price );
}
return $price_html;
}
Code goes in functions.php file of your active child theme (or active theme).
This code is tested and works (still works on WooCommerce 5.1.x).
Note: you can increase the hook priority from 20 to 1000 (or even 2000) when using some few specific plugins or others customizations.
Related:
Set cart item price from a hidden input field custom price in Woocommerce 3
Change cart item prices based on custom cart item data in Woocommerce
Set a specific product price conditionally on Woocommerce single product page & cart
Add a select field that will change price in Woocommerce simple products
With WooCommerce version 3.2.6, #LoicTheAztec's answer works for me if I increase the priority to 1000.
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price', 1000, 1);
I tried priority values of 10,99 and 999 but the price and total in my cart did not change (even though I was able to confirm with get_price() that set_price() had actually set the price of the item.
I have a custom hook that adds a fee to my cart and I'm using a 3rd party plugin that adds product attributes. I suspect that these WooCommerce "add-ons" introduce delays that require me to delay my custom action.