Display a custom message based on customer shipping zone in Woocommerce - php

In woocommerce, I need to display custom message on cart or checkout page, based on shipping zone, like "you'll be charged 10% more for this zip code".
My workaround is about customizing that kind of default message :
add_filter( 'woocommerce_no_shipping_available_html', 'wf_customize_default_message', 10, 1 );
// For Checkout page
add_filter( 'woocommerce_cart_no_shipping_available_html', 'wf_customize_default_message', 10, 1 );
function wf_customize_default_message( $default_msg ) {
$zip_array = array(
'30031',
);
if ( in_array( WC()->customer->get_shipping_postcode() , $zip_array) ) {
$custom_msg = "Call us for quotation - 1-800-XXX-XXXX";
if( empty( $custom_msg ) ) {
return $default_msg;
}
return $custom_msg;
}
return $default_msg;
}

Updated
Try the following code based on a shipping Zones name (with postcodes restrictions) that will display your message on the shipping total lines (but that will not generate a woocommerce notice):
add_action( 'woocommerce_cart_totals_after_shipping' , 'shipping_zone_targeted_postcodes_custom_notice' );
add_action( 'woocommerce_review_order_after_shipping' , 'shipping_zone_targeted_postcodes_custom_notice' );
function shipping_zone_targeted_postcodes_custom_notice() {
// HERE DEFINE YOUR SHIPPING ZONE NAME(S)
$targeted_zones_names = array('France'); // <====== <====== <====== <====== <======
// Get the customer shipping zone name
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' ); // The chosen shipping mehod
$chosen_method = explode(':', reset($chosen_methods) );
$shipping_zone = WC_Shipping_Zones::get_zone_by( 'instance_id', $chosen_method[1] );
$current_zone_name = $shipping_zone->get_zone_name();
if( in_array( $current_zone_name, $targeted_zones_names ) ){
echo '<tr class="shipping">
<td colspan="2" style="text-align:center">' . sprintf(
__( "You'll be charged %s more for %s zip code", "woocommerce"),
'<strong>10%</strong>',
'<strong>' . WC()->customer->get_shipping_postcode() . '</strong>'
) . '</td>
</tr>';
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.

Using #LoicTheAztec's solution above, I modified it to give a message based on customer's country.
add_action( 'woocommerce_cart_totals_after_shipping' , 'out_of_zone_shipping_notice' );
add_action( 'woocommerce_review_order_after_shipping' , 'out_of_zone_shipping_notice' );
function out_of_zone_shipping_notice() {
// HERE DEFINE YOUR SHIPPING COUNTRY NAMES
$targeted_country_names = array("CA", "US"); //
// Get the customer shipping country
$shipping_country = WC()->customer->get_shipping_country();
if( !in_array( $shipping_country, $targeted_country_names ) ){
echo '<tr class="shipping"><td colspan="2" style="text-align:center">You are outside of our regular shipping zone. Please contact us with your address so we can get an accurate cost to ship.</td></tr>';
}
}
In order to see what format the $shipping_country showed up in, I used
echo $shipping_country
just below $shipping_country = WC()->customer->get_country(); to show on the cart page what the actual country code syntax was so that I could match it with the if statement. However, this code segment was removed prior to deployment to avoid random characters on the screen for the customer.

Related

Display message based on shipping zone and shipping class on woocommerce checkout

i'm trying to display a message based on user's shipping zone when the users have a product with a specific shipping class in cart, in order to inform them the product isn't available for shipping in their shipping zone
so far, what i get is this, which i found on a similar post ( based on shipping zones x categories) and tried to adapt to my situation :
add_action( 'woocommerce_before_checkout_form', 'shipping_zone_targeted_postcodes_custom_notice' );
add_action( 'woocommerce_before_cart_table', 'shipping_zone_targeted_postcodes_custom_notice' );
function shipping_zone_targeted_postcodes_custom_notice() {
// HERE DEFINE YOUR SHIPPING ZONE NAME(S)
$targeted_zones_names = array('Province', 'Zone 1', 'Zone 2', 'Outre Mer 1', 'Outre Mer 2'); // <====== <====== <====== <======
// Get the customer shipping zone name
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' ); // The chosen shipping mehod
$chosen_method = explode(':', reset($chosen_methods) );
$shipping_zone = WC_Shipping_Zones::get_zone_by( 'instance_id', $chosen_method[1] );
$current_zone_name = $shipping_zone->get_zone_name();
// Set your special category name, slug or ID here:
$shipping_classes = array('specific-shipping-class');
$bool = false;
// Loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item ) {
if ( has_term( $shipping_classes, $cart_item['product_id'] ) )
$bool = true;
}
if( ! in_array( $current_zone_name, $targeted_zones_names ) && $bool ){
echo '<p class="zone-message-text"><span style="background:#e02b20;padding:20px;">MY CUSTOM MESSAGE</p>';
}
}
But it doesn't work. How could i fix this ?

