skip to Main Content

I am trying to add custom fields to my WooCommerce site to update the price based on the warranty status selected by the customer when choosing a product variation. Specifically, I want the price to adjust according to whether the warranty is expired, within 1-6 months, over 6 months, or unactivated.

screenshot

I have implemented the following code, which correctly displays the warranty options and the adjusted price on the page. However, the price does not update correctly when changing variations, leading to calculation errors. Here’s my code:

add_action('woocommerce_single_variation', 'add_custom_options', 5);
function add_custom_options() {
    ?>
    <div class="custom-options-wrapper" style="width: 100%;">
        <h4>Great, let's talk about the more details:</h4>
        <div class="custom-warranty-option" style="margin-bottom: 20px;">
            <label>1. Is it still under warranty?</label>
            <label style="display: block;">
                <input type="radio" name="warranty_status" value="no" required> No, the warranty has expired.
            </label>
            <label style="display: block;">
                <input type="radio" name="warranty_status" value="1-6months" required> Yes, 1-6 months warranty
            </label>
            <label style="display: block;">
                <input type="radio" name="warranty_status" value="over6months" required> Yes, over 6 months warranty
            </label>
            <label style="display: block;">
                <input type="radio" name="warranty_status" value="unactivated" required> Yes, 1 year warranty & unactivated
            </label>
        </div>
    </div>

    <script type="text/javascript">
    jQuery(document).ready(function($) {
        var originalPrice;

        function getOriginalPrice() {
            var priceText = $('.woocommerce-variation-price .woocommerce-Price-amount.amount').first().text();
            return parseFloat(priceText.replace(/[^d.]/g, ''));
        }

        function updatePrice() {
            if (isNaN(originalPrice)) {
                originalPrice = getOriginalPrice();
            }

            var warrantyMultiplier = 1.00;

            if ($('input[name="warranty_status"]:checked').val() === 'no') {
                warrantyMultiplier = 0.90; // No warranty, deduct 10%
            } else if ($('input[name="warranty_status"]:checked').val() === '1-6months') {
                warrantyMultiplier = 0.92; // 1-6 months warranty, deduct 8%
            } else if ($('input[name="warranty_status"]:checked').val() === 'over6months') {
                warrantyMultiplier = 0.95; // Over 6 months warranty, deduct 5%
            }

            var newPrice = Math.round(originalPrice * warrantyMultiplier);

            $('.woocommerce-variation-price .woocommerce-Price-amount.amount').html('<bdi><span class="woocommerce-Price-currencySymbol">$</span>' + newPrice + '</bdi>');

            // Update hidden input to ensure the new price is used when added to the cart
            $('#custom_price').val(newPrice);
        }

        // Clear the selected state of all custom options
        function resetCustomOptions() {
            $('input[name="warranty_status"]').prop('checked', false);
        }

        $('form.variations_form').on('woocommerce_variation_has_changed', function() {
            resetCustomOptions();  // Reset custom options
            originalPrice = getOriginalPrice();  // Get new price each time a variation is switched
            updatePrice();
        });

        $('input[name="warranty_status"]').change(function() {
            updatePrice();
        });

        originalPrice = getOriginalPrice();
        updatePrice();
    });
    </script>
   
    <?php
}

Main Issue: When a user switches between variations, the price does not correctly update based on the selected warranty status. How can I ensure the price is recalculated and updated when the variation changes?
I would appreciate any suggestions to resolve this issue! Thank you!

2

Answers


  1. First, it is better to include the variations warranty prices HTML to the variable product data form. Then you should use WooCommerce variation JS events to change your prices.

    Try the following revised code:

    // Add variation warranty prices html to the variable product data form
    add_action( 'woocommerce_available_variation', 'add_variation_custom_warranty_prices_html', 10, 3 );
    function add_variation_custom_warranty_prices_html( $variation_data, $product, $variation ) {
        $display_price         = (float) $variation_data['display_price'];
        $display_regular_price = (float) $variation_data['display_regular_price'];
    
        // Variation is on sale
        if ( $display_price !== $display_regular_price ) {
            $variation_data['warranty_prices'] = array(
                'no'            => wc_format_sale_price($display_regular_price * 0.9, $display_price * 0.9),
                '1-6months'     => wc_format_sale_price($display_regular_price * 0.92, $display_price * 0.92),
                'over6months'   => wc_format_sale_price($display_regular_price * 0.95, $display_price * 0.95),
                'inactivated'   => wc_format_sale_price($display_regular_price, $display_price),
            );
        } 
        // Variation is not on sale
        else {
            $variation_data['warranty_prices'] = array(
                'no'            => wc_price($display_price * 0.9),
                '1-6months'     => wc_price($display_price * 0.92),
                'over6months'   => wc_price($display_price * 0.95),
                'inactivated'   => wc_price($display_price),
            );
        }
        return  $variation_data;
    }
    
    add_action( 'woocommerce_single_variation', 'add_custom_warranty_options', 5 );
    function add_custom_warranty_options() {
        // HTML output
        echo '<div class="custom-options-wrapper" style="width: 100%;">
            <h4>'. esc_html__("Great, let's talk about the more details:") . '</h4>
            <div class="custom-warranty-option" style="margin-bottom: 20px;">
                <label>'. esc_html__("1. Is it still under warranty?") . '</label>
                <label style="display: block;">
                    <input type="radio" name="warranty_status" value="no" required> '. esc_html__("No, the warranty has expired.") . '
                </label>
                <label style="display: block;">
                    <input type="radio" name="warranty_status" value="1-6months" required> '. esc_html__("Yes, 1-6 months warranty") . '
                </label>
                <label style="display: block;">
                    <input type="radio" name="warranty_status" value="over6months" required> '. esc_html__("Yes, over 6 months warranty") . '
                </label>
                <label style="display: block;">
                    <input type="radio" name="warranty_status" value="inactivated" required> '. esc_html__("Yes, 1 year warranty & inactivated") . '
                </label>
            </div>
        </div>';
    
        // Enqueued Javascript (jQuery ready event is included)
        wc_enqueue_js( "var warrantyPrices = undefined, selectedWarranty = undefined;
        function updateVariationPriceHtml( warrantyPrices, selectedWarranty ) {
            $.each( warrantyPrices, function( warrantyOption, priceHtml ){
                if( warrantyPrices === undefined || selectedWarranty === undefined ) {
                    return false;
                }
                if ( selectedWarranty === warrantyOption ) {
                    $('.woocommerce-variation-price > .price').html(priceHtml);
                }
            });
        }
    
        $('form.variations_form').on('show_variation', function(event, data){ 
            warrantyPrices = data.warranty_prices;
            updateVariationPriceHtml( warrantyPrices, selectedWarranty );
        }).on('hide_variation', function() {
            warrantyPrices = selectedWarranty = undefined;
            $('input[name=warranty_status]').prop('checked', false);
        }).on('change', 'input[name=warranty_status]', function(){
            selectedWarranty = $(this).val();
            updateVariationPriceHtml( warrantyPrices, selectedWarranty );
        });" );
    }
    

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

    Now it will work without any issue.


    Addition:

    Reset the Warranty selected option on selected variation change

    To reset the Warranty selected option on selected variation change, you can change the JavaScript code block inside the 2nd function like:

        // Enqueued Javascript (jQuery ready event is included)
        wc_enqueue_js( "var warrantyPrices = undefined, selectedWarranty = undefined;
        $('form.variations_form').on('show_variation', function(event, data){ 
            warrantyPrices = data.warranty_prices;
            selectedWarranty = undefined;
            $('input[name=warranty_status]').prop('checked', false);
        }).on('hide_variation', function() {   
            warrantyPrices = selectedWarranty = undefined;
            $('input[name=warranty_status]').prop('checked', false);
        }).on('change', 'input[name=warranty_status]', function(){
            selectedWarranty = $(this).val();
            $.each( warrantyPrices, function( warrantyOption, priceHtml ){
                if( warrantyPrices === undefined || selectedWarranty === undefined ) {
                    return false;
                }
                if ( selectedWarranty === warrantyOption ) {
                    $('.woocommerce-variation-price > .price').html(priceHtml);
                }
            });
        });" );
    
    Login or Signup to reply.
  2. It looks like the main issue you’re facing is that the originalPrice value doesn’t get updated properly when switching between product variations. This can happen if the originalPrice variable holds onto an old value, leading to incorrect price calculations.

    To address this, let’s make sure we:

    Properly retrieve and reset the originalPrice every time a variation changes.
    Ensure the custom price is reset when switching variations to avoid conflicts.

    add_action('woocommerce_single_variation', 'add_custom_options', 5);
    function add_custom_options() {
        ?>
        <div class="custom-options-wrapper" style="width: 100%;">
            <h4>Great, let's talk about the more details:</h4>
            <div class="custom-warranty-option" style="margin-bottom: 20px;">
                <label>1. Is it still under warranty?</label>
                <label style="display: block;">
                    <input type="radio" name="warranty_status" value="no" required> No, the warranty has expired.
                </label>
                <label style="display: block;">
                    <input type="radio" name="warranty_status" value="1-6months" required> Yes, 1-6 months warranty
                </label>
                <label style="display: block;">
                    <input type="radio" name="warranty_status" value="over6months" required> Yes, over 6 months warranty
                </label>
                <label style="display: block;">
                    <input type="radio" name="warranty_status" value="unactivated" required> Yes, 1 year warranty & unactivated
                </label>
            </div>
        </div>
    
        <script type="text/javascript">
        jQuery(document).ready(function($) {
            var originalPrice = null;
    
            // Function to get the original price from the variation
            function getOriginalPrice() {
                var priceText = $('.woocommerce-variation-price .woocommerce-Price-amount.amount').first().text();
                return parseFloat(priceText.replace(/[^d.]/g, ''));
            }
    
            // Function to update the displayed price based on warranty status
            function updatePrice() {
                // Retrieve the price only if it's not already set
                if (isNaN(originalPrice) || originalPrice === null) {
                    originalPrice = getOriginalPrice();
                }
    
                var warrantyMultiplier = 1.00;
    
                // Determine the multiplier based on selected warranty status
                if ($('input[name="warranty_status"]:checked').val() === 'no') {
                    warrantyMultiplier = 0.90; // No warranty, deduct 10%
                } else if ($('input[name="warranty_status"]:checked').val() === '1-6months') {
                    warrantyMultiplier = 0.92; // 1-6 months warranty, deduct 8%
                } else if ($('input[name="warranty_status"]:checked').val() === 'over6months') {
                    warrantyMultiplier = 0.95; // Over 6 months warranty, deduct 5%
                }
    
                var newPrice = Math.round(originalPrice * warrantyMultiplier);
    
                $('.woocommerce-variation-price .woocommerce-Price-amount.amount').html('<bdi><span class="woocommerce-Price-currencySymbol">$</span>' + newPrice + '</bdi>');
    
                // Update hidden input to ensure the new price is used when added to the cart
                $('#custom_price').val(newPrice);
            }
    
            // Clear the selected state of all custom options
            function resetCustomOptions() {
                $('input[name="warranty_status"]').prop('checked', false);
            }
    
            // When variation changes, reset options and original price
            $('form.variations_form').on('woocommerce_variation_has_changed', function() {
                originalPrice = null;  // Reset original price when variation changes
                resetCustomOptions();  // Reset warranty options
                originalPrice = getOriginalPrice();  // Fetch updated original price
                updatePrice();  // Recalculate based on new variation
            });
    
            // Trigger price update when warranty option changes
            $('input[name="warranty_status"]').change(function() {
                updatePrice();
            });
    
            // Initial setting of original price and price update
            originalPrice = getOriginalPrice();
            updatePrice();
        });
        </script>
       
        <?php
    }
    
    

    By setting originalPrice to null when a variation changes, we ensure the original price is fetched from the updated variation. The resetCustomOptions() function now clears the previously selected warranty option each time a variation is changed.The getOriginalPrice() function is called on every variation change to ensure the correct base price is used.

    Make sure to add a hidden input field with the ID custom_price if you need to pass the updated price to the WooCommerce cart.

    If you encounter rounding issues, you might want to use toFixed(2) on newPrice to format it correctly.

    Feel free to leave a comment on the solution, I’ll surely share my thoughts on that.

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