skip to Main Content

In WooCommerce, I need to add a "billing_legal_person" custom field to my Checkout page that is selectable between "Person" and "Company" to reflect whether the customer should be taxed as a company or as a person. Then, the applicable VAT percentage should be different according to the content of that billing_legal_person selection.

I have successfully created 2 tax classes: the default (standard) one for persons and a new "companies" class for companies.

I can also successfully apply programmatically one tax class or the other with this code:

add_filter( 'woocommerce_product_get_tax_class', 'my_set_taxrate_for_billing_legal_person', 10, 2 );
add_filter( 'woocommerce_product_variation_get_tax_class', 'my_set_taxrate_for_billing_legal_person', 10, 2 );

function my_set_taxrate_for_billing_legal_person( $tax_class, $product ) {
    // Commenting the line below, standard tax class is applied
    $tax_class = 'companies';

    return $tax_class;
}

I used Checkout Field Editor plugin to easily create the billing_legal_person select field (values ‘person’ or ‘company´).

I also managed to force the recalculation of the taxes when changing the content of my billing_legal_person custom field:

add_filter( 'woocommerce_checkout_fields', 'my_checkout_fields_trigger_refresh', 9999 );

function my_checkout_fields_trigger_refresh( $fields ) {
   $fields['billing']['billing_legal_person']['class'][] = 'update_totals_on_change';

   return $fields;
}

My problem is that inside my_set_taxrate_for_billing_legal_person(), I’m unable to access the content of the billing_legal_person custom field selected by the user.

If I check WC()->checkout->get_checkout_fields['billing_legal_person'], I always get an empty value.

If I check WC()->checkout->checkout->get_value('billing_first_name'), I get the intended value (customer first name). However, if I then check WC()->checkout->checkout->get_value('billing_legal_person'), I get a NULL value. I suppose I can access billing_first_name because it’s a standard field but I can’t access billing_legal_person because it is a custom field, but I could not find any clue on how to access custom fields.

All approaches based on checking $_POST['billing_legal_person'] fail because I need to show the amount of taxes to be paid BEFORE the user submits the check-out form.

Any help on how I could access the content of the billing_legal_person custom field from my_set_taxrate_for_billing_legal_person() so I can check it and return one tax class or another?

2

