/// <summary>
 /// Initializes a new instance of the <see cref="AppliedDiscountApplication" /> class.
 /// </summary>
 /// <param name="discountApplication">Discount application.</param>
 /// <param name="value">The value of the discount application.</param>
 /// <param name="appliedQuantities">The applied quantities.</param>
 /// <param name="isDiscountLineGenerated">A value indicating whether the discount lines have been generated.</param>
 internal AppliedDiscountApplication(
     DiscountApplication discountApplication,
     decimal value,
     Dictionary <int, decimal> appliedQuantities,
     bool isDiscountLineGenerated)
     : this(
         discountApplication,
         value,
         appliedQuantities,
         decimal.Zero,
         null,
         isDiscountLineGenerated)
 {
 }
            internal bool CanCompound(DiscountApplication discountApplication)
            {
                bool canCompound = true;

                if (this.Discount is OfferDiscount)
                {
                    canCompound = !object.ReferenceEquals(this, discountApplication);
                }
                else
                {
                    canCompound = !string.Equals(this.Discount.OfferId, discountApplication.Discount.OfferId, StringComparison.OrdinalIgnoreCase);
                }

                return(canCompound);
            }
            internal void GetDiscountApplicationHonorQuantityForOneItem(
                List <DiscountApplication> discountApplications,
                int itemGroupIndex,
                HashSet <decimal> discountLineNumberSet,
                DiscountableItemGroup[] discountableItemGroups,
                decimal quantity,
                string discountCodeUsed)
            {
                if (quantity > decimal.Zero && discountLineNumberSet != null && discountLineNumberSet.Count > 0)
                {
                    decimal price = discountableItemGroups[itemGroupIndex].Price;

                    decimal discountLineNumberWithBestDeal = decimal.Zero;
                    decimal unitDiscountAmountBest         = decimal.Zero;

                    // Find the best deal from all discount lines.
                    foreach (decimal discountLineNumber in discountLineNumberSet)
                    {
                        RetailDiscountLine discountLineDefinition = this.DiscountLines[discountLineNumber];
                        decimal            unitDiscountAmount     = GetUnitDiscountAmount(discountLineDefinition, price);

                        if (unitDiscountAmount > unitDiscountAmountBest)
                        {
                            unitDiscountAmountBest         = unitDiscountAmount;
                            discountLineNumberWithBestDeal = discountLineNumber;
                        }
                    }

                    if (unitDiscountAmountBest > decimal.Zero)
                    {
                        RetailDiscountLine  discountLineDefinition = this.DiscountLines[discountLineNumberWithBestDeal];
                        DiscountApplication result = new DiscountApplication(this, applyStandalone: true)
                        {
                            // DISCOUNTPERF: replace list with array?
                            RetailDiscountLines = new List <RetailDiscountLineItem>(1)
                            {
                                new RetailDiscountLineItem(itemGroupIndex, discountLineDefinition)
                            },
                            DiscountCode = discountCodeUsed
                        };
                        result.ItemQuantities.Add(itemGroupIndex, quantity);
                        result.HonorQuantity = true;

                        discountApplications.Add(result);
                    }
                }
            }
            /// <summary>
            /// Initializes a new instance of the <see cref="AppliedDiscountApplication" /> class.
            /// </summary>
            /// <param name="discountApplication">Discount application.</param>
            /// <param name="value">The value of the discount application.</param>
            /// <param name="appliedQuantities">The applied quantities.</param>
            /// <param name="totalAmountForCoveredLines">Total amount for the covered lines.</param>
            /// <param name="itemPrices">Item prices.</param>
            /// <param name="isDiscountLineGenerated">A value indicating whether the discount lines have been generated.</param>
            internal AppliedDiscountApplication(
                DiscountApplication discountApplication,
                decimal value,
                Dictionary <int, decimal> appliedQuantities,
                decimal totalAmountForCoveredLines,
                decimal[] itemPrices,
                bool isDiscountLineGenerated)
            {
                this.DiscountApplication = discountApplication;

                this.Value = value;
                this.TotalAmountForCoveredLines = totalAmountForCoveredLines;
                this.ItemPrices = itemPrices;

                this.ItemGroupIndexToDiscountLineQuantitiesLookup = new Dictionary <int, IList <DiscountLineQuantity> >();
                this.ItemQuantities = new Dictionary <int, decimal>();
                this.ItemQuantities.AddRange(appliedQuantities);

                this.IsDiscountLineGenerated = isDiscountLineGenerated;
            }
            /// <summary>
            /// Gets all of the possible applications of this discount to the specified transaction and line items.
            /// </summary>
            /// <param name="transaction">The transaction to consider for discounts.</param>
            /// <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="priceContext">The pricing context to use.</param>
            /// <param name="appliedDiscounts">Applied discount application.</param>
            /// <param name="itemsWithOverlappingDiscounts">Items with overlapping discounts.</param>
            /// <param name="isInterrupted">A flag indicating whether it's interrupted for too many discount applications.</param>
            /// <returns>The possible permutations of line items that this discount can apply to, or an empty collection if this discount cannot apply.</returns>
            public override IEnumerable <DiscountApplication> GetDiscountApplications(
                SalesTransaction transaction,
                DiscountableItemGroup[] discountableItemGroups,
                decimal[] remainingQuantities,
                PriceContext priceContext,
                IEnumerable <AppliedDiscountApplication> appliedDiscounts,
                HashSet <int> itemsWithOverlappingDiscounts,
                out bool isInterrupted)
            {
                if (discountableItemGroups == null)
                {
                    throw new ArgumentNullException("discountableItemGroups");
                }

                if (remainingQuantities == null)
                {
                    throw new ArgumentNullException("remainingQuantities");
                }

                if (priceContext == null)
                {
                    throw new ArgumentNullException("priceContext");
                }

                List <DiscountApplication> discountAppliations = new List <DiscountApplication>();

                isInterrupted = false;

                // Get the discount code to use for any discount lines, if one is required.
                string discountCodeUsed = this.GetDiscountCodeForDiscount(transaction);

                for (int x = 0; x < discountableItemGroups.Length; x++)
                {
                    DiscountableItemGroup discountableItemGroup = discountableItemGroups[x];
                    HashSet <decimal>     discountLineNumberSet;
                    if (remainingQuantities[x] != 0M && this.ItemGroupIndexToDiscountLineNumberSetMap.TryGetValue(x, out discountLineNumberSet))
                    {
                        bool hasOverlap = this.HasOverlap(x, itemsWithOverlappingDiscounts);

                        foreach (decimal discountLineNumber in discountLineNumberSet)
                        {
                            RetailDiscountLine  line   = this.DiscountLines[discountLineNumber];
                            DiscountApplication result = new DiscountApplication(this, applyStandalone: !hasOverlap)
                            {
                                RetailDiscountLines = new List <RetailDiscountLineItem>(1)
                                {
                                    new RetailDiscountLineItem(x, line)
                                },
                                SortIndex    = GetSortIndexForRetailDiscountLine(line),
                                SortValue    = GetSortValue(line, discountableItemGroup),
                                DiscountCode = discountCodeUsed
                            };
                            result.ItemQuantities.AddRange(GetItemQuantitiesForDiscountApplication(x, discountableItemGroup.Quantity));

                            discountAppliations.Add(result);
                        }
                    }
                }

                return(discountAppliations);
            }
            public override AppliedDiscountApplication GetAppliedDiscountApplication(
                DiscountableItemGroup[] discountableItemGroups,
                decimal[] remainingQuantities,
                IEnumerable <AppliedDiscountApplication> appliedDiscounts,
                DiscountApplication discountApplication,
                PriceContext priceContext)
            {
                if (discountApplication == null || !discountApplication.RetailDiscountLines.Any() || discountableItemGroups == null || remainingQuantities == null)
                {
                    return(null);
                }

                decimal[] prices = new decimal[discountableItemGroups.Length];
                Dictionary <int, IList <DiscountLineQuantity> > discountDictionary = this.GetExistingDiscountDictionaryAndDiscountedPrices(
                    discountableItemGroups,
                    remainingQuantities,
                    appliedDiscounts,
                    discountApplication,
                    true,
                    true,
                    prices);

                RetailDiscountLineItem retailDiscountLineItem = discountApplication.RetailDiscountLines.ElementAt(0);

                DiscountOfferMethod discountMethod = (DiscountOfferMethod)retailDiscountLineItem.RetailDiscountLine.DiscountMethod;
                decimal             dealPrice      = decimal.Zero;

                decimal discountValue = decimal.Zero;
                decimal discountAmountForDiscountLine = decimal.Zero;

                switch (discountMethod)
                {
                case DiscountOfferMethod.DiscountAmount:
                    discountValue = retailDiscountLineItem.RetailDiscountLine.DiscountAmount;
                    discountAmountForDiscountLine = discountValue;
                    break;

                case DiscountOfferMethod.DiscountPercent:
                    discountValue = prices[retailDiscountLineItem.ItemIndex] * (retailDiscountLineItem.RetailDiscountLine.DiscountPercent / 100M);
                    break;

                case DiscountOfferMethod.OfferPrice:
                    dealPrice = retailDiscountLineItem.RetailDiscountLine.OfferPrice;
                    decimal bestExistingDealPrice = 0m;
                    bool    hasExistingDealPrice  = DiscountBase.TryGetBestExistingDealPrice(discountDictionary, retailDiscountLineItem.ItemIndex, out bestExistingDealPrice);

                    // We don't use discounted price here.
                    discountValue = DiscountBase.GetDiscountAmountFromDealPrice(discountableItemGroups[retailDiscountLineItem.ItemIndex].Price, hasExistingDealPrice, bestExistingDealPrice, dealPrice);
                    discountAmountForDiscountLine = discountValue;
                    break;

                default:
                    break;
                }

                // When has no competing discounts or compounded, apply all remaining quantity.
                bool    applyAllAvailableQuantity = (discountApplication.ApplyStandalone || this.CanCompound) && !discountApplication.HonorQuantity;
                decimal quantityToApply           = applyAllAvailableQuantity ?
                                                    remainingQuantities[retailDiscountLineItem.ItemIndex] : discountApplication.ItemQuantities[retailDiscountLineItem.ItemIndex];

                decimal result = discountValue * quantityToApply;

                AppliedDiscountApplication newAppliedDiscountApplication = null;

                if (result > decimal.Zero)
                {
                    Dictionary <int, decimal> itemQuantities;

                    if (applyAllAvailableQuantity)
                    {
                        itemQuantities = new Dictionary <int, decimal>();
                        itemQuantities[retailDiscountLineItem.ItemIndex] = quantityToApply;
                    }
                    else
                    {
                        itemQuantities = discountApplication.ItemQuantities;
                    }

                    newAppliedDiscountApplication = new AppliedDiscountApplication(discountApplication, result, itemQuantities, isDiscountLineGenerated: true);

                    DiscountLine discountLine = this.NewDiscountLine(discountApplication.DiscountCode, discountableItemGroups[retailDiscountLineItem.ItemIndex].ItemId);

                    discountLine.PeriodicDiscountType = PeriodicDiscountOfferType.Offer;
                    discountLine.DealPrice            = dealPrice;
                    discountLine.Amount     = discountAmountForDiscountLine;
                    discountLine.Percentage = retailDiscountLineItem.RetailDiscountLine.DiscountPercent;

                    newAppliedDiscountApplication.AddDiscountLine(retailDiscountLineItem.ItemIndex, new DiscountLineQuantity(discountLine, itemQuantities[retailDiscountLineItem.ItemIndex]));

                    if (discountApplication.RemoveItemsFromLookupsWhenApplied)
                    {
                        this.RemoveItemIndexGroupFromLookups(retailDiscountLineItem.ItemIndex);
                    }
                }

                return(newAppliedDiscountApplication);
            }
            public override IEnumerable <DiscountApplication> GetDiscountApplicationsNonOverlappedWithBestDeal(
                SalesTransaction transaction,
                DiscountableItemGroup[] discountableItemGroups,
                decimal[] remainingQuantities,
                PriceContext priceContext,
                IEnumerable <AppliedDiscountApplication> appliedDiscounts,
                HashSet <int> itemsWithOverlappingDiscounts,
                HashSet <int> itemsWithOverlappingDiscountsCompoundedOnly)
            {
                if (priceContext == null)
                {
                    throw new ArgumentNullException("priceContext");
                }

                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("itemsWithOverlappingDiscountsCompoundedOnly");
                }

                List <DiscountApplication> discountApplications = new List <DiscountApplication>();
                string discountCodeUsed = this.GetDiscountCodeForDiscount(transaction);

                foreach (KeyValuePair <int, HashSet <decimal> > pair in this.ItemGroupIndexToDiscountLineNumberSetMap)
                {
                    int  itemGroupIndex = pair.Key;
                    bool isOverlappedWithNonCompoundedDiscounts = this.IsItemIndexGroupOverlappedWithNonCompoundedDiscounts(
                        itemGroupIndex,
                        itemsWithOverlappingDiscounts,
                        itemsWithOverlappingDiscountsCompoundedOnly);

                    if (!isOverlappedWithNonCompoundedDiscounts)
                    {
                        HashSet <decimal> discountLineNumberSet = pair.Value;
                        decimal           quantity = remainingQuantities[itemGroupIndex];

                        if (quantity > decimal.Zero && discountLineNumberSet.Count == 1)
                        {
                            RetailDiscountLine  discountLineDefinition = this.DiscountLines[discountLineNumberSet.First()];
                            DiscountApplication result = new DiscountApplication(this, applyStandalone: true, removeItemsFromLookupsWhenApplied: true)
                            {
                                RetailDiscountLines = new List <RetailDiscountLineItem>(1)
                                {
                                    new RetailDiscountLineItem(itemGroupIndex, discountLineDefinition)
                                },
                                DiscountCode = discountCodeUsed
                            };
                            result.ItemQuantities.Add(itemGroupIndex, quantity);

                            discountApplications.Add(result);
                        }
                    }
                }

                return(discountApplications);
            }
