Skip to content
Advertisement

Add checkout custom fields data to shipping packages for calculations in WooCommerce

I’m creating a plugin that will calculate custom shipping variants with API. I have a jQuery script that calculates postal and fias codes based on an entered address.

$("#billing_address_1").suggestions({
        serviceUrl: "https://suggestions.dadata.ru/suggestions/api/4_1/rs",
        token: php_vars.dadata_suggest_token,
        type: "ADDRESS",
        count: 5,
        onSelect: function (suggestion) {
            $("#billing_city").val(suggestion.data.city);
            $("#billing_state").val(suggestion.data.region);
            $("#billing_postcode").val(suggestion.data.postal_code);
            if (suggestion.data.settlement_fias_id)
                $("#billing_fias_code").val(suggestion.data.settlement_fias_id);
            else if (suggestion.data.city_fias_id)
                $("#billing_fias_code").val(suggestion.data.city_fias_id);
            else
                $("#billing_fias_code").val('');
        }
    }); 

To store the fias code, I created custom field.

add_filter( 'woocommerce_checkout_fields' , array( $this, 'custom_checkout_fields' ));
add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'shipping_apartment_update_order_meta') ); 
function custom_checkout_fields( $fields ) {
        $fields['shipping']['shipping_fias_code'] = array(
            'type' => 'text',
            'label' => __('FIAS', 'woocommerce'),
            'placeholder' => _x('Код', 'placeholder', 'woocommerce'),
            'required' => false,
            'class' => array('form-row-wide'),
            'clear' => true
        );
        $fields['billing']['billing_fias_code'] = array(
            'type' => 'text',
            'label' => __('FIAS', 'woocommerce'),
            'placeholder' => _x('Код', 'placeholder', 'woocommerce'),
            'required' => false,
            'class' => array('form-row-wide'),
            'clear' => true
        );
        
        return $fields;
    }

    function shipping_apartment_update_order_meta( $order_id ) {
        if ( ! empty( $_POST['shipping_fias_code'] ) ) {
            update_post_meta( $order_id, 'shipping_fias_code', sanitize_text_field( $_POST['shipping_fias_code'] ) );
        }
        if ( ! empty( $_POST['billing_fias_code'] ) ) {
            update_post_meta( $order_id, 'billing_fias_code', sanitize_text_field( $_POST['billing_fias_code'] ) );
        }
    }

The calculate_shipping() method in WC_Shipping_Method in woocommerce calculates shipping options using the $ package variable, which has an ‘destination’ array field with information about the shipping address. The postal code is passed into this array by default. But I also need to pass my custom field inside $package.

As I understand it, the field that I created will save the information added there via the jQuery script, only after the form is posted. But other fields included in $package[‘destination’] are saved immediately after adding information to them.

How do I add data from a custom field in the checkout form to the $package['destination'] variable?

Advertisement

Answer

I can’t test (or modify) your jQuery code, so you will have to handle it yourself, maybe making some changes to it. I have revisited completely all your code (except your jQuery code) and everything works as you expect.

So $package['destination'] will have an additional entry for 'fias_code'.

The commented code:

// Add billing and shipping fields
add_filter( 'woocommerce_billing_fields' , 'custom_billing_fields' );
add_filter( 'woocommerce_shipping_fields' , 'custom_shipping_fields' );

function custom_shipping_fields( $fields ) {
    $fields['shipping_fias_code'] = array(
        'type' => 'text',
        'label' => __('FIAS', 'woocommerce'),
        'placeholder' => _x('Код', 'placeholder', 'woocommerce'),
        'required' => false,
        'class' => array('form-row-wide'),
        'clear' => true
    );

    return $fields;
}

function custom_billing_fields( $fields ) {
    $fields['billing_fias_code'] = array(
        'type' => 'text',
        'label' => __('FIAS', 'woocommerce'),
        'placeholder' => _x('Код', 'placeholder', 'woocommerce'),
        'required' => false,
        'class' => array('form-row-wide'),
        'clear' => true
    );
    return $fields;
}

// Ajax sender
add_action( 'wp_footer', 'checkout_send_fias_code_via_ajax_js' );
function checkout_send_fias_code_via_ajax_js() {
    if ( is_checkout() && ! is_wc_endpoint_url() ) :
    ?><script type="text/javascript">
    jQuery( function($){
        if (typeof wc_checkout_params === 'undefined')
            return false;

        // Function that send the Ajax request
        function sendAjaxRequest( value, fieldset = 'billing' ) {
            $.ajax({
                type: 'POST',
                url: wc_checkout_params.ajax_url,
                data: {
                    'action': 'fias_code',
                    'fias_code': value,
                    'fieldset' : fieldset
                },
                success: function (result) {
                    $(document.body).trigger('update_checkout'); // Update checkout processes
                    console.log( result ); // For testing (output data sent)
                }
            });
        }

        // Billing fias code change & input events
        $(document.body).on( 'change input', 'input[name=billing_fias_code]', function() {
            sendAjaxRequest( $(this).val() );
        });

        // Shipping fias code change & input events
        $(document.body).on( 'change input', 'input[name=shipping_fias_code]', function() {
            sendAjaxRequest( $(this).val(), 'shipping' );
        });
    });
    </script>
    <?php
    endif;
}

