How can I charge different tax rates based on the shipping method a customer selects at checkout in Woocommerce? My store has one shipping option that lets international customers avoid the 7% VAT charged here in Thailand.
Here's how to disable taxes when Local Pickup is selected as the shipping option according to Woocommerce documentation:
add_filter( 'woocommerce_apply_base_tax_for_local_pickup', '__return_false' );
But how do I disable taxes on a custom shipping option?
I've started to work out a solution, but I could use some help with line 2. i.e. How to get the current shipping method?
function remove_tax_for_fob( $cart ) {
$ok_remove = get_shipping_method( 'FOB' );
if ($ok_remove){
$cart->remove_taxes();
}
return $cart;
}
add_action( 'woocommerce_calculate_totals', 'remove_tax_for_fob' );
Here is the solution. Thanks for your help, Anand Shah!
/* Remove tax from cart for FOB orders */
function remove_tax_for_fob( $cart ) {
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
$chosen_shipping = $chosen_methods[0];
if($chosen_shipping =='FOB') {
$cart->remove_taxes();
}
return $cart;
}
add_action( 'woocommerce_calculate_totals', 'remove_tax_for_fob' );
Try the following, will need a bit of polishing though
add_action( 'woocommerce_review_order_before_submit','custom_review_order_before_submit');
function custom_review_order_before_submit() {
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
$chosen_shipping = $chosen_methods[0];
if( "FOB" == $chosen_shipping ) {
WC()->customer->is_vat_exempt = true;
} else {
WC()->customer->is_vat_exempt = false;
}
}
Related
I've set up a custom shipping method, i need to calculate shipping cost every time users get to the cart page.
It seems that woocommerce_package_rates action (where I calculate the custom shipping costs) runs only when user does click on a shipping method. This way the cart total is wrong most of the times, the worst is when the custom shipping method is already selected (user doesn't need to click it, so its cost doesn't update).
Is this the normal behavior of woocommerce_package_rates hook?
How to force woocommerce_package_rates to be always executed before displaying the cart totals?
EDIT
Here's some code i'm trying to hack with:
add_action( 'woocommerce_before_cart', 'force_shipping_calc', 1, 0 );
function force_shipping_calc() {
foreach( WC()->session->get('shipping_for_package_0')['rates'] as $rate_id =>$rate) {
// looking for the shipping method to recalc
if($rate->method_id == 'flat_rate') {
// mk1: set_shipping_total won't work, i'm using woocommerce < 3
//WC()->cart->set_shipping_total( MY_calculate_shipping() );
// mk2: doesn't work, "Indirect modification of overloaded property"
WC()->cart->totals['shipping_total'] = wc_format_decimal( MY_calculate_shipping(), wc_get_price_decimals() );
// mk3: cart total nor shipping total affected (?!)
WC()->cart->shipping_total = MY_calculate_shipping();
// mk4: ... ?! work in progress...
}
}
function MY_calculate_shipping() {
return 99.99;
}
add_filter( 'woocommerce_package_rates', 'fty_shipping_flat_rate_cost_calculation', 10, 2 );
function fty_shipping_flat_rate_cost_calculation($rates, $package) {
foreach($rates as $rate_key => $rate_values) {
$method_id = $rate_values->method_id;
$rate_id = $rate_values->id;
if ( 'flat_rate' === $method_id ){
$dist_cost = MY_calculate_shipping();
$price_excl_tax = $rates[$rate_id]->cost + $dist_cost;
$rates[$rate_id]->cost = number_format($price_excl_tax, 2);
$tax_keys = array_keys($rates[$rate_id]->taxes);
$price_excl_tax = $rates[$rate_id]->cost + $dist_cost;
$rates[$rate_id]->cost = number_format($price_excl_tax, 2);
$tax_calculation = $rates[$rate_id]->taxes[$tax_keys[0]] + $dist_cost*(TAX_AMOUNT_IVA-1);
$rates[$rate_id]->taxes[$tax_keys[0]] = number_format($tax_calculation, 2);
$rates[$rate_id]->cost += $dist_cost;
}
}
return $rates;
}
EDIT, again
This (mk. ~17786) seems to be in the right direction.
I changed the hook and force calculate_shipping() from WC_Shipping
add_action( 'woocommerce_cart_totals_before_shipping', 'fty_force_calculate_shipping', 1, 2550 );
function fty_force_calculate_shipping() {
WC()->shipping->calculate_shipping(WC()->shipping->packages);
WC()->cart->calculate_totals();
}
but it's not perfect yet, i think this hook makes a loop in the checkout page...
This needs to be done in woocommerce_package_rates only. In your code there is many errors or mistakes… Try the following:
function custom_calculated_shipping() {
return 99.99;
}
add_filter( 'woocommerce_package_rates', 'custom_shipping_rate_cost_calculation', 10, 2 );
function custom_shipping_rate_cost_calculation( $rates, $package ) {
foreach( $rates as $rate_key => $rate ) {
if ( 'flat_rate' === $rate->method_id ){
// Get rate cost and Custom cost
$initial_cost = $rates[$rate_key]->cost;
$additional_cost = custom_calculated_shipping();
// Calculation
$new_cost = $initial_cost + $additional_cost;
// Set Custom rate cost
$rates[$rate_key]->cost = round($new_cost, 2);
// Taxes rate cost (if enabled)
$new_taxes = array();
$has_taxes = false;
foreach ( $rate->taxes as $key => $tax ){
if( $tax > 0 ){
// Calculating the tax rate unit
$tax_rate = $tax / $initial_cost;
// Calculating the new tax cost
$new_tax_cost = $tax_rate * $new_cost;
// Save the calculated new tax rate cost in the array
$new_taxes[$key] = round( $new_tax_cost, 2 );
$has_taxes = true;
}
}
// Set new tax rates cost (if enabled)
if( $has_taxes )
$rate->taxes = $new_taxes;
}
}
return $rates;
}
Code goes in function.php file of your active child theme (or theme). Tested and works. It will work in all woocommerce versions since 2.6…
You should need to refresh the shipping caches:
1) First this code is already saved on your function.php file.
2) Empty cart.
3) In Shipping settings, enter in a Shipping Zone and disable a Shipping Method and "save". Then re-enable that Shipping Method and "save". You are done.
I'm trying to write a function for WooCommerce that will disable free shipping if a product with a certain shipping class is present in the cart.
Here is what I have which is not working. e-packet is the shipping class that should disable free shipping if it is present in the cart.
function hide_shipping_when_class_is_in_cart( $rates, $package ) {
$shipping_classes = array('e-packet');
$if_exists = false;
foreach( $package['contents'] as $key => $values ) {
if( in_array( $values[ 'data' ]->get_shipping_class(), $shipping_classes ) )
$if_exists = true;
}
if( $if_exists ) unset( $rates['free_shipping:9'] );
return $rates;
}
add_filter( 'woocommerce_package_rates', 'hide_shipping_when_class_is_in_cart', 10, 2 );
Based on similar: Unsetting WooCommerce shipping method based on cart items shipping classes
I have make very small changes. This code is tested and works (see the note at the end):
add_filter( 'woocommerce_package_rates', 'hide_shipping_when_class_is_in_cart', 100, 2 );
function hide_shipping_when_class_is_in_cart( $rates, $package ) {
// Initialisation
$free_shipping_method = 'free_shipping:9';
$shipping_classes = array('e-packet');
$class_exists = false;
foreach( $package['contents'] as $cart_item )
if( in_array( $cart_item['data']->get_shipping_class(), $shipping_classes ) ) {
$class_exists = true;
break; // Stop the loop
}
if( $class_exists )
unset( $rates[$free_shipping_method] );
return $rates;
}
Code goes in function.php file of your active child theme (or active theme).
You should need to refresh the shipping caches:
1) First this code is already saved on your function.php file.
2) In Shipping settings, enter in a Shipping Zone and disable a Shipping Method and "save". Then re-enable that Shipping Method and "save". You are done.
here's my custom plugin code :
class Class_name
{
public function __construct()
{
if ( is_admin() ) {
//Custom admin menu
add_action( 'admin_menu', array( $this,'custom_submenu_page'));
add_filter( 'woocommerce_package_rates', array($this , 'conditional_shipping', 10, 2 ));
}
}
function conditional_shipping( $rates, $packages ) {
if ( isset( $rates['flat_rate:32'] ) ) {
// Set the cost to $100
$rates['flat_rate:32']->cost = 100;
}
return $rates;
}
}
new Class_name();
I'm triying to use this filter but he doesn't work. Nothing happens in the front end. I don't know where is the error.
It's not like that I should use the filter?
Update related to tax calculation on the Shipping Method.
There is some mistakes in your code like:
add_filter( 'woocommerce_package_rates', array( $this , 'conditional_shipping', 10, 2 ) );
… that should be instead:
add_filter( 'woocommerce_package_rates', array( $this , 'conditional_shipping' ), 10, 2 );
Or at the end of your code:
new Class_name();
… that should be instead:
$GLOBALS['class_name'] = new Class_name();
So you should try something like this complete code (empty the cart before):
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
if ( ! class_exists( 'Class_Name' ) ) {
class Class_Name {
public function __construct() {
//Custom admin menu
add_action( 'admin_menu', array( $this,'custom_submenu_page'));
add_filter( 'woocommerce_package_rates', array( $this , 'conditional_shipping' ), 10, 2 );
}
public function conditional_shipping( $rates, $package ) {
// HERE Defined Method rate ID
$rate_key = 'flat_rate:32';
// HERE Define New cost
$new_cost = 100;
## -- 1. Set the rate cost -- ##
if ( isset( $rates[$rate_key] ) ){
// Get original cost
$original_cost = $rates[$rate_key]->cost;
// Set the new cost
$rates[$rate_key]->cost = number_format($new_cost, 2);
// calculate the conversion rate (for taxes)
$conversion_rate = $new_cost / $original_cost;
}
## -- 2. Taxes rate cost calculation (if enabled) -- ##
$taxes = array();
foreach ($rates[$rate_key]->taxes as $key => $tax){
if( $tax > 0 ){
// set the new line tax cost in the array
$taxes[$key] = number_format($tax * $conversion_rate, 2);
}
}
// Set the new taxes costs
$rates[$rate_key]->taxes = $taxes;
## -- Return new costs -- ##
return $rates;
}
}
$GLOBALS['class_name'] = new Class_Name();
}
This code goes in a plugin file. It is tested and works.
You may need to refresh shipping methods:
Go to WooCommerce shipping settings and in the related shipping zone for your "Flat rate", disable / save and re-enable / save the corresponding "flat rate".
I try to disable a shipping method if a specific shipping class is in the cart. I'm using the newest woocommerce version.
Below is my Code for my task.
It's placed at the end of my functions.php file of my Theme.
Sadly its not working.
add_filter( 'woocommerce_package_rates', 'businessbloomer_hide_free_shipping_for_shipping_class', 10, 2 );
function businessbloomer_hide_free_shipping_for_shipping_class( $rates, $package ) {
$shipping_class_target = 513; // ID OF MY SHIPPING_CLASS
$in_cart = false;
foreach( WC()->cart->cart_contents as $key => $values ) {
if( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target ) {
$in_cart = true;
break;
}
}
if( $in_cart ) {
unset( $rates['flat_rate:2'] ); //VALUE:ID OF MY SHIPPING METHOD
}
return $rates;
}
I have tested simplifying a little your code (with the ids of my WC settings) and it works:
add_filter( 'woocommerce_package_rates', 'custom_hide_shipping_methods', 10, 2 );
function custom_hide_shipping_methods( $rates, $package ) {
foreach( WC()->cart->get_cart() as $cart_item ) {
$product = $cart_item[ 'data' ]; // The WC_Product object
$shipping_class_id = $product->get_shipping_class_id();
if( isset($rates['flat_rate:2']) && $shipping_class_id == 513 ) { // <== ID OF MY SHIPPING_CLASS
unset( $rates['flat_rate:2'] ); // Removing specific shipping method
break; // we stop the loop
}
}
return $rates;
}
So your code should work too (if you have set the correct IDs)
BUT you need (after saving your code to the function.php file of your active theme):
To remove all cart items that are remaining in cart when testing.
To refresh the shipping caches:
To do it, you can go in a shipping zone and disable one "flat rate" (for example) and "save". Then re-enable that "flat rate" and "save". You are done.
Now you can test again and it should work
I've created a webshop in Woocommerce (wordpress). It's a wine shop and I need to add an extra cost: 0,08 euro per bottle (product).
I've found this and adjusted it, but I cannot get the number of products (bottles) to multiply with 0.08 euro.
In the cart I do get an extra line but the value is 0.
Can anyone explain me what I'm doing wrong?
function get_cart_contents_count() {
return apply_filters( 'woocommerce_cart_contents_count', $this->cart_contents_count );
}
function woo_add_cart_fee() {
global $woocommerce;
$woocommerce->cart->add_fee( __('Custom', 'woocommerce'), $get_cart_contents_count * 0.08 );
}
add_action( 'woocommerce_cart_calculate_fees', 'woo_add_cart_fee' );
Try this code in your functions.php but it will be applied on over all cart
// Hook before adding fees
add_action('woocommerce_cart_calculate_fees' , 'add_custom_fees');
function add_custom_fees( WC_Cart $cart ){
$fees = 0.08;
$cart->add_fee( 'Handling fee', $fees);
}
EDIT:
To multiply it with each product do something like
add_action('woocommerce_cart_calculate_fees' , 'add_custom_fees');
function add_custom_fees( WC_Cart $cart ){
$fees = 0;
foreach( $cart->get_cart() as $item ){
$fees += $item[ 'quantity' ] * 0.08;
}
if( $fees != 0 ){
$cart->add_fee( 'Handling fee', $fees);
}
}
You are talking about adding surcharge to the cart. You can get all the related information from the Woocommerce Doc in here.