1
votes

I'm trying to create a discount programmatically for orders on my store. My users have a variable discount rate e.g. 10%. I want to apply their discount to the order before checkout, however, some items in the store are not applicable for a discount.

These items have a toggle set so I can check which products do or do not allow the discount to be applied.

Currently, I'm looping through the order to check which items are applicable and using a 'fixed_cart'coupon to add the discount.

This works, however, the in the admin the coupon applies to all line items even the items which should be skipped. Making it impossible to work how much to refund to customers when a refund is required.

Checking the cart items

$tradeDiscountTotal = 0;
foreach( WC()->cart->get_cart() as $cart_item ) {
    if(!get_field('trade_discount_exempt', $cart_item['product_id'])) {
        $tradeDiscountTotal += $cart_item['line_subtotal'];
    }
}

Applying the total discount for this order

$coupon->set_discount_type('fixed_cart');
$coupon->set_amount($tradeDiscountTotal);
return $coupon;

How can I create a bespoke discount for each order and ensure the products that are discounted are represented correctly in the admin area?

1
"...I want to apply their discount to the order before checkout..." Is it necessary to create a coupon? against granting the discount directly at the checkout? - 7uc1f3r
@7uc1f3r No a coupon is not necessary. I've looked at the WooCommerce add_fee method but everything I've seen on their GitHub issue requests shows this shouldn't be used for negative numbers ... discounts :( - archvist
A negative fee is indeed not recommended by WooCommerce, but often used in the past, however there are other ways, see my answer - 7uc1f3r

1 Answers

1
votes

UPDATE

A solution is to create a coupon manually with a percentage discount with for example 10%. Then we add the following codes based on the couponcode

  1. First part to ensure that a discount of 0 is applied to products that meet the condition
  2. Second part to ensure that the coupon is automatically added to the shopping cart

So we get:

  • Discount amount on trade_discount_exempt = 0
function filter_woocommerce_coupon_get_discount_amount( $discount, $price_to_discount , $cart_item, $single, $coupon ) {
    // Product ID in cart
    $product_id = $cart_item['product_id'];
    
    // If 'trade_discount_exempt'
    if ( get_field( 'trade_discount_exempt', $product_id ) ) {
        $discount = 0;
    }

    return $discount;
}
add_filter( 'woocommerce_coupon_get_discount_amount', 'filter_woocommerce_coupon_get_discount_amount', 10, 5 );
  • Apply coupon programmatically if NOT trade_discount_exempt in cart
function action_woocommerce_before_calculate_totals( $cart ) {
    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

   if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
        return;

    // Only cart
    if( ! is_cart() )
        return;

    // Coupon code
    $coupon_code = 'testcoupon';
    
    // Format
    $coupon_code = wc_format_coupon_code( $coupon_code );
    
    // Iterating though each cart items
    foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
        // Product ID in cart
        $product_id = $cart_item['product_id'];
      
        // NOT 'trade_discount_exempt'
        if ( ! get_field( 'trade_discount_exempt', $product_id ) ) {
            // Applied coupons
            $applied_coupons = $cart->get_applied_coupons();

            // Is applied
            $is_applied = in_array( $coupon_code, $applied_coupons );

            // NOT applied
            if ( ! $is_applied ) {
                // Apply
                $cart->apply_coupon( $coupon_code );
                break;
            }
        }
    }
}
add_action( 'woocommerce_before_calculate_totals', 'action_woocommerce_before_calculate_totals', 10, 1 );


Optional: Here are 2 other possible solutions:

1. Use the woocommerce_calculated_total filter hook, apply a discount globally on cart

function filter_woocommerce_calculated_total( $total, $cart ) {
    // Discount (percentage)
    $percentage = 10; // 10 %
    
    // Set variable, used in loop
    $new_total = 0;
    
    // Iterating though each cart items
    foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
        // Product ID in cart
        $product_id = $cart_item['product_id'];

        // NOT 'trade_discount_exempt'
        if( ! get_field( 'trade_discount_exempt', $product_id ) ) {
            // Product price
            $price = $cart_item['data']->get_price();

            // Caclulate new price
            $new_price = $price * ( 1 - ( $percentage / 100 ) ); 

            // Add new price to new total
            $new_total += $new_price;
        }
    }
    
    // New total greater than
    if ( $new_total > 0 ) {
        $total = $new_total;
    }

    return $total;
}
add_filter( 'woocommerce_calculated_total', 'filter_woocommerce_calculated_total', 10, 2 );

2. Use the woocommerce_before_calculate_totals action hook, grant the discount on the product price

function action_woocommerce_before_calculate_totals( $cart ) {  
    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
        return;
    
    // Discount (percentage)
    $percentage = 10; // 10 %
    
    // Iterating though each cart items
    foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
        // Product ID in cart
        $product_id = $cart_item['product_id'];
        
        // NOT 'trade_discount_exempt'
        if( ! get_field( 'trade_discount_exempt', $product_id ) ) {
            // Product price
            $price = $cart_item['data']->get_price();
            
            // Caclulate new price
            $new_price = $price * ( 1 - ( $percentage / 100 ) ); 

            // Set price
            $cart_item['data']->set_price( $new_price );
        }
    }
}
add_action( 'woocommerce_before_calculate_totals', 'action_woocommerce_before_calculate_totals', 10, 1 );