1

With WooCommerce last version, I need to display on product page the weight of the variation chosen in short description and I would like to display the total weight just beside the add to cart button when you choose more than 1 quantity.

For example: I select 4 quantity. Unit weight is 4 kgs. I need to display: "The total weight is 16kgs".

This code works well if the general product has a weight, but it doesn't works with variations weight:

function woocommerce_total_product_price() {
    global $woocommerce, $product;
    
    // let's setup our divs
    echo sprintf('<div id="product_total_price" style="margin-bottom:20px;">%s %s</div>', __('Totale Prodotto:','woocommerce'),'<span class="price">' . $product->get_price() . '</span>');
    echo sprintf('<div id="product_total_weight" style="margin-bottom:20px;">%s %s</div>', __('Totale Peso:','woocommerce'),'<span class="weight">' . $product->get_weight() . '</span>');
    
    ?>
        <script>
            jQuery(function($) {
                var price = <?php echo $product->get_price(); ?>, 
                    currency = '<?php echo get_woocommerce_currency_symbol(); ?>';
                    
                var weight = <?php echo $product->get_weight(); ?>;

                $('[name=quantity]').change(function() {
                    if (!(this.value < 1)) {
                        var product_total = parseFloat(price * this.value);

                        $('#product_total_price .price').html( currency + product_total.toFixed(2) );
                        
                        var weight_total = parseFloat(weight * this.value);
                        
                        $('#product_total_weight .weight').html( weight_total.toFixed(2) + ' kg');                      
                    }
                });
            });
        </script>
    <?php
}
add_action( 'woocommerce_single_product_summary', 'woocommerce_total_product_price', 31 );
Vincenzo Di Gaetano
  • 3,892
  • 3
  • 13
  • 32
Nesta78
  • 25
  • 4

2 Answers2

1

You can create a javascript object that will contain the necessary values based on the type of product.

The idea is this:

  1. Create a PHP array with the product type and other necessary fields (price, currency and weight);
  2. Convert PHP array to Javascript object to use in script
  3. Based on the type of product, it obtains the price and weight of the product (if the product is variable, it obtains the values of the selected variation)

In the previous function I used the variation slugs to determine which option was chosen. This method works only if the variable product has a single select.

Thanks to @LoicTheAztec's answer you find here:

I figured I could get the value of the chosen variation id from the hidden input with the variation_id class.

It will now also work with variable products that have more than one select options.

I changed the function by splitting it in two.

  1. The first script adds the content to the product page (under the add to cart form).

  2. The second adds the script in the footer to handle changing values.

So:

// shows the total price and total weight based on the quantity entered
add_action( 'woocommerce_single_product_summary', 'show_total_price_and_total_weight_by_quantity', 31 );
function show_total_price_and_total_weight_by_quantity() {
    // create the divs that will contain the total price and total weight
    echo sprintf('<div id="product_total_price" style="display:none;margin-bottom:20px;">%s %s</div>', __('Totale Prodotto:','woocommerce'),'<span class="price"></span>');
    echo sprintf('<div id="product_total_weight" style="display:none;margin-bottom:20px;">%s %s</div>', __('Totale Peso:','woocommerce'),'<span class="weight"></span>');
}

