Search code examples
phpwoocommercecoupondiscount

Add a discount on undiscounted items with specfic attribute value in WooCommerce


I am trying to exclude any products where any coupon code is applied, from my custom discount code, on the checkout page.

In my code, I am applying a discount to the order when x amount of products are added to the cart, with a specific variation taxonomy. I am trying to make this code work, so no discount may be given on items where ANY coupon code is used.

The discount code should only apply on products where no other coupon code has been applied/is active.

add_action( 'woocommerce_cart_calculate_fees','wc_cart_item_quantity_discount' );
function wc_cart_item_quantity_discount( $cart ) {
    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    // INITIALIZING VARIABLES
    $discount = $items_count = $percent = $items_subtotal = 0;
    $taxonomy   = 'pa_variant'; // Taxonomy
    $term_slugs = array('flaske'); // Term/terms
    $couponcode = WC()->cart->get_applied_coupons(); // check for any coupon code

    // Loop through cart items
    foreach( $cart->get_cart() as $cart_item_key => $cart_item ) {
        $product = $cart_item['data'];
        // Product variations
        if( $product->is_type('variation') ) {
            // Loop through variation attributes
            foreach ($cart_item['variation'] as $attribute => $term_slug) {
                if ($attribute === 'attribute_' . $taxonomy && in_array($term_slug, $term_slugs)) {
                    $items_count += $cart_item['quantity'];
                    $items_subtotal += $cart_item['line_subtotal'];
                }
            }
        } 
        // Simple products
        elseif ( $product->is_type('simple') ) {
            $attributes = $product->get_attributes();

            if( ! empty($attributes) && array_key_exists($taxonomy, $attributes) ) {
                $terms = (array) $attributes[$taxonomy]->get_terms(); // array of WP_Term objects
                $slugs = array_map(function($term) { return $term->slug; }, $terms); // Extract only the term slugs

                if (count( array_intersect($slugs, $term_slugs) ) > 0 ) {
                    $items_count += $cart_item['quantity'];
                    $items_subtotal += $cart_item['line_subtotal'];
                }
            }
        }
    }
    // CONDITIONAL PERCENTAGE
    if ($items_count >= 6 && ! $couponcode ) { // CHECK COUPON
        $percent = 15;
    }
    // DISCOUNT (TAXABLE)
    if ($items_count > 0 ) {
        // Calculation
        $discount -= ($items_subtotal / 100) * $percent;
        $cart->add_fee(__("Mix & Match rabat - $percent%", "woocommerce"), $discount, true);
    }
}

What I have tried

I thought this could easily be done by checking if a coupon has been applied to cart, using WC()->cart->get_applied_coupons().

However, if ($items_count >= 6 && ! $couponcode ) { removes the discount completely when a coupon code is applied. I only want to exclude products with coupons active, from the discounted calculations.

Is there any way to achieve this?

Reference:

Check if a coupon is applied to cart in WooCommerce

Check if ANY coupon code is applied in WooCommerce

Remove one payment gateway if a coupon is applied


Solution

  • You need something a bit different, optimized in a simpler way.

    • First, let replace the term slug by the term name and use the method get_attribute(), to target only specific cart items (works with simple products and product variations).
    • To target undiscounted cart items, the item subtotal need to be the same as the item total (so we don't need to check for applied coupons codes, as it doesn't work when a coupon has restrictions at product level).

    So your code will be:

    add_action( 'woocommerce_cart_calculate_fees', 'wc_cart_item_quantity_discount' );
    function wc_cart_item_quantity_discount( $cart ) {
        if ( is_admin() && ! defined( 'DOING_AJAX' ) )
            return;
    
        $discount  = $items_count = $percent = $items_subtotal = 0; // Initializing variables
        $taxonomy  = 'pa_variant'; // Attribute taxonomy (or taxonomy label name too)
        $term_name = 'Flaske'; // Attribute term NAME
    
        // Loop through cart items
        foreach( $cart->get_cart() as $item ) {
            $product = $item['data'];
    
            // Targeting undiscounted items that have a specific product attribute value
            if ( $product->get_attribute($taxonomy) === $term_name 
            && $item['line_subtotal'] === $item['line_total'] ) {
                $items_count    += $item['quantity'];
                $items_subtotal += $item['line_subtotal'];
            }
        }
        // Discount percentage based on item count
        if ($items_count >= 6 ) { 
            $percent   = 15; // Discount percentage
            $discount -= $items_subtotal * $percent / 100; // Calculation
            $cart->add_fee( __('Mix & Match rabat', 'woocommerce') . " - {$percent}%", $discount );
        }
    }
    

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

    Note: Using a fee with a negative amount to get a discount, is a trick, and in this case, it is always taxable even if you set the $taxable argument as false.