// The WordPress Ajax PHP receiver (set data to a WC_Session variable)
add_action( 'wp_ajax_fias_code', 'set_fias_code_to_wc_session' );
add_action( 'wp_ajax_nopriv_fias_code', 'set_fias_code_to_wc_session' );
function set_fias_code_to_wc_session() {
    $field_key = 'fias_code';
    if ( isset($_POST[$field_key]) && isset($_POST['fieldset']) ){
        // Get data from custom session variable
        $values = (array) WC()->session->get($field_key);

        // Initializing when empty
        if( ! empty($values) ) {
            $values = array(
                'billing' => WC()->customer->get_meta('billing_'.$field_key),
                'shipping' => WC()->customer->get_meta('shipping_'.$field_key)
            );
        }

        // Sanitizing data sent
        $fieldset  = esc_attr($_POST['fieldset']);
        $fias_code = sanitize_text_field($_POST[$field_key]);

        // Set / udpate custom WC_Session variable
        $values[$fieldset] = $fias_code;
        WC()->session->set($field_key, wc_clean($values));

        // Send back to javascript the data received as an array (json encoded)
        echo json_encode(array($fieldset.'_'.$field_key => $fias_code));
        wp_die(); // always use die() or wp_die() at the end to avoird errors
    }
}

// Update checkout fields 'fias_code' values from custom WC_session variable
add_filter('woocommerce_checkout_get_value', 'update_fias_code_checkout_fields_values', 10, 2 );
function update_fias_code_checkout_fields_values( $value, $input ) {
    $field_key = 'fias_code';

    // Get data from custom session variable
    $values = (array) WC()->session->get($field_key);

    if ( ! empty($values) ) {
        if ( 'billing_'.$field_key === $input ) {
            $value = $values['billing'];
        }
        if ( 'shipping_'.$field_key === $input ) {
            $value = $values['shipping'];
        }
    }
    return $value;
}

// Add 'fias_code' data to destination shipping packages
add_filter( 'woocommerce_cart_shipping_packages', 'add_fias_code_to_destination_shipping_package' );
function add_fias_code_to_destination_shipping_package( $packages ) {
    $customer   = WC()->customer; // The WC_Customer Object

    // Get 'fias_code' data from customer meta data
    $main_key   = 'fias_code';
    $meta_value = $customer->get_meta('shipping_'.$main_key);
    $meta_value = empty($meta_value) ? $customer->get_meta('billing_'.$main_key) : $meta_value;

    // Get data from custom session variable
    $values = (array) WC()->session->get($main_key);

    if ( ! empty($values) ) {
        $session_value = $values['shipping'];

        if ( $session_value === $meta_value ) {
            $session_value = $values['billing'];

            if ( $session_value !== $meta_value ) {
                $meta_value = $values['billing'];
            }
        } else {
            $meta_value = $session_value;
        }
    }

    // Loop through shipping packages
    foreach ( $packages as $key => $package ) {
        // Set to destination package the "fias_code"
        $packages[$key]['destination'][$main_key] = $meta_value;
    }
    return $packages;
}

// Remove custom WC_Session variable once order has been created (before thankyou)
add_action( 'woocommerce_checkout_order_created', 'remove_fias_code_custom_wc_session_variable' );
function remove_fias_code_custom_wc_session_variable() {
    // Remove the custom WC_Session variable
    WC()->session->__unset('fias_code');
}

This code goes in functions.php file of the active child theme (or active theme). Tested and works.


Important notes:

Displaying and save fias_code checkout fields:
I am using woocommerce_billing_fields and woocommerce_shipping_fields filter hooks instead of woocommerce_checkout_fields as that way the data is saved itself as order meta data and user meta data. So your last function is not required anymore.
The order meta keys start with an underscore like all billing and shipping order metadata.
The fields will be displayed on My account edit addresses… So if you want to avoid that you will need to add a condition to both related hooks.

Regarding my jQuery code:
You should better copy it to an external file and register/enqueue it in a clean WordPress way, restricting output to checkout page only. Then you will remove the related action hook and the hooked function…

Regarding action and filter hooks:
You will have to change all add_action() and add_filter() for your plugin like:

add_filter( 'woocommerce_billing_fields' , array($this, 'custom_billing_fields') );
add_filter( 'woocommerce_shipping_fields' , array($this, 'custom_shipping_fields') );
add_action( 'wp_footer', array($this, 'checkout_send_fias_code_via_ajax_js') );
add_action( 'wp_ajax_fias_code', array($this, 'set_fias_code_to_wc_session') );
add_action( 'wp_ajax_nopriv_fias_code', array($this, 'set_fias_code_to_wc_session') );
add_filter('woocommerce_checkout_get_value', array($this, 'update_fias_code_checkout_fields_values'), 10, 2 );
add_filter( 'woocommerce_cart_shipping_packages', array($this, 'add_fias_code_to_destination_shipping_package') );
add_action( 'woocommerce_checkout_order_created', array($this, 'remove_fias_code_custom_wc_session_variable') );
User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement