wordpresswoocommerceproduct-variations

Showing "Out of Stock" text next to variation in Woocommerce product with multuiple variations


I have been looking EVERYWHERE and I cannot seem to find a solution to a very simple problem. I have a Woocommerce store and I want to show to the user a message "Out of stock" next to the variation if it is out of stock. I do not want it grayed out, I want the user to be able to select it so they can view the price if they wish. Most snippets I find online only works when they product has ONE variation but I need it to work for at least two. My website sells mobile devices so for example:

If a user selects the storage as 64GB then the color list updates showing which colors are out of stock in 64GB. If all colors are out of stock then next to 64GB it shows "Out of stock" also.

If the color is selected first, then the storage - the color list is then updated with what is out of stock.

Can anyone help me out here? Going insane trying to get this working. The below code works to gray out out of stock products in the exact way I mentioned above (it works for multiple products) but I can't figure out how to modify it for the use case mentioned above.

add_filter( 'woocommerce_variation_is_active', 'grey_out_variations_when_out_of_stock', 10, 2 );
function grey_out_variations_when_out_of_stock( $grey_out, $variation ){
if ( ! $variation->is_in_stock() ){
  return false;
 }else{
print_r($option_class);
  return $term_name . ' - Out of Stock';;
 }
}

Solution

  • You can check the availability of a product variation only if all but one of the attributes have been selected, not before.

    All the necessary data is present on the product page and therefore you can use a jQuery script to process it.

    In the variation form there is the data-product_variations attribute which contains an array with all the details of the product variations (variation id, stock status, attributes it uses, etc ...).

    You can then make a comparison between the selected attributes and possible product variations.

    The idea is this:

    1. Check that there is more than one attribute (dropdown) on the product page
    2. If all attributes (dropdowns) have been selected except one, get the attribute slug and the attribute value of the selected options
    3. It compares them with the attributes that each single variation uses and, if the selected attributes are the same, gets the stock status of the last attribute to be selected
    4. Adds the text "Out of stock" to each option whose product variation is not in stock. To do this it will use the woocommerce_update_variation_values event which fires after updating the options via Ajax (otherwise the changes will be overwritten)

    The following code will work for 2 or more attributes (dropdowns) in the variable product page.

    // add the text "Out of stock" to each option of the last unselected attribute dropdown
    add_action( 'wp_footer', 'add_out_of_stock_text_to_the_last_unselected_attribute_dropdown' );
    function add_out_of_stock_text_to_the_last_unselected_attribute_dropdown() {
    
        ?>
        <script type="text/javascript">
    
        // initializes a global variable that will contain the attribute values to be updated ("Out of stock")
        let globalAttributesToUpdate;
    
        jQuery(function($){
    
            // check if only one attribute is missing to be selected
            function isLastAttribute(){
                // if there is only one dropdown it returns false
                if ( $('form.variations_form select').length == 1 ) {
                    return false;
                }
                // counts all selected attributes (excluding "Choose an option")
                let count = 0;
                $('form.variations_form select').each(function(){
                    if ( $(this).find(':selected').val() ) {
                        count++;
                    }
                });
                // if an attribute has not yet been selected, it returns true
                if ( $('form.variations_form select').length - count == 1 ) {
                    return true;
                } else {
                    return false;
                }
            }
    
            $('form.variations_form select').change(function() {
                if ( isLastAttribute() ) {
                    // clear the global variable every time
                    globalAttributesToUpdate = [];
                    let attrToFind = {}; // contains an object with slug and value of the selected attributes
                    let attrToSet; // contains the slug of the attribute not yet selected
                    $('form.variations_form select').each(function(index,object){
                        if ( $(this).find(":selected").val() ) {
                            attrToFind[$(this).data('attribute_name')] = $(this).find(":selected").val();
                        } else {
                            attrToSet = $(this).data('attribute_name');
                        }
                    });
                    // gets the value of the "data-product_variations" attribute of the variations form
                    let variationData = $('form.variations_form').data("product_variations");
                    $(variationData).each(function(index,object) {
                        let attrVariation = object.attributes;
                        let attrVariationLenght = Object.keys(attrVariation).length;
                        let found = 0;
                        let toSet;
                        let valueToSet;
                        // check all possible combinations of attributes (for single variation)
                        // based on the selected attributes
                        $.each( attrVariation, function( attr, value ) {
                            if ( attr in attrToFind && attrToFind[attr] == value ) {
                                found++;
                            } else {
                                // if the variation is out of stock it gets slug and value to add the text "Out of stock"
                                if ( object.is_in_stock == false ) {
                                    toSet = attr;
                                    valueToSet = value;
                                }
                            }
                        });
                        // if only one attribute is missing
                        if ( attrVariationLenght - found == 1 ) {
                            if ( toSet == attrToSet ) {
                                let obj = {};
                                obj[toSet] = valueToSet;
                                globalAttributesToUpdate.push(obj);
                            }
                        }
                    });
                }
            });
    
            // inserts the text "Out of stock" after updating the variations
            // based on the "globalAttributesToUpdate" global variable
            $('body').on('woocommerce_update_variation_values', function(){
                if ( globalAttributesToUpdate !== undefined && globalAttributesToUpdate.length ) {
                    $.each( globalAttributesToUpdate, function( key, attribute ) {
                        $.each( attribute, function( attrName, attrValue ) {
                            $('select[name='+attrName+'] > option[value="'+attrValue+'"]').append( " (Out of stock)" );
                        });
                    });
                }
            });
        });
        </script>
        <?php
    
    }
    

    The code has been tested and works. Add it to your active theme's functions.php.

    RESULT

    enter image description here

    RELATED ANSWERS