I need to dynamically get shipping costs at checkout from an external system. I've accessed the api and have the costs in variables and now want to 'overwrite' the costs coming from woocommerce with these new ones.
I'm hooking into the woocommerce_package_rates hook but don't seem to be able to get it to work. In the example below, I've just swapped out the variable for a number (100) for simplicity. Any ideas?
function flat_rates_cost( $rates, $package ) {
if ( isset( $rates['flat_rate:5'] ) ) {
$rates['flat_rate:5']->cost = 100;
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'flat_rates_cost', 10, 2 );
function flat_rates_cost( $rates, $package ) {
foreach($rates as $key => $rate){
// Put your desired slug here for API service
if($rate->method_id == 'flat_rate'){
// modify the value here
$rates[$key]->cost = 25;
}
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'flat_rates_cost', 10, 2 );
I can relate an article with exact same scenario. If you go through that article it will describe you how to change flat rates applying your own logic.
Related
How can I show to my user on the checkout page a specific shipping option ?
I know how to remove a shipping option but I can't add a new one (an existing one) in $rates.
I tried to add :
array_push($rates, 'flat_rate:5');
Isn't array_push supposed to do the job ?
Here is a basic snippet, from my function files.
add_filter( 'woocommerce_package_rates', 'custom_package_rates', 10, 2 );
function custom_package_rates( $rates, $package ) {
$total = WC()->cart->cart_contents_total;
if( $total < 100 ) {
// remove from shipping options
unset( $rates['advanced_free_shipping'] );
// Tryed it but critical error is thrown
array_push($rates, 'flat_rate:5');
}
return $rates;
}
Tried everybit of code that I found on stack and other places, seems I'm the only one to have an issue...
$rates is not a simple array, it contains also an object with data for each rate, so adding an array incl. the object is something you want to avoid. But you don't need to. Instead you can do it like this:
...
if( $total < 100 ) {
// remove from shipping options
unset( $rates['advanced_free_shipping'] );
}
else {
// only available when total is <= 100
unset( $rates['flat_rate:5'] );
}
...
It basically does the same.
I am trying to get woocommerce to remove all shipping classes and charge for just the two highest charges found within the cart but my function is not working. It is removing all the shipping charges and setting the shipping cost to zero.
EDIT: There is default functionality within a shipping zone to charge shipping classes set as "Per order: Charge shipping for the most expensive shipping class" but I need this to be "Per order: Charge shipping for the two most expensive shipping classes"
I have commented each step to show my understanding.
function charge_for_two_highest_shipping_classes( $rates ) {
// Sort rates by cost, in ascending order
usort( $rates, function( $a, $b ) {
return $a->cost - $b->cost;
} );
// Remove all shipping rates except the two highest
$highest_rates = array_slice( $rates, -2 );
// Set all other rates to zero
foreach ( $rates as $rate ) {
if ( ! in_array( $rate, $highest_rates, true ) ) {
$shipping_class_id = 0;
foreach ( $rate->get_meta_data() as $meta ) {
if ( 'shipping_class_id' === $meta->key ) {
$shipping_class_id = (int) $meta->value;
break;
}
}
$rate->cost = 0;
$rate->taxes = array();
$rate->set_meta_data( 'shipping_class_id', $shipping_class_id );
}
}
return $highest_rates;
}
add_filter( 'woocommerce_package_rates', 'charge_for_two_highest_shipping_classes', 10, 1 );
What you are trying to achieve is not as simple as you think.
The woocommerce_package_rates hook is wrong because all shipping class calculations are already done at this point.
The WooCommerce code you are looking for is here:
wp-content/plugins/woocommerce/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php
See in the calculate_shipping function and look for the $highest_class_cost.
You could patch this function, but I wouldn't recommend it. There is no hook for this, you could only create new flat rates programmatically and do your logic there.
To sorts the rates in descending order and takes the first two elements (the two highest rates) and removes all other rates from the array. you can use this modification, Hope it might helpful to you 🙂 Thanks
function charge_for_two_highest_shipping_classes( $rates ) {
// Sort rates by cost, in descending order
usort( $rates, function( $a, $b ) {
return $b->cost - $a->cost;
} );
// Remove all shipping rates except the two highest
$highest_rates = array_slice( $rates, 0, 2 );
foreach ( $rates as $key => $rate ) {
if ( ! in_array( $rate, $highest_rates, true ) ) {
unset( $rates[$key] );
}
}
return $highest_rates;
}
add_filter( 'woocommerce_package_rates', 'charge_for_two_highest_shipping_classes', 10, 1 );
I need to hide a shipping method depending on if a discount code is used or not.
But I don't find how to hide a shipping method in the order confirmation page.
I saw that answer but it does nothing, I tried this code to verify:
add_filter( 'woocommerce_package_rates', 'custom_shipping_rates', 100, 2 );
function custom_shipping_rates( $rates, $package ) {
exit();
}
Which should block page display but the page is well loaded.
EDIT: The filter is never called because wordpress uses stored rates in class-wc-shipping.php:
if ( ! is_array( $stored_rates ) || $package_hash !== $stored_rates['package_hash'] || 'yes' === get_option( 'woocommerce_shipping_debug_mode', 'no' ) )
{
...
// Filter the calculated rates.
$package['rates'] = apply_filters( 'woocommerce_package_rates',
$package['rates'], $package );
...
}
else
{
$package['rates'] = $stored_rates['rates'];
}
That code should actually work unless you have an old wc version. You can verify if that hook exist by searching for it in /plugins/woocommerce code base. If you don't find it, then something is wrong with the updates.
If you find it and your code still doesn't work, then the only other reason is that you placed that snipped of code in a point that never get fired or you simple come too late.
Could you please add where and how you placed that code so that we can see better what is going on?
Edit:
if you look above the snipped you copied you will see:
// Get rates stored in the WC session data for this package.
$wc_session_key = 'shipping_for_package_' . $package_key ;
$stored_rates = WC()->session->get( $wc_session_key );
which makes me think that is a session-related thing. So in order to fully test you need to either open the website with another browser, empty your browser cache + cookie or use an anonymous browser session
There's a trick to disable the usage of cache, I limited it to the "update order review" action that way:
if (preg_match('#update_order_review#i',$_SERVER['REQUEST_URI']))
{
add_filter('transient_shipping-transient-version', function($value, $name)
{
return false;
}, 10, 2);
}
add_filter( 'woocommerce_package_rates', 'custom_shipping_rates', 100, 2 );
function custom_shipping_rates( $rates, $package )
{
//get coupons
$reduc=WC()->cart->get_coupons();
$hidemr=isset($reduc['thediscountcode']);
if ($hidemr)
{
foreach($rates as $k=>$v)
{
if (preg_match('#MethodLabelToHide#i',$v->get_label()))
unset($rates[$k]);
}
}
return $rates;
}
In Woocommerce I have the following situation: there are shipping methods before 100$, and after 100$ only one (free shipping) is available. So when a client buys a product 102$ and then apply the (10%) promo code, the price will be 91,80$. Because I unset the shipping methods after 100$ and the free shipping appears after 100$ only, for the client shows: "There are no shipping methods available..."
add_filter( 'woocommerce_package_rates', 'woocommerce_hide_shipping', 10, 2 );
function woocommerce_hide_shipping( $rates, $package ) {
$threshold = 100;
if ( WC()->cart->subtotal >= $threshold ) {
unset( $rates['flat_rate:45'] );
unset( $rates['flat_rate:75'] );
}
if ( WC()->cart->subtotal >= $threshold && !empty(WC()->cart->applied_coupons) ) {
//code here
}
return $rates;
}
Its possible to show the free shipping if the price was >100$ before promo code and a coupon was applied? I set up the free shipping to show a minimum order amount (100$) but there is a way to show, initialize? Other approaches are also welcomed.
The problem is.... Free Shipping that you have applied is bounded by min order and that is referring to order total (NOT SUBTOTAL).....
Whereas subtotal gives you the value without discount.
So,
Remove condition (min order total) from your Admin settings - Free shipping.... Now it will work for every order...
Then modify your code ---
add_filter( 'woocommerce_package_rates', 'woocommerce_hide_shipping', 10, 2 );
function woocommerce_hide_shipping( $rates, $package ) {
$threshold = 100;
if ( WC()->cart->subtotal >= $threshold ) {
unset( $rates['flat_rate:45'] );
unset( $rates['flat_rate:75'] );
}
if ( WC()->cart->subtotal < $threshold ) {
//code here
unset( $rates['free_shipping:45'] );
unset( $rates['free_shipping:75'] );
}
return $rates;
}
I run an online store with WooCommerce and sell normal products and books. I want to reach the following:
If somebody has a cart value of < 50 he should pay for shipping
If somebody has a cart value of > 50 shipping should be free
If somebody adds a book to the cart shipping is always free
I try this with the following code:
function custom_free_per_class( $return, $package ) {
// Setup an array of shipping classes that allow Flat Rate Shipping
$shippingclass_array = array( 'bookshipping');
// loop through the cart checking the shipping classes
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
$shipping_class = get_the_terms( $values['product_id'], 'product_shipping_class' );
if ( isset( $shipping_class[0]->slug ) && in_array( $shipping_class[0]->slug, $shippingclass_array ) ) {
return true;
break;
}//if
}//foreach
}//function
add_filter( 'woocommerce_shipping_free_shipping_is_available', 'custom_free_per_class', 10, 2 );
add_filter( 'woocommerce_package_rates', 'hide_shipping_when_free_is_available', 10, 2 );
/**
* Hide shipping rates when free shipping is available
*
* #param array $rates Array of rates found for the package
* #param array $package The package array/object being shipped
* #return array of modified rates
*/
function hide_shipping_when_free_is_available( $rates, $package ) {
// Only modify rates if free_shipping is present
if ( isset( $rates['free_shipping'] ) ) {
// To unset all methods except for free_shipping, do the following
$free_shipping = $rates['free_shipping'];
$rates = array();
$rates['free_shipping'] = $free_shipping;
}
return $rates;
}
I set it up in the WooCommerce backend that free shipping should be available after a cart value of 50 has been reached. But however this does not work. The above code works - so free shipping is guaranteed when somebody adds a book, but this seems to hinder the other intention (cart > 50) from working. If I remove the code
add_filter( 'woocommerce_shipping_free_shipping_is_available', 'custom_free_per_class', 10, 2 );
the functionality with adding free shipping when 50 is reached works, but surely books are not free anymore.
Des anybody has an idea what is going wrong here?
I would really appreciate it if anyone can help me.
Thanks!
I solved it by myself. The input parameter for the function is not ($return, $package) - it is just ($is_available).