// change the total price and total weight based on the quantity entered
add_action( 'wp_footer', 'change_total_price_and_total_weight_by_quantity', 99 );
function change_total_price_and_total_weight_by_quantity() {
    // only on the product page
    if ( ! is_product() ) {
        return;
    }

    global $product;
    
    // initializes the array that will contain the product data
    $product_data = array();

    // do divs need to be shown?
    $show = true;

    // gets the currency and unit of weight
    $currency = get_woocommerce_currency_symbol();
    $weight_unit = get_option('woocommerce_weight_unit');

    // if the product is simple
    if ( $product->is_type( 'simple' ) ) {
        // set the type of product
        $product_data['type']  = 'simple';
        // get simple product data
        $product_data['price'] = $product->get_price();
        $product_data['currency'] = $currency;
        $product_data['weight'] = $product->get_weight();
        $product_data['weight_unit'] = $weight_unit;
    }

    // if the product is variable
    if ( $product->is_type( 'variable' ) ) {
        // set the type of product
        $product_data['type']  = 'variable';
        // get the ids of the product variations
        $variation_ids = $product->get_children();
        foreach ( $variation_ids as $variation_id ) {
            // gets the object of the variation product
            $variation = wc_get_product( $variation_id );
            // gets product variation data
            $product_data['variation'][$variation_id]['price'] = $variation->get_price();
            $product_data['variation'][$variation_id]['currency'] = $currency;
            $product_data['variation'][$variation_id]['weight'] = $variation->get_weight();
            $product_data['variation'][$variation_id]['weight_unit'] = $weight_unit;
        }
        // hides the div
        $show = false;
    }

    // returns the JSON representation of a data
    $product_data_json = json_encode( $product_data );

    ?>
        <script>
            jQuery(function($) {
                // create a javascript object with product data
                <?php
                echo "var productData = ". $product_data_json . ";";
                if ( $show ) {
                    ?>
                    var product_total = parseFloat( productData.price * $('[name=quantity]').val());
                    $('#product_total_price .price').html( productData.currency + product_total.toFixed(2) );
                    var weight_total = parseFloat(productData.weight * $('[name=quantity]').val());
                    $('#product_total_weight .weight').html( weight_total.toFixed(2) + ' ' + productData.weight_unit);
                    $('#product_total_price').show();
                    $('#product_total_weight').show();
                    <?php
                }
                ?>
                // when the quantity is changed or a product option is selected
                jQuery('[name=quantity], table.variations select').on('change',function(){
                    // shows data based on product type
                    switch( productData.type ) {
                        case 'simple':
                            // update the fields
                            var product_total = parseFloat(productData.price * $(this).val());
                            $('#product_total_price .price').html( productData.currency + product_total.toFixed(2) );
                            var weight_total = parseFloat(productData.weight * $(this).val());
                            $('#product_total_weight .weight').html( weight_total.toFixed(2) + ' ' + productData.weight_unit);                      
                            break;
                        case 'variable':
                            // gets the id variation based on the options chosen
                            var variation_id = $('input.variation_id').val();
                            // if the variation id is valid and the current option is different from "Choose an option"
                            if ( parseInt( $('input.variation_id').val() ) > 0 && $('input.variation_id').val() != '' && $(this).find('option').filter(':selected').val() != '' ) {
                                $('#product_total_price').show();
                                $('#product_total_weight').show();
                                // gets the object based on the selected variation
                                var obj = productData.variation[variation_id];
                                // update the fields
                                var product_total = parseFloat(obj.price * $(this).val());
                                $('#product_total_price .price').html( obj.currency + ' ' + product_total.toFixed(2) );
                                var weight_total = parseFloat(obj.weight * $(this).val());
                                $('#product_total_weight .weight').html( weight_total.toFixed(2) + ' ' + obj.weight_unit);
                            // otherwise it hides the divs
                            } else {
                                $('#product_total_price').hide();
                                $('#product_total_weight').hide();
                            }
                            break;
                    }
                });
            });
        </script>
    <?php

}

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