Answers


  1. First, don’t use Checkout field editor plugin to add ‘billing_legal_person’ custom checkout field, instead we will use a hand coded function.

    Also, remove your my_set_taxrate_for_billing_legal_person() function.

    Here JavaScript, Ajax and WC_Session are required to achieve your goal.

    The following code below will:

    • add a custom required select field for "Legal person" in checkout and My account > edit billing address pages.
    • The "Legal person" will show/hide the WooCommerce Billing Company field depending on the selected value.
    • When "Company" will be selected, cart items tax rate will change to "companies" and if "Person" is selected, cart items tax rate will be reseted to default…

    The Code (commented):

    add_filter('woocommerce_billing_fields', 'add_legal_person_billing_field');
    function add_legal_person_billing_field( $fields ) {
        $fields['billing_company']['class'][] = 'hidden'; 
    
        $fields['billing_legal_person'] = array(
            'label'       => __( 'Legal person', 'woocommerce' ),
            'type'        => 'select',
            'options'     => [
                ''        => __("Choose an option",  "woocommerce"),
                'Person'  => __("Person",  "woocommerce"),
                'Company' => __("Company", "woocommerce"),
            ],
            'required'    => true,
            'priority'    => 25,
        );
        return $fields;
    }
    
    // Validate Billing company field
    add_action( 'woocommerce_checkout_process', 'validate_custom_checkout_fields' );
    function validate_custom_checkout_fields() {
        if ( isset($_POST['billing_legal_person']) && $_POST['billing_legal_person'] === 'Company' 
        && isset($_POST['billing_company']) && empty($_POST['billing_company']) ) {
            wc_add_notice('Billing company is a required field.', 'error');
        }
    }
    
    // PHP: Replace "(optional)" with required for Billing company
    add_filter( 'woocommerce_form_field' , 'remove_checkout_optional_fields_label', 10, 4 );
    function remove_checkout_optional_fields_label( $field, $key, $args, $value ) {
        // Only on checkout page
        if( ( ( is_checkout() && ! is_wc_endpoint_url() ) 
        || is_wc_endpoint_url('edit-address') ) && $key === 'billing_company' ) {
            $optional = '&nbsp;<span class="optional">(' . esc_html__( 'optional', 'woocommerce' ) . ')</span>';
            $required = '&nbsp;<abbr class="required" title="required">*</abbr>';
            $field = str_replace( $optional, $required, $field );
        }
        return $field;
    }
    
    // Hide Billing company field on checkout page load 
    add_action('wp_head', 'checkout_legal_person_css');
    function checkout_legal_person_css() {
        if ( ( is_checkout() && ! is_wc_endpoint_url() ) || is_wc_endpoint_url('edit-address') ) : ?>
        <style>
            #billing_company_field.hidden {display:none !important;}
        </style>
        <?php endif;
    }
    
    // JavaScript / Ajax: Show/hide billing company and set the tax rate via Ajax
    add_action( 'template_redirect' , 'checkout_legal_person_js' );
    function checkout_legal_person_js() {
        if ( ( is_checkout() && ! is_wc_endpoint_url() ) || is_wc_endpoint_url('edit-address') ) :
    
        $legal_person = WC()->customer->get_meta('billing_legal_person');
        if ( is_checkout() && $legal_person ) {
            WC()->session->set('legal_person', $legal_person);
        }
        
        wc_enqueue_js("
            const company         = '#billing_company',       
                  companyField    = company+'_field', 
                  legalPers       = '#billing_legal_person',
                  isCheckout      = ".( is_checkout() ? 'true' : 'false' ).";
    
            function showHideCompanyField( legalPers, companyField ) {
                if ( $(legalPers).val() === 'Company' ) {
                    $(companyField).show();
                } else {
                    $(companyField).hide();
                }
            }
    
            // On start:
            $(legalPers).selectWoo();
            $(companyField).addClass('validate-required woocommerce-invalid woocommerce-invalid-required-field');
            showHideCompanyField( legalPers, companyField );
            $(companyField).removeClass('hidden');
    
            // On Legal Person change: Show/Hide company field
            $('form.woocommerce-checkout,.woocommerce-edit-address form').on('change', legalPers, function() {
                showHideCompanyField( legalPers, companyField );
                
                if ( isCheckout ) {
                    $.ajax({
                        type: 'POST',
                        url: '".admin_url('admin-ajax.php')."',
                        data: {
                            'action': 'legal_person_tax',
                            'legal_person' : $(legalPers).val(), 
                        },
                        success: function (response) {
                            $(document.body).trigger('update_checkout');
                            console.log(response);
                        }
                    });
                }
            });
        ");
        endif;
    }
    
    // PHP AJAX receiver: Set "legal_person" session variable
    add_action('wp_ajax_legal_person_tax', 'set_legal_person_tax' );
    add_action('wp_ajax_nopriv_legal_person_tax', 'set_legal_person_tax' );
    function set_legal_person_tax() {
        if ( isset($_POST['legal_person']) && ! empty($_POST['legal_person']) ) {
            WC()->session->set('legal_person', esc_attr($_POST['legal_person']));
        }
        wp_die(esc_attr($_POST['legal_person']));
    }
    
    // Dynamically change tax rate based on chosen "legal_person"
    add_action('woocommerce_before_calculate_totals', 'define_cart_item_tax_class_for_companies');
    function define_cart_item_tax_class_for_companies( $cart )
    {
        if ((is_admin() && !defined('DOING_AJAX')))
            return;
    
        if (did_action('woocommerce_before_calculate_totals') >= 2)
            return;
    
        $legal_person = WC()->session->get('legal_person');
        $legal_person = $legal_person ? $legal_person : WC()->customer->get_meta('billing_legal_person');
    
        foreach ($cart->get_cart() as $item) {
            if ( $legal_person === 'Company' ) {
                $item['data']->set_tax_class('companies'); // Set tax class
            } else {
                $item['data']->set_tax_class('');
            }
        }
    }
    
    // Remove session variable when order is placed
    add_action('woocommerce_checkout_order_created', 'unset_legal_person_session_variable');
    function unset_legal_person_session_variable() {
        WC()->session->__unset('legal_person');
    }
    
    // Change tax rate if customer is a a company
    add_filter( 'woocommerce_product_get_tax_class', 'tax_rate_based_on_chosen_legal_person', 10, 2 );
    add_filter( 'woocommerce_product_variation_get_tax_class', 'tax_rate_based_on_chosen_legal_person', 10, 2 );
    function tax_rate_based_on_chosen_legal_person( $tax_class, $product ) {
        if ( is_admin() ) return $tax_class;
        
        $legal_person = WC()->customer->get_meta('billing_legal_person');
    
        if ( $legal_person === 'Company' ) {
            $tax_class = 'companies';
        }
        return $tax_class;
    }
    

    Code goes in functions.php file of your child theme (or in a plugin). Tested and works.

    Login or Signup to reply.
  2. Just found your code (which is exactly what I needed, thank you very much for it) but I’m having an issue I cannot save: The code works perfectly and applies the taxes correctly depending on the legal user chosen, but when I repeat the purchase logged in with the same account and try to purchase the same or different product (actually I only have one with two variations), the tax rate applied is the same of the first purchase, even if I change the billing legal person (the taxes applied on the checkout are the same that the legal person I chose on the first purchase and the total amount doesn’t update). Because of the business type, there’s a chance the customers may want to purchase an item for their company, with the corresponding tax deductions, and, later, another item as a regular customers and I’m really struggling trying to fix it. Could you help me? Any thoughts?

    Thank you very much in advance <3

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search