I'm looking to create a PHP code snippet that I would add to my functions.php file but I'm not sure how to proceed.
Someone asked the same question 2 years ago (Woocommerce - disable certain shipping methods based on time of day) but finally opted for a plugin that does that. I already use add-ons for detailed delivery options and I don't want to change or add another one just for this simple function if something can be done easily with php.
So I'm looking to disable a shipping method if the client order after 11AM (website time zone).
So I have this code for now :
if (date('H') > 11) {
$shippingmethod1.disable();
}
Can someone give me the proper code to make it work?
This is quiet simple using woocommerce_package_rates filter hook.
Now you need to find out what is the shipping rate Id reference that you need to target. For that you will inspect the desired shipping method radio button (<input> field) with your browser tools to get value as shown below:
Once you get it you will set that in the code below:
add_filter( 'woocommerce_package_rates', 'hide_shipping_method_based_on_time', 10, 2 );
function hide_shipping_method_based_on_time( $rates, $package )
{
// Set your default time zone (http://php.net/manual/en/timezones.php)
date_default_timezone_set('Europe/London');
// Here set your shipping rate Id
$shipping_rate_id = 'local_pickup:13';
// When this shipping method is available and after 11 AM
if ( array_key_exists( $shipping_rate_id, $rates ) && date('H') > 11 ) {
unset($rates[$shipping_rate_id]); // remove it
}
return $rates;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Clearing shipping caches:
You will need to empty your cart, to clear cached shipping data
Or In shipping settings, you can disable / save any shipping method, then enable back / save.
Related
I need the Woocommerce cart to be cleaned in case I send more than four items to it via url.
I came up with this code, but it only cleans the cart if there are already five items in it.
//in functions.php
add_filter( 'woocommerce_add_to_cart_validation', 'remove_cart_item_before_add_to_cart', 20, 3 );
function remove_cart_item_before_add_to_cart( $passed, $product_id, $quantity ) {
if( WC()->cart->get_cart_contents_count() >= 5 )
WC()->cart->empty_cart();
return $passed;
}
Another problem is that, even the code above that is not useful to me, does not work if I add multiple courses via url, as in:
https://exemple.net/cart?fill_cart=100,101,102,103,104,105,106
That is, the code in functions.php only works from the website, and not by url.
All I need is clear the cart when sending more than 4 items by url.
I prefer a solution in PHP, but a JS solution will do. Thanks for who can help me.
I found a solution using the free plugin Cart links for WooCommerce, and making a few change in its code.
I will describe below what I did:
1) To start install the plugin, this will allow you to add products using the fill_cart parameter in url (as described in my question)
2) edit the plugin file soft79-cart-links-for-woocommerce.php found at: yoursite/wp-content/plugins/soft79-cart-links-for-woocommerce/
3) go to line 141 or find the declaration of the method fill_cart
Inside the fill_cart method uncomment (or add) the code to clean the cart:
public function fill_cart( $fill_string ) {
$original_notices = wc_get_notices();
wc_clear_notices();
////Clear the cart
//WC()->cart->empty_cart(); <-- uncomment here!
Save the file. Now any new items that arrive via url must clean the cart first.
Completely new to Woocommerce/wordpress here. On the cart page mydomain.local/cart What filter should I use to show/hide a flat rate shipping method at certain times. From admin I managed to add an extra flat rate method and named it "Next day". Now I would like to show that flat rate method only before 4PM. I tried in functions.php
add_filter( 'woocommerce_package_rates', 'custom_change_shipping', 10);
function custom_change_shipping($rates) {
var_dump($rates);
}
Nothing seems to change and also I cant seem to debug the $rates variable as nothing is outputted when var_dump($rates);. I tried it both anonymous and as an admin but nothing seems to work.
Thanks #LoicTheAztec for confirming whether I was using the right filter. My other problem was that I was not able to output var_dump on the /cart page.
I found out that I need to clear my "cart cache" by going to woocommerce->settings->shipping and in the region, disable then enable again and click on save changes.
By doing this I could see my output from var_dump.
Filter posted earlier by #LoicTheAztec (as requested by #robert)
add_filter( 'woocommerce_package_rates', 'custom_hide_shipping', 100 );
function custom_hide_shipping( $rates ) {
$current_time = date_i18n(('H'), current_time('timestamp')); //Uses Timezone Settings > General
$maximum_time = array (
'time' =>'16'); //4PM
if ($current_time >= $maximum['time']){
unset( $rates['flat_rate:X'] ); // change X to your rate id
}
return $rates;
}
I have a use case for a website and I can't figure out how to configure WooCommerce Shipping to do what I want.
Regarding shipping in that store:
There are products that can be delivered ONLY locally (for free). If someone is outside a defined the area, a message should notify him that this is a only local product.
Other products can be delivered locally (for free) or to outside areas.
Within these products that can be delivered to outside areas, different delivery rates apply (Product A is +$25 , but Product B is +$30, and Product C is +$10...)
I set up two Shipping zones:
Local (only a list of Zip codes) -> Free Rate,
Outside Area (everywhere) -> put a bunch of classes such as +10 , +20 , +25 ... and gave them rates
I have 2 main problems here:
I dont know how to force some products to be local ONLY.
when doing the setup above, the products that are on the outside area for which I gave a shipping class are not showing in local if someone is trying to checkout from a local Zip code.
How can this be solved?
These is somewhat debatable but you don't need to code to get this done. As I think you're already there.
I set up two Shipping zones:
Local (only a list of Zip codes) -> Free Rate, Outside Area
(everywhere) -> put a bunch of classes such as +10 , +20 , +25 ... and
gave them rates
In your Local and Everywhere should have same shipping classes but should have different values.
Let us say that in your settings be like this
Shipping Zone > Local
Zone regions: add list of zip codes or region.
Shipping methods: add a Flat Rate method
Flat Rate settings:
Shipping Zone > Everywhere
Zone regions: add list of zip codes or region or leave it blank.
Shipping methods: add a Flat Rate method
Flat Rate settings:
I have 2 main problems here:
I dont know how to force some products to be local ONLY.
when doing the setup above, the products that are on the outside area for which I gave a shipping class are not showing in local if
someone is trying to checkout from a local Zip code.
You don't. Just add the correct shipping class for the product and the shipping will do it itself.
This is where you lost me. Here's a screenshot of my cart. Local vs Everywhere.
Local
Everywhere
Above two images are demonstrating a product that has a "Class A" shipping class.
As you have enabled that free local shipping rate, you will need to set it the code below. It can be a "Free Shipping" method or a "Local Pickup" method (set with 0 cost and no tax).
You will have also to set in the function the array of products IDs that need this shipping method enabled (hiding all others).
add_filter( 'woocommerce_package_rates', 'conditionally_hide_shipping_methods', 10, 2 );
function conditionally_hide_shipping_methods( $rates, $package ) {
// Set HERE the shipping method to use:
// It can be 'free_shipping' or 'local_pickup' (set with 0 cost and no tax)
$shipping_method_id = 'local_pickup';
// Set HERE your "LOCAL free" product IDs
$local_free_ids = array( 40, 37 );
$found = false;
foreach( WC()->cart->get_cart() as $cart_item ) {
if( in_array($cart_item[ 'product_id' ], $local_free_ids) ) { // <== ID OF MY SHIPPING_CLASS
$found = true;
break; // Stop the loop
}
}
// If on of this product Ids are in cart we hide all other shipping methods
if ( $found ){
$local_free = array();
foreach ( $rates as $rate_id => $rate ) {
if ( $shipping_method_id === $rate->method_id ) {
$local_free[ $rate_id ] = $rate;
break;
}
}
return ! empty( $local_free ) ? $local_free : $rates;
} else {
return $rates;
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Sometimes is necessary to refresh the shipping caches:
1) First your cart is empty.
2) The code is already saved on your function.php file.
3) 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.
Based on: WooCommerce - Hide other shipping methods when FREE SHIPPING is available
To notify customers for Local products only, this should be asked in a new question...
Maybe I overlooked it, but as much as I searched, I could not find which action to hook when a subscription changes price or frequency in Woocommerce Subscriptions.
The documentation says that to support price changes in your payment gateway you have to list subscription_amount_changes, but nowhere it says which function will be called when the amount actually changes..
Also in the Action reference I was unable to find an action hook which is called when the subscription amount or frequency changes. If anyone has a clue which hook to use or how to implement this particular feature, please tell me.
Edit
Ok thanks for the comments and answer by #Reigel, so if I understand correctly the change of a subscription in the admin menu (which I indeed refer to), has to be handled by the save_post action. Could you provide a small example how to hook this action and check if it is a subscription and get the $order_id (I guess this is the same as post_id?) to use in the subscription management calls?
Thank you very much already!
This should be considered an addon to the answer by #Reigel. If you upvote this, upvote his answer too.
Here's an example of hooking the pre_post_update action. It occurs a little before the save_post action. Both actions are triggered in the wp_insert_post() function in post.php.
function post_save_subscription_check( $post_ID, $data )
{
if( $data['post_type'] == 'product' ) {
if (!empty($_POST['_subscription_price']) && get_post_meta($post_ID, '_subscription_price', true) != $_POST['_subscription_price']) {
/* do stuff here */
}
if (!empty($_POST['_subscription_period']) && get_post_meta($post_ID, '_subscription_period', true) != $_POST['_subscription_period']) {
/* do stuff here */
}
}
}
add_action('pre_post_update', 'post_save_subscription_check', 10, 2 );
In the logic we are checking for the old value, obtained with get_post_meta() and the new value, held in the $_POST variable and comparing them.
This code only executes when a post updates, not for a new post
The code gets placed in your theme functions.php or custom plugin code.
In live code I would recommend cleaning any $_POST data before using it. I haven't bothered here.
I will try to explain about supports.
subscription_amount_changes is just a support and will not fire anything. You can use it for conditional statements, like:
if ( !$chosen_gateway->supports( 'subscription_amount_changes' )) {
echo 'Please be considerate and do not change the price for the chosen gateway does not support it.';
}
now, other plugins can then check if the chosen gateway supports subscription_amount_changes and do their appropriate actions.
action hook which is called when the subscription amount or frequency
changes
subscription is just a product type. Which means this is just a post with a post_type of product. The amount and frequency are just post meta. all are handled on save_post action. add_action( 'save_post', __CLASS__ . '::save_subscription_meta', 11 );. This is on the post_type=product. You have to check also for save_post on post_type=shop_order as it's more appropriate for checking support. Because there's already a gateway chosen.
UPDATED QUESTION AFTER Scriptonomy ANSWER BELOW
I have a woocommerce site in which I have a few different shipping options. I need to hide and show these shipping options based on the "Ship to different address button being checked"
//remove local pickup
function ddc_hide_shipping( $rates, $package ) {
unset( $rates['local_pickup'] );
return $rates;
}
//check if needs shipping address so we can unset local pickup
function ddc_update_shipping_options($instance) {
parse_str(html_entity_decode($instance), $postData);
if ( #$postData['ship_to_different_address'] ) {
add_filter('woocommerce_package_rates', 'ddc_hide_shipping');
}
}
add_action('woocommerce_checkout_update_order_review', 'ddc_update_shipping_options');
Using the above code that got from help from Scriptonomy I am getting closer. The problem I am having as this doesn't seem to work when the user checks and un checks the ship to different billing address. This only works for me on first page load, not on every ajax call to update the order review area.
Thank you.
WC()->cart->needs_shipping_address() is based on store settings and item settings...so not ideal in this case, it will trigger every time in most cases.
Use the $instance data instead which is a url encoded string.
Decode and parse the string then test for the ship_to_different_address option
Suppress invalid array index errors when the option is not selected
Here's your solution:
parse_str(html_entity_decode($instance), $postData);
if ( #$postData['ship_to_different_address'] ) {
add_filter( 'woocommerce_package_rates', 'ddc_hide_shipping', 10, 2 );
}
Put this in your action hook.