/// <summary>
        /// Calculates any product specific discounts for the basket
        /// </summary>
        /// <param name="basket">Basket to calculate discounts for</param>
        /// <param name="productsToExclude">Any products to exclude from discounting</param>
        /// <returns>The total discount applied to the basket</returns>
        private static LSDecimal CalculateProductDiscounts(Basket basket, List <int> productsToExclude)
        {
            LSDecimal totalDiscount = 0;

            //GET ALL PRODUCTS FROM THE BASKET THAT HAVE DISCOUNTS DIRECTLY ASSOCIATED
            //TODO: UPDATE THE METHOD BELOW TO FACTOR IN THE COMBINE VARIANTS IN LINE ITEM DISCOUNT MODE SETTING
            DiscountedBasketProduct[] discountedItems = DiscountCalculator.GetDiscountedBasketProducts(basket.BasketId, productsToExclude);
            if (discountedItems != null && discountedItems.Length > 0)
            {
                //EVALUATE EACH ITEM TO SEE WHETHER THE PRODUCT DISCOUNT APPLIES
                foreach (DiscountedBasketProduct discountedProduct in discountedItems)
                {
                    VolumeDiscount appliedDiscount = null;
                    LSDecimal      discountAmount  = -1;
                    //LOOP ALL AVAILABLE DISCOUNTS AND FIND THE BEST ONE
                    VolumeDiscountCollection availableDiscounts = VolumeDiscountDataSource.LoadForProduct(discountedProduct.ProductId);
                    foreach (VolumeDiscount testDiscount in availableDiscounts)
                    {
                        if (testDiscount.IsValidForUser(basket.User))
                        {
                            LSDecimal tempDiscountAmount = testDiscount.CalculateDiscount(discountedProduct.Quantity, discountedProduct.Quantity, discountedProduct.ExtendedPrice, discountedProduct.ExtendedPrice);
                            if (tempDiscountAmount > discountAmount)
                            {
                                discountAmount  = tempDiscountAmount;
                                appliedDiscount = testDiscount;
                            }
                        }
                    }

                    //CHECK WHETHER A DISCOUNT APPLIES TO THIS PRODUCT FOR THIS USER
                    if (appliedDiscount != null)
                    {
                        //productsToExclude.Add(discountedProduct.ProductId);
                        List <BasketItem> newItems = new List <BasketItem>();
                        //LOOP ALL ITEMS IN BASKET, CALCULATE DISCOUNT FOR MATCHING PRODUCTS
                        foreach (BasketItem basketItem in basket.Items)
                        {
                            if (basketItem.ProductId == discountedProduct.ProductId)
                            {
                                //THIS IS A DISCOUNTED PRODUCT, CREATE THE DISCOUNT LINE ITEM
                                discountAmount = appliedDiscount.CalculateDiscount(basketItem.Quantity, discountedProduct.Quantity, basketItem.ExtendedPrice, discountedProduct.ExtendedPrice);
                                if (discountAmount > 0)
                                {
                                    //DISCOUNT AMOUNT SHOULD NOT BE GREATER THEN PARENT ITEM TOTAL
                                    if (discountAmount > basketItem.ExtendedPrice)
                                    {
                                        discountAmount = basketItem.ExtendedPrice;
                                    }

                                    //DISCOUNT MUST BE ADJUSTED FOR OPTIONS THAT ARE GROUPED
                                    BasketItem discountLineItem = new BasketItem();
                                    discountLineItem.BasketId         = basket.BasketId;
                                    discountLineItem.OrderItemType    = OrderItemType.Discount;
                                    discountLineItem.ParentItemId     = basketItem.BasketItemId;
                                    discountLineItem.BasketShipmentId = basketItem.BasketShipmentId;
                                    discountLineItem.Name             = appliedDiscount.Name;
                                    discountLineItem.Sku       = appliedDiscount.VolumeDiscountId.ToString();
                                    discountLineItem.Price     = (-1 * discountAmount);
                                    discountLineItem.Quantity  = 1; // basketItem.Quantity;
                                    discountLineItem.TaxCodeId = basketItem.TaxCodeId;
                                    discountLineItem.Shippable = basketItem.Shippable;
                                    discountLineItem.Save();
                                    newItems.Add(discountLineItem);
                                    totalDiscount += discountAmount;
                                }
                            }
                        }
                        //ADD ANY NEW ITEMS TO THE BASKET COLLECTION
                        foreach (BasketItem basketItem in newItems)
                        {
                            basket.Items.Add(basketItem);
                        }

                        //AT LEAST ONE DISCOUNT WAS APPLICABLE TO THIS USER
                        //ADD THIS PRODUCT TO EXCLUDE LIST
                        if (productsToExclude.IndexOf(discountedProduct.ProductId) < 0)
                        {
                            productsToExclude.Add(discountedProduct.ProductId);
                        }
                    }
                }
            }
            return(totalDiscount);
        }
        private static LSDecimal Calculate_LineItemMode(Basket basket)
        {
            //KEEP TRACK OF TOTAL DISCOUNT APPLIED
            LSDecimal totalDiscount = 0;

            //CLEAR EXISTING DISCOUNTS FROM THE BASKET
            ClearExistingDiscounts(basket);

            //BUILD A LIST OF PRODUCTS THAT HAVE DISCOUNTS SPECIFICALLY APPLIED
            //THESE PRODUCTS CANNOT RECEIVE ANY FURTHER DISCOUNTS
            List <int> productsToExclude = new List <int>();

            //INITIALLY POPULATE THE LIST WITH ANY GIFT CERTIFICATE PRODUCT IS AS THESE
            //PRODUCTS ARE NOT ALLOWED TO BE DISCOUNTED
            productsToExclude.AddRange(DiscountCalculator.GetGiftCertificateProductIds(basket.BasketId));

            //CALCULATE THE PRODUCT LEVEL DISCOUNTS
            totalDiscount += CalculateProductDiscounts(basket, productsToExclude);

            //GET POTENTIAL PRODUCT DISCOUNTS
            Dictionary <int, List <PotentialDiscount> > potentialDiscounts = DiscountCalculator.GetPotentialDiscounts(basket, productsToExclude, GroupingMode.Product);

            //BUILD A QUANTITY AND VALUE LOOKUP TABLE, IF NEEDED TO COMBINE OPTIONS
            Dictionary <int, int>       productQuantityLookup = null;
            Dictionary <int, LSDecimal> productValueLookup    = null;
            bool combineVariantsInLineItemDiscountMode        = Store.GetCachedSettings().CombineVariantsInLineItemDiscountMode;

            if (combineVariantsInLineItemDiscountMode)
            {
                productQuantityLookup = new Dictionary <int, int>();
                productValueLookup    = new Dictionary <int, LSDecimal>();
                foreach (BasketItem bi in basket.Items)
                {
                    if (productQuantityLookup.ContainsKey(bi.ProductId))
                    {
                        productQuantityLookup[bi.ProductId] += bi.Quantity;
                        productValueLookup[bi.ProductId]    += bi.ExtendedPrice;
                    }
                    else
                    {
                        productQuantityLookup[bi.ProductId] = bi.Quantity;
                        productValueLookup[bi.ProductId]    = bi.ExtendedPrice;
                    }
                }
            }

            //LOOP BASKET ITEMS
            List <BasketItem> newItems = new List <BasketItem>();

            foreach (BasketItem bi in basket.Items)
            {
                //SEE WHETHER THIS PRODUCT HAS ANY POTENTIAL DISCOUNTS
                if (potentialDiscounts.ContainsKey(bi.ProductId))
                {
                    //GET THE POTENTIAL DISCOUNTS FOR THIS PRODUCT
                    List <PotentialDiscount> productDiscounts = potentialDiscounts[bi.ProductId];
                    //FIND THE BEST DISCOUNT
                    VolumeDiscount appliedDiscount = null;
                    LSDecimal      discountAmount  = -1;
                    for (int i = 0; i < productDiscounts.Count; i++)
                    {
                        VolumeDiscount tempDiscount = VolumeDiscountDataSource.Load(productDiscounts[i].VolumeDiscountId);
                        //DETERMINE THE QUANTITY USED TO CALCULATE DISCOUNT
                        int       totalProductQuantity;
                        LSDecimal totalProductValue;
                        if (combineVariantsInLineItemDiscountMode)
                        {
                            totalProductQuantity = productQuantityLookup[bi.ProductId];
                            totalProductValue    = productValueLookup[bi.ProductId];
                        }
                        else
                        {
                            totalProductQuantity = bi.Quantity;
                            totalProductValue    = bi.ExtendedPrice;
                        }
                        //CHECK THIS DISCOUNT AMOUNT
                        LSDecimal tempDiscountAmount = tempDiscount.CalculateDiscount(bi.Quantity, totalProductQuantity, bi.ExtendedPrice, totalProductValue);
                        //SEE WHETHER THIS CALCULATED DISCOUNT IS THE GREATEST VALUE
                        if (tempDiscountAmount > discountAmount)
                        {
                            //THIS DISCOUNT HAS A HIGHER VALUE THAN THE LAST TESTED
                            //MAKE THIS THE APPLIED DISCOUNT
                            discountAmount  = tempDiscountAmount;
                            appliedDiscount = tempDiscount;
                        }
                    }

                    //CREATE THE DISCOUNT ITEM IF REQUIRED
                    if (discountAmount > 0)
                    {
                        //DISCOUNT AMOUNT SHOULD NOT BE GREATER THEN PARENT ITEM TOTAL
                        if (discountAmount > bi.ExtendedPrice)
                        {
                            discountAmount = bi.ExtendedPrice;
                        }

                        BasketItem discountLineItem = new BasketItem();
                        discountLineItem.BasketId         = basket.BasketId;
                        discountLineItem.OrderItemType    = OrderItemType.Discount;
                        discountLineItem.ParentItemId     = bi.BasketItemId;
                        discountLineItem.BasketShipmentId = bi.BasketShipmentId;
                        discountLineItem.Name             = appliedDiscount.Name;
                        discountLineItem.Sku       = appliedDiscount.VolumeDiscountId.ToString();
                        discountLineItem.Price     = (-1 * discountAmount);
                        discountLineItem.Quantity  = 1; //bi.Quantity;
                        discountLineItem.TaxCodeId = bi.TaxCodeId;
                        discountLineItem.Shippable = bi.Shippable;
                        newItems.Add(discountLineItem);
                        totalDiscount += discountAmount;
                    }
                }
            }

            //ADD DISCOUNT ITEMS TO BASKET
            foreach (BasketItem bi in newItems)
            {
                basket.Items.Add(bi);
                bi.Save();
            }

            //RETURN THE TOTAL DISCOUNT APPLIED
            return(totalDiscount);
        }
        private static LSDecimal Calculate_GroupingMode(Basket basket)
        {
            //KEEP TRACK OF TOTAL DISCOUNT APPLIED
            LSDecimal totalDiscount = 0;

            //CLEAR EXISTING DISCOUNTS FROM THE BASKET
            ClearExistingDiscounts(basket);

            //BUILD A LIST OF PRODUCTS THAT HAVE DISCOUNTS SPECIFICALLY APPLIED
            //THESE PRODUCTS CANNOT RECEIVE ANY FURTHER DISCOUNTS
            List <int> productsToExclude = new List <int>();

            //INITIALLY POPULATE THE LIST WITH ANY GIFT CERTIFICATE PRODUCT IS AS THESE
            //PRODUCTS ARE NOT ALLOWED TO BE DISCOUNTED
            productsToExclude.AddRange(DiscountCalculator.GetGiftCertificateProductIds(basket.BasketId));

            //CALCULATE THE PRODUCT LEVEL DISCOUNTS
            totalDiscount += CalculateProductDiscounts(basket, productsToExclude);

            //GET POTENTIAL CATEGORY DISCOUNTS
            //THE DICTIONARY KEY WILL BE CATEGORY LEVEL
            //POTENTIAL DISCOUNTS ARE THOSE THAT COULD APPLY AT THE SAME SPECIFICITY
            //THE POTENTIAL DISCOUNTS ARE ORDERED FROM MOST SPECIFIC CATEGORY LEVEL TO LEAST
            Dictionary <int, List <PotentialDiscount> > potentialDiscounts = DiscountCalculator.GetPotentialDiscounts(basket, productsToExclude, GroupingMode.Category);

            //LOOP THE DISCOUNTED CATEGORIES
            //WE HAVE TO CHECK ALL POTENTIAL DISCOUNTS AT EACH CATEGORY LEVEL
            //TO DETERMINE WHICH ONE TO APPLY
            List <BasketItem> newItems = new List <BasketItem>();

            foreach (int categoryLevel in potentialDiscounts.Keys)
            {
                //IF WE FIND A DISCOUNT AT THIS LEVEL, IT SHOULD BE APPLIED, THEN WE
                //SHOULD RECHECK DISCOUNTS AT THIS LEVEL TO SEE IF ANY OTHERS APPLY
                bool       recheckThisLevel  = false;
                List <int> appliedCategories = new List <int>();
                do
                {
                    LSDecimal                appliedDiscountAmount = -1;
                    VolumeDiscount           appliedDiscount       = null;
                    int                      appliedCategoryId     = 0;
                    List <PotentialDiscount> discountGroup         = potentialDiscounts[categoryLevel];
                    foreach (PotentialDiscount pd in discountGroup)
                    {
                        if (appliedCategories.IndexOf(pd.CategoryId) < 0)
                        {
                            //GET ALL BASKET ITEMS ELIGIBLE FOR DISCOUNT IN THIS CATEGORY
                            BasketItemCollection eligibleItems = DiscountCalculator.GetCategoryItems(pd.CategoryId, basket, productsToExclude);
                            //TOTAL UP ITEMS FOR GROUPING MODE
                            int totalQuantity = eligibleItems.TotalQuantity();
                            if (totalQuantity > 0)
                            {
                                LSDecimal      totalPrice   = eligibleItems.TotalPrice();
                                VolumeDiscount tempDiscount = VolumeDiscountDataSource.Load(pd.VolumeDiscountId);
                                //JUST USE TOTALS TO DETERMINE OVERALL DISCOUNT
                                LSDecimal tempDiscountAmount = tempDiscount.CalculateDiscount(totalQuantity, totalQuantity, totalPrice, totalPrice);
                                if (tempDiscountAmount > appliedDiscountAmount)
                                {
                                    appliedDiscountAmount = tempDiscountAmount;
                                    appliedDiscount       = tempDiscount;
                                    appliedCategoryId     = pd.CategoryId;
                                }
                            }
                        }
                    }

                    //SEE WHETHER WE FOUND A DISCOUNT AT THIS LEVEL
                    if (appliedDiscount != null)
                    {
                        //GET ALL BASKET ITEMS ELIGIBLE FOR DISCOUNT IN THIS CATEGORY
                        BasketItemCollection eligibleItems = DiscountCalculator.GetCategoryItems(appliedCategoryId, basket, productsToExclude);
                        //TOTAL UP ITEMS FOR GROUPING MODE
                        int       totalQuantity = eligibleItems.TotalQuantity();
                        LSDecimal totalPrice    = eligibleItems.TotalPrice();
                        //LOOP ALL BASKET ITEMS TO ADD DISCOUNTS TO BASKET
                        foreach (BasketItem bi in eligibleItems)
                        {
                            LSDecimal discountAmount = appliedDiscount.CalculateDiscount(bi.Quantity, totalQuantity, bi.ExtendedPrice, totalPrice);
                            if (discountAmount > 0)
                            {
                                //DISCOUNT AMOUNT SHOULD NOT BE GREATER THEN PARENT ITEM TOTAL
                                if (discountAmount > bi.ExtendedPrice)
                                {
                                    discountAmount = bi.ExtendedPrice;
                                }

                                //DISCOUNT MUST BE ADJUSTED FOR OPTIONS THAT ARE GROUPED
                                BasketItem discountLineItem = new BasketItem();
                                discountLineItem.BasketId         = basket.BasketId;
                                discountLineItem.OrderItemType    = OrderItemType.Discount;
                                discountLineItem.ParentItemId     = bi.BasketItemId;
                                discountLineItem.BasketShipmentId = bi.BasketShipmentId;
                                discountLineItem.Name             = appliedDiscount.Name;
                                discountLineItem.Sku       = appliedDiscount.VolumeDiscountId.ToString();
                                discountLineItem.Price     = (-1 * discountAmount);
                                discountLineItem.Quantity  = 1; //bi.Quantity;
                                discountLineItem.TaxCodeId = bi.TaxCodeId;
                                discountLineItem.Shippable = bi.Shippable;
                                basket.Items.Add(discountLineItem);
                                discountLineItem.Save();
                                totalDiscount += discountAmount;
                            }
                            if (productsToExclude.IndexOf(bi.ProductId) < 0)
                            {
                                productsToExclude.Add(bi.ProductId);
                            }
                        }
                    }
                    //IF A CATEGORY DISCOUNT WAS APPLIED
                    //AND THERE IS MORE THAN ONE DISCOUNT AT THIS LEVEL,
                    //WE MUST RECHECK THIS LEVEL
                    recheckThisLevel = ((appliedCategoryId > 0) && (potentialDiscounts[categoryLevel].Count > 1));
                } while (recheckThisLevel);
            }

            //ADD DISCOUNT ITEMS TO BASKET
            foreach (BasketItem bi in newItems)
            {
                basket.Items.Add(bi);
                bi.Save();
            }

            //RETURN THE CALCULATED DISCOUNT
            return(totalDiscount);
        }