phpjquerywordpresswoocommercediscount

Quantity discount based on product radio buttons in WooCommerce


In my theme's functions.php file, I added the following code snippet, that adds a small discount based on quantity, for a specific product which name contains "Hydrogel".

However, it don't apply that discount on checkout, it still keeps the original product price.

Here is the code :

function display_quantity_discount_message() {
    global $product;

    // Check if the product name contains "Hydrogel"
    if (strpos($product->get_name(), 'Hydrogel') !== false) {
        // Define your quantity-based discounts here
        $discounts = array(
            2 => 60, // For 2 pieces, you pay 60 Lei
            3 => 85, // For 3 pieces, you pay 55 Lei
            // Add more quantities and discounts as needed
        );

        // Get the current quantity input field
        echo '<div class="quantity-discount-message">';
        echo '<p>Reducere:</p>';
        echo '<ul>';

        foreach ($discounts as $quantity => $price) {
            echo '<li>';
            echo '<input type="radio" name="quantity_discount" value="' . $quantity . '" data-price="' . $price . '"> ';
            echo 'Cumpără <span style="color:green; font-weight:bold;">' . $quantity . '</span> pentru doar <span style="text-decoration: line-through; color:red; font-weight:bold;">' . ($quantity * $product->get_price()) . '</span> <span style="color:red; font-weight:bold;">' . $price . ' Lei</span>';
            echo '</li>';
        }

        echo '</ul>';
        echo '</div>';

        // Enqueue JavaScript to update the price when a radio button is selected
        wc_enqueue_js('
            jQuery(document).ready(function($) {
                $("input[name=\'quantity_discount\']").change(function() {
                    var selectedPrice = parseInt($(this).data("price"));
                    $("input[name=\'quantity\']").val($(this).val());
                    $(".single_add_to_cart_button").data("price", selectedPrice).attr("data-price", selectedPrice);
                    $(".single_add_to_cart_button").html("Adaugă în coș - " + selectedPrice + " Lei");

                    // Update the cart item price
                    if( selectedPrice > 0 ) {
                        var data = {
                            action: "update_cart_item_price",
                            price: selectedPrice
                        };
                        $.post(wc_cart_params.ajax_url, data, function(response) {
                            $("body").trigger("update_checkout");
                        });
                    }
                });
            });
        ');
    }
}

// Hook the function to display the message below the "Add to Cart" button
add_action('woocommerce_before_add_to_cart_button', 'display_quantity_discount_message');

// Add an action to update cart item price
add_action('wp_ajax_update_cart_item_price', 'update_cart_item_price');
add_action('wp_ajax_nopriv_update_cart_item_price', 'update_cart_item_price');

function update_cart_item_price() {
    if (isset($_POST['price'])) {
        $new_price = floatval($_POST['price']);
        WC()->cart->set_discount_total(0); // Remove any discounts to avoid conflicts
        foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) {
            $cart_item['data']->set_price($new_price);
        }
        WC()->cart->calculate_totals();
        echo json_encode(array('success' => true));
    }
    wp_die();
}

Update:

I wrote incorrectly the code block. Now everything is working fine except one thing: The total amount in "Total" is not calculated correctly. I think something is wrote incorrectly where $cart_item['data']->set_price($new_price); is written.


