WooCommerce – Buy 10, get 3 free – only the cheapest 3 are free

Solution:

Below in this custom function hooked in woocommerce_cart_calculate_fees action hook, customer will get a discount based on the sum of the cheapest 3 items price for each 10 items in cart.

You will have to define a product category, and optionally you will get a custom notice, when the discount is applied…

Here is the code:

add_action( 'woocommerce_cart_calculate_fees', 'free_cheapest_3_each_10_items', 10, 1 );
function free_cheapest_3_each_10_items( $wc_cart ) {
    if ( is_admin() && ! defined('DOING_AJAX') ) return;

    // HERE define your product category (or categories) in the array (IDs slugs or names)
    $cat_id = array('paints');
    $prices = array();
    $cat_id = array('clothing');
    $discount = $items_count = 0;

    foreach ( $wc_cart->get_cart() as $cart_item ){
        $sale_price = $cart_item['data']->get_sale_price();
        // Only for the defined product category(ies) and no items in sale
        if( has_term( $cat_id, 'product_cat', $cart_item['product_id']) && ( empty($sale_price) || $sale_price == 0 ) ) {
            for( $i = 0; $i < $cart_item['quantity']; $i++){
                $prices[] = floatval( $cart_item['data']->get_regular_price() );
                $items_count++;
            }
        }
    }

    if( $items_count >= 10 ){
        // Ordering prices
        asort($prices);

        // Get the occurence number for 3 free items each 10 items
        for( $i = 0, $j = -1; $i < $items_count; $i++ )
            if( $i % 10 == 0 ) $j++;

        $count = $j*3;

        // Get the 3 free items for each 10 items in cart (for the defined product category(ies))
        $free_cheapest_items = array_slice($prices, 0, $count, true);

        // Calculate the discount amount
        foreach( $free_cheapest_items as $item_price )
            $discount -= $item_price;

        // The discount
        if( $discount != 0 ){
            $wc_cart->add_fee( "Bulk discount", $discount, true );

            // Displaying a custom notice (optional)
            wc_clear_notices();
            wc_add_notice( __("You get $count free items for the $items_count items in cart"), 'notice');
        }
    }
}

Code goes in function.php file of your active child theme (or theme) or also in any plugin file.

Tested on WooCommerce 3 and works.