/// <summary> /// Compare two estimates. /// </summary> /// <param name="x">First estimate.</param> /// <param name="y">Second estimate.</param> /// <returns>Comparison result.</returns> internal static int Compare(DiscountDealEstimate x, DiscountDealEstimate y) { if (x != null && y != null) { return(x.MarginalDiscountAmountPerOverlappedQuantity.CompareTo(y.MarginalDiscountAmountPerOverlappedQuantity)); } else if (y == null) { return(1); } return(-1); }
private static DiscountDealEstimate BuildEstimates( Dictionary <string, DiscountDealEstimate> offerIdToEstimateNonCompoundedLookupHolder, List <DiscountBase> compoundedDiscountsHolder, DiscountDealEstimate existingCombinedEstimatesForCompounded, Dictionary <string, DiscountBase> discounts, DiscountableItemGroup[] discountableItemGroups, decimal[] remainingQuantities, decimal[] remainingQuantitiesForCompound, HashSet <int> itemsWithOverlappingDiscounts, HashSet <int> itemsWithOverlappingDiscountsCompoundedOnly) { using (SimpleProfiler profiler = new SimpleProfiler("OverlappedDiscounts.BuildEstimates", 2)) { DiscountDealEstimate combinedEstimateForCompounded = existingCombinedEstimatesForCompounded; foreach (KeyValuePair <string, DiscountBase> pair in discounts) { DiscountBase discount = pair.Value; DiscountDealEstimate estimate = discount.GetDiscountDealEstimate( discountableItemGroups, discount.CanCompound ? remainingQuantitiesForCompound : remainingQuantities, itemsWithOverlappingDiscounts, itemsWithOverlappingDiscountsCompoundedOnly); if (discount.CanCompound) { if (combinedEstimateForCompounded == null) { combinedEstimateForCompounded = estimate; } else { combinedEstimateForCompounded = DiscountDealEstimate.Combine(combinedEstimateForCompounded, estimate); } compoundedDiscountsHolder.Add(discount); } else { offerIdToEstimateNonCompoundedLookupHolder[discount.OfferId] = estimate; } } // returns combined estimate for compounded return(combinedEstimateForCompounded); } }
internal static DiscountDealEstimate Combine(DiscountDealEstimate x, DiscountDealEstimate y) { if (!x.CanCompound || !y.CanCompound) { return(x); } Dictionary <int, decimal> combinedLookup = new Dictionary <int, decimal>(x.ItemGroupIndexToQuantityNeededFromOverlappedLookup); foreach (KeyValuePair <int, decimal> pair in y.ItemGroupIndexToQuantityNeededFromOverlappedLookup) { int itemGroupIndex = pair.Key; decimal quantity = pair.Value; decimal existingQuantity = decimal.Zero; // Takes the max quantity of the two. if (combinedLookup.TryGetValue(itemGroupIndex, out existingQuantity)) { combinedLookup[itemGroupIndex] = Math.Max(quantity, existingQuantity); } else { combinedLookup[itemGroupIndex] = quantity; } } return(new DiscountDealEstimate( true, string.Empty, x.TotalApplicableQuantityWithOverlapped + y.TotalApplicableQuantityWithOverlapped, x.TotalDiscountAmountWithOverlapped + y.TotalDiscountAmountWithOverlapped, x.TotalApplicableQuantityWithoutOverlapped + y.TotalApplicableQuantityWithoutOverlapped, x.TotalDiscountAmountWithoutOverlapped + y.TotalDiscountAmountWithoutOverlapped, combinedLookup)); }
internal DiscountBase[] GetSortedDiscountsToApplyInFastMode( DiscountableItemGroup[] discountableItemGroups, decimal[] remainingQuantities, decimal[] remainingQuantitiesForCompound, HashSet <int> itemsWithOverlappingDiscounts, HashSet <int> itemsWithOverlappingDiscountsCompoundedOnly) { using (SimpleProfiler profiler = new SimpleProfiler("OverlappedDiscounts.GetDiscountsToApplyInFastMode", 2)) { Dictionary <string, DiscountDealEstimate> offerIdToEstimateNonCompoundedLookup = new Dictionary <string, DiscountDealEstimate>(StringComparer.OrdinalIgnoreCase); // Consolidate all compounded discounts into one estimate, to be sorted with the rest later. List <DiscountBase> compoundedDiscounts = new List <DiscountBase>(); DiscountDealEstimate combinedEstimateForCompounded = null; // Build estimates for offer discounts. combinedEstimateForCompounded = OverlapppedDiscounts.BuildEstimates( offerIdToEstimateNonCompoundedLookup, compoundedDiscounts, combinedEstimateForCompounded, this.OfferDiscounts, discountableItemGroups, remainingQuantities, remainingQuantitiesForCompound, itemsWithOverlappingDiscounts, itemsWithOverlappingDiscountsCompoundedOnly); // Build estimates for mix and match and quantity discounts. combinedEstimateForCompounded = OverlapppedDiscounts.BuildEstimates( offerIdToEstimateNonCompoundedLookup, compoundedDiscounts, combinedEstimateForCompounded, this.MixAndMatchAndQuantityDiscounts, discountableItemGroups, remainingQuantities, remainingQuantitiesForCompound, itemsWithOverlappingDiscounts, itemsWithOverlappingDiscountsCompoundedOnly); List <DiscountDealEstimate> estimatedSorted = new List <DiscountDealEstimate>(offerIdToEstimateNonCompoundedLookup.Values); if (combinedEstimateForCompounded != null) { estimatedSorted.Add(combinedEstimateForCompounded); } estimatedSorted.Sort(DiscountDealEstimate.GetComparison()); #if DEBUG foreach (DiscountDealEstimate estimate in estimatedSorted) { estimate.DebugDisplay(); } #endif DiscountBase[] discountsSorted = new DiscountBase[this.MixAndMatchAndQuantityDiscounts.Count + this.OfferDiscounts.Count]; int discountIndex = 0; for (int i = estimatedSorted.Count - 1; i >= 0; i--) { DiscountDealEstimate estimate = estimatedSorted[i]; if (estimate.CanCompound) { for (int compoundedIndex = 0; compoundedIndex < compoundedDiscounts.Count; compoundedIndex++) { discountsSorted[discountIndex] = compoundedDiscounts[compoundedIndex]; discountIndex++; } } else { DiscountBase discount = null; if (this.MixAndMatchAndQuantityDiscounts.TryGetValue(estimate.OfferId, out discount)) { discountsSorted[discountIndex] = discount; discountIndex++; } else if (this.OfferDiscounts.TryGetValue(estimate.OfferId, out discount)) { discountsSorted[discountIndex] = discount; discountIndex++; } } } return(discountsSorted); } }
/// <summary> /// Gets the discount deal estimate. /// </summary> /// <param name="discountableItemGroups">The valid sales line items on the transaction to consider.</param> /// <param name="remainingQuantities">The remaining quantities of each of the sales lines to consider.</param> /// <param name="itemsWithOverlappingDiscounts">Items with overlapping discounts.</param> /// <param name="itemsWithOverlappingDiscountsCompoundedOnly">Hast set of overlapped item group indices, compounded only.</param> /// <returns>Discount deal estimate.</returns> protected internal override DiscountDealEstimate GetDiscountDealEstimate( DiscountableItemGroup[] discountableItemGroups, decimal[] remainingQuantities, HashSet <int> itemsWithOverlappingDiscounts, HashSet <int> itemsWithOverlappingDiscountsCompoundedOnly) { if (discountableItemGroups == null) { throw new ArgumentNullException("discountableItemGroups"); } if (remainingQuantities == null) { throw new ArgumentNullException("remainingQuantities"); } if (itemsWithOverlappingDiscounts == null) { throw new ArgumentNullException("itemsWithOverlappingDiscounts"); } if (itemsWithOverlappingDiscountsCompoundedOnly == null) { throw new ArgumentNullException("itemsWithOverlappingDiscounts"); } decimal totalApplicableQuantityWithOverlapped = decimal.Zero; decimal totalDiscountAmountWithOverlapped = decimal.Zero; decimal totalApplicableQuantityWithoutOverlapped = decimal.Zero; decimal totalDiscountAmountWithoutOverlapped = decimal.Zero; Dictionary <int, decimal> itemGroupIndexToQuantityNeededFromOverlappedLookup = new Dictionary <int, decimal>(); foreach (KeyValuePair <int, HashSet <decimal> > pair in this.ItemGroupIndexToDiscountLineNumberSetMap) { int itemGroupIndex = pair.Key; HashSet <decimal> discountLineNumberSet = pair.Value; decimal quantity = remainingQuantities[itemGroupIndex]; if (quantity > decimal.Zero) { decimal price = discountableItemGroups[itemGroupIndex].Price; decimal unitDiscountAmount = decimal.Zero; if (discountLineNumberSet.Any()) { unitDiscountAmount = GetUnitDiscountAmount(this.DiscountLines[discountLineNumberSet.First()], price); } decimal effectiveDiscountAmount = unitDiscountAmount * quantity; totalApplicableQuantityWithOverlapped += quantity; totalDiscountAmountWithOverlapped += effectiveDiscountAmount; if (this.IsItemIndexGroupOverlappedWithNonCompoundedDiscounts(itemGroupIndex, itemsWithOverlappingDiscounts, itemsWithOverlappingDiscountsCompoundedOnly)) { itemGroupIndexToQuantityNeededFromOverlappedLookup[itemGroupIndex] = quantity; } else { totalApplicableQuantityWithoutOverlapped += quantity; totalDiscountAmountWithoutOverlapped += effectiveDiscountAmount; } } } DiscountDealEstimate estimate = new DiscountDealEstimate( this.CanCompound, this.OfferId, totalApplicableQuantityWithOverlapped, totalDiscountAmountWithOverlapped, totalApplicableQuantityWithoutOverlapped, totalDiscountAmountWithoutOverlapped, itemGroupIndexToQuantityNeededFromOverlappedLookup); return(estimate); }