Solution

  • You don't need Ajax here, as it's a quantity discount. I have revisited all your function, extracting your discount settings in its own function, and adding the necessary functions to update the product "Hydrogel" price based on the quantity.

    I have lightly improved your jQuery code, so if you change the quantity it updates the price too and select/unselect the correct radio button.

    The code:

    // Define your quantity-based discounts below
    function get_hydrogel_discounts() {
        return  array(
            'product'   => 'Hydrogel', // Product word to search
            'discounts' => array(
                2 => 60, // For 2 pieces, you pay 60 Lei
                3 => 55, // For 3 pieces, you pay 55 Lei
            ) 
        );
    }
    
    // Add input radio buttons to the product (quantity discount)
    add_action('woocommerce_before_add_to_cart_button', 'display_quantity_discount_message');
    function display_quantity_discount_message() {
        global $product;
    
        $settings = get_hydrogel_discounts(); // Load discounts settings
    
        // Check if the product name contains "Hydrogel"
        if (strpos($product->get_name(), $settings['product']) !== false) {
    
            echo '<div class="quantity-discount-message"><p>Reducere:</p><ul>';
    
            foreach ($settings as $quantity => $price) {
                printf('<li><input type="radio" name="quantity_discount" value="%s" data-price="%s"> Cumpără 
                <span style="color:green; font-weight:bold;">%s</span> pentru doar 
                <span style="text-decoration: line-through; color:red; font-weight:bold;">%s</span> 
                <span style="color:red; font-weight:bold;">%s Lei</span></li>', 
                $quantity, $price, $quantity, ($quantity * $product->get_price()), $price );
            }
    
            echo '</ul></div>';
            
            // Enqueue JavaScript to update the price when a radio button is selected
            // and when quatity is changed
            wc_enqueue_js("const a = '.single_add_to_cart_button', b = $(a).html();
            $('input[name=quantity_discount]').on('change', function(){
                const selectedPrice = parseInt($(this).data('price'));
                $('input[name=quantity]').val($(this).val());
                $(a).data('price', selectedPrice).attr('data-price', selectedPrice);
                $(a).html('Adaugă în coș - '+selectedPrice+' Lei');
            });
            $('input[name=quantity]').on('change input', function(){
                if ( $(this).val() == 1 ) {
                    $('input[name=quantity_discount]').prop('checked', false);
                    $(a).html(b);
                } else {
                    const v = $(this).val() == 0 ? 1 : $(this).val();
                    $('input[name=quantity_discount][value='+v+']').trigger('click');
                }
            });");
        }
    }
    
    // For mini cart: Update the displayed price
    add_action( 'woocommerce_cart_item_price', 'filter_cart_item_price', 10, 2 );
    function filter_cart_item_price( $price_html, $item ) {
        global $product;
    
        $settings = get_hydrogel_discounts(); // Load discounts settings
    
        // Check if the product name contains "Hydrogel"
        if (strpos($item['data']->get_name(), $settings['product']) !== false && $item['quantity'] > 1 ) {
            if ( $item['quantity'] > 2 ) {
                $args = array( 'price' => floatval( $settings['discounts'][3]) ); 
            } elseif ( $item['quantity'] == 2 ) {
                $args = array( 'price' => floatval( $settings['discounts'][2]) ); 
            }
    
            if ( WC()->cart->display_prices_including_tax() ) {
                $product_price = wc_get_price_including_tax( $item['data'], $args );
            } else {
                $product_price = wc_get_price_excluding_tax( $item['data'], $args );
            }
            return wc_price( $product_price );
        }
        return $price_html;
    }
    
    // Update cart item price based on the selected quantity
    add_action( 'woocommerce_before_calculate_totals', 'apply_cart_item_discounted_price', 100 );
    function apply_cart_item_discounted_price( $cart ) {
        if ( ( is_admin() && ! defined( 'DOING_AJAX' ) ) )
            return;
    
        if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
            return;
    
        $settings = get_hydrogel_discounts(); // Load discounts settings
    
        // Loop through cart items
        foreach ( $cart->get_cart() as $item ) {
            // Check if the product name contains "Hydrogel"
            if (strpos($item['data']->get_name(), $settings['product']) !== false) {
                if ( $item['quantity'] > 2 ) {
                    $item['data']->set_price($settings['discounts'][3]); // Set the discounted price
                } elseif ( $item['quantity'] == 2 ) {
                    $item['data']->set_price($settings['discounts'][2]); // Set the discounted price
                }
            }
        }
    }
    

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