skip to Main Content

I need to make discounts to certain user roles of some products, when a certain number of this product is purchased.

The discount should only be applied to the number of products that we have chosen in:

Between 1 and 9 products: no discount.

Product 10: 5% discount.

Between 11 and 19 products: no discount.

20 products: 5% discount.

Between 21 and 29 products, the discount does not apply,

30 products, the 5% discount is applied, etc…

and so on always in batches of 10 (10, 20, 30, 40, ..multiples of 10)

For example: Buy 12 TVs and a 5% discount is applied

So, a user buys 14 televisions, at a price of €19.00, a 5% discount will be applied to the first 12 units, the remaining 2 do not receive the discount.

I EDIT THE QUESTION WITH NEW EXAMPLES

They’ve refined two examples for me, but they don’t work.
The first of the examples applies the discount to all products after reaching 10 products, but when we go beyond 10, for example 11, 12 or 13, the discount to number 10 disappears.
Then, when reaching 20 products, the discount starts again, but it doesn’t work either because it applies the discount to all products from 20

Can you see what’s wrong?

function apply_discount_for_quantity($cart) {
  if (is_admin()) return;
  global $woocommerce;
  $user = wp_get_current_user();
  $user_roles = $user->roles;
  $discount_products = array(383); // products eligible for discount
  $discount_role = 'wholesale_customer'; // user role eligible for discount
  $subtotal = $woocommerce->cart->cart_contents_total;
  $count = $woocommerce->cart->cart_contents_count;
  $discount_percent = 0;
  if (in_array($discount_role, $user_roles) && $count > 0) {
      $discount_eligible_products = array();
      foreach ($cart->cart_contents as $product) {
          if (in_array($product['product_id'], $discount_products)) {
              $discount_eligible_products[] = $product;
          }
      }
      $discount_eligible_product_count = count($discount_eligible_products);
      if ($discount_eligible_product_count > 0) {
          $discount_eligible_product_quantity = 0;
          foreach ($discount_eligible_products as $product) {
              $discount_eligible_product_quantity += $product['quantity'];
          }
          if ($discount_eligible_product_quantity >= 10) {
              if ($discount_eligible_product_quantity % 10 == 0) {
                  $discount_percent = 5;
              } else if ($discount_eligible_product_quantity >= 20 && $discount_eligible_product_quantity <= 29) {
                  $discount_percent = 5;
              }
          } else if ($discount_eligible_product_quantity == 20) {
              $discount_percent = 5;
          }
      }
  }
  if ($discount_percent > 0) {
      $discount_amount = $subtotal * ($discount_percent / 100);
      $cart->add_fee('Descuento por cantidad', -$discount_amount);
  }
}
add_action('woocommerce_cart_calculate_fees', 'apply_discount_for_quantity');

I have some other option that always displays a text for the discount, but I can’t get the echo discount to save. I want to say that, when you go from 10 products, for example, the discount disappears until I add more products until I reach 20 products, that the discount corresponding to that amount is added, but in products from 10 to 20, the discount disappears 5% made to the 10 products

The discount already obtained must be maintained if we increase the products until we reach the next number that is a multiple of 10, which we would add another 5%. I cannot find the correct form. This next function manages to keep the text in the checkout, but when we have quantities of products that are between 10 and 20, are between 20 and 30, are between 30 and 40, etc., the text is set to zero, without saving the discount already obtained

It’s the closest we’ve come to achieving the goal.

See what I’m doing wrong:

function apply_discount_for_quantity($cart)
{
  if (is_admin()) return;
  global $woocommerce;
  $user = wp_get_current_user();
  $user_roles = $user->roles;
  $discount_products = array(383, 411, 412); // productos elegibles para descuento
  $discount_role = 'wholesale_customer'; // rol de usuario elegible para descuento
  $subtotal = $woocommerce->cart->cart_contents_total;
  $count = $woocommerce->cart->cart_contents_count;
  $discount_percent = 0;
  $eligible_products_count = 0; // variable para contar la cantidad de productos elegibles para descuento
  $discount_applied = 0; // variable para guardar el descuento aplicado

  // cycle through the products in the cart to count thecantidad de productos elegibles
  foreach ($cart->cart_contents as $product) {
    if (in_array($product['product_id'], $discount_products)) {
      $eligible_products_count += $product['quantity'];
    }
  }

  if (in_array($discount_role, $user_roles) && $eligible_products_count > 0) {
    // Change the conditions to apply the discount only in multiples of 10
    if ($eligible_products_count % 10 == 0) {
      $discount_percent = 5;
    }
  }

  // Save the applied discount in a variable
  $discount_applied = $subtotal * ($discount_percent / 100);

  // Add the discount to the cart to always show it, even if it does not apply to quantities that are not multiples of 10
  $cart->add_fee('Descuento por cantidad', -$discount_applied);

  //Apply the discount if necessaryo
  if ($discount_percent > 0) {
    $cart->add_fee('Descuento por cantidad', -$discount_applied);
  }
}
add_action('woocommerce_cart_calculate_fees', 'apply_discount_for_quantity');


