I have an ecommerce webiste using woocommerce
In the checkout page I need to activate a custom required field "Codice Fiscale" if the billing country is set to "Italy", otherwise that extra field must be removed
The code in my child theme functions.php is
add_filter( 'woocommerce_checkout_fields' , 'field_cfpiva1' );
function field_cfpiva1( $fields ) {
$fields['billing']['billing_cf'] = array(
'label' => __('Codice Fiscale', 'woocommerce'),
'placeholder' => _x('Codice Fiscale', 'placeholder', 'woocommerce'),
'required' => false,
'class' => array('form-row-wide'),
'clear' => true
);
return $fields;
}
add_filter( 'woocommerce_admin_billing_fields' , 'admin_field_cfpiva1' );
function admin_field_cfpiva1( $fields ) {
$fields['cf'] = array(
'label' => __('Codice Fiscale', 'woocommerce'),
'show' => true
);
return $fields;
}
But I've no idea on how to do this dynamically on country change
I know this question is a bit old, but here was my solution to change the maxlength of the zip code field. My client was using the WooCommerce Table Rates shipping plugin, and in the USA, if the zip code entered contained the full 9 digits (xxxxx-xxxx), the plugin wouldn't properly calculate shipping. We were charging international rates for people in the same state.
I was going to use a hook to limit the post_code field to 5, but many countries have longer post code strings (like Canada, which is 6). Thanks to #Sonic Advisor. I was able to quickly modify the code to selectively change the maxlength attribute of the post_code form field as shown below:
<script>
//Limit zip code to 5 digits for United States ONLY
jQuery(document).ready(function (){
if (jQuery('#billing_country').val() == 'US'){
jQuery('#billing_postcode').attr('maxlength','5');
}
jQuery('#billing_country').on('change',function() {
if (jQuery('#billing_country').val() == 'US'){
jQuery('#billing_postcode').attr('maxlength','5');
} else {
jQuery('#billing_postcode').attr('maxlength','15');
}
})
if (jQuery('#shipping_country').val() == 'US'){
jQuery('#shipping_postcode').attr('maxlength','5');
}
jQuery('#shipping_country').on('change',function() {
if (jQuery('#shipping_country').val() == 'US'){
jQuery('#shipping_postcode').attr('maxlength','5');
} else {
jQuery('#shipping_postcode').attr('maxlength','15');
}
})
})
</script>
I've been attempting to achieve something very similar, but instead to show a custom field when a particular shipping method is selected.
Previously I had the following jquery successfully working by adding it to the cart-shipping.php template, but I can't seem to get it working on the 'state' field. Perhaps this may help (both of us) somehow reach the answer we're both after...
<script>
$(document).ready(function (){
if ($('#shipping_method_0').val() == 'flat_rate:delivered-vic-only'){
$('#newfield').show();
}
$('#shipping_method_0').on('change',function() {
if ($('#shipping_method_0').val() == 'flat_rate:delivered-vic-only'){
$('#newfield').show();
} else {
$('#newfield').hide();
}
})
})
</script>
Related
For a WooCommerce shop, based on a Shipping Zone I have three custom fieldgroups showing at checkout. I want to hide two of those fieldgroups depending on the total weight of the Cart.
By default the checkout shows all three fieldgroups because they are tied to the same shipping zone.
Three weight categories:
upto 500kg (Should just show this fieldgroup meta name)
_review_order_before_payment_blue_zone_500)
501-1000kg (Should just show this fieldgroup meta name) _review_order_before_payment_collection_blue_zone_500_1000
1001+kg (Should just show this fieldgroup meta name)
_review_order_before_payment_collection_blue_zone_1001
This is the code I've cobbled together, but its not hiding the fieldgroup. Any ideas?
// Unset checkout field based on cart weight
add_filter('woocommerce_checkout_fields', 'remove_custom_checkout_field', 999 );
function remove_custom_checkout_field( $fields ) {
if ( WC()->cart->get_cart_contents_weight() >= 500 ) {
unset($fields['_review_order_before_payment_collection_blue_zone_500_1000']); // Unset field
}
if ( WC()->cart->get_cart_contents_weight() >= 1001 ) {
unset($fields['_review_order_before_payment_collection_blue_zone_1001']); // Unset field
}
return $fields;
}
Expected fieldgroups to disappear when weight was calculated.
This is how you should declare field based on if its billing, shipping, order etc. Read more about here - https://woocommerce.com/document/tutorial-customising-checkout-fields-using-actions-and-filters/
In my case my weight is set to kg and testing product is 0.5kg. You can add your condition in custom_woocommerce_billing_fields and skip remove_checkout_fields_by_weight function.
add_filter('woocommerce_billing_fields', 'custom_woocommerce_billing_fields');
function custom_woocommerce_billing_fields($fields)
{
$fields['billing_test'] = array(
'label' => __('Test field', 'woocommerce'),
'placeholder' => _x('', 'placeholder', 'woocommerce'),
'required' => false,
'clear' => false,
'type' => 'text',
'class' => array('my-css')
);
return $fields;
}
function remove_checkout_fields_by_weight( $fields ) {
if ( WC()->cart->get_cart_contents_weight() >= 0.5 ) {
unset( $fields['billing']['billing_test'] );
}
return $fields;
}
add_filter( 'woocommerce_checkout_fields', 'remove_checkout_fields_by_weight' );
add_filter('woocommerce_checkout_fields', 'readdonly_country_select_field');
function readdonly_country_select_field( $wc_fields) {
global $woocommerce;
if(get_woocommerce_currency() == "INR"){
WC()->customer->set_billing_country('IN');
WC()->customer->set_shipping_country('IN');
$wc_fields['billing']['billing_country']['custom_attributes'] = array( 'disabled' => 'disabled' );
$wc_fields['shipping']['shipping_country']['custom_attributes'] = array( 'disabled' => 'disabled' );
}
else {
WC()->customer->set_billing_country('');
WC()->customer->set_shipping_country('');
}
return $wc_fields;
}
in my above code i have used the concept if the currency is in inr then my billing country should auto update with india this is working fine but need to refresh whole page.
I would like to add custom validation to the phone number in checkout page of woocommerce. How do I do this??
In my opinion there's no need to override default Woocommerce fields, nor use Javascript validation.
You can add a custom validation, that will be added to the Woocommerce's default billing phone field validation, hooking to an action triggered after submitting checkout.
This is the code I just implemented for a client.
// Custom validation for Billing Phone checkout field
add_action('woocommerce_checkout_process', 'custom_validate_billing_phone');
function custom_validate_billing_phone() {
$is_correct = preg_match('/^[0-9]{6,20}$/', $_POST['billing_phone']);
if ( $_POST['billing_phone'] && !$is_correct) {
wc_add_notice( __( 'The Phone field should be <strong>between 6 and 20 digits</strong>.' ), 'error' );
}
}
Of course, instead of my preg_match you can check anything else and adjust your conditional code to your needs.
You can also add custom validation for other default fields, by checking on the right $_POST variable, or your own custom checkout fields, after you correctly set them up, of course, but this is another topic :)
Hope this helps.
BY default woocommerce already have regular expression validation. The easiest way would be to validate it through jquery.
EDIT: Try not to make any changes to the woocommerce core as they will be over ridden on next update. Try this code
// Hook in
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );
// Our hooked in function - $fields is passed via the filter!
function custom_override_checkout_fields( $fields ) {
$fields['shipping']['shipping_phone'] = array(
'label' => __('Phone', 'woocommerce'),
'placeholder' => _x('Phone', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('form-row-wide'),
'clear' => true
);
return $fields;
}
I am trying to add a form to my checkout page so when a user clicks the 'Tax Exempt' checkbox, a textbox will popup and ask the user what the Tax Exempt Id number is.
I got all of that working great, and I even added the update_totals_on_change class to my form field so it will update the totals.
My next step was to add an action/filter on a method so when the update_totals_on_change executes, I can set the tax to 0 and then it will finish calculating the total.
Does anyone know which functions I can hook on to?
Looking at the checkout.js file in WooCommerce, they set the action to woocommerce_update_order_review for the ajax operation.
I tried following that but soon got lost.
I was thinking I could add some post data by hooking in to woocommerce_checkout_update_order_review
and then hooking in to woocommerce_before_calculate_totals to modify the tax stuff, but I have no idea what I need to modify.
Am I even on the right path?
Alright, I finally figured it out in case anyone is interested.
In my plugin, I made a form after the order notes by hooking in to this function: 'woocommerce_before_order_notes'
add_action('woocommerce_before_order_notes', array(&$this, 'taxexempt_before_order_notes') );
my 'taxexempt_before_order_notes' function contained:
function taxexempt_before_order_notes( $checkout ) {
echo '<div style="clear: both"></div>
<h3>Tax Exempt Details</h3>';
woocommerce_form_field( 'tax_exempt_checkbox', array(
'type' => 'checkbox',
'class' => array('tiri taxexempt'),array( 'form-row-wide', 'address-field' ),
'label' => __('Tax Exempt'),
), $checkout->get_value( 'tax_exempt_checkbox' ));
woocommerce_form_field( 'tax_exempt_name', array(
'type' => 'text',
'class' => array('form-row-first', 'tiri', 'taxexempt', 'textbox', 'hidden'),
'label' => __('Tax Exempt Name'),
), $checkout->get_value( 'tax_exempt_name' ));
woocommerce_form_field( 'tax_exempt_id', array(
'type' => 'text',
'class' => array('form-row-last', 'tiri', 'taxexempt', 'textbox', 'hidden', 'update_totals_on_change'),
'label' => __('Tax Exempt Id'),
), $checkout->get_value( 'tax_exempt_id' ));
}
Then the most important woocommerce function to hook was: 'woocommerce_checkout_update_order_review'
add_action( 'woocommerce_checkout_update_order_review', array(&$this, 'taxexempt_checkout_update_order_review' ));
function taxexempt_checkout_update_order_review( $post_data ) {
global $woocommerce;
$woocommerce->customer->set_is_vat_exempt(FALSE);
parse_str($post_data);
if ( isset($tax_exempt_checkbox) && isset($tax_exempt_id) && $tax_exempt_checkbox == '1' && !empty($tax_exempt_id))
$woocommerce->customer->set_is_vat_exempt(true);
}
I simply parsed out the $post_data that is the serialized form data from the checkout.js file in woocommerce and checked if my part of the form was filled out correctly.
If it was, then I would set the tax exempt for the user.
The accepted solution didn't work for me, but I modified it to use the following:
//=============================================================================
// ADD TAX EXEMPT CHECKMARK
// =============================================================================
add_action( 'woocommerce_after_order_notes', 'qd_tax_exempt');
function qd_tax_exempt( $checkout ) {
echo '<div id="qd-tax-exempt"><h3>'.__('Tax Exempt').'</h3>';
woocommerce_form_field( 'shipping_method_tax_exempt', array(
'type' => 'checkbox',
'class' => array(),
'label' => __('My organization is tax exempt.'),
'required' => false,
), $checkout->get_value( 'shipping_method_tax_exempt' ));
echo '</div>';
}
add_action( 'woocommerce_checkout_update_order_review', 'taxexempt_checkout_update_order_review');
function taxexempt_checkout_update_order_review( $post_data ) {
global $woocommerce;
$woocommerce->customer->set_is_vat_exempt(FALSE);
parse_str($post_data);
if ( isset($shipping_method_tax_exempt) && $shipping_method_tax_exempt == '1')
$woocommerce->customer->set_is_vat_exempt(true);
}
The key here is understanding that any field with a name that starts with shipping_method is going to inherit this updating order functionality (which was the part that didn't work for me). I found this answer at http://www.affectivia.com/blog/have-a-checkbox-on-the-checkout-page-which-updates-the-order-totals/
After a long search I found that there is a method for the cart object called remove_taxes() .
So, after setting a user meta for the tax exempt users, this cancels the tax totals.
function remove_tax_for_exempt( $cart ) {
global $current_user;
$ok_taxexp = get_the_author_meta( 'granted_taxexempt', $current_user->ID );
if ($ok_taxexp){ // now 0 the tax if user is tax exempt
$cart->remove_taxes();
}
return $cart;
}
add_action( 'woocommerce_calculate_totals', 'remove_tax_for_exempt' );
Because $cart->remove_taxes(); is deprecated. This is what I used instead.
I didn't have a form on the frontend, but have a user roll that is tax exempt. This was my solution.
Also worth noting that set_is_vat_exempt(true) also works in the US to set as tax exempt.
/**
* Set customer as tax exempt if user is a wholesale customer
*/
function remove_tax_for_exempt( $cart ) {
global $woocommerce;
if ( is_user_logged_in() && current_user_can( 'wholesale_customer' ) ) {
$woocommerce->customer->set_is_vat_exempt(true);
}
return $cart;
}
add_action( 'woocommerce_calculate_totals', 'remove_tax_for_exempt' );
Since this answer still pops up on google, I thought I'd share that setting the customer as tax exempt only works during checkout, if you need to edit the order on the back-end after it is placed and use the "recalculate" button, the taxes will still appear. Fortunately there is a hook for this as well:
function remove_tax_for_exempt($exempt, $order){
return $exempt || user_can($order->get_user_id(), 'wholesale_customer');
}
add_filter('woocommerce_order_is_vat_exempt', 'remove_tax_for_exempt', 10, 2);
I created a small module for altering forms called "form_mods". The form I'm altering is the "user_profile_form". I added a category for extra fields called "Profile".
I created a select field in the Drupal admin called "profile_state" and I'm altering it in my module to have a key => value list of states and It's working for me when logged in as an admin but an anonymous user that's trying to register sees an empty states select field. Is there a permissions issue here? I tried to add 'access' => user_access('access content') to the field but that didn't work. Here is my code:
function form_mods_form_alter($form, $form_state, $form_id) {
switch ($form_id) {
## user_profile_form ###################################################################################
case 'user_profile_form': // This is our form ID.
//echo '###'.$form['_category']['#value'].'###';
if($form['_category']['#value'] == 'Profile'){
// Modify the states dropdown list
$states = load_states_list();
$form['Profile']['profile_state'] = array(
'#type' => 'select',
'#title' => t('State'),
'#options' => $states,
'#required' => TRUE,
'#default_value' => isset($form['Profile']['profile_state']['#default_value']) ? $form['Profile']['profile_state']['#default_value'] : '',
'#element_validate' => array('profile_state_validate')
);
}
####################################################################################
break;
}
}
function load_states_list() {
$states = array('' => '-- Select a state'); // add a default empty value
$results = db_query("SELECT * FROM {states_list} ORDER BY name ASC");
while ($state = db_fetch_array($results)) {
$states[$state['code']] = $state['name'];
}
return $states;
}
Thanks
First of all, are you sure you're function ever gets run? Having the function named the way you do, I don't think it is. If the module name is "form_mods", your function should be called
function form_mods_form_alter
Or...since you're only modifying the user_profile_form form, you could use the function name
function form_mods_user_profile_form_alter
Now, the reason it isn't working is because you don't have the & in front of the $form in the parameter list. The & basically passes the variable as reference, and so any changes you make to the $form variable will be saved and passed back to the calling function. So, your function should look like
function form_mods_form_alter(&$form, &$form_state, $form_id) {
OR
function form_mods_user_profile_form_alter(&$form, &$form_state) {
Reference: http://api.drupal.org/api/drupal/developer--hooks--core.php/function/hook_form_alter/6
Thank you mikesir87 for the reference link. I figured out my problem.
There are 2 different forms that are using those fields I created. They have different form id's. I have to look for "user_profile_form" and "user_register" form id's.
Here is my new code that works:
function form_mods_form_alter($form, $form_state, $form_id) {
if( (($form_id == 'user_profile_form') && ($form['_category']['#value'] == 'Profile')) || ($form_id == 'user_register') ){
// Modify the states dropdown list
$states = form_mods_load_states_list();
$form['Profile']['profile_state'] = array(
'#type' => 'select',
'#title' => t('State'),
'#options' => $states,
'#required' => TRUE,
'#default_value' => isset($form['Profile']['profile_state']['#default_value']) ? $form['Profile']['profile_state']['#default_value'] : ''
);
}
}
thanks