How to hide state field on WooCommerce checkout based on country - php

Based on How to remove all Woocommerce checkout billing fields without errors answer code, this is my code attempt:
add_action( 'woocommerce_before_checkout_form', 'hide_checkout_billing_country', 5 );
function hide_checkout_billing_country() {
echo '<style>#billing_state_field{display:none !important;}</style>';
}
add_filter('woocommerce_billing_fields', 'customize_checkout_fields', 100 );
function customize_checkout_fields( $fields ) {
if ( is_checkout() ) {
// HERE set the required key fields below
$chosen_fields = array('first_name', 'last_name', 'address_1', 'address_2', 'city', 'postcode', 'country', 'state');
foreach( $chosen_fields as $key ) {
if( isset($fields['billing_'.$key]) && $key !== 'state') {
unset($fields['billing_'.$key]); // Remove all define fields except country
}
}
}
return $fields;
}
My goal is to hide the state field depenging on country. Eg.
If country = Italy then the state field is shown
If country = France then state field is hidden..
So I tried to insert an if condition but without the desired result.
If someone could give me advice, it would be very appreciated.

Your code contains some errors and shortcomings
The modification in your code attempt, based on the state does not apply to the country
Your code contains an Fatal error: "Uncaught Error: call_user_func_array(): Argument #1 ($callback) must be a valid callback, function "customize_checkout_fields" not found or.. I've edited this though
You can use WC()->customer->get_billing_country() to get the billing country
So you get:
function filter_woocommerce_billing_fields( $fields ) {
// Returns true when viewing the checkout page
if ( is_checkout() ) {
// Get billing country
$billing_country = WC()->customer->get_billing_country();
// Multiple country codes can be added, separated by a comma
$countries = array( 'IT' );
// NOT in array
if ( ! in_array( $billing_country, $countries ) ) {
// HERE set the fields below, multiple fields can be added, separated by a comma
$chosen_fields = array( 'state' );
// Loop through
foreach( $chosen_fields as $key ) {
// Isset
if ( isset( $fields['billing_' . $key] ) ) {
// Remove all defined fields
unset( $fields['billing_' . $key] );
}
}
}
}
return $fields;
}
add_filter( 'woocommerce_billing_fields', 'filter_woocommerce_billing_fields', 10, 1 );

Related

How to prevent postcode/zip field from getting hidden for some countries in WooCommerce

I want to show postcode/zip field even for the countries that do not use postcodes/zip on WooCommerce checkout page.
WooCommerce hides postcode/zip field by default for countries that don't use them.
I have used following filter in theme functions.php but it doesn't work.
add_filter( 'woocommerce_default_address_fields' , 'custom_override_default_address_fields' );
function custom_override_default_address_fields( $address_fields ) {
$address_fields['postcode']['hidden'] = false;
return $address_fields;
}
How can I override this behavior?
You can use the woocommerce_get_country_locale filter hook, to unhide this by default for all countries.
So you get:
function filter_woocommerce_get_country_locale( $country_locale ) {
// Loop through
foreach( $country_locale as $key => $locale ) {
// Isset
if ( isset ( $locale['postcode']['hidden'] ) ) {
// When true
if ( $locale['postcode']['hidden'] == true ) {
// Set to false
$country_locale[$key]['postcode']['hidden'] = false;
}
}
}
return $country_locale;
}
add_filter( 'woocommerce_get_country_locale', 'filter_woocommerce_get_country_locale', 10, 1 );

WooCommerce: Set default Country in Billing (using Geo Location) but not in Shipping in checkout page