```

2

Answers


  1. This is the way I would go about this problem:

    1. Gather the numbers of items to be discounted
    2. Integer divide the total number of items by $required_quantity
    3. Take and multiply that number by recalculated value of the discount for each set of required quantity.

    I would also determine if they were a subscriber before passing the cart to the apply_discount function.

    A simple version of this would be:

    function apply_discount($cart): float {
        $required_quantity = 11; 
        // for this case I am going to assume €19.00 each at 4% discount.
        $block_discount = 19 * 11 * 0.04;
                    
        foreach( $cart->get_cart() as $cart_item ){
            $discount = 0
            if(in_array( $cart_item['product_id'], $product_ids )) {
                $discount = $block_discount * intdiv($cart_item['quantity']), $required_quantity);
            }
            if($discount > 0 ){
                $cart->add_fee( 'Discount', -$discount, true, 'standard' );
            }
        }    
    }
    

    As for the €88 discount that is roughly 10x what it should be. My guess is $discount = $cart_item['quantity'] * 10;

    Login or Signup to reply.
  2. As I can see in your last exemple

    First you can break the logic earlier for

    if (!in_array($discount_role, $user_roles)) { return ;}
    

    As you will not apply discount for them

    Then you do :

    if ($eligible_products_count % 10 == 0) {
          $discount_percent = 5;
        }
    

    If you have 31 eligible products you will got 1 == 0 // false and your discount percent will not change and stay to 0

    In your code, only some product can be eligible to trigger the discount, it’s look like you will apply the discount to the not eligible product. You should put some of then in your test cart.

    So look like to apply more than one discount like Meechew do.

    Here my try :

    function apply_discount_for_quantity($cart)
            {
                // For sample, I let you choose of to define the rules and send it here
                $discount_rules = [
                    ['eligible_product_ids' => [383, 411], 'required_count' => 12, 'discount_percent' => 4],
                    ['eligible_product_ids' => [412], 'required_count' => 10, 'discount_percent' => 5],
                ];
                
                $user = wp_get_current_user();
                $user_roles = $user->roles;
                $discount_role = 'wholesale_customer'; // rol de usuario elegible para descuento
    
                if (!in_array($discount_role, $user_roles)) {
                    return;
                }
                
                // Will use counter per lligible product
                $discounts_counter_cache = [];
                
                // cycle through the products in the cart to count thecantidad de productos elegibles
                foreach ($cart->cart_contents as $product) {
                    
                    //You can put this one on another function to make it more readble)
                    foreach ($discount_rules as $rule) {
                        // if product is eligible to one of our discount rule
                        if (in_array($product['product_id'], $rule['eligible_product_ids'])) {
    
                            // let's count of many product whe got 
                            $discounts_counter_cache[ $product['product_id'] ] = ($discounts_counter_cache[ $product['product_id'] ] ?? 0) + 1;
    
                            // Apply per elligible product only when it's reach the limit
                            if ($discounts_counter_cache[ $product['product_id'] ] == $rule['required_count']) {
                                // We trigger the discount 
                                $cart->add_fee(
                                    'Descuento por cantidad: ' . $rule['discount_percent'] .'% por '. $rule['required_count'] .'x'. $product['product_id'],
                                    // You can also set the value in the rule 
                                    -($product['product_price'] * $rule['required_count'] * ($rule['discount_percent'] / 100))
                                );
                                
                                // Reset counter
                                $discounts_counter_cache[ $product['product_id'] ] = 0;
                            }
                        }
                    }
                }
            }
    

    hope it will help you to found what’s you need

    Here sample test (test it: https://3v4l.org/ot8A5h):

    <?php
    
    class Cart 
    {
        protected array $cart = [];
        protected array $fees = [];
        
        public function __construct()
        {
            // generate test Cart
            $testProducts = [
                ['id' => 1 , 'nb' => 22, 'price' => 23],
                ['id' => 383 , 'nb' => 38, 'price' => 19],
                ['id' => 412, 'nb' => 11, 'price' => 34],
            ];
            foreach ($testProducts as $testProduct) {
                while ($testProduct['nb']-- > 0) {
                    $this->addProduct($testProduct['id'], $testProduct['price']);
                }
            }
        }
        
        public function cart_contents(): array
        {
            return $this->cart;
        }
        
        public function addProduct(int $id, int $price)
        {
            $this->cart[] = ['product_id' => $id, 'price' => $price];
        }
        
        public function add_fee(string $txt, float $price)
        {
            $this->fees[] = ['txt' => $txt, 'price' => $price];
        }
        
        function apply_discount_for_quantity()
        {
                // For sample, I let you choose of to define the rules and send it here
                $discount_rules = [
                    ['eligible_product_ids' => [383, 411], 'required_count' => 12, 'discount_percent' => 4],
                    ['eligible_product_ids' => [412], 'required_count' => 10, 'discount_percent' => 5],
                ];
                
                // Will use counter per lligible product
                $discounts_counter_cache = [];
                
                // cycle through the products in the cart to count thecantidad de productos elegibles
                foreach ($this->cart_contents() as $product) {
                    
                    foreach ($discount_rules as $rule) {
                        // if product is eligible to one of our discount rule
                        if (!in_array($product['product_id'], $rule['eligible_product_ids'])) {
                            // product not eligible for a discount
                            continue;
                        }
    
                        // let's count of many product whe got 
                        $discounts_counter_cache[ $product['product_id'] ] = ($discounts_counter_cache[ $product['product_id'] ] ?? 0) + 1;
    
                        // Apply per elligible product only when it's reach the limit
                        if ($discounts_counter_cache[ $product['product_id'] ] == $rule['required_count']) {
                            echo sprintf(' -- Found discount of %d%% to apply on the batch of %d product (id %d) in the cart' . PHP_EOL,
                                $rule['discount_percent'],
                                $rule['required_count'],
                                $product['product_id']
                            );
                            // We trigger the discount 
                            $this->add_fee(
                                'Descuento por cantidad: ' . $rule['discount_percent'] .'% por '. $rule['required_count'] .'x'. $product['product_id'],
                                // You can also set the value in the rule 
                                -($product['price'] * $rule['required_count'] * ($rule['discount_percent'] / 100))
                            );
                            
                            // Reset counter
                            $discounts_counter_cache[ $product['product_id'] ] = 0;
                        }
                        
                    }
                }
                
            }
        
        public function show_cart()
        {
            
            $products = [];
            foreach ($this->cart as $entry) {
                if (!isset($products[$entry['product_id']])) {
                    $products[$entry['product_id']] = [
                        'id' => $entry['product_id'],
                        'nb' => 0,
                        'price' => $entry['price'],
                        'total' => $entry['price'],
                    ];
                }
                $products[$entry['product_id']]['nb']++;
                $products[$entry['product_id']]['price'] += $entry['price'];
            }
            echo 'Cart:'. PHP_EOL;
            foreach ($products as $product) {
                echo sprintf(
                    '> Product Id:"%d" - %d$ x %d = %d$'.PHP_EOL,
                    $product['id'],
                    $product['price'],
                    $product['nb'],
                    $product['total']
                );
               
            }
            echo PHP_EOL;
            if (!empty($this->fees)) {
                echo 'Fees:'.PHP_EOL;
                $totalFees = ['nb' => 0, 'total' => 0];
                foreach ($this->fees as $fee) {
                    $totalFees['nb']++;
                    $totalFees['total']+= $fee['price'];
                    echo sprintf(
                     '> %d$ - %s' . PHP_EOL,
                     $fee['price'],
                     $fee['txt']
                    );
                }
                echo sprintf(
                     '>> Total of %d discounts for %d$' . PHP_EOL,
                     $totalFees['nb'],
                     $totalFees['total']
                    );
            }
            
        }
                
    }
            
            
     $cart = new Cart();
     echo 'Before discount:'.PHP_EOL;
     echo $cart->show_cart();
     $cart->apply_discount_for_quantity();
     echo PHP_EOL.'After discount:'.PHP_EOL;
     echo $cart->show_cart();
    

    Result:

    Before discount: Cart:

    Product Id:"1" – 529$ x 22 = 23$
    Product Id:"383" – 741$ x 38 = 19$
    Product Id:"412" – 408$ x 11 = 34$

    — Found discount of 4% to apply on the batch of 12 product (id 383)
    in the cart — Found discount of 4% to apply on the batch of 12
    product (id 383) in the cart — Found discount of 4% to apply on the
    batch of 12 product (id 383) in the cart — Found discount of 5% to
    apply on the batch of 10 product (id 412) in the cart

    After discount: Cart:

    Product Id:"1" – 529$ x 22 = 23$
    Product Id:"383" – 741$ x 38 = 19$
    Product Id:"412" – 408$ x 11 = 34$

    Fees:

    -9$ – Descuento por cantidad: 4% por 12×383
    -9$ – Descuento por cantidad: 4% por 12×383
    -9$ – Descuento por cantidad: 4% por 12×383
    -17$ – Descuento por cantidad: 5% por 10×412

    Total of 4 discounts for -44$

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search