Show/hide shipping methods based on a WooCommerce checkout field value - php

In WooCommerce, I have set different shipping methods, but one in particular must be exclusive for companies.
For this I am using the following code:
add_filter( 'woocommerce_package_rates', array( $this, 'package_rates' ), 10, 2 );
public function package_rates( $rates, $package ) {
$company_rate_id = 'flat_rate:7';
if(!empty(WC()->customer->get_billing_company())){
$company_rates = $rates[ $company_rate_id ];
$rates = array( $company_rate_id => $company_rates );
}else{
unset( $rates[ $company_rate_id ] );
}
return $rates;
}
The solution works, but only if the billing company already exist and is saved in the database. So if a customer updates this information on the checkout page, it doesn't work.
A possible solution would be to save this field live (billing_company).
I tried the following function:
add_filter( 'woocommerce_checkout_fields' , 'trigger_update_checkout_on_change' );
function trigger_update_checkout_on_change( $fields ) {
$fields['billing']['billing_company']['class'][] = 'update_totals_on_change';
return $fields;
}
This updates the shipping method, the problem is that again, the field is not saved in the database and the package_rates() function can not find it live.

This is a bit more complicated than that… jQuery and Ajax code are required to allow showing/hiding shipping methods based on a checkout field user input.
The following code will enable show/hide pre defined shipping methods based on checkout billing company field:
// Conditionally show/hide shipping methods
add_filter( 'woocommerce_package_rates', 'shipping_package_rates_filter_callback', 100, 2 );
function shipping_package_rates_filter_callback( $rates, $package ) {
// The defined rate id
$company_rate_id = 'flat_rate:7';
if( WC()->session->get('company' ) === '1' ) {
$rates = array( $company_rate_id => $rates[ $company_rate_id ] );
} else {
unset( $rates[ $company_rate_id ] );
}
return $rates;
}
// function that gets the Ajax data
add_action( 'wp_ajax_get_customer_company', 'wc_get_customer_company' );
add_action( 'wp_ajax_nopriv_get_customer_company', 'wc_get_customer_company' );
function wc_get_customer_company() {
if ( isset($_POST['company']) && ! empty($_POST['company']) ){
WC()->session->set('company', '1' );
} else {
WC()->session->set('company', '0' );
}
die(); // (required)
}
// The Jquery Ajax script
add_action( 'wp_footer', 'custom_checkout_script' );
function custom_checkout_script() {
if( WC()->session->__isset('company') )
WC()->session->__unset('company');
// Only on checkout when billing company is not defined
if( is_checkout() && ! is_wc_endpoint_url() ):
?>
<script type="text/javascript">
jQuery( function($){
if (typeof wc_checkout_params === 'undefined')
return false;
var fieldId = 'input#billing_company';
function companyTriggerAjax( company ){
$.ajax({
type: 'POST',
url: wc_checkout_params.ajax_url,
data: {
'action': 'get_customer_company',
'company': company,
},
success: function (result) {
// Trigger refresh checkout
$('body').trigger('update_checkout');
}
});
}
// On start
if( $(fieldId).val() != '' ) {
companyTriggerAjax( $(fieldId).val() );
}
// On change
$(fieldId).change( function () {
companyTriggerAjax( $(this).val() );
});
});
</script>
<?php
endif;
}
// Enabling, disabling and refreshing session shipping methods data
add_action( 'woocommerce_checkout_update_order_review', 'refresh_shipping_methods', 10, 1 );
function refresh_shipping_methods( $post_data ){
$bool = true;
if ( WC()->session->get('company' ) === '1' )
$bool = false;
// Mandatory to make it work with shipping methods
foreach ( WC()->cart->get_shipping_packages() as $package_key => $package ){
WC()->session->set( 'shipping_for_package_' . $package_key, $bool );
}
WC()->cart->calculate_shipping();
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Related: Remove shipping cost if custom checkbox is checked in WooCommerce Checkout

Related

Set a default custom billing state field value in Woocommerce Admin page

I want to add some custom billing states and then set a default state in the Admin panel. So far I have added the states as follows (code below; also not sure this is right):
add_filter( 'woocommerce_states', 'custom_woocommerce_states' );
function custom_woocommerce_states( $states ) {
$states['PE'] = array(
'PE1' => __('StateA', 'woocommerce')
'PE2' => __('StateB', 'woocommerce')
);
return $states;
}
How do I set the default value to be StateA in the Admin Panel?
First, there is a mistake in your current code.
In Admin WooCommerce order pages when adding (or editing) billing (or shipping) address(es), to set custom 'PE1' as default state when selected country is Peru (PE), use the following instead:
add_filter( 'woocommerce_states', 'custom_woocommerce_states_for_peru' );
function custom_woocommerce_states_for_peru( $states ) {
// For PERU
$states['PE'] = array(
'PE1' => __('StateA', 'woocommerce'),
'PE2' => __('StateB', 'woocommerce')
);
return $states;
}
// Admin orders: Set a default state for PERU country
add_action( 'admin_footer', 'custom_admin_shop_order_js' );
function custom_admin_shop_order_js() {
global $pagenow, $post_type;
if ( in_array( $pagenow, array('post-new.php', 'post.php') ) && 'shop_order' === $post_type ) :
?><script type='text/javascript'>
jQuery( function($) {
// Billing state
$(document.body).on( 'change', 'select#_billing_country,select#_shipping_country', function(){
var country = 'PE', // Set country
defaultState = 'PE1', // Set default state (for country)
parent = $(this).parent().parent(),
billingState = parent.find('select#_billing_state'),
shippingState = parent.find('select#_shipping_state');
if( country === $(this).val() ) {
if ( '' === billingState.val() ) {
billingState.val(defaultState).trigger("change");
} else if ( '' === shippingState.val() ) {
shippingState.val(defaultState).trigger("change");
}
}
});
});
</script><?php
endif;
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
You can add some new custom states for a country using this code:
add_filter( 'woocommerce_states', 'custom_woocommerce_states' );
function custom_woocommerce_states( $states ) {
$states['XX'] = array( // XX is Country Code
'XX1' => 'State 1',
'XX2' => 'State 2'
);
return $states;
}
Then you can change the country and state default value using this code:
add_filter( 'default_checkout_billing_country', 'change_default_checkout_country' );
add_filter( 'default_checkout_billing_state', 'change_default_checkout_state' );
function change_default_checkout_country() {
return 'XX'; // country code
}
function change_default_checkout_state() {
return 'XX'; // state code
}
If you are trying to make it dynamic and choose your default state from your admin panel, you can add a section and an option to your Woocommerce settings and get it using get_option('custom_id'). You can get help for creating a custom setting for Woocommerce here.

Show/Hide WooCommerce Shipping Rates Based on Radio Buttons

I've been looking for a solution to this for a while now... I am trying to show or hide specific shipping rates based on a radio button option added to the Woocommerce checkout page. But I don't know anything about Ajax & JQuery which I believe it requires.
Basically if a user selects radio option_1 it will ONLY show 'flat_rate:1' & 'flat_rate:2'. If the user selects radio option_2 it will ONLY show 'flat_rate:3' & 'flat_rate:4'
Click here for example of checkout screen
Here is the code for my radio buttons displayed on checkout page:
add_action( 'woocommerce_review_order_before_payment','tc_checkout_radio_choice' );
function tc_checkout_radio_choice() {
$businesstype = array(
'type' => 'radio',
'class' => array( 'business_residential' ),
'required' => true,
'options' => array(
'option_1' => 'Business',
'option_2' => 'Residential',
),
);
echo '<div id="checkout-radio">';
echo '<h3>Select Your Business Type</h3>';
woocommerce_form_field( 'radio_choice', $businesstype );
echo '</div>';
}
The closest example I've seen comes from an answer provided by 'LoicTheAztec' in this post here: Show/hide shipping methods based on a WooCommerce checkout field value
I'm completely lost with this and the more I try to solve it, the more I get confused.
Here is the correct way to show hide shipping methods based on multiple radio buttons choice on checkout page, that requires to use PHP, Ajax, jQuery and WC Session.
When "Business" radio button is selected (default choice), flat_rate:3 and flat_rate:4 shipping methods will be hidden, so customer will only be able to choose flat_rate:1 or flat_rate:2 shipping methods.
If "Residential" radio button is selected then flat_rate:1 and flat_rate:2 shipping methods will be hidden, so customer will only be able to choose flat_rate:3 or flat_rate:4 shipping methods.
The code:
// Display a Custom radio buttons fields for shipping options
add_action( 'woocommerce_review_order_before_payment','checkout_customer_type_radio_buttons' );
function checkout_customer_type_radio_buttons() {
$field_id = 'customer_type';
echo '<div id="customer-type-radio">';
echo '<h3>' . __("Select Your Business Type", "woocommerce") . '</h3>';
// Get the selected radio button value (if selected)
$field_value = WC()->session->get( $field_id );
// Get customer selected value on last past order
if( empty($field_value) )
$field_value = WC()->checkout->get_value( $field_id );
// The default value fallback
if( empty($field_value) )
$field_value = 'Business';
woocommerce_form_field( $field_id, array(
'type' => 'radio',
'class' => array( $field_id ),
'required' => true,
'options' => array(
'Business' => __('Business', 'woocommerce'),
'Residential' => __('Residential', 'woocommerce'),
),
), $field_value );
echo '</div>';
}
// Conditionally show/hide shipping methods
add_filter( 'woocommerce_package_rates', 'shipping_package_rates_filter_callback', 100, 2 );
function shipping_package_rates_filter_callback( $rates, $package ) {
$customer_type = WC()->session->get( 'customer_type' ); // Get the customere type
if ( $customer_type === 'Business' ) {
if( isset( $rates['flat_rate:3'] ) )
unset( $rates['flat_rate:3'] );
if( isset( $rates['flat_rate:4'] ) )
unset( $rates['flat_rate:4'] );
}
elseif ( $customer_type === 'Residential' ) {
if( isset( $rates['flat_rate:1'] ) )
unset( $rates['flat_rate:1'] );
if( isset( $rates['flat_rate:2'] ) )
unset( $rates['flat_rate:2'] );
}
return $rates;
}
// function that gets the Ajax data
add_action( 'wp_ajax_get_customer_type', 'wc_get_customer_type' );
add_action( 'wp_ajax_nopriv_get_customer_type', 'wc_get_customer_type' );
function wc_get_customer_type() {
$field_id = 'customer_type';
if ( isset($_POST[$field_id]) && ! empty($_POST[$field_id]) ){
WC()->session->set($field_id, $_POST[$field_id] );
}
echo json_encode( WC()->session->get( $field_id ) );
die(); // (required)
}
// The Jquery Ajax script
add_action( 'wp_footer', 'custom_checkout_ajax_jquery_script' );
function custom_checkout_ajax_jquery_script() {
$field_id = 'customer_type';
if( WC()->session->__isset($field_id) )
WC()->session->__unset($field_id);
// Only on checkout when billing company is not defined
if( is_checkout() && ! is_wc_endpoint_url() ):
?>
<script type="text/javascript">
jQuery( function($){
if (typeof wc_checkout_params === 'undefined')
return false;
var fieldId = 'p#customer_type_field input';
function userTypeTriggerAjax( customerType ){
$.ajax({
type: 'POST',
url: wc_checkout_params.ajax_url,
data: {
'action': 'get_customer_type',
'customer_type': customerType,
},
success: function (result) {
// Trigger refresh checkout
$('body').trigger('update_checkout');
console.log(result);
}
});
}
// On start
if( $(fieldId).val() != '' ) {
userTypeTriggerAjax( $(fieldId).val() );
}
// On change
$(fieldId).change( function () {
userTypeTriggerAjax( $(this).val() );
});
});
</script>
<?php
endif;
}
// Enabling, disabling and refreshing session shipping methods data
add_action( 'woocommerce_checkout_update_order_review', 'refresh_shipping_methods', 10, 1 );
function refresh_shipping_methods( $post_data ){
$bool = true;
if ( in_array( WC()->session->get('customer_type' ), ['Business', 'Residential'] ) )
$bool = false;
// Mandatory to make it work with shipping methods
foreach ( WC()->cart->get_shipping_packages() as $package_key => $package ){
WC()->session->set( 'shipping_for_package_' . $package_key, $bool );
}
WC()->cart->calculate_shipping();
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Code based on this related threads:
Remove shipping cost if custom checkbox is checked in WooCommerce Checkout
Show/hide shipping methods based on a WooCommerce checkout field value
If anyone wants to know how to make this a required option with an error notification before payment, this is what I did...
To de-select the radio buttons I removed the value from:
//if( empty($field_value) )
$field_value = '';
And added this code to the checkout process at the end of the code from Loic, which adds a notification message if a radio button has not been selected:
// Show notice if customer does not select a business type
add_action( 'woocommerce_checkout_process', 'business_type_error_message' );
function business_type_error_message() {
if ( ! (int) isset( $_POST['customer_type'] ) ) {
wc_add_notice( __( 'Please select an order type to calculate the correct shipping and proceed with your order.' ), 'error' );
}
}
Hope this helps others out there!

Disable a shipping method for a specific payment method in Woocommerce

On Woocommerce, I have enabled 2 shipping methods: Free shipping or Flat rate. I have enabled 2 payment methods: Bank transfer (bacs) and PayPal (paypal).
What I want to achieve:
If a customer selects PayPal as payment type he should be forced to select "Flat rate" as shipping method. "Free shipping" should be either hidden or greyed out or something like that.
If bank transfer is chosen then both shipping methods should be available.
Any help is appreciated.
If anyone is interested, I found a solution:
function alter_payment_gateways( $list ){
// Retrieve chosen shipping options from all possible packages
$chosen_rates = ( isset( WC()->session ) ) ? WC()->session->get( 'chosen_shipping_methods' ) : array();
if( in_array( 'free_shipping:1', $chosen_rates ) ) {
$array_diff = array('WC_Gateway_Paypal');
$list = array_diff( $list, $array_diff );
}
return $list;
}
add_action('woocommerce_payment_gateways', 'alter_payment_gateways');
This code will deactivate PayPal if a customer selects free shipping.
Update 2: The following code will disable "free_shipping" shipping method (method ID) when "paypal" is the chosen payment method:
add_filter( 'woocommerce_package_rates', 'shipping_methods_based_on_chosen_payment', 100, 2 );
function shipping_methods_based_on_chosen_payment( $rates, $package ) {
// Checking if "paypal" is the chosen payment method
if ( WC()->session->get( 'chosen_payment_method' ) === 'paypal' ) {
// Loop through shipping methods rates
foreach( $rates as $rate_key => $rate ){
if ( 'free_shipping' === $rate->method_id ) {
unset($rates[$rate_key]); // Remove 'Free shipping'shipping method
}
}
}
return $rates;
}
// Enabling, disabling and refreshing session shipping methods data
add_action( 'woocommerce_checkout_update_order_review', 'refresh_shipping_methods', 10, 1 );
function refresh_shipping_methods( $post_data ){
$bool = true;
if ( WC()->session->get('chosen_payment_method' ) ) $bool = false;
// Mandatory to make it work with shipping methods
foreach ( WC()->cart->get_shipping_packages() as $package_key => $package ){
WC()->session->set( 'shipping_for_package_' . $package_key, $bool );
}
WC()->cart->calculate_shipping();
}
// Jquery script for checkout page
add_action('wp_footer', 'refresh_checkout_on_payment_method_change' );
function refresh_checkout_on_payment_method_change() {
// Only checkout page
if( is_checkout() && ! is_wc_endpoint_url() ):
?>
<script type="text/javascript">
jQuery(function($){
// On shipping method change
$('form.checkout').on( 'change', 'input[name^="payment_method"]', function(){
$('body').trigger('update_checkout'); // Trigger Ajax checkout refresh
});
})
</script>
<?php
endif;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
To get the related shipping methods rate IDs, something like flat_rate:12, inspect with your browser code inspector each related radio button attribute name like:
Note: Since WooCommerce new versions changes, sorry, the code doesn't work anymore.

Change shipping method based on payment method in WooCommerce [duplicate]

I need to disable specific shipping method if user selected payment "Cash on Delivery". The problem is that the following code works only if I reset
WooCommerce transients each time and refresh. It doesn't work on user selection back and forth.
add_filter( 'woocommerce_package_rates', 'alter_shipping_methods', 100 );
function alter_shipping_methods( $rates ) {
$chosen_gateway = WC()->session->chosen_payment_method;
// If payment is Cash on delivery remove specific shipping
if($chosen_gateway == 'cod') {
foreach ( $rates as $rate_id => $rate ) {
if ( $rate->label === 'Hrvatska pošta' ) {
unset( $rates[ $rate_id ] );
}
}
}
return $rates;
}
I do have this code which should trigger and I see the output in console when I click around options.
jQuery(document.body).on('change', 'input[name="payment_method"]', function() {
console.log('Payment method changed');
jQuery('body').trigger('update_checkout');
});
I have tried with this, it doesn't work:
function action_woocommerce_checkout_update_order_review($array, $int) {
WC()->cart->calculate_shipping();
return;
}
add_action('woocommerce_checkout_update_order_review', 'action_woocommerce_checkout_update_order_review', 10, 2);
And I have also tried custom AJAX call which calls a PHP function and inside this filter, no result:
add_filter( 'woocommerce_package_rates', 'alter_shipping_methods', 100 );
What should I try next?
Updated on March 2019
For COD payment gateways, you can just add in its settings the "Flat rate" shipping methods that you want to enable for it, like:
For Cod and other methods or for others payment gateways, here is the complete working way to disable a specific shipping method(s) for specific payment gateway(s).
You will have to set in the first function the shipping method Id that you wish to hide.
The code:
add_action( 'woocommerce_package_rates','show_hide_shipping_methods', 10, 2 );
function show_hide_shipping_methods( $rates, $package ) {
// HERE Define your targeted shipping method ID
$payment_method = 'cod';
$chosen_payment_method = WC()->session->get('chosen_payment_method');
if( $payment_method == $chosen_payment_method ){
unset($rates['flat_rate:12']);
}
return $rates;
}
add_action( 'woocommerce_review_order_before_payment', 'payment_methods_trigger_update_checkout' );
function payment_methods_trigger_update_checkout(){
// jQuery code
?>
<script type="text/javascript">
(function($){
$( 'form.checkout' ).on( 'change blur', 'input[name^="payment_method"]', function() {
setTimeout(function(){
$(document.body).trigger('update_checkout');
}, 250 );
});
})(jQuery);
</script>
<?php
}
add_action( 'woocommerce_checkout_update_order_review', 'refresh_shipping_methods' );
function refresh_shipping_methods( $post_data ){
// HERE Define your targeted shipping method ID
$payment_method = 'cod';
$bool = true;
if ( WC()->session->get('chosen_payment_method') === $payment_method )
$bool = false;
// Mandatory to make it work with shipping methods
foreach ( WC()->cart->get_shipping_packages() as $package_key => $package ){
WC()->session->set( 'shipping_for_package_' . $package_key, $bool );
}
WC()->cart->calculate_shipping();
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
To be able to get the correct shipping method ID you can use your browser inspector, this way:
You may need to empty cart before testing this code.

Disable shipping method based on chosen payment method in Woocommerce

I need to disable specific shipping method if user selected payment "Cash on Delivery". The problem is that the following code works only if I reset
WooCommerce transients each time and refresh. It doesn't work on user selection back and forth.
add_filter( 'woocommerce_package_rates', 'alter_shipping_methods', 100 );
function alter_shipping_methods( $rates ) {
$chosen_gateway = WC()->session->chosen_payment_method;
// If payment is Cash on delivery remove specific shipping
if($chosen_gateway == 'cod') {
foreach ( $rates as $rate_id => $rate ) {
if ( $rate->label === 'Hrvatska pošta' ) {
unset( $rates[ $rate_id ] );
}
}
}
return $rates;
}
I do have this code which should trigger and I see the output in console when I click around options.
jQuery(document.body).on('change', 'input[name="payment_method"]', function() {
console.log('Payment method changed');
jQuery('body').trigger('update_checkout');
});
I have tried with this, it doesn't work:
function action_woocommerce_checkout_update_order_review($array, $int) {
WC()->cart->calculate_shipping();
return;
}
add_action('woocommerce_checkout_update_order_review', 'action_woocommerce_checkout_update_order_review', 10, 2);
And I have also tried custom AJAX call which calls a PHP function and inside this filter, no result:
add_filter( 'woocommerce_package_rates', 'alter_shipping_methods', 100 );
What should I try next?
Updated on March 2019
For COD payment gateways, you can just add in its settings the "Flat rate" shipping methods that you want to enable for it, like:
For Cod and other methods or for others payment gateways, here is the complete working way to disable a specific shipping method(s) for specific payment gateway(s).
You will have to set in the first function the shipping method Id that you wish to hide.
The code:
add_action( 'woocommerce_package_rates','show_hide_shipping_methods', 10, 2 );
function show_hide_shipping_methods( $rates, $package ) {
// HERE Define your targeted shipping method ID
$payment_method = 'cod';
$chosen_payment_method = WC()->session->get('chosen_payment_method');
if( $payment_method == $chosen_payment_method ){
unset($rates['flat_rate:12']);
}
return $rates;
}
add_action( 'woocommerce_review_order_before_payment', 'payment_methods_trigger_update_checkout' );
function payment_methods_trigger_update_checkout(){
// jQuery code
?>
<script type="text/javascript">
(function($){
$( 'form.checkout' ).on( 'change blur', 'input[name^="payment_method"]', function() {
setTimeout(function(){
$(document.body).trigger('update_checkout');
}, 250 );
});
})(jQuery);
</script>
<?php
}
add_action( 'woocommerce_checkout_update_order_review', 'refresh_shipping_methods' );
function refresh_shipping_methods( $post_data ){
// HERE Define your targeted shipping method ID
$payment_method = 'cod';
$bool = true;
if ( WC()->session->get('chosen_payment_method') === $payment_method )
$bool = false;
// Mandatory to make it work with shipping methods
foreach ( WC()->cart->get_shipping_packages() as $package_key => $package ){
WC()->session->set( 'shipping_for_package_' . $package_key, $bool );
}
WC()->cart->calculate_shipping();
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
To be able to get the correct shipping method ID you can use your browser inspector, this way:
You may need to empty cart before testing this code.

Categories