WooCommerce notice: How re-trigger on address change?

Inspired by this answer code, I've write the code below which works to add an error notice in WooCommerce checkout under a condition (they add a non-shippable product, (id'd by shipping class) to the cart and are outside of my local delivery zones (i.e. their postal code falls into the rest of the world zone) and only pickup is available (identified by only shipping method available).
add_action('woocommerce_after_checkout_validation', 'get_zone_info', 10 );
function get_zone_info( ) {
// Get cart shipping packages
$shipping_packages = WC()->cart->get_shipping_packages();
// Get the WC_Shipping_Zones instance object for the first package
$shipping_zone = wc_get_shipping_zone( reset( $shipping_packages ) );
$zone_id = $shipping_zone->get_id(); // Get the zone ID
$zone_name = $shipping_zone->get_zone_name(); // Get the zone name
$shipping_class_target = 87;
$in_cart = 0;
foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
if ( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target ) {
$in_cart = 1;
break;
}
}
if ( $zone_name=='Locations not covered by your other zones' && $in_cart==1 || $zone_id==0 && $in_cart==1 ) {
//wc_print_notice( __( '<p>Count_ships: ' .$counter_ship. ' Cart id: ' . $in_cart . ' | Zone id: ' . $zone_id . ' | Zone name: ' . $zone_name . '</p>', 'woocommerce' ), 'success' );
wc_print_notice( __( '<b>ONLY PICKUP IS AVAILABLE</b><br>To have courier delivery, please goto your cart and remove products which cannot be shipped', 'woocommerce' ), 'success' );
} else {
//donothing
}
}
I have two Issues:
1- The notice check doesn't retrigger each time the address changes
2- I use the Multi-Step Checkout Pro for WooCommerce by Silkypress and want this notice to come up during the ORDER section...however the above notice doesn't work at all when I activate this plugin.
I've contacted them for support, but appreciate any help.
Alternatively, you can think differently, automatically disabling all shipping methods except local pickup (if there is at least one product in the cart with the shipping class id equal to the one specified).
Based on:
Hide shipping methods for specific shipping class in WooCommerce
Hide shipping methods for specific shipping classes in WooCommerce
Then you can use a custom function to check if there are products in the cart with a specific shipping class id:
// check if the products in the cart have a specific shipping class id
function check_product_in_cart_according_shipping_class_id() {
$shipping_class_target = 87;
$in_cart = false;
// check if in the cart there is at least one product with the shipping class id equal to "87"
foreach ( WC()->cart->get_cart() as $cart_item ) {
$product = $cart_item['data'];
$shipping_class = $product->get_shipping_class_id();
if ( $shipping_class == $shipping_class_target ) {
$in_cart = true;
return $in_cart;
}
}
return $in_cart;
}
And with another custom function you get the list of products that cannot be shipped (and therefore those that have the shipping class id equal to 87):
// gets products that have a specific shipping class
function get_product_in_cart_according_shipping_class_id() {
$shipping_class_target = 87;
$product_list = '<ul>';
// check if in the cart there is at least one product with the shipping class id equal to "87"
foreach ( WC()->cart->get_cart() as $cart_item ) {
$product = $cart_item['data'];
$shipping_class = $product->get_shipping_class_id();
if ( $shipping_class == $shipping_class_target ) {
$sku = $product->get_sku();
$name = $product->get_name();
$qty = $cart_item['quantity'];
$product_list .= '<li>' . $qty . ' x ' . $name . ' (' . $sku . ')</li>';
}
}
$product_list .= '</ul>';
return $product_list;
}
Then disable all shipping methods except local pickup:
// disable all shipping methods except local pickup based on product shipping class
add_filter( 'woocommerce_package_rates', 'disable_shipping_methods_based_on_product_shipping_class', 10, 2 );
function disable_shipping_methods_based_on_product_shipping_class( $rates, $package ) {
$in_cart = check_product_in_cart_according_shipping_class_id();
// if it is present, all shipping methods are disabled except local pickup
if ( $in_cart ) {
foreach( $rates as $rate_key => $rate ) {
if ( $rate->method_id != 'local_pickup' ) {
unset($rates[$rate_key]);
}
}
}
return $rates;
}
Finally, it adds the custom notice on the cart and checkout page.I created two functions because on the cart page the notice needs to update when a product is added or removed and using the woocommerce_before_calculate_totals hook would cause multiple notices in the checkout.
In the cart:
// add custom notice in cart
add_action( 'woocommerce_before_calculate_totals', 'add_custom_notice_in_cart' );
function add_custom_notice_in_cart() {
// only on the cart page
if ( ! is_cart() ) {
return;
}
$in_cart = check_product_in_cart_according_shipping_class_id();
if ( $in_cart ) {
$products = get_product_in_cart_according_shipping_class_id();
wc_clear_notices();
wc_add_notice( sprintf( __( 'Only <strong>Local Pickup</strong> shipping method is available. These products cannot be shipped:<br>%s', 'woocommerce' ), $products ), 'notice' );
}
}
In the checkout:
// add custom notice in checkout
add_action( 'woocommerce_checkout_before_customer_details', 'add_custom_notice_in_checkout' );
function add_custom_notice_in_checkout() {
$in_cart = check_product_in_cart_according_shipping_class_id();
if ( $in_cart ) {
$products = get_product_in_cart_according_shipping_class_id();
wc_clear_notices();
wc_add_notice( sprintf( __( 'Only <strong>Local Pickup</strong> shipping method is available. These products cannot be shipped:<br>%s', 'woocommerce' ), $products ), 'notice' );
}
}
The code has been tested and works. Add it to your active theme's functions.php.

