Change WooCommerce cart item price based on quantity discount for a category

61 views Asked by At

I need certain items to have a mix and match quantity discount based on category. I have seen some other codes that accomplish this using an overall discount at the bottom of the cart, however I need the price of each item to reflect the discount, and I also need that price to carry over to the sales order so we can invoice appropriately. Here's what I have so far:

add_action( 'woocommerce_cart_calculate_fees','custom_bulk_discount', 20, 1 );
function custom_bulk_discount( $cart ) {

    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    $category = 'ferns';

    $calculated_qty = 0;
    $calculated_total = 0;

    foreach($cart->get_cart() as $cart_item):

        if(has_term($category, 'product_cat', $cart_item['product_id'])):
            $item_price = version_compare( WC_VERSION, '3.0', '<' ) ? $cart_item['data']->price : $cart_item['data']->get_price(); // The price for one (assuming that there is always 2.99)
            $item_qty = $cart_item["quantity"];// Quantity
            $item_line_total = $cart_item["line_total"]; // Item total price (price x quantity)
            $calculated_qty += $item_qty; // ctotal number of items in cart
            $calculated_total += $item_line_total; // calculated total items amount
        endif;
    endforeach;

    if ( $calculated_qty >= 100 && $calculated_qty < 200 ){
        foreach($cart->get_cart() as $cart_item):

            $price = $cart_item['data']->price - 1;
            if(has_term($category, 'product_cat', $cart_item['product_id'])):
                $cart_item['data']->set_price( $price );
            endif;
        endforeach;
    }else if( $calculated_qty >= 200){
        foreach($cart->get_cart() as $cart_item):

            $price = $cart_item['data']->price - 1.5;
            if(has_term($category, 'product_cat', $cart_item['product_id'])):
                $cart_item['data']->set_price( $price );
            endif;
        endforeach;
    }
}

The problem is the subtotal isn't updating with the correct price. I'm assuming that is because I'm only changing the display price, not the price that is actually calculated. Is there a way to do what I'm asking?

1

There are 1 answers

4
LoicTheAztec On BEST ANSWER

First, you are not using the right hook, your code can be simplified and there are missing things.

To discount cart items based on quantity for specific product category try:

// Utility function: Get specific category discount based on quantity
function get_category_custom_discount( $cart, $category ) {
    $calculated_qty = 0; // Initializing

    foreach( $cart->get_cart() as $cart_item ) {
        if( has_term( $category, 'product_cat', $cart_item['product_id'] ) ) {
            $calculated_qty   += $cart_item["quantity"]; 
        }
    }

    if ( $calculated_qty >= 200 ) {
        return 1.5;
    } else if( $calculated_qty >= 100 ) {
        return 1;
    } else {
        return 0;
    }
}

// Change cart item price
add_action( 'woocommerce_before_calculate_totals', 'category_ferns_set_bulk_discount', 20, 1 );
function category_ferns_set_bulk_discount( $cart ) {
    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    if ( did_action('woocommerce_before_calculate_totals') > 1 )
        return;


    $category = 'ferns';
    $discount = get_category_custom_discount( $cart, $category );

    if ( $discount > 0 ) {
        foreach($cart->get_cart() as $cart_item) {
            if( has_term($category, 'product_cat', $cart_item['product_id']) ) {
                $product = wc_get_product( $cart_item['variation_id'] > 0 ? $cart_item['variation_id'] : $cart_item['product_id'] );
                $cart_item['data']->set_price( $product->get_price() - $discount );
            }
        }
    }
}

// Handle mini cart displayed price
add_action( 'woocommerce_cart_item_price', 'category_ferns_display_bulk_discount', 20, 2 );
function category_ferns_display_bulk_discount( $price_html, $cart_item ) {
    $cart     = WC()->cart;
    $category = 'ferns';
    $discount = get_category_custom_discount( $cart, $category );

    if( $discount > 0 && has_term($category, 'product_cat', $cart_item['product_id']) ) {
        $product = wc_get_product( $cart_item['variation_id'] > 0 ? $cart_item['variation_id'] : $cart_item['product_id'] );
        $args = array( 'price' => floatval( $product->get_price() - $discount ) ); 

        if ( $cart->display_prices_including_tax() ) {
            $product_price = wc_get_price_including_tax( $product, $args );
        } else {
            $product_price = wc_get_price_excluding_tax( $product, $args );
        }
        return wc_price( $product_price );
    }
    return $price_html;
}

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