WooCommerce Distance Rate Shipping causing tax rate to reset - woocommerce

I am on the verge of losing it with this problem. So what I am attempting is to apply Zero rate tax rate when a specific coupon ('12345') is applied to the order. This site also has the plugin WooCommerce Distance Rate Shipping installed which adds its own costs and tax to the order based on billing distance.
While the billing address fields are empty, the following code successfully detects the coupon in question and sets tax rate to 'Zero Rate':
function apply_matched_coupons() {
$coupon_code = '12345';
if ( !is_admin() && WC()->cart->has_discount( $coupon_code ) ) {
$tax_class = 'Zero rate';
}
return $tax_class;
};
add_action( 'woocommerce_product_tax_class','apply_matched_coupons' );
But when an address is typed in the billing fields, the Woocommerce order update AJAX works and the WooCommerce Distance Rate Shipping plugin reads the billing addresss, re-checks its rules, applies its costs and also adds its tax. Hence, overwriting my Zero Tax rate set earlier. Screenshot is attached. I believe a hook like woocommerce_checkout_update_order_review may work which fires each time the order is updated on checkout, but can't figure out exactly how to set it.
The plugin's page here does show an example to set the $rule_cost to 0 with this code:
add_filter( 'woocommerce_distance_rate_shipping_rule_cost_distance_shipping', function( $rule_cost, $rule, $distance, $package ) {
$order_total = $package['contents_cost'];
if ( $order_total > 100 && $distance <= 5 ) {
$rule_cost = 0;
}
return $rule_cost;
}, 10, 4 );
Could this help in any way?
screenshot of extra tax added by the said plugin

Related

make a coupon that discount based on weight

I have been trying to make a Coupon that makes a discount based on the cart weight when a coupon is used.
I have achieved something using a fixed amount coupon (with fixed value zero) and adding fees :
$cart_weight = $cart->get_cart_contents_weight() ;
and
$cart->add_fee( 'Discount ' . $coupon_code, $cost* -1 , false );
Even it works ,I dont like this, I would like to have it as a regular coupon. Is there any way? Thanks
Sure, it's possible, you only have to create a unique coupon and apply it to the cart instead of adding a negative fee
//ADD coupon based of cart weight
add_action( 'woocommerce_before_calculate_totals', 'generate_coupon_based_on_total_weight' );
function generate_coupon_based_on_total_weight( $cart ) {
// Set the threshold weight for applying the coupon
$threshold_weight = 10;
$discount_amount = 2;
$coupon_prefix = 'weight_';
$total_weight = $cart->get_cart_contents_weight() ;
// Check if the total weight is greater than or equal to the threshold weight
if ( $total_weight >= $threshold_weight ) {
// Check if the coupon code with the "weight_" prefix has already been applied
if ( has_coupon_code_with_prefix( $coupon_prefix ) ) {
// Coupon code already applied, do nothing
return;
}
// Generate a unique coupon code
$coupon_code = uniqid( $coupon_prefix );
// Create a new coupon object
$coupon = new WC_Coupon();
$coupon->set_code( $coupon_code );
$coupon->set_amount( $discount_amount ); // Set the coupon amount
$coupon->set_individual_use( true ); // Set the coupon to be used only once
$coupon->set_usage_limit( 1 ); // Set the usage limit to 1
$coupon->save(); // Save the coupon
// Apply the coupon to the cart
$cart->add_discount( $coupon_code );
} else {
// Total weight is less than the threshold, remove the coupon if it exists
remove_coupon_from_cart( $coupon_prefix );
}
}
function has_coupon_code_with_prefix( $prefix ) {
// Get the applied coupons
$applied_coupons = WC()->cart->get_applied_coupons();
// Check if there are any applied coupons
if ( ! empty( $applied_coupons ) ) {
// Iterate through the applied coupons
foreach ( $applied_coupons as $coupon_code ) {
// Check if the coupon code starts with the specified prefix
if ( strpos( $coupon_code, $prefix ) === 0 ) {
// There is a coupon code applied that starts with the specified prefix
return true;
}
}
}
// No coupon code was found that starts with the specified prefix
return false;
}
function remove_coupon_from_cart($prefix) {
global $woocommerce;
// Get the cart object
$cart = $woocommerce->cart;
// Get the coupon codes applied to the cart
$coupon_codes = $cart->get_coupons();
// Loop through the coupon codes and remove any that start with the specified prefix
foreach ($coupon_codes as $code => $coupon) {
if (strpos($code, $prefix) === 0) {
$cart->remove_coupon($code);
// Save the updated cart
$cart->calculate_totals();
}
}
}
The main idea of this function is to apply a discount coupon to the user's cart if the total weight of the products in the cart meets a certain threshold, and to remove the coupon if the total weight falls below the threshold.
The function is triggered by the woocommerce_before_calculate_totals action, which occurs before the total cost of the products in the cart is calculated.
The function first sets a few variables:
$threshold_weight: This is the weight that the total weight of the products in the cart must meet or exceed in order for the coupon to be applied.
$discount_amount: This is the amount of the discount that will be applied if the coupon is applied.
$coupon_prefix: This is a prefix that will be added to the coupon code to make it unique.
The function then calculates the total weight of the products in the cart using the get_cart_contents_weight method.
Next, the function checks if the total weight is greater than or equal to the threshold weight. If it is, the function generates a unique coupon code using the uniqid function and the $coupon_prefix variable, and creates a new coupon object using the WC_Coupon class. The coupon object is then configured with the discount amount, individual use, and usage limit. Finally, the coupon is saved and applied to the cart using the add_discount method.
If the total weight is less than the threshold weight, the function removes the coupon from the cart if it exists by calling the remove_coupon_from_cart function and passing in the $coupon_prefix variable.
Overall, this function allows you to apply a discount coupon to the user's cart if the total weight of the products in the cart meets a certain threshold, and to remove the coupon if the total weight falls below the threshold.

