How to show specific shipping options - php

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.

Related

WooCommerce: Charge only the 2 highest shipping classes if more exists

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 );

Hide shipping method in the woocommerce order confirmation page

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;
}

WooCommerce update_checkout won't update shipping methods

On my WooCommerce site I use this function to add a 4€ fee if you select cash on delivery as payment method:
add_action('woocommerce_cart_calculate_fees', 'increase_cod_cost');
function increase_cod_cost() {
if(WC()->session->chosen_payment_method=='cod')
WC()->cart->add_fee(__('COD Fee'), 4);
}
I also use this function to immediately update the checkout whenever you change payment method so that the fee is added when you select cod, and removed when you select any other method:
add_action('woocommerce_review_order_before_payment', 'custom_checkout_update');
function custom_checkout_update() {
echo '
<script type="text/javascript">
(function($){
$(\'form.checkout\').on(\'change\', \'input[name="payment_method"]\', function() {
$(\'body\').trigger(\'update_checkout\');
});
})(jQuery);
</script>
';
}
Both functions work 100%.
Now, Instead of adding a fee, I'd like to properly increase the actual shipping cost, so I've tried this function instead of the first one:
add_filter('woocommerce_package_rates', 'increase_cod_cost_2', 10, 2);
function increase_cod_cost_2($rates, $package) {
if(WC()->session->chosen_payment_method=='cod')
$rates['flat_rate:1']->cost=$rates['flat_rate:1']->cost+4;
return $rates;
}
This function also works, but only if I empty the cart and then add a product again. For some reason it doesn't immediately update the shipping cost whenever I select cod. I really don't understand why the jQuery function would work with the first php function by adding the fee, but not with this one by changing the shipping cost. Can you please help me and tell me what's wrong? Thank you.
Please try this.
add_filter( 'woocommerce_package_rates', 'custom_shipping_costs', 20, 2 );
function custom_shipping_costs( $rates, $package ) {
// New shipping cost (can be calculated)
$new_cost = 1000;
$tax_rate = 0.4;
foreach( $rates as $rate_key => $rate ){
// Excluding free shipping methods
if( $rate->method_id != 'free_shipping'){
// Set rate cost
$rates[$rate_key]->cost = $new_cost;
// Set taxes rate cost (if enabled)
$taxes = array();
foreach ($rates[$rate_key]->taxes as $key => $tax){
if( $rates[$rate_key]->taxes[$key] > 0 )
$taxes[$key] = $new_cost * $tax_rate;
}
$rates[$rate_key]->taxes = $taxes;
}
}
return $rates;
}
I cannot actually help you to fully understand what is going wrong, but I can share you some experience:
Try to include log commands in these cases, it helps you to see when the hook is called, if it is called and depending on your log details if the code went through the if clause or fell into the else case
From experience and what you write, the second hook is not always called, therefore it works when you freshly add a product but not during an shipping method change during checkout.
So I am wildly guessing you probably need another hook like
do_action( 'woocommerce_shipping_method_chosen', $chosen_method );
or something better which triggers whenever woocommerce goes to look for the available shipping methods.
Be careful that the hooks are not called sequentially and you add 4 USD more han 1 time...
PD: I've noticed that the suggested hook is not ideal neither, you could try one of these:
add_action( 'woocommerce_load_shipping_methods', 'action_woocommerce_load_shipping_methods', 10, 1 );
add_filter( 'woocommerce_package_rates', 'woocommerce_package_rates' );
or else check here all the woocommerce hooks:
https://docs.woocommerce.com/wc-apidocs/hook-docs.html

wordpress woocommerce (2.6.9) override shipping

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.

update woocommerce cart after changing shipping method

I am currently working on an online shop with WooCommerce. I faced the problem that I want to grant a discount to customers who chose a specific shipping method. The discount is 0.50 for every single product. I basically solved this problem with the following code in my "functions.php":
add_action('woocommerce_before_calculate_totals', 'woo_add_cart_fee');
function woo_add_cart_fee() {
global $woocommerce;
$cart = $woocommerce->cart->get_cart();
//Calculating Quantity
foreach ($cart as $cart_val => $cid) {
$qty += $cid['quantity'];
}
if ($woocommerce->cart->shipping_label == "specific shipping method") {
$woo_fee = $qty * (-0.5);
$woo_name = "discount for specific shipping method";
}
$woocommerce->cart->add_fee(__($woo_name, 'woocommerce'), $woo_fee, true);
}
The code technically works, the only problem I have now is that if a customer changes the shipping method i.e. from the "specific shipping method" to a "normal one" (without any discount) or the other way round, it always displays and calculates the discount value from the previously chosen shipping method. In other words it is always one step back and therefore displays the customer the wrong total amount.
Does anyone has an idea to solve this problem?
This solutions is for Woocommerce 2.1.X!
I am not sure if this might help. I was facing a similar problem, where I needed to retrieve the chosen shipping method. In the file \wp-content\plugins\woocommerce\includes\wc-cart-functions.php I found a method called wc_cart_totals_shipping_html().
Within this method there is a check of the current selected shipping method that contains the following code:
$packages = WC()->shipping->get_packages();
foreach ( $packages as $i => $package ) {
$chosen_method = isset( WC()->session->chosen_shipping_methods[ $i ] ) ? WC()->session->chosen_shipping_methods[ $i ] : '';
}
I used this code in my own functions.php to check for the currently selected shipping method and it works. Example:
add_filter( 'woocommerce_billing_fields', 'wc_change_required_fields');
function wc_change_required_fields($address_fields) {
$packages = WC()->shipping->get_packages();
foreach ( $packages as $i => $package ) {
$chosen_method = isset( WC()->session->chosen_shipping_methods[ $i ] ) ? WC()->session->chosen_shipping_methods[ $i ] : '';
}
if ($chosen_method == 'local_delivery') {
$address_fields['billing_address_1']['required'] = true;
// place your changes that depend on the shipping method here...
}
}
Hope that helps!
This is very old, but I ran into this issue myself and had to work out the solution.
Woocommerce stores pre-calculated cart totals in the database, rather than calculating them on the fly. But the shipping method choice is stored as a session variable. So shipping changes are not reflected immediately at checkout without a visit or refresh of a cart page.
With the original posted code, the shipping changes were not reflected because they aren't recalculated and stored. To do this, the function needs to be tricked into thinking it's a cart page first, and then recalculating the totals to be stored.
GLOBAL $woocommerce;
if ( ! defined('WOOCOMMERCE_CART') ) {
define( 'WOOCOMMERCE_CART', true );
}
And then at the end of the function, after all the desired changes have been made refresh and store.
WC()->cart->calculate_totals();
See also CODEX for WC_AJAX::update_shipping_method()
http://docs.woothemes.com/wc-apidocs/source-class-WC_AJAX.html#148-174
Mark's answer worked for me, however I had to delete all transient values prior to running the code. Otherwise, it would simply restore the saved values.
public function clear_shipping_transients() {
global $wpdb;
$wpdb->query( "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE ('_transient_cp_quote_%') OR `option_name` LIKE ('_transient_timeout_cp_quote_%') OR `option_name` LIKE ('_transient_wc_ship_%')" );
}

Categories