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...
Related
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.
In WoCommerce, I would like to disable particular payment methods and show particular payment methods for a subscription products in WooCommerce (and vice versa).
This is the closest thing we've found but doesn't do what I am expecting.
Yes, there are plugins that will do this but we want to achieve this without using another plugin and without making our stylesheet any more nightmarish than it already is.
Any help on this please?
Here is an example with a custom hooked function in woocommerce_available_payment_gateways filter hook, where I can disable payment gateways based on the cart items (product type):
add_filter('woocommerce_available_payment_gateways', 'conditional_payment_gateways', 10, 1);
function conditional_payment_gateways( $available_gateways ) {
// Not in backend (admin)
if( is_admin() )
return $available_gateways;
foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$prod_variable = $prod_simple = $prod_subscription = false;
// Get the WC_Product object
$product = wc_get_product($cart_item['product_id']);
// Get the product types in cart (example)
if($product->is_type('simple')) $prod_simple = true;
if($product->is_type('variable')) $prod_variable = true;
if($product->is_type('subscription')) $prod_subscription = true;
}
// Remove Cash on delivery (cod) payment gateway for simple products
if($prod_simple)
unset($available_gateways['cod']); // unset 'cod'
// Remove Paypal (paypal) payment gateway for variable products
if($prod_variable)
unset($available_gateways['paypal']); // unset 'paypal'
// Remove Bank wire (Bacs) payment gateway for subscription products
if($prod_subscription)
unset($available_gateways['bacs']); // unset 'bacs'
return $available_gateways;
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
All code is tested on Woocommerce 3+ and works.
This is just an example to show you how things can work. You will have to adapt it
This code has been very useful to me, but there is an error in it that I had to fix: the line
$prod_variable = $prod_simple = $prod_subscription = false;
must be put OUTSIDE (before) the FOREACH otherwise it will reset the flag everytime a new item is executed. I my case, I needed to unset a specific payment method whenever a subscription product was on the cart. As it is, this code will work only if there is just a single subscription product. If I put another different item on cart, the flag will be turn to false again and the payment method will load. Putting the line outside the FOREACH will fix this problem.
How do I go about limiting some products to be shipped locally only (a range of Zip codes)? P.S: Other products may ship locally and/or country-wide.
A scenario of this situation would be:
the user selects a product, adds it to cart, checks out, enters the shipping address, then the system would notify him that this item is only available for shipping locally.
I checked all the settings on Woocommerce but nothing worked.
Try looking for a plugin:
https://woocommerce.com/product-category/woocommerce-extensions/shipping-methods/delivery-shipping-options/
If that doesn't work, you can code it:
You need to flag some products as 'local delivery only', but not others. That way at checkout time you can display the appropriate shipping options. You'll also need to know the user's Zip code etc.
Step 1: Add the flag to the product.
Create a check box on the product that allows you to flag it's a local ship item.
The code below will add the option in Products -> Product data -> Advanced
add_action('woocommerce_product_options_advanced', 'localship_options');
function localship_options()
{
echo '<div class="options_group">';
woocommerce_wp_checkbox(array(
'id' => 'localship',
'value' => get_post_meta(get_the_ID(), 'localship', true),
'label' => 'Local Ship',
'desc_tip' => true,
'description' => 'Only ships locally',
));
echo '</div>';
}
add_action('woocommerce_process_product_meta', 'localship_save_fields', 10, 2);
function localship_save_fields($id, $post)
{
if (!empty($_POST['localship'])) {
update_post_meta($id, 'localship', $_POST['localship']);
} else {
delete_post_meta($id, 'localship');
}
}
To see if a product is flagged or not, use get_post_meta. You'll need the id of the
product
$is_localship = get_post_meta( /*product id here */, 'localship', true);
Now you can use $is_localship to see if the product is a ship local.
Step 2: Check the Buyer's Zip Code
You'll need to get the user's order information.
The wc_get_order function should do it:
$order = wc_get_order( $order_id )
From there you should be able to pull out the user's address, zip code etc.
See if the buyer's zip code is in an array of local Zip codes.
Step 3: Display or Hide Shipping Accordingly
Now that you know if the product is local ship only and the Zip code is a match, you can show or hide the shipping option in your template.
You can use the $is_localship to display or hide the shipping box in your template.
That should get you going in the right direction.
I am currently working on a WordPress project using WooCommerce and I need a really specific feature (not included in WooCommerce):
How to increase stock when an order is complete instead of decreasing it ?
What I have found so far is that I might need to use Woocommerce API in order to accomplish that WC_AJAX::increase_order_item_stock();. Nevertheless I am not really comfortable using complex PHP...
Do you have some lines of thinking to accomplish this?
Maybe using a plugin (which I did not find)? Or with raw code?
To sum up everything: I want to build a website for a restaurant with an inventory management and the possibility for cooks to order goods from different suppliers. So when a cook order something from the woocommerce shop page, purchased items's inventory have to increase and not decrease.
I have tried different things like 'WC Vendors' or 'Marketplace' but without success…
Thanks.
You could try This custom function hooked in woocommerce_order_status_completed action hook, that will increase back each product stock with the items quantity of this order when status is set to completed:
add_action( 'woocommerce_order_status_completed', 'action_on_order_completed' , 10, 1 );
function action_on_order_completed( $order_id )
{
// Get an instance of the order object
$order = wc_get_order( $order_id );
// Iterating though each order items
foreach ( $order->get_items() as $item_id => $item_values ) {
// Item quantity
$item_qty = $item_values['qty'];
// getting the product ID (Simple and variable products)
$product_id = $item_values['variation_id'];
if( $product_id == 0 || empty($product_id) ) $product_id = $item_values['product_id'];
// Get an instance of the product object
$product = wc_get_product( $product_id );
// Get the stock quantity of the product
$product_stock = $product->get_stock_quantity();
// Increase back the stock quantity
wc_update_product_stock( $product, $item_qty, 'increase' );
}
}
The code works with simple or variables products that have their own stock management enabled. So may be you might need to make some changes on it, depending on your WooCommerce settings. This is just an example that gives you an idea, a way…
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code don't throw errors on WooCommerce version 2.6.x, and should work.
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.