Hide express paid shipping options when stock of the product is 0 or less than 0 (backorder cases) in Wocommerce checkout

I set 2 methods of delivery:
Express shipping 48 hours at a cost which es a % of the total cart $ amount
Normal Free shipping in 29 days
The functionality that I want is the following:
If the stock of the product is = 0 or less than 0 (backorder is allowed), the Express shipping option should be hidden. Just must show the Normal free shipping option.
If the stock of the product is >0, both options should be shown.
If I selected more than one product, and one or more of the products does not have stock, the Express shipping option also should be hidden.
From another answer related to this question, I tried the script showed in the answer.
The problem is the following: in the last part of the script there is a mention to a variable in an external plugin.
How I adapt the code to a more generic woocommerce without that plugin?
Script:
/* !Hide Shipping Options Woocommerce */
add_filter( 'woocommerce_available_shipping_methods', 'hide_shipping_based_on_quantity' , 10, 1 );
function check_cart_for_oos() {
// load the contents of the cart into an array.
global $woocommerce;
$found = false;
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
$_product_quantity = $_product->get_stock_quantity();
$_cart_quantity = $values['quantity'];
if (($_product_quantity <= 0) || ($_cart_quantity > $_product_quantity)) {
$found = true;
break;
}
}
return $found;
}
function hide_shipping_based_on_quantity( $available_methods ) {
// use the function check_cart_for_oos() to check the cart for products with 0 stock.
if ( check_cart_for_oos() ) {
// remove the rate you want
unset( $available_methods['table_rate_shipping_next-day'] ); // Replace "table_rate_shipping_next-day" with "table_rate_shipping_your-identifier".
}
// return the available methods without the one you unset.
return $available_methods;
}
From: Remove next day shipping method for out of stock items in Woocommerce
In that answer, it's already mentioned to use your own shipping method identifier. So you just need to replace the shipping method identifier with yours.
// use the function check_cart_for_oos() to check the cart for products with 0 stock.
if ( check_cart_for_oos() ) {
// remove the rate you want
unset( $available_methods['USE_YOUR_OWN_IDENTIFIER_HERE'] ); // Replace "table_rate_shipping_next-day" with "table_rate_shipping_your-identifier".
}

