/// <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); }