Vincenzo Di Gaetano
  • 3,892
  • 3
  • 13
  • 32
  • Oh than you so much ! It's working. But if i change variation, weight doesn't update correctly. I need to add a new quantity to make it change and to be well updated. Example in this video to understand : https://drive.google.com/file/d/1Dtkk0NcRSabVXA7mQDAbqzhSnaXTq8v2/view?usp=sharing – Nesta78 Feb 27 '21 at 11:25
  • Thank you for your time, it's very nice ! I tried to replace the code, but i have again the same issue. As you can see here : https://drive.google.com/file/d/1Ds2yAtOgFIVj_f10NtGMVKU9NDAO3IYk/view?usp=sharing ==> D100 / 0,60mm = unit weight 1kg ==> D200 / 0,60mm = unit weight 5kg As you can see, if i change, product weight is not correct, i have to change quantity Thanks a lot :) – Nesta78 Feb 27 '21 at 16:39
  • First of all make sure it's not a cache issue. If you use a plugin, empty it. Also try incognito. If that doesn't work, got errors in the browser console? If you inspect the select elements, do you find them both inside the **table** element with the **variations** class? Try adding the`console.log(productData);` line just after``. Refresh the page with the browser console open and publish the result by updating your question. I tested the code and it works either by choosing an option or by changing quantity. – Vincenzo Di Gaetano Feb 27 '21 at 21:16
  • Hi, and again, a big thank you for taking time on my issue :) I think i found I have this issue. It's because of the plugin "YITH WooCommerce Minimum Maximum Quantity". I use differents options for "Allow users to select product variations only in groups of". For example, for a variation, you can only order by 5 unit. And for others, only by 1 unit. If i desactivate this plugin, all works good. Do you know how i could resolve this issue ? I'll definitively can pay you to have a look on my server :) Thanks a lot :) – Nesta78 Feb 28 '21 at 11:44
  • I tried to change the function. Now the script is in the footer, where it should be. The "YITH WooCommerce Minimum Maximum Quantity" plugin shouldn't interfere in any way as it handles different fields. I tested the plugin online demo and everything seems to work fine. – Vincenzo Di Gaetano Feb 28 '21 at 16:25
  • Thank you. We are almost good :) Now it works well, but it's the quantity value which is not good anymore when you change variation. Example in this video (D100 = 100kg, D200 = 200kg, D300 = 300kg) Total weight is well calculated if you change variation, but the quantity displayed is not the good one https://drive.google.com/file/d/1A-PH7ZuUc6rWAn2yNOBN2VzegwHxLF1T/view?usp=sharing Thanks a lot ! – Nesta78 Feb 28 '21 at 16:46
  • Do you use a script that resets the input quantity to 1 after selecting the product variation? Try increasing the priority of the `wp_footer` hook filter. – Vincenzo Di Gaetano Feb 28 '21 at 17:37
  • I don't know, maybe it's the plugin YITH WooCommerce Minimum Maximum Quantity ? How can I increase priority of wp_footer hook filter ? Thank you. – Nesta78 Feb 28 '21 at 17:48
  • Try replacing the`add_action( 'wp_footer', 'change_total_price_and_total_weight_by_quantity', 99 );` line with`add_action( 'wp_footer', 'change_total_price_and_total_weight_by_quantity', 9999 );`. If that doesn't work, it may be due to an Ajax call to the plugin. In this case you should contact their support. I can not do anything else. – Vincenzo Di Gaetano Feb 28 '21 at 22:54
0

I see a quantity_input.php file in the theme i use, maybe it's here that quantity input is reset.

if ( $max_value && $min_value === $max_value ) {
?>
<div class="quantity hidden">
    <input type="hidden" id="<?php echo esc_attr( $input_id ); ?>" class="qty" name="<?php echo esc_attr( $input_name ); ?>" value="<?php echo esc_attr( $min_value ); ?>" />
</div>
<?php
} else {
/* translators: %s: Quantity. */
$labelledby = ! empty( $args['product_name'] ) ? sprintf( esc_html__( '%s quantity', 'essentials' ), strip_tags( $args['product_name'] ) ) : '';
?>
<div class="quantity mr-2 pix-px-10 bg-white rounded-lg shadow-sm d-inline-block">
    <label class="screen-reader-text sr-only" for="<?php echo esc_attr( $input_id ); ?>"><?php esc_html_e( 'Quantity', 'essentials' ); ?></label>
    <a href="#" class="minus d-inline-block text-body-default" >-</a>
    <input
        type="number"
        id="<?php echo esc_attr( $input_id ); ?>"
        class="input-text qty text form-control shadow-0 d-inline-block"
        step="<?php echo esc_attr( $step ); ?>"
        min="<?php echo esc_attr( $min_value ); ?>"
        max="<?php echo esc_attr( 0 < $max_value ? $max_value : '' ); ?>"
        name="<?php echo esc_attr( $input_name ); ?>"
        value="<?php echo esc_attr( $input_value ); ?>"
        title="<?php echo esc_attr_x( 'Qty', 'Product quantity input tooltip', 'essentials' ); ?>"

        size="4"
        pattern="<?php echo esc_attr( $pattern ); ?>"
        inputmode="<?php echo esc_attr( $inputmode ); ?>"
        aria-labelledby="<?php echo esc_attr( $labelledby ); ?>" />
        <a href="#" class="plus d-inline-block text-body-default" >+</a>
</div>
<?php
}

And i also have a frontend.js file in plugin folder :