Beispiel #8
0
 public ActionResult Delete_POST(DiscountApplication application)
 {
     _discountApplicationAdminService.Delete(application);
     return(RedirectToAction("Edit", "Discount", new { id = application.Discount.Id }));
 }
Beispiel #9
0
 public ActionResult Delete(DiscountApplication discountApplication)
 {
     return(View(discountApplication));
 }
Beispiel #10
0
 public RedirectToRouteResult Edit_POST(DiscountApplication application)
 {
     _discountApplicationAdminService.Update(application);
     return(RedirectToAction("Edit", "Discount", new { id = application.Discount.Id }));
 }
Beispiel #11
0
 public ViewResult Edit(DiscountApplication application)
 {
     return(View(application));
 }
Beispiel #12
0
 public RedirectToRouteResult Add(
     [IoCModelBinder(typeof(AddDiscountApplicationModelBinder))] DiscountApplication application)
 {
     _discountApplicationAdminService.Add(application);
     return(RedirectToAction("Edit", "Discount", new { id = application.Discount.Id }));
 }
Beispiel #13
0
 public void Delete(DiscountApplication application)
 {
     _session.Transact(session => session.Delete(application));
 }
Beispiel #14
0
 public void Update(DiscountApplication application)
 {
     _session.Transact(session => session.Update(application));
 }
Beispiel #15
0
 public void Add(DiscountApplication application)
 {
     _session.Transact(session => session.Save(application));
 }
Beispiel #16
0
 public abstract DiscountApplicationInfo Apply(DiscountApplication application, CartModel cart, CheckLimitationsResult checkLimitationsResult);