woocommerce tiered flat rate based on a custom field

I have a website where customer chooses a zone in a dropdown in billing fields. These are 4 zones: zone1, zone2 and zone3 as an example.
The idea is that if customer chose zone1 and total cart is less than $1000, customer needs to pay a shipping rate of $100. If more than $1000, customer gets free shipping.
I need to replicate that for the other 2 zones with different cart amounts and shipping rates. Is this possible?
Right now, I have the following working code but customer has to manually choose a flat rate. Problem is, of course, customers... If free shipping is displayed, they will choose that one knowing they dont apply for that option when total amount is less for their zone.
/* Change shipping based on total cost purchased. */
add_filter( 'woocommerce_package_rates', 'bbloomer_woocommerce_tiered_shipping', 10, 2 );
function bbloomer_woocommerce_tiered_shipping( $rates, $package ) {
$threshold_centro = 1000;
$threshold_hasta_el_5 = 1500;
$threshold_arelauquen = 2000;
if ( WC()->cart-> cart_contents_total < $threshold_centro ) {
unset( $rates['free_shipping:9'] );
}
elseif ( WC()->cart-> cart_contents_total < $threshold_hasta_el_5 ) {
unset( $rates['flat_rate:2'] );
}
elseif ( WC()->cart-> cart_contents_total < $threshold_arelauquen ) {
unset( $rates['flat_rate:2'], $rates['flat_rate:3'] );
}
else {
unset( $rates['flat_rate:2'], $rates['flat_rate:3'], $rates['flat_rate:4'], $rates['flat_rate:5'], $rates['flat_rate:6'] );
}
return $rates;
}
and the custom field is this one: billing_wcccf_id_qXZZegrC774HULq
Try to set a WC session when you trigger the selectbox and the use it in your if statement to get the right shipping rate or use a switch
WC->session->set('your session name', 'value')
WC->session->get('your session')

Woocommerce tax based on subtotal amount [duplicate]

In My Wordpress e-commerce web site I use WP Hotel Booking, a plugin for hotel room bookings. The checkout process is done using WooCommerce.
The Issue: We have different rooms with different pricing.For example :
Room A price - 1500
Room B Price - 2700
Room c price - 2200
GST Tax is set at 12% for rooms wich price is below 2500 and 18% for rooms above 2500.
Since I am using WP Hotel Booking for this custom product (room Management), I am unable to use the Additional Tax Classes option in woocommerce to set different tax classes.
I need your help in writing a function to check the room value and then decide what tax needs to be set for the given room.
Thanks
This is something accessible and easy.
1°) you need to create in your WooCommerce Tax settings 2 new Tax classes. In this example I have named that tax classes "Tax 12" and "Tax 18". Then for each of them you will have to set a different percentage of 12% and 18%.
2°) Now here is a custom function hooked in woocommerce_before_calculate_totals action hook that is going to apply a tax class based on the product price. I don't use the tax class names, but the tax class slugs, that are in lowercase and spaces are replace by a hyphen.
So Here is that code:
add_action( 'woocommerce_before_calculate_totals', 'change_cart_items_prices', 10, 1 );
function change_cart_items_prices( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
foreach ( $cart->get_cart() as $cart_item ) {
// get product price
$price = $cart_item['data']->get_price();
// Set conditionaly based on price the tax class
if ( $price < 2500 )
$cart_item['data']->set_tax_class( 'tax-12' ); // below 2500
if ( $price >= 2500 )
$cart_item['data']->set_tax_class( 'tax-18' ); // Above 2500
}
}
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 on WooCommerce version 3+

I have this error in woocommerce "Gateway Disabled: PayPal does not support your store currency"