jQuery( function ( $ ) {

    if ( ywmmq.variations ) {

        $( document ).on(
            'found_variation',
            function () {

                var product_id   = parseInt( $( '.single_variation_wrap .product_id, .single_variation_wrap input[name="product_id"]' ).val() ),
                    variation_id = parseInt( $( '.single_variation_wrap .variation_id, .single_variation_wrap input[name="variation_id"]' ).val() );

                if ( ! isNaN( product_id ) && ! isNaN( variation_id ) ) {

                    get_variation_rules( product_id, variation_id );

                }

            }
        );

    }

    function get_variation_rules( product_id, variation_id ) {

        var container       = $( '.ywmmq-rules-wrapper' ),
            variations_form = $( '.single_variation_wrap' ),
            raq_button      = $( '.add-request-quote-button' );

        if ( variations_form.is( '.processing' ) ) {
            return false;
        }

        variations_form.addClass( 'processing' );
        raq_button.addClass( 'disabled' );
        variations_form.block(
            {
                message   : null,
                overlayCSS: {
                    background: '#fff',
                    opacity   : 0.6
                }
            }
        );

        $.ajax(
            {
                type    : 'POST',
                url     : ywmmq.ajax_url,
                data    : {
                    action      : 'ywmmq_get_rules',
                    product_id  : product_id,
                    variation_id: variation_id
                },
                success : function ( response ) {

                    if ( response.status === 'success' ) {

                        container.html( response.rules );

                        if ( parseInt( response.limits.max ) !== 0 ) {

                            $( '.single_variation_wrap .quantity input[name="quantity"]' ).attr( 'max', response.limits.max );

                        } else {

                            $( '.single_variation_wrap .quantity input[name="quantity"]' ).removeAttr( 'max' );

                        }

                        if ( parseInt( response.limits.min ) !== 0 ) {

                            $( '.single_variation_wrap .quantity input[name="quantity"]' ).attr( 'min', response.limits.min ).val( response.limits.min );

                        } else {

                            $( '.single_variation_wrap .quantity input[name="quantity"]' ).attr( 'min', 1 ).val( 1 );

                        }

                        if ( parseInt( response.limits.step ) !== 0 ) {

                            $( '.single_variation_wrap .quantity input[name="quantity"]' ).attr( 'step', response.limits.step );

                        } else {

                            $( '.single_variation_wrap .quantity input[name="quantity"]' ).attr( 'step', 1 ).val( 1 );

                        }

                        $( document ).trigger( 'ywmmq_additional_operations', [response.limits.min] );

                    } else {

                        container.html();

                    }

                    variations_form.removeClass( 'processing' ).unblock();
                    raq_button.removeClass( 'disabled' );

                },
                dataType: 'json'
            }
        );

        return false;

    }

    $( document ).on(
        'yith_wcpb_found_variation_after',
        function ( event, form, variation ) {

            if ( form.is( '.processing' ) ) {
                return false;
            }

            form.addClass( 'processing' );
            form.block(
                {
                    message   : null,
                    overlayCSS: {
                        background: '#fff',
                        opacity   : 0.6
                    }
                }
            );

            $.ajax(
                {
                    type    : 'POST',
                    url     : ywmmq.ajax_url,
                    data    : {
                        action      : 'ywmmq_get_rules',
                        product_id  : form.data( 'product_id' ),
                        variation_id: form.find( '.variation_id' ).val()
                    },
                    success : function ( response ) {

                        if ( response.status === 'success' ) {

                            if ( parseInt( response.limits.max ) !== 0 ) {

                                form.find( '.yith-wcpb-bundled-quantity' ).attr( 'max', response.limits.max );

                            } else {

                                form.find( '.yith-wcpb-bundled-quantity' ).removeAttr( 'max' );

                            }

                            if ( parseInt( response.limits.min ) !== 0 ) {

                                form.find( '.yith-wcpb-bundled-quantity' ).attr( 'min', response.limits.min ).val( response.limits.min );

                            } else {

                                form.find( '.yith-wcpb-bundled-quantity' ).attr( 'min', 1 ).val( 1 );

                            }

                            if ( parseInt( response.limits.step ) !== 0 ) {

                                form.find( '.yith-wcpb-bundled-quantity' ).attr( 'step', response.limits.step );

                            } else {

                                form.find( '.yith-wcpb-bundled-quantity' ).attr( 'step', 1 ).val( 1 );

                            }

                        }

                        form.removeClass( 'processing' ).unblock();

                    },
                    dataType: 'json'
                }
            );

        }
    )

}
);
Nesta78
  • 25
  • 4