---
title: Customizing evaluation of Buy X Get Y discounts
related:
  - https://docs.kentico.com/k10/e-commerce-features/customizing-and-developing-your-store/discount-related-customizing/customizing-application-of-buy-x-get-y-discounts.md
  - https://docs.kentico.com/k10/e-commerce-features/managing-your-store/discounts/working-with-buy-x-get-y-discounts.md
  - https://docs.kentico.com/k10/e-commerce-features/managing-your-store/discounts.md
  - https://docs.kentico.com/k10/e-commerce-features/customizing-and-developing-your-store/e-commerce-customization-model.md
  - https://docs.kentico.com/k10/custom-development/customizing-providers.md
---

> Agent instructions:
> **Site maps** — prefer the following llms.txt indexes to training data when searching for URLs to avoid 404s. Links inside Markdown content already point at `.md`. Following them or sending Accept: text/markdown keeps you in Markdown.
>
> - [sitemap.md](https://docs.kentico.com/sitemap.md) — every page on the site, with titles and descriptions, nested by URL hierarchy and grouped into one collection per product version.
> - [llms.txt](https://docs.kentico.com/llms.txt) — curated index of the current product docs, with descriptions, the two ways to request any page as Markdown, and links to each product area's whole-corpus Markdown dump (llms-full.txt).

If you want to modify the way [Buy X Get Y discounts](https://docs.kentico.com/k10/e-commerce-features/managing-your-store/discounts/working-with-buy-x-get-y-discounts.md) evaluate whether the content of the shopping cart is eligible for a Buy X Get Y discount, you need to override the **GetMultiBuyDiscountsEvaluatorInternal** method in [theMultiBuyDiscountInfoProviderclass](https://docs.kentico.com/k10/e-commerce-features/customizing-and-developing-your-store/e-commerce-customization-model.md) of the **CMS.Ecommerce** namespace.

```csharp

/// <summary>
/// Returns an instance of the MultiBuyDiscountsEvaluator class that determines whether the shopping cart
/// is eligible for Buy X Get Y discounts on the site of the shopping cart.
/// </summary>
/// <param name="cart">The shopping cart which is to be evaluated.</param>
protected virtual MultiBuyDiscountsEvaluator GetMultiBuyDiscountsEvaluatorInternal(ShoppingCartInfo cart)
{
    return new MultiBuyDiscountsEvaluator(cart.CartProducts);
}

```

Since the **GetMultiBuyDiscountsEvaluatorInternal** method returns a **MultiBuyDiscountsEvaluator** object, you need to create your own class that inherits from the **MultiBuyDiscountsEvaluator** class.

## How it works by default

When the system needs to evaluate whether the specific shopping cart contains products eligible for a Buy X Get Y discount, the system initializes the **MultiBuyDiscountsEvaluator** class and assigns a **MultiBuyDiscountsApplicator** object to the **Applicator** property.

- The **EvaluateDiscounts** method loops through all applicable Buy X Get Y discounts on the current site (i.e., discounts that are enabled on the current site in the time of evaluation for the given user role and whether the coupon was entered) and tries to apply the discount on the content of the shopping cart. Through the loop, if the system finds out that a discount forbids lower priority discounts to be applied, the system stops the loop.
  - While the method is trying to apply the discount on shopping cart items, the **PrepareItemsForDiscount** sorts the **SortedItems** list (which is sorted according to the price of the items) to the **PrioritizedItems** list according to the priority set by the discount (the discounted items from the "get" conditions are first in the list). The prioritization works as set by the **PrioritizeItems** method which is located in the class of the Buy X Get Y discount, originally in the **IMultiBuyDiscount** interface.
  - The process of trying to apply the discount occurs in the **TryApplyDiscount** method. The method loops through all shopping cart items where it evaluates whether it is usable for the currently looped discount. The method also creates a list that records units of items that are used either for the "buy" conditions of the discount or the "get" conditions (either items required for the discount to be applied or items that are discounted).
    - The process of evaluating of the discount applicability occurs in the **FindDiscountApplication** method. It finds out whether the shopping cart contains any item that is in the "buy" or "get" conditions of the Buy X Get Y discount. It also checks whether a product should be autoadded to the shopping cart (the **AcceptMissedDiscount** method of the applicator).
      - The process of finding out whether the shopping cart contains any item in the "buy" conditions occurs in the **FindItemsToBaseDiscountOn** method. The method finds out the items with the lowest priority that fulfill the "buy" conditions and records how many units of the item was used for the discount.
        - The process of checking whether the item is required for the discount occurs in the **CanBaseDiscountOn** method. The method checks whether there is any item not used for the "buy" or "get" conditions. The method also checks whether the discount requires the item for being applied (the **IsBasedOn** method of the discount object).
        - The process of creating a record of how many units of the specific item was used occurs in the **AddItemsCount** method.
      - The process of finding out whether the shopping cart contains any item in the "get" conditions occurs in the **FindItemsToBeDiscounted** method. The method finds out the items with the highest priority that fulfill the "get" conditions and record how man units of the item was used for the discount.
        - The process of checking whether the item is to be discounted occurs in the **CanBeDiscounted** method. The method checks whether there is any item not used for the "buy" or "get" conditions. The method also checks whether the discount permits to apply the discount to the item (the **IsApplicableOn** method of the discount object).
        - The process of creating a record of how man units of the specific item was discounted occurs in the **AddItemsCount** method.
    - The process of applying the discount occurs in the **ApplyDiscount** method. The method loops through all discounted items and calls the applicator of the discount to apply the discount.
    - The process of creating a record of which shopping cart was used and how many units were used occurs in the **RememberUsedItems** method. The method loops through all the items used for the discount (both for the "buy" conditions and the "get" conditions) and records it.

### Source code

**See the whole source code of the class**

```csharp

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CMS.Ecommerce
{
    /// <summary>
    /// The class handling evaluating Buy X Get Y discounts on a set of shopping cart items.
    /// </summary>
    public class MultiBuyDiscountsEvaluator
    {
        #region "Variables"
        private List<ShoppingCartItemInfo> mSortedItems;
        private readonly List<ShoppingCartItemInfo> mOriginalItems;
        private readonly Dictionary<ShoppingCartItemInfo, int> mUsedDiscountItems = new Dictionary<ShoppingCartItemInfo, int>();
        #endregion

        #region "Properties"
        /// <summary>
        /// List of the shopping cart items sorted by priority given by the discount.
        /// The discounted items from the "get" conditions are at the beginning of the list.
        /// </summary>
        protected List<ShoppingCartItemInfo> PrioritizedItems
        {
            get;
            set;
        }

        /// <summary>
        /// List of the shopping cart items sorted by their price.
        /// </summary>
        protected IEnumerable<ShoppingCartItemInfo> SortedItems
        {
            get
            {
                // Check whether the sorted list already exists.
                if (mSortedItems == null)
                {
                    // If the list does not exist, create a new list and sort it from the cheapest to the most expensive.
                    mSortedItems = new List<ShoppingCartItemInfo>(mOriginalItems);
                    mSortedItems.Sort(CompareByUnitPriceWithOptions);
                }

                // Return the sorted list.
                return mSortedItems;
            }
        }

        /// <summary>
        /// Dictionary containing the number of units of shopping cart items used for the discount (for both "buy" and "get" conditions).
        /// </summary>
        private Dictionary<ShoppingCartItemInfo, int> UsedDiscountItems
        {
            get
            {
                return mUsedDiscountItems;
            }
        }

        /// <summary>
        /// Buy X Get Y discounts applicator which is used for the actual application of the discount.
        /// </summary>
        public IMultiBuyDiscountsApplicator Applicator
        {
            get;
            set;
        }
        #endregion

        #region "Constructor"
        /// <summary>
        /// Creates a new instance of the evaluator for the given shopping cart items.
        /// </summary>
        /// <param name="cartItems">Shopping cart items to be evaluated.</param>
        public MultiBuyDiscountsEvaluator(IEnumerable<ShoppingCartItemInfo> cartItems)
        {
            mOriginalItems = cartItems.ToList();
        }
        #endregion

        #region "Discount evaluation"
        /// <summary>
        /// Resets the evaluator to its initial state.
        /// </summary>
        protected virtual void Reset()
        {
            // Reset the applicator.
            if (Applicator != null)
            {
                Applicator.Reset();
            }

            // Resets the number of units of the items used for the discount.
            mOriginalItems.ForEach(item => UsedDiscountItems.Add(item, 0));
        }

        /// <summary>
        /// Evaluates the given Buy X Get Y discounts and applies the matching discounts to the corresponding shopping cart items.
        /// </summary>
        /// <param name="discounts">Buy X Get Y discounts to be evaluated.</param>
        public void EvaluateDiscounts(IEnumerable<IMultiBuyDiscount> discounts)
        {
            // Reset the evaluator to its initial state.
            Reset();

            // Loop through all Buy X Get Y discounts to check the eligibility.
            foreach (var discount in discounts)
            {
                // Initialize the discount for application.
                discount.Init();

                // Prepare the shopping cart item list for discount application.
                PrepareItemsForDiscount(discount);

                // Try to apply the discount on the shopping cart items.
                var applied = TryApplyDiscount(discount);

                // Stop processing when the discount already applied and does not allow processing further Buy X Get Y discounts.
                if (applied && !discount.ApplyFurtherDiscounts)
                {
                    break;
                }
            }
        }

        /// <summary>
        /// Ensures that the shopping cart items from the sorted list are prioritized according to the given Buy X Get Y discount.
        /// </summary>
        /// <param name="discount">Discount which prioritizes the sorted shopping cart items.</param>
        protected virtual void PrepareItemsForDiscount(IMultiBuyDiscount discount)
        {
            // Create a new list from the list sorted by the price.
            PrioritizedItems = new List<ShoppingCartItemInfo>(SortedItems);

            // Prioritize the items according to the given discount.
            discount.PrioritizeItems(PrioritizedItems);
        }

        /// <summary>
        /// Evaluates all shopping cart items whether they are eligible for the given Buy X Get Y discount
        /// and tries to apply the discount on the valid items.
        /// </summary>
        /// <param name="discount">Discount that evaluates the shopping cart items.</param>
        /// <returns>Returns true it the discount was applied at least once.</returns>
        private bool TryApplyDiscount(IMultiBuyDiscount discount)
        {
            var applied = false;

            // Create a new clear dictionary for items that are in the "get" conditions (discounted items).
            var itemsToBeDiscounted = new Dictionary<ShoppingCartItemInfo, int>();

            // Create a new clear dictionary for items that are in the "buy" conditions (items required by the discount). 
            var itemsToBaseDiscountOn = new Dictionary<ShoppingCartItemInfo, int>();

            // Loop while the discount is allowed to be applied more times.
            do
            {
                // Clear the dictionaries.
                itemsToBeDiscounted.Clear();
                itemsToBaseDiscountOn.Clear();

                // Find discount application possibility.
                var isApplicable = FindDiscountApplication(discount, itemsToBaseDiscountOn, itemsToBeDiscounted);

                // Stop the evaluation if the discount is not applicable.
                if (!isApplicable)
                {
                    break;
                }

                // Mark the discounted items.
                ApplyDiscount(discount, itemsToBeDiscounted);

                // Remember the used items.
                RememberUsedItems(itemsToBaseDiscountOn, itemsToBeDiscounted);

                // Set a flag indicating application of the given discount.
                applied = true;
            } while (discount.AllowsMoreUses());

            // Return true if the discount was applied at least once.
            return applied;
        }

        #region "Searching for the items needed for the discount application"
        /// <summary>
        /// Finds a possibility to apply the given discount by checking whether there are the required items in the shopping cart.
        /// </summary>
        /// <param name="discount">Discount which is tried to be applied.</param>
        /// <param name="itemsToBaseDiscountOn">Empty dictionary where the key is the shopping cart item and the value is
        /// the number of units calculated for the discount. Found required items for the given discount are added here.</param>
        /// <param name="itemsToBeDiscounted">Empty dictionary where the key is the shopping cart item and the value is
        /// the number of units to be discounted. Found items discounted by the given discount are added here.</param>
        /// <returns>Returns true if the given discount is applicable.</returns>
        private bool FindDiscountApplication(IMultiBuyDiscount discount, IDictionary<ShoppingCartItemInfo, int> itemsToBaseDiscountOn, IDictionary<ShoppingCartItemInfo, int> itemsToBeDiscounted)
        {
            // Check whether the shopping cart contains items required by the given Buy X Get Y discount.
            if (!FindItemsToBaseDiscountOn(discount, itemsToBaseDiscountOn))
            {
                return false;
            }

            // Check whether the shopping cart contains at least one item that can be discounted.
            if (FindItemsToBeDiscounted(discount, itemsToBaseDiscountOn, itemsToBeDiscounted))
            {
                return true;
            }

            // Check by the applicator if a product should be autoadded.
            if (Applicator != null)
            {
                return Applicator.AcceptsMissedDiscount(discount);
            }

            return false;
        }

        /// <summary>
        /// Finds items required by "buy" conditions of the given Buy X Get Y discount.
        /// </summary>
        /// <param name="discount">Discount with the specified required items.</param>
        /// <param name="itemsToBaseDiscountOn">Empty dictionary. Found items are placed here with their counts.</param>
        /// <returns>Returns true if the found items fully satisfy discount requirement "buy" conditions.</returns>
        protected virtual bool FindItemsToBaseDiscountOn(IMultiBuyDiscount discount, IDictionary<ShoppingCartItemInfo, int> itemsToBaseDiscountOn)
        {
            // Get the number of products needed to enable the given discount.
            var minDiscountingCount = discount.BasedOnUnitsCount;

            // Check whether enough items are found to apply the discount.
            for (var i = 0; i < minDiscountingCount; i++)
            {
                // Find the item with the lowest priority which satisfies the "buy" conditions.
                var discounting = PrioritizedItems.LastOrDefault(item => CanBaseDiscountOn(item, discount, itemsToBaseDiscountOn));

                // Check whether there is no more items left to fulfill the "buy" conditions.
                if (discounting == null)
                {
                    return false;
                }

                // Add 1 to the count of the specific discounting item.
                AddItemsCount(itemsToBaseDiscountOn, discounting, 1);
            }

            return true;
        }

        /// <summary>
        /// Finds items to be discounted with the given discount based on the given items.
        /// </summary>
        /// <param name="discount">Discount with the specific "get" conditions.</param>
        /// <param name="itemsToBaseDiscountOn">Shopping cart items used for requirement "buy" conditions.</param>
        /// <param name="itemsToBeDiscounted">Shopping cart items found to be discounted by this discount.</param>
        /// <returns>Returns true when at least one discounted item was found.</returns>
        protected virtual bool FindItemsToBeDiscounted(IMultiBuyDiscount discount, IDictionary<ShoppingCartItemInfo, int> itemsToBaseDiscountOn, IDictionary<ShoppingCartItemInfo, int> itemsToBeDiscounted)
        {
            // Get the number of discounted units.
            var maxDiscountedCount = discount.ApplyOnUnitsCount;

            // Check which items are to be discounted.
            for (var i = 0; i < maxDiscountedCount; i++)
            {
                // Find the item with the highest priority which can be discounted.
                var discounted = PrioritizedItems.FirstOrDefault(item => CanBeDiscounted(item, discount, itemsToBaseDiscountOn, itemsToBeDiscounted));

                // Check whether no more items can be discounted.
                if (discounted == null)
                {
                    return i > 0;
                }

                // Add 1 to the count of the specific discounted item.
                AddItemsCount(itemsToBeDiscounted, discounted, 1);
            }

            return true;
        }

        /// <summary>
        /// Checks if the given item can be discounted using the given discount.
        /// </summary>
        /// <param name="item">Discounted item to be checked.</param>
        /// <param name="discount">Discount, which discounts the item, to be checked.</param>
        /// <param name="basedOnItems">Shopping cart items with the count of the items found as
        /// the satisfying items for the given discount.</param>
        /// <param name="alreadyDiscountedItems">Shopping cart items with the count of items already
        /// found as suitable for applying the discount for the given discount.</param>
        /// <returns>Returns true if the given discount is applicable on the given items.</returns>
        protected virtual bool CanBeDiscounted(ShoppingCartItemInfo item, IMultiBuyDiscount discount, IDictionary<ShoppingCartItemInfo, int> basedOnItems, IDictionary<ShoppingCartItemInfo, int> alreadyDiscountedItems)
        {
            // Get the count of the items used for "buy" conditions of the Buy X Get Y discount.
            var alreadyUsedUnits = GetItemsCount(basedOnItems, item);

            // Get the count of the already discounted items.
            var alreadyDiscountedUnits = GetItemsCount(alreadyDiscountedItems, item);

            // Check whether the shopping cart contains more unused and not free units of items then the units of items
            // used for "buy" conditions and the already discounted products. (Only paid items having some unit
            // not used for "buy" conditions can be used.)
            var availableUnits = GetUnusedNonFreeUnits(item) - item.CartItemAutoAddedUnits;
            if (!discount.AutoAddEnabled)
            {
                availableUnits -= item.CartItemAutoAddedUnits;
            }
            var hasEnoughUnits = (availableUnits - alreadyUsedUnits - alreadyDiscountedUnits > 0);

            // Return true if the shopping cart contains enough units and the discount is applicable, i.e.,
            // affects the price of the given shopping cart item.
            return hasEnoughUnits && discount.IsApplicableOn(item);
        }

        /// <summary>
        /// Checks if the given item can be used to fulfill discount "buy" conditions.
        /// </summary>
        /// <param name="item">Item to be checked.</param>
        /// <param name="discount">Discount to be checked.</param>
        /// <param name="alreadyBasedOnItems">Items with the count of the item units already found as fulfilling
        /// the requirements of the "buy" conditions of the given discount.</param>
        /// <returns>Returns true if the shopping cart contains enough units for the discount.</returns>
        protected virtual bool CanBaseDiscountOn(ShoppingCartItemInfo item, IMultiBuyDiscount discount, IDictionary<ShoppingCartItemInfo, int> alreadyBasedOnItems)
        {
            // Get the count of the items used for the "get" conditions of the discount.
            var alreadyUsedUnits = GetItemsCount(alreadyBasedOnItems, item);

            // Check whether the shopping cart contains more unused and not free units of items then
            // units of items used for "get" conditions. (Only paid items having some unit not used
            // for "get" conditions can be used.)
            var hasEnoughUnits = (GetUnusedNonFreeUnits(item) - alreadyUsedUnits > 0);

            // Return true if the shopping cart contains enough units and the discount.
            return hasEnoughUnits && discount.IsBasedOn(item);
        }
        #endregion
        #endregion

        #region "Discount application"
        /// <summary>
        /// Applies the Buy X Get Y discount on the given number of units of the given shopping cart item.
        /// </summary>
        /// <param name="discount">Discount which is applied.</param>
        /// <param name="itemsToBeDiscounted">Dictionary containing the items as the keys
        /// and the count of the discounted units as the values.</param>
        private void ApplyDiscount(IMultiBuyDiscount discount, IEnumerable<KeyValuePair<ShoppingCartItemInfo, int>> itemsToBeDiscounted)
        {
            // Loop through all discounted items.
            foreach (var itemCount in itemsToBeDiscounted)
            {
                var item = itemCount.Key;
                var count = itemCount.Value;

                // Apply the discount on the specific item.
                ApplyDiscount(discount, item, count);

                // Set that the discount was applied.
                discount.AcceptApplication(item, count);
            }
        }

        /// <summary>
        /// Applies the Buy X Get Y discount to the given number of units of the given shopping cart item.
        /// </summary>
        /// <param name="discount">Discount which is applied.</param>
        /// <param name="itemToBeDiscounted">Shopping cart item to apply the discount on.</param>
        /// <param name="units">Number of units to be discounted.</param>
        protected virtual void ApplyDiscount(IMultiBuyDiscount discount, ShoppingCartItemInfo itemToBeDiscounted, int units)
        {
            if (Applicator != null)
            {
                // Apply the discount on the specific item with the applicator.
                Applicator.ApplyDiscount(discount, itemToBeDiscounted, units);
            }
        }
        #endregion

        #region "Management of items already used for a discount."
        /// <summary>
        /// Makes a record that the given items in the given number of units were used for the given discount.
        /// </summary>
        /// <param name="discountingItemsCounts">Shopping cart items used for the requirement "buy" conditions.
        /// The value is the number of units used for discount.</param>
        /// <param name="discountedItemsCounts">Shopping cart items used for the "get" conditions (the discounted items).
        /// The value is the number of units used for discount.</param>
        protected virtual void RememberUsedItems(IEnumerable<KeyValuePair<ShoppingCartItemInfo, int>> discountingItemsCounts, IEnumerable<KeyValuePair<ShoppingCartItemInfo, int>> discountedItemsCounts)
        {
            // Loop through all the shopping cart items used for the requirement "buy" conditions.
            foreach (var item in discountingItemsCounts)
            {
                // Add the used units of the item to the count of all used item units.
                UsedDiscountItems[item.Key] += item.Value;
            }

            // Loop through all the shopping cart items used for the "get" conditions (the discounted items).
            foreach (var item in discountedItemsCounts)
            {
                // Add the used units of the item to the count of all used item units.
                UsedDiscountItems[item.Key] += item.Value;
            }
        }

        /// <summary>
        /// Returns the number of units not used for any discount (neither as the requirement "buy" conditions
        /// items nor as the "get" conditions items - the discounted items).
        /// </summary>
        /// <param name="item">Item to get number of units for.</param>
        /// <returns>Returns the number of units not used for any discount.</returns>
        protected virtual int GetUnusedNonFreeUnits(ShoppingCartItemInfo item)
        {
            return item.CartItemUnits - UsedDiscountItems[item];
        }
        #endregion

        #region "Helper methods"
        /// <summary>
        /// Gets the unit count of the given shopping cart item.
        /// </summary>
        /// <param name="items">Dictionary of shopping cart items. The key is the shopping cart item object,
        /// the value is number of units of the item.</param>
        /// <param name="item">Shopping cart item with the desired number of units.</param>
        /// <returns>The unit count of the given shopping cart item (if exists; otherwise, it returns 0).</returns>
        private int GetItemsCount(IDictionary<ShoppingCartItemInfo, int> items, ShoppingCartItemInfo item)
        {
            var units = 0;

            if (items.ContainsKey(item))
            {
                units = items[item];
            }

            return units;
        }

        /// <summary>
        /// Adds the number of units to the specified shopping cart item in the list of shopping cart items.
        /// </summary>
        /// <param name="items">Dictionary of shopping cart items. The key is the shopping cart item object,
        /// the value is number of units of the item.</param>
        /// <param name="item">Shopping cart item to which the units are to be added.</param>
        /// <param name="units">Number of units to be added to the specified shopping cart item.</param>
        private void AddItemsCount(IDictionary<ShoppingCartItemInfo, int> items, ShoppingCartItemInfo item, int units)
        {
            if (!items.ContainsKey(item))

            {
                items.Add(item, 0);
            }

            items[item] += units;
        }

        /// <summary>
        /// Computes the difference between two prices. By default, it uses the unit price.
        /// If the unit price is not available, the method uses the shopping cart item price.
        /// </summary>
        /// <param name="item1">Dictionary of shopping cart items. The key is the shopping cart item object,
        /// the value is number of units of the item.</param>
        /// <param name="item2">Shopping cart item with the desired number of units.</param>
        /// <returns>The unit count of the given shopping cart item (if exists; otherwise, it returns 0).</returns>
        private int CompareByUnitPriceWithOptions(ShoppingCartItemInfo item1, ShoppingCartItemInfo item2)
        {
            // For both prices, if the price is not unexpected value, use the unit price.
            // Otherwise, use shopping cart item price.
            var price1 = double.IsNaN(item1.CartItemPrice) ? item1.UnitPrice : item1.CartItemPrice;
            var price2 = double.IsNaN(item2.CartItemPrice) ? item2.UnitPrice : item2.CartItemPrice;

            // Return a number indicating the relative difference between the two prices.
            return price1.CompareTo(price2);
        }
        #endregion
    }
}

```

## Creating a custom evaluating process of Buy X Get Y discounts

To customize the Buy X Get Y discount evaluation:

1. [Override theCMS.Ecommerce.MultiBuyDiscountInfoProvider.GetMultiBuyDiscountsEvaluatorInternalmethod.](#customizing-the-multibuydiscountinfoprovider-class)
2. [Implement a class which inherits from theMultiBuyDiscountsEvaluatorclass.](#customizing-the-multibuydiscountsevaluator-class)

### Customizing the MultiBuyDiscountInfoProvider class

Override the **GetMultiBuyDiscountsEvaluatorInternal** virtual method to be able to integrate your custom **MultiBuyDiscountsEvaluator** class.

1. Create a new class file in your project in Visual Studio, for example **CustomMultiBuyInfoProvider**\*\*.cs\*\* that inherits from the **MultiBuyDiscountInfoProvider** class.
2. Add the **CMS** and **CMS.Ecommerce** namespaces to the _using_ block.
3. Register the new InfoProvider class:

   ```csharp

   [assembly: RegisterCustomProvider(typeof(CustomMultiBuyInfoProvider))]

   ```
4. Override the **GetMultiBuyDiscountsEvaluatorInternal** method with your custom (not yet existing) **MultiBuyDiscountsEvaluator** class:

   ```csharp

   public class CustomMultiBuyInfoProvider : MultiBuyDiscountInfoProvider
   {
       protected override MultiBuyDiscountsEvaluator GetMultiBuyDiscountsEvaluatorInternal(ShoppingCartInfo cart)
       {
           return new CustomMultiBuyEvaluator(cart.CartProducts);
       }
   }

   ```

   ![Creating the CustomMultiBuyInfoProvider class](https://docs.kentico.com/docsassets/k10/customizing-evaluation-of-buy-x-get-y-discounts/creating_custommultibuyinfoprovider_class.png "Creating the CustomMultiBuyInfoProvider class")

The system now returns yet non-existing **CustomMultiBuyEvaluator** when evaluating Buy X Get Y discounts. Continue with the next steps to achieve the desired functionality.

### Customizing the MultiBuyDiscountsEvaluator class

Implement a class inheriting from the **MultiBuyDiscountsEvaluator** class to provide your custom logic of evaluating Buy X Get Y discounts.

1. Create a new class file in your project in Visual Studio, for example **CustomMultiBuyEvaluator.cs** that inherits from the **MultiBuyDiscountsEvaluator** class.
2. Add the **CMS** and **CMS.Ecommerce** namespaces to the _using_ block.
3. Assign the inheritance from the **MultiBuyDiscountsEvaluator** class to the **CustomMultiBuyEvaluator** class:

   ```csharp

   public class CustomMultiBuyEvaluator : MultiBuyDiscountsEvaluator
   {
   }

   ```
4. Create a suitable constructor. The constructor of the **MultiBuyDiscountsEvaluator** class needs the _IEnumerable\<ShoppingCartItemInfo_ object. For example:

   ```csharp

   public CustomMultiBuyEvaluator(IEnumerable<ShoppingCartItemInfo> cartItems)
       : base(cartItems)
   { }

   ```

   ![](https://docs.kentico.com/docsassets/k10/customizing-evaluation-of-buy-x-get-y-discounts/creating_custommultibuyevaluator_class.png)
5. Override the methods you want to change. The **MultiBuyDiscountsEvaluator** class contains the following virtual methods:

   - **protected virtual void Reset()** – Resets the evaluator to its initial state.
   - **protected virtual void PrepareItemsForDiscount(IMultiBuyDiscount discount)** – Ensures that the shopping cart items from the **SortedItems** sorted list are prioritized according to the given Buy X Get Y discount.
     - _IMultiBuyDiscount discount_ – Discount which prioritizes the sorted shopping cart items.
   - **protected virtual bool FindItemsToBaseDiscountOn(IMultiBuyDiscount discount, IDictionary itemsToBaseDiscountOn)** – Finds items required by "buy" conditions of the given Buy X Get Y discount. Returns true if the found items fully satisfy discount requirement "buy" conditions.
     - _IMultiBuyDiscount discount_ – Discount with the specified required items.
     - _IDictionary itemsToBaseDiscountOn_ – Empty dictionary. Found items are placed here with their counts.
   - **protected virtual bool FindItemsToBeDiscounted(IMultiBuyDiscount discount, IDictionary itemsToBaseDiscountOn, IDictionary itemsToBeDiscounted)** – Finds items to be discounted with the given discount based on the given items. Returns true when at least one discounted item was found.
     - _IMultiBuyDiscount discount_ – Discount with the specific "get" conditions.
     - _IDictionary itemsToBaseDiscountOn_ – Shopping cart items used for requirement "buy" conditions.
     - _IDictionary itemsToBeDiscounted_ – Shopping cart items found to be discounted by this discount.
   - **protected virtual bool CanBeDiscounted(ShoppingCartItemInfo item, IMultiBuyDiscount discount, IDictionary basedOnItems, IDictionary alreadyDiscountedItems)** – Checks if the given item can be discounted using the given discount. Returns true if the given discount is applicable on the given items.
     - _ShoppingCartItemInfo item_ – Discounted item to be checked.
     - _IMultiBuyDiscount discount_ – Discount, which discounts the item, to be checked.
     - _IDictionary basedOnItems_ – Shopping cart items with the count of the items found as the satisfying items for the given discount.
     - _IDictionary alreadyDiscountedItems_ – Shopping cart items with the count of items already found as suitable for applying the discount for the given discount.
   - **protected virtual bool CanBaseDiscountOn(ShoppingCartItemInfo item, IMultiBuyDiscount discount, IDictionary alreadyBasedOnItems)** – Checks if the given item can be used to fulfill discount "buy" conditions. Returns true if the shopping cart contains enough units for the discount.
     - _ShoppingCartItemInfo item_ – Item to be checked.
     - _IMultiBuyDiscount discount_ – Discount to be checked.
     - _IDictionary alreadyBasedOnItems_ – Items with the count of the item units already found as fulfilling the requirements of the "buy" conditions of the given discount.
   - **protected virtual void ApplyDiscount(IMultiBuyDiscount discount, ShoppingCartItemInfo itemToBeDiscounted, int units)** – Applies the Buy X Get Y discount to the given number of units of the given shopping cart item.
     - _IMultiBuyDiscount discount_ – Discount which is applied.
     - _ShoppingCartItemInfo itemToBeDiscounted_ – Shopping cart item to apply the discount on.
     - _int units_ – Number of units to be discounted.
   - **protected virtual void RememberUsedItems(IEnumerable> discountingItemsCounts, IEnumerable> discountedItemsCounts)** – Makes a record that the given items in the given number of units were used for the given discount.
     - _IEnumerable> discountingItemsCounts_ – Shopping cart items used for the requirement "buy" conditions. The value is the number of units used for discount.
     - _IEnumerable> discountedItemsCounts_ – Shopping cart items used for the "get" conditions (the discounted items). The value is the number of units used for discount.
   - **protected virtual int GetUnusedNonFreeUnits(ShoppingCartItemInfo item)** – Returns the number of units not used for any discount (neither as the requirement "buy" conditions items nor as the "get" conditions items – the discounted items).
     - _ShoppingCartItemInfo item_ – Item to get number of units for.

> **Tip:** See the complete default source code [above](#source-code).

The Buy X Get Y discounts are now evaluated according to your changes.