Minimum order amount except for specific shipping method in WooCommerce

In WooCommerce, I use the following code to set a minimum order amount:
add_action( 'woocommerce_checkout_process', 'wc_minimum_order_amount' );
add_action( 'woocommerce_before_cart' , 'wc_minimum_order_amount' );
function wc_minimum_order_amount() {
$minimum = 50; // Hier gibst du den Mindestbestellwert ein
if ( WC()->cart->total < $minimum ) {
if( is_cart() ) {
wc_print_notice( sprintf( 'Der Mindestbestellwert beträgt %s pro Bestellung. Der aktuelle Bestellwert beträgt %s.' , // Text fuer Warenkorb
wc_price( $minimum ),
wc_price( WC()->cart->total )
), 'error' );
} else {
wc_add_notice( sprintf( 'Der Mindestbestellwert beträgt %s pro Bestellung. Der aktuelle Bestellwert beträgt %s.' , // Text fuer Kasse
wc_price( $minimum ),
wc_price( WC()->cart->total )
), 'error' );
}
}
}
Now if the customer chooses "self pickup" ("Local pickup shipping method), I don't want any minimum required order amount.
How can I set Minimum order amount except for "Local pickup" shipping method in WooCommerce?
Based on Getting minimum order amount for 'Free Shipping' method in checkout page answer code and also Set a minimum order amount in WooCommerce answer code, here is the correct way to set a Minimum order amount except for specific shipping method in WooCommerce:
add_action( 'woocommerce_check_cart_items', 'wc_minimum_required_order_amount' );
function wc_minimum_required_order_amount() {
// HERE Your settings
$minimum_amount = 50; // The minimum cart total amount
$shipping_method_id = 'local_pickup'; // The targeted shipping method Id (exception)
// Get some variables
$cart_total = (float) WC()->cart->total; // Total cart amount
$chosen_methods = (array) WC()->session->get( 'chosen_shipping_methods' ); // Chosen shipping method rate Ids (array)
// Only when a shipping method has been chosen
if ( ! empty($chosen_methods) ) {
$chosen_method = explode(':', reset($chosen_methods)); // Get the chosen shipping method Id (array)
$chosen_method_id = reset($chosen_method); // Get the chosen shipping method Id
}
// If "Local pickup" shipping method is chosen, exit (no minimun is required)
if ( isset($chosen_method_id) && $chosen_method_id === $shipping_method_id ) {
return; // exit
}
// Add an error notice is cart total is less than the minimum required
if ( $cart_total < $minimum_amount ) {
wc_add_notice( sprintf(
__("The minimum required order amount is %s (your current order amount is %s).", "woocommerce"), // Text message
wc_price( $minimum_amount ),
wc_price( $cart_total )
), 'error' );
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
or also you can use:
add_action( 'woocommerce_checkout_process', 'wc_minimum_required_order_amount' );
add_action( 'woocommerce_before_cart' , 'wc_minimum_required_order_amount' );
function wc_minimum_required_order_amount() {
// HERE Your settings
$minimum_amount = 100; // The minimum cart total amount
$shipping_method_id = 'local_pickup'; // The targeted shipping method Id (exception)
// Get some variables
$cart_total = (float) WC()->cart->total; // Total cart amount
$chosen_methods = (array) WC()->session->get( 'chosen_shipping_methods' ); // Chosen shipping method rate Ids (array)
// Only when a shipping method has been chosen
if ( ! empty($chosen_methods) ) {
$chosen_method = explode(':', reset($chosen_methods)); // Get the chosen shipping method Id (array)
$chosen_method_id = reset($chosen_method); // Get the chosen shipping method Id
}
// If "Local pickup" shipping method is chosen, exit (no minimun is required)
if ( isset($chosen_method_id) && $chosen_method_id === $shipping_method_id ) {
return; // exit
}
// Add an error notice is cart total is less than the minimum required
if ( $cart_total < $minimum_amount ) {
$text_notice = sprintf(
__("The minimum required order amount is %s (your current order amount is %s).", "woocommerce"), // Text message
wc_price( $minimum_amount ),
wc_price( $cart_total )
);
if ( is_cart() ) {
wc_print_notice( $text_notice, 'error' );
} else {
wc_add_notice( $text_notice, 'error' );
}
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.

Add a delivery time based on custom fields calculations for WooCommerce Flat rate Shipping method

I use code that displays a custom field on the product edit page. This text box shows the cooking time on the single product page.
Here is the code:
// Backend: Display additional product fields
add_action( 'woocommerce_product_options_general_product_data', 'add_time_field_general_product_data' );
function add_time_field_general_product_data() {
// Custom Time Field
woocommerce_wp_text_input( array(
'id' => '_custom_time',
'label' => __( 'Time for cooking', 'woocommerce' ),
));
}
// Backend: Save the data value from the custom fields
add_action( 'woocommerce_admin_process_product_object', 'save_time_custom_fields_values' );
function save_time_custom_fields_values( $product ) {
// Save Custom Time Field
if( isset( $_POST['_custom_time'] ) ) {
$product->update_meta_data( '_custom_time', sanitize_text_field( $_POST['_custom_time'] ) );
}
}
// Display custom fields values under item name in checkout
add_filter( 'woocommerce_checkout_cart_item_quantity', 'custom_time_field_checkout_item_name', 10, 3 );
function custom_time_field_checkout_item_name( $item_qty, $cart_item, $cart_item_key ) {
if( $value4 = $cart_item['data']->get_meta('_custom_time') ) {
$item_qty .= '<br /><div class="my-custom-style"><strong>' . __("Time for cooking", "woocommerce") . ':</strong> ' . $value4 . ' min.</div>';
}
return $item_qty;
}
// Display custom fields values on orders and email notifications
add_filter( 'woocommerce_order_item_name', 'custom_time_field_order_item_name', 10, 2 );
function custom_time_field_order_item_name( $item_name, $item ) {
$product = $item->get_product();
if( $value4 = $product->get_meta('_custom_time') ) {
$item_name .= '<br /><span class="my-custom-style"><strong>' . __("Time for cooking", "woocommerce") . ':</strong> ' . $value4 . ' min.</span>';
}
return $item_name;
}
How can get the following functionality based on this code?
For example, a customer adds several dishes to a cart and checkout order. He sees how much time will be cooking this or that dish.
But when the customer chooses the delivery by courier (Flat Rate), in the same block, the delivery time is shown.
Flat Rate = $10
Delivery time = (the longest cooking time is selected from the order) min. + 45 min.
As I understand it, need to get the data of the custom field '_custom_time' when placing the order. Then, somehow need to get the highest value of this field and add 45 minutes.
I ask for your help! I hope that the answer to this question will be useful to many developers.
Try the following codethat will display a delivery time (calculated from the highest item cooking time value + 45 minutes) when a "flat rate" shipping method is selected on checkout page:
add_action( 'woocommerce_after_shipping_rate', 'action_after_shipping_rate_callback', 10, 2 );
function action_after_shipping_rate_callback( $method, $index ) {
$chosen_shipping_id = WC()->session->get( 'chosen_shipping_methods' )[$index];
if( is_checkout() && $method->method_id === 'flat_rate' && $method->id === $chosen_shipping_id ) {
$extra_time = 45; // Additional time to be added
$data_array = []; // Initializing
// Loop through car items
foreach ( WC()->cart->get_cart() as $cart_item ) {
if( $cooking_time = $cart_item['data']->get_meta('_custom_time') ) {
$data_array[] = (int) $cooking_time;
}
}
if ( sizeof($data_array) ) {
$max_time = (int) max($data_array);
$delivery_time = $max_time + $extra_time;
echo '<br><small style="margin-left:2em;border:solid 1px #ccc;padding:2px 5px;"><strong>' . __("Delivery time", "woocommerce") . '</strong>: ' . $delivery_time . ' min.</small>';
}
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
To enable that in cart page too, remove is_checkout() && from the IF statement.

Hide WooCommerce payment methods for specific shipping zones and min subtotal

In WooCommerce, I'm trying to remove "Cash on delivery" payment method when cart subtotal is up to $250 for specific shipping zones names (Zone 1, Zone 4 and Zone 7).
All others zones must not have this restriction.
Here is my incomplete code based on this thread:
add_filter( 'woocommerce_available_payment_gateways', 'change_payment_gateway', 20, 1);
function change_payment_gateway( $gateways ){
$zone = $shipping_zone->get_zone_name();
if( WC()->cart->subtotal > 250 ) && if($zone=='Zone 1','Zone 4','Zone 7'){
unset( $gateways['cod'] );
}
return $gateways;
}
Any help is appreciated.
The following will remove "Cash on delivery" payment gateway for specific shipping zones and when cart subtotal is up to 250:
add_filter( 'woocommerce_available_payment_gateways', 'conditionally_remove_payment_methods', 20, 1);
function conditionally_remove_payment_methods( $gateways ){
// Not in backend (admin)
if( is_admin() )
return $gateways;
// HERE below your targeted zone names
$targeted_zones_names = array('Zone 1','Zone 4','Zone 7');
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' ); // The chosen shipping mehod
$chosen_method = explode(':', reset($chosen_methods) );
$shipping_zone = WC_Shipping_Zones::get_zone_by( 'instance_id', $chosen_method[1] );
$current_zone_name = $shipping_zone->get_zone_name();
if( WC()->cart->subtotal > 250 && in_array( $current_zone_name, $targeted_zones_names ) ){
unset( $gateways['cod'] );
}
return $gateways;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.

Categories