I currently use below code to set default country (based on User's current Geo-location).
function custom_update_allowed_countries( $countries ) {
// Only on frontend
if( is_admin() ) return $countries;
if( class_exists( 'WC_Geolocation' ) ) {
$location = WC_Geolocation::geolocate_ip();
if ( isset( $location['country'] ) ) {
$countryCode = $location['country'];
} else {
// If there is no country, then return allowed countries
return $countries;
}
} else {
// If you can't geolocate user country by IP, then return allowed countries
return $countries;
}
// If everything went ok then I filter user country in the allowed countries array
$user_country_code_array = array( $countryCode );
$intersect_countries = array_intersect_key( $countries, array_flip( $user_country_code_array ) );
return $intersect_countries;
}
add_filter( 'woocommerce_countries_allowed_countries', 'custom_update_allowed_countries', 30, 1 );
The above code affects not only the Billing Section but also the Shipping Section. In this case, a US user can only ship to US; a user from Canada can only ship to Canada and so on.
If there's a way that it will still default to the Goelocated country on the Billing, but User can be able to ship to Other countries?
I've checked every hook/filter and found nothing useful. So I came up with this solution. Add this code to your child theme functions.php file:
add_action( 'wp_enqueue_scripts', 'wp_enqueue_scripts_action' );
function wp_enqueue_scripts_action() {
wp_register_script( 'test-script', get_stylesheet_directory_uri() . '/test.js', [ 'jquery', ] );
wp_enqueue_script( 'test-script' );
wp_localize_script( 'test-script', 'test_script', [
'customer_country' => get_customer_geo_location_country()
]
);
}
/**
* Returns the customer country
*
* #return string|null
*/
function get_customer_geo_location_country(): ?string {
if ( class_exists( 'WC_Geolocation' ) ) {
$location = WC_Geolocation::geolocate_ip();
if ( isset( $location['country'] ) ) {
return $location['country'];
}
}
return null;
}
add_action( 'woocommerce_after_checkout_validation', 'woocommerce_after_checkout_validation_action', 10, 2 );
function woocommerce_after_checkout_validation_action( $fields, $errors ) {
$billing_country = $fields['billing_country'];
if ( ! empty( $billing_country ) && $billing_country !== get_customer_geo_location_country() ) {
$errors->add( 'validation', 'You are not allowed to select this billing country!' );
}
}
First we add a new script. If you already have a script, you can just copy the wp_localize_script function and change the handler and the object name.
With this function we can pass the current country of the customer to our JavaScript file. Inside this file we do this stuff:
(function ( $ ) {
$( document ).ready( function () {
if (test_script.customer_country) {
$( '#billing_country option' ).each( function () {
if ($( this ).val() !== test_script.customer_country) {
$( this ).remove();
}
} );
}
} );
})( jQuery );
This little function removes every country from our billing select that doesn't match the customers country. If you want you can remove all countries in an else statement to be sure the customer can't order in case there is no country available.
The customer should now only see his home country in the billing country dropdown.
To be sure he don't hacks us, we add a little validation to the checkout where we verify the selected country again.
If you test this on localhost, no country will be available so be sure it's on a live website in the web (or even staging).
This is a basic idea. You need to test it and maybe adjust it too your needs.

Change the order of admin billing address fields in WooCommerce orders

I have problem with woocommerce order in admin I want the billing_address_2 show at the end of the page as exmple bellow.
can any one please help me.
The core file that is responsible to displayinng that fields is located in WooCommerce plugin under: includes/admin/meta-boxes/class-wc-meta-box-order-data.php.
The only available and efficient hook is: woocommerce_admin_shipping_fields.
But you will only be able to change the admin billing fields order using something like:
add_filter( 'woocommerce_admin_billing_fields' , 'change_order_admin_billing_fields' );
function change_order_admin_billing_fields( $fields ) {
global $the_order;
$address_2 = $fields['address_2'];
unset($fields['address_2']);
$fields['address_2'] = $address_2;
return $fields;
}
Which will give you something like:
So as you can see you will not get the billing address_2 field to be displayed after the transaction ID as you wish, but only under the billing phone field.
Addition - Showing the billing_address_2 field before billing_country field:
add_filter( 'woocommerce_admin_billing_fields' , 'change_order_admin_billing_fields' );
function change_order_admin_billing_fields( $fields ) {
global $the_order;
$sorted_fields = [];
$address_2 = $fields['address_2'];
unset($fields['address_2']);
foreach ( $fields as $key => $values ) {
if( $key === 'country' ) {
$sorted_fields['address_2'] = $address_2;
}
$sorted_fields[$key] = $values;
}
return $sorted_fields;
}

Set cart item custom prices from session data in WooCommerce 3

I Made a custom form option on site to update price and send that data through ajax and try to get in filter woocommerce_before_calculate_totals Session.
Here is code
add_action( 'woocommerce_before_calculate_totals', 'calculate_total_price', 99 );
function calculate_total_price( $cart_object )
{
global $woocommerce;
session_start();
$tac_dd_discounted_price = $_SESSION['wdm_user_price_data'];
$target_product_id = $_SESSION['wdm_user_product_id_data'].'<br/>.';
$_SESSION['productlist'][] =
[
'price' => $tac_dd_discounted_price,
'productid' => $target_product_id
];
$arrys = array_merge( $_SESSION[ "productlist" ]);
$_SESSION[ "productlist" ] = array_unique($arrys);
// This unique array created in seesion fro multi product which show correct data.
$price_blank="1";
foreach ( $cart_object->get_cart() as $cart_item ) {
$id= $cart_item['data']->get_id();
//$target_product_id=$arrys['productlist']['productid'];
//$tac_dd_discounted_price=$arrys['productlist']['price'];
if ( $id == $target_product_id ) {
$cart_item['data']->set_price($tac_dd_discounted_price);
}
else
{
$cart_item['data']->set_price($my_price['productlist']['price']);
}
}
}
But issue is for one product in cart price show correct but when try to add two product the Seession variable append same value in both product
First, instead of using PHP $_SESSION you should better use WooCommerce dedicated WC_Session class:
// Set the data (the value can be also an indexed array)
WC()->session->set( 'custom_key', 'value' );
// Get the data
WC()->session->get( 'custom_key' );
Now in your code function, you are just getting from PHP Session one product and one price in:
$tac_dd_discounted_price = $_SESSION['wdm_user_price_data'];
$target_product_id = $_SESSION['wdm_user_product_id_data']; // ==> Removed .'<br/>.'
Instead you should need to get an array of product IDs and prices when there is many products in cart.
Also, you don't need global $woocommerce;…
As you don't show all other related JS/Ajax/PHP code and as your question is not detailed, is not possible to help you more than that.
I did it anyone who used custom session for price need to update the cart and checkout page this is the hook which will help you a lot.
add_filter( 'woocommerce_add_cart_item' , 'set_woo_prices');
add_filter( 'woocommerce_get_cart_item_from_session', 'set_session_prices', 20 , 3 );
function set_woo_prices( $woo_data ) {
session_start();
$tac_dd_discounted_price = $_SESSION['wdm_user_price_data'];
$target_product_id = $_SESSION['wdm_user_product_id_data'];
if ( ! isset($tac_dd_discounted_price ) || empty ($tac_dd_discounted_price ) ) { return $woo_data; }
$woo_data['data']->set_price( $tac_dd_discounted_price );
$woo_data['my_price'] = $tac_dd_discounted_price;
return $woo_data;
}
function set_session_prices ( $woo_data , $values , $key ) {
if ( ! isset( $woo_data['my_price'] ) || empty ( $woo_data['my_price'] ) ) { return $woo_data; }
$woo_data['data']->set_price( $woo_data['my_price'] );
return $woo_data;
}

Woocommerce never save shipping address on checkout page

By default Woocommerce saves the billing and shipping address on the checkout page.
I am searching for a way to prevent Woocommerce from saving the values in the shipping address. So all the fields in the shipping address should be empty.
In another thread I found a partial solution. It works great, but it also makes the billing address empty:
add_filter('woocommerce_checkout_get_value','__return_empty_string',10);
Is there a way to do this only for shipping address?
Big thx!
You could change the code like this...
add_filter( 'woocommerce_checkout_get_value', 'reigel_empty_checkout_shipping_fields', 10, 2 );
function reigel_empty_checkout_shipping_fields( $value, $input ) {
/*
Method 1
you can check the field if it has 'shipping_' on it...
if ( strpos( $input, 'shipping_' ) !== FALSE ) {
$value = '';
}
Method 2
put all the fields you want in an array...
*/
$shipping_fields = array(
//'shipping_first_name',
//'shipping_last_name',
'shipping_company',
'shipping_country',
'shipping_address_1',
'shipping_address_2',
'shipping_city',
'shipping_state',
'shipping_country',
'shipping_postcode'
);
if ( in_array( $input, $shipping_fields ) ) {
$value = '';
}
return $value;
}

Categories