I have this error in my woocommerce web site:
Gateway Disabled: PayPal does not support your store currency.
Any one have specific solution for this?
I am using currency Saudi Riyaal(SAR) and american Dollars($).
I have been working on this problem for few days and here are the solutions I found. In my case I need BGN, so the codes are for that currency (they can be easily adapted for other currencies).
=== Solution 1 ===
The code in this solution is for a plugin file called paypal-bgn-to-euro.php (saved as UTF-8 without BOM for the Bulgarian words) that is in a folder called paypal-bgn-to-euro. The plugin tells PayPal to support BGN. Then after the user leaves the order page and goes to the PayPal site, it converts the currency (and amount) to EUR (an actual PayPal supported currency). The exchange rate is updated automatically twice daily from a free API. The user pays in EUR but the created order in WooCommerce is still in BGN. WooCommerce detects an error (or sometimes two) in the validation because the currencies (and amounts) do not match and it puts the order on hold. The plugin then changes the status to processing for any order with an order note saying something about PayPal currencies or amounts not matching. Read the comments in the code for more info.
<?php
/*
Plugin Name: Paypal BGN support
Description: Plugin description here.
Author: Nikolay Nikolov
Version: 1.0.0
*/
/////////////////////BEGIN segment 1
//this segment I got from here and changed it a little: http://devseon.com/en/wordpress-tutorials/woocommerce-add-a-paypal-unsupported-currency
//it lies to PayPal that the BGN currency is supported, and converts it to EUR when the payment is made
//it does not change the currency in the WooCommerce order, it remains the same, and WooCommerce detects an error and it puts the order on hold
//but we fix that in segment 3
add_filter( 'woocommerce_paypal_supported_currencies', 'pbte_add_bgn_paypal_valid_currency' );
function pbte_add_bgn_paypal_valid_currency( $currencies )
{
array_push ( $currencies , 'BGN' );
return $currencies;
}
add_filter('woocommerce_paypal_args', 'pbte_convert_bgn_to_eur');
function pbte_convert_bgn_to_eur($paypal_args)
{
if ( $paypal_args['currency_code'] == 'BGN')
{
$convert_rate = get_option('pbte_eur_to_bgn_rate'); //set the converting rate
$paypal_args['currency_code'] = 'EUR'; //change BGN to EUR
$i = 1;
while (isset($paypal_args['amount_' . $i]))
{
$paypal_args['amount_' . $i] = round( $paypal_args['amount_' . $i] / $convert_rate, 2);
++$i;
}
}
return $paypal_args;
}
/////////////////////END segment 1
/////////////////////BEGIN segment 2
//I made this segment so the exchange rate is updated automatically with a wordpress cron job twice daily
//runs on plugin activation
register_activation_hook( plugin_dir_path( __FILE__ )."paypal-bgn-to-euro.php", 'pbte_activate_plugin' );
//runs on plugin deactivation
register_deactivation_hook( plugin_dir_path( __FILE__ )."paypal-bgn-to-euro.php", 'pbte_deactivate_plugin' );
//when the cron job runs, we call a function to update the exchange rate option value
add_action('pbte_twicedaily_check_eur_event', 'pbte_update_eur_rate_option');
//runs on plugin activation
function pbte_activate_plugin()
{
pbte_update_eur_rate_option(); //we update the exchange rate option
if (!wp_next_scheduled('pbte_twicedaily_check_eur_event')) //adds an cron job (if it is not already added) to udpate the exchange rate twice daily
wp_schedule_event(time(), 'twicedaily', 'pbte_twicedaily_check_eur_event');
}
//runs on plugin deactivation
function pbte_deactivate_plugin()
{
wp_clear_scheduled_hook('pbte_twicedaily_check_eur_event'); //removes the cron job we added
}
//gets the exchange rate from a free api and updates our option
function pbte_update_eur_rate_option()
{
$data = json_decode(file_get_contents("http://api.fixer.io/latest?symbols=BGN&base=EUR")); //gets the exchange rate from a free api
if(!empty($data->rates->BGN))
{
if(get_option('pbte_eur_to_bgn_rate'))
update_option('pbte_eur_to_bgn_rate', floatval($data->rates->BGN));
else
add_option('pbte_eur_to_bgn_rate', floatval($data->rates->BGN)); //if the option does not exist for some reason, we create it
}
else //something went wrong while getting the data from the api so we will email the admin
{
$message = "This is a message from ".get_site_url()
.". There is a problem getting the API data in the plugin PayPal BGN support.";
$subject = "Problem with Paypal BGN support";
$to_email = get_bloginfo('admin_email');
$headers[] = 'Content-Type: text/html; charset=UTF-8';
wp_mail($to_email, $subject, $message, $headers);
}
}
/////////////////////END segment 2
/////////////////////BEGIN segment 3
//Since the currencies do not match, WooCommerce puts the order on hold. We fix this with this segment.
//this runs when a new note is added to the order
add_filter( 'woocommerce_new_order_note_data', 'pbte_fix_order_status', 10, 2 );
//if the note says that the PayPal currencies or amounts do not match, then we will change the status to processing
function pbte_fix_order_status($a_note, $a_order)
{
//the check is done in two languages
if ( strpos($a_note['comment_content'],'PayPal валутите не съвпадат') !== false
|| strpos($a_note['comment_content'],'PayPal currencies do not match') !== false
|| strpos($a_note['comment_content'],'PayPal наличността не отговаря') !== false
|| strpos($a_note['comment_content'],'PayPal amounts do not match') !== false )
{
//we create the order var
$order = new WC_Order($a_order['order_id']);
//if the current status is on-hold - we change it to processing and add an optional note
if($order->status == 'on-hold')
$order->update_status('processing', 'The PayPal BGN support plugin did this note.');
}
return $a_note;
}
/////////////////////END segment 3
?>
=== Solution 2 ===
--- Step 1 ---
You need to add this code with a small plugin or in your functions.php:
add_filter( 'woocommerce_paypal_supported_currencies', 'add_my_own_paypal_currency' );
function add_my_own_paypal_currency( $currencies )
{
array_push ( $currencies , 'BGN' );
return $currencies;
}
--- Step 2 ---
We convert the currency (and amount) to a supported one while the user is on the order page in our site, as he chooses the PayPal method.
So we need a different currency for different WooCommerce gateways. Which is exactly one of the features of a free plugin called Booster for WooCommerce (it has a paid version too). This feature is called Payment Gateways Currency.
When we activate the plugin and the selected feature we can choose a different currency only for PayPal and we enter there a conversion rate (for some reason it wants a comma for a separator and not a point). The paid version though is said to support automatic updates of the conversion rates (I haven't tested it).
After the payment is made, the order in WooCommerce is with the new currency now (not like Solution 1). Which might affect other plugins that you are using if they assume all orders are in the WooCommerce store currency.
=== Solution 3 ===
I haven't tested this one, but I found this paid plugin called "PAYPAL CURRENCY CONVERTER PRO FOR WOOCOMMERCE" and the author claims that it solves the problem.
if you are still having issues with this, here is a solution which i found.
Change the currency to correspond to your currency.
add_filter( 'woocommerce_paypal_supported_currencies', 'add_a_paypal_valid_currency' );
function add_a_paypal_valid_currency( $currencies ) {
array_push ( $currencies , 'AED' ); // AED = United Arab Emirates Dirham.
return $currencies;
}
add_filter('woocommerce_paypal_args', 'convert_to_usd');
function convert_to_usd($paypal_args){
if ( $paypal_args['currency_code'] == 'AED'){
$convert_rate = 3.67; // set the converting rate here
$paypal_args['currency_code'] = 'USD'; // change to USD
$i = 1;
while (isset($paypal_args['amount_' . $i])) {
$paypal_args['amount_' . $i] = round( $paypal_args['amount_' . $i] / $convert_rate, 2);
++$i;
}
}
return $paypal_args;
}

Resources