public void Can_limit_only_relative_rewards(int amount, int maxLimit, decimal expected, AmountType type)
        {
            var currency = new Currency(new Language("en-US"), "USD (840)");
            var money    = new Money(100m, currency);
            //Arrange
            var reward = new PromotionReward()
            {
                Amount     = amount,
                AmountType = type,
                MaxLimit   = maxLimit,
                Promotion  = new Promotion()
                {
                    Id          = "id",
                    Description = ""
                }
            };

            //Act
            var discount = new Discount();

            discount = reward.ToDiscountModel(money);

            //Assert
            var expectedAmount = new Money(expected, currency);

            Assert.Equal(discount.Amount, expectedAmount);
        }
Example #2
0
        public static PromotionReward ToPromotionReward(this marketingDto.PromotionReward rewardDto, Currency currency)
        {
            var result = new PromotionReward
            {
                CategoryId           = rewardDto.CategoryId,
                Coupon               = rewardDto.Coupon,
                Description          = rewardDto.Description,
                IsValid              = rewardDto.IsValid ?? false,
                LineItemId           = rewardDto.LineItemId,
                MeasureUnit          = rewardDto.MeasureUnit,
                ProductId            = rewardDto.ProductId,
                PromotionId          = rewardDto.PromotionId,
                Quantity             = rewardDto.Quantity ?? 0,
                MaxLimit             = (decimal)(rewardDto.MaxLimit ?? 0),
                Amount               = (decimal)(rewardDto.Amount ?? 0),
                AmountType           = EnumUtility.SafeParse(rewardDto.AmountType, AmountType.Absolute),
                CouponAmount         = new Money(rewardDto.CouponAmount ?? 0, currency),
                CouponMinOrderAmount = new Money(rewardDto.CouponMinOrderAmount ?? 0, currency),
                Promotion            = rewardDto.Promotion.ToPromotion(),
                RewardType           = EnumUtility.SafeParse(rewardDto.RewardType, PromotionRewardType.CatalogItemAmountReward),
                ShippingMethodCode   = rewardDto.ShippingMethod,
                ConditionalProductId = rewardDto.ConditionalProductId,
                ForNthQuantity       = rewardDto.ForNthQuantity,
                InEveryNthQuantity   = rewardDto.InEveryNthQuantity,
            };

            return(result);
        }
        private void AddDiscountToEntity(StorageEntity entity, PromotionReward reward, decimal discountAmount)
        {
            var orderForm = entity as OrderForm;
            var lineItem  = entity as LineItem;
            var shipment  = entity as Shipment;

            if (orderForm != null && reward is CartSubtotalReward)
            {
                var discount = orderForm.Discounts.FirstOrDefault(x => x.PromotionId == reward.PromotionId);
                if (discount == null)
                {
                    discount = new OrderFormDiscount
                    {
                        PromotionId    = reward.PromotionId,
                        DiscountName   = reward.Promotion.Name,
                        DisplayMessage = reward.Promotion.Description,
                        OrderFormId    = orderForm.OrderFormId,
                        DiscountCode   = reward.Promotion.Coupon != null ? reward.Promotion.Coupon.Code : null
                    };
                    orderForm.Discounts.Add(discount);
                }
                discount.DiscountAmount += discountAmount;
            }
            else if (lineItem != null && reward is CatalogItemReward)
            {
                var discount = lineItem.Discounts.FirstOrDefault(x => x.PromotionId == reward.PromotionId);
                if (discount == null)
                {
                    discount = new LineItemDiscount
                    {
                        PromotionId    = reward.PromotionId,
                        DiscountName   = reward.Promotion.Name,
                        DisplayMessage = reward.Promotion.Description,
                        LineItemId     = lineItem.LineItemId,
                        DiscountCode   = reward.Promotion.Coupon != null ? reward.Promotion.Coupon.Code : null
                    };
                    lineItem.Discounts.Add(discount);
                }
                discount.DiscountAmount += discountAmount;
            }
            else if (shipment != null && reward is ShipmentReward)
            {
                var discount = shipment.Discounts.FirstOrDefault(x => x.PromotionId == reward.PromotionId);
                if (discount == null)
                {
                    discount = new ShipmentDiscount
                    {
                        PromotionId    = reward.PromotionId,
                        DiscountName   = reward.Promotion.Name,
                        DisplayMessage = reward.Promotion.Description,
                        ShipmentId     = shipment.ShipmentId,
                        DiscountCode   = reward.Promotion.Coupon != null ? reward.Promotion.Coupon.Code : null
                    };
                    shipment.Discounts.Add(discount);
                }
                discount.DiscountAmount += discountAmount;
            }
        }
Example #4
0
 /// <summary>
 /// Adds the promotion item record.
 /// </summary>
 /// <param name="reward">The reward.</param>
 /// <param name="entriesCollection">The entries collection.</param>
 /// <returns></returns>
 public void AddPromotionItemRecord(PromotionReward reward, params PromotionEntriesSet[] entrySetsCollection)
 {
     foreach (PromotionEntriesSet entrySet in entrySetsCollection)
     {
         PromotionItemRecord record = new PromotionItemRecord(this.PromotionContext.TargetEntriesSet, entrySet, reward);
         record.PromotionItem = PromotionContext.CurrentPromotion;
         PromotionContext.PromotionResult.PromotionRecords.Add(record);
     }
 }
Example #5
0
        public PromotionReward[] GetRewards()
        {
            var retVal = new PromotionReward[] { };

            if (Children != null)
            {
                retVal = Children.OfType <IRewardExpression>().SelectMany(x => x.GetRewards()).ToArray();
            }
            return(retVal);
        }
Example #6
0
        public static PromotionReward ToWebModel(this VirtoCommerceMarketingModuleWebModelPromotionReward serviceModel, Currency currency)
        {
            var webModel = new PromotionReward();

            webModel.InjectFrom<NullableAndEnumValueInjecter>(serviceModel);

            webModel.Amount = new Money(serviceModel.Amount ?? 0, currency.Code);
            webModel.AmountType = EnumUtility.SafeParse(serviceModel.AmountType, AmountType.Absolute);
            webModel.CouponAmount = new Money(serviceModel.CouponAmount ?? 0, currency.Code);
            webModel.CouponMinOrderAmount = new Money(serviceModel.CouponMinOrderAmount ?? 0, currency.Code);
            webModel.Promotion = serviceModel.Promotion.ToWebModel();
            webModel.RewardType = EnumUtility.SafeParse(serviceModel.RewardType, PromotionRewardType.CatalogItemAmountReward);

            return webModel;
        }
        public static PromotionReward ToWebModel(this MarketingModule.Client.Model.PromotionReward serviceModel, Currency currency)
        {
            var webModel = new PromotionReward();

            webModel.InjectFrom <NullableAndEnumValueInjecter>(serviceModel);

            webModel.Amount               = (decimal)(serviceModel.Amount ?? 0);
            webModel.AmountType           = EnumUtility.SafeParse(serviceModel.AmountType, AmountType.Absolute);
            webModel.CouponAmount         = new Money(serviceModel.CouponAmount ?? 0, currency);
            webModel.CouponMinOrderAmount = new Money(serviceModel.CouponMinOrderAmount ?? 0, currency);
            webModel.Promotion            = serviceModel.Promotion.ToWebModel();
            webModel.RewardType           = EnumUtility.SafeParse(serviceModel.RewardType, PromotionRewardType.CatalogItemAmountReward);
            webModel.ShippingMethodCode   = serviceModel.ShippingMethod;

            return(webModel);
        }
        /// <summary>
        /// Gets the discount amount for one entry only.
        /// </summary>
        /// <param name="record">The record.</param>
        /// <param name="reward">The reward.</param>
        /// <returns></returns>
        private static decimal GetDiscountAmount(PromotionItemRecord record, PromotionReward reward)
        {
            decimal discountAmount = 0;

            if (reward.RewardType == PromotionRewardType.EachAffectedEntry || reward.RewardType == PromotionRewardType.AllAffectedEntries)
            {
                if (reward.AmountType == PromotionRewardAmountType.Percentage)
                {
                    discountAmount = record.AffectedEntriesSet.TotalCost * reward.AmountOff / 100;
                }
                else // need to split discount between all items
                {
                    discountAmount += reward.AmountOff; // since we assume only one entry in affected items
                }
            }
            return(Math.Round(discountAmount, 2));
        }
Example #9
0
        public void TestToDiscountModel()
        {
            //Arrange
            var samplePromotion = new Promotion {
                Id = "PromotionId", Description = "Description"
            };
            var promotionReward = new PromotionReward {
                Amount = 10m, AmountType = AmountType.Relative, Quantity = 1, IsValid = true, Promotion = samplePromotion
            };
            var currency = new Currency(Language.InvariantLanguage, "USD");

            //Act
            var discount = promotionReward.ToDiscountModel(new Money(4.6m, currency), 1);

            //Assert
            Assert.Equal(0.46m, discount.Amount.Amount);
        }
        private decimal GetDiscountAmount(PromotionItemRecord record, PromotionReward reward)
        {
            decimal discountAmount = 0;

            if (reward.RewardType != PromotionRewardType.EachAffectedEntry && reward.RewardType != PromotionRewardType.AllAffectedEntries)
            {
                return(Math.Round(discountAmount, 2));
            }
            if (reward.AmountType == PromotionRewardAmountType.Percentage)
            {
                discountAmount = record.AffectedEntriesSet.TotalCost * reward.AmountOff / 100;
            }
            else
            {
                discountAmount += reward.AmountOff;
            }
            return(Math.Round(discountAmount, 2));
        }
Example #11
0
        public static Discount ToDiscountWebModel(this PromotionReward reward, decimal amount, int quantity, Currency currency)
        {
            var discount = new Discount();

            decimal absoluteDiscountAmount = 0;
            if (reward.AmountType == AmountType.Absolute)
            {
                absoluteDiscountAmount = reward.Amount.Amount * quantity;
            }
            if (reward.AmountType == AmountType.Relative)
            {
                absoluteDiscountAmount = (amount * quantity * reward.Amount.Amount) / 100;
            }

            discount.Amount = new Money(absoluteDiscountAmount, currency.Code);
            discount.Description = reward.Promotion.Description;
            discount.PromotionId = reward.Promotion.Id;
            discount.Type = reward.RewardType;

            return discount;
        }
Example #12
0
        public virtual PromotionReward ToPromotionReward(marketingDto.PromotionReward serviceModel, Currency currency)
        {
            var result = new PromotionReward();

            result.CategoryId  = serviceModel.CategoryId;
            result.Coupon      = serviceModel.Coupon;
            result.Description = serviceModel.Description;
            result.IsValid     = serviceModel.IsValid ?? false;
            result.LineItemId  = serviceModel.LineItemId;
            result.MeasureUnit = serviceModel.MeasureUnit;
            result.ProductId   = serviceModel.ProductId;
            result.PromotionId = serviceModel.PromotionId;
            result.Quantity    = serviceModel.Quantity ?? 0;

            result.Amount               = (decimal)(serviceModel.Amount ?? 0);
            result.AmountType           = EnumUtility.SafeParse(serviceModel.AmountType, AmountType.Absolute);
            result.CouponAmount         = new Money(serviceModel.CouponAmount ?? 0, currency);
            result.CouponMinOrderAmount = new Money(serviceModel.CouponMinOrderAmount ?? 0, currency);
            result.Promotion            = serviceModel.Promotion.ToWebModel();
            result.RewardType           = EnumUtility.SafeParse(serviceModel.RewardType, PromotionRewardType.CatalogItemAmountReward);
            result.ShippingMethodCode   = serviceModel.ShippingMethod;

            return(result);
        }
Example #13
0
        protected virtual void EvaluateReward(PromotionEvaluationContext promoContext, bool couponIsValid, PromotionReward reward)
        {
            reward.Promotion = this;
            reward.IsValid   = couponIsValid && (DynamicExpression?.IsSatisfiedBy(promoContext) ?? false);

            //Set productId for catalog item reward
            if (reward is CatalogItemAmountReward catalogItemReward && catalogItemReward.ProductId == null)
            {
                catalogItemReward.ProductId = promoContext.PromoEntry.ProductId;
            }
        }
 private decimal GetDiscountAmount(PromotionItemRecord record, PromotionReward reward)
 {
     decimal discountAmount = 0;
     if (reward.RewardType != PromotionRewardType.EachAffectedEntry && reward.RewardType != PromotionRewardType.AllAffectedEntries)
     {
         return Math.Round(discountAmount, 2);
     }
     if (reward.AmountType == PromotionRewardAmountType.Percentage)
     {
         discountAmount = record.AffectedEntriesSet.TotalCost * reward.AmountOff / 100;
     }
     else
     {
         discountAmount += reward.AmountOff;
     }
     return Math.Round(discountAmount, 2);
 }
Example #15
0
        protected virtual void EvaluateReward(PromotionEvaluationContext promoContext, bool couponIsValid, PromotionReward reward)
        {
            reward.Promotion = this;
            reward.IsValid   = couponIsValid && Condition(promoContext);

            //Set productId for catalog item reward
            if (reward is CatalogItemAmountReward catalogItemReward && catalogItemReward.ProductId == null)
            {
                catalogItemReward.ProductId = promoContext.PromoEntry.ProductId;
            }
        }
Example #16
0
        /// <summary>
        /// Evaluates the promotions.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="promotions">The promotions.</param>
        /// <param name="filter">The filter.</param>
        /// <returns>Collection of promotions that were applied. Look inside PromotionContext for actual rewards and for items
        /// that have been affected by these promotions.</returns>
        public PromotionItemCollection EvaluatePromotions(PromotionContext context, PromotionItemCollection promotions, PromotionFilter filter)
        {
            // Start checking discounts
            List <int> rowIndexes = new List <int>();

            // Retrieve all the coupons customer entered
            List <string> coupons = context.Coupons;

            // Retrieve customer segments, it should be initialized beforehand
            List <int> segments = context.Segments;

            // Retrieve customer id, it should be initialized beforehand
            Guid customerId = context.CustomerId;

            foreach (PromotionItem item in promotions)
            {
                // Set currently executed promotion
                context.CurrentPromotion = item;

                // If discount is global and other discount has been applied already, skip it
                if (item.DataRow.ExclusivityType.Equals(ExclusionType.GlobalLevel) && rowIndexes.Count != 0)
                {
                    continue;
                }

                // Check if it belongs to a group specified
                if (!String.IsNullOrEmpty(context.TargetGroup) && !context.TargetGroup.Equals(item.DataRow.PromotionGroup, StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }

                // Check group exclusivity
                if (context.ExclusiveGroups.Contains(item.DataRow.PromotionGroup))
                {
                    continue;
                }

                // Check limits here
                int currentOrderPromotionCount = context.PromotionResult.GetCount(item.DataRow.PromotionId);

                // Start with per order limit
                if (item.DataRow.PerOrderLimit > 0 && currentOrderPromotionCount >= item.DataRow.PerOrderLimit)
                {
                    continue;
                }

                // Check application limit limit, only check when limit is set
                if (item.DataRow.ApplicationLimit > 0 && item.TotalUsedCount - context.ReservedCount + currentOrderPromotionCount >= item.DataRow.ApplicationLimit)
                {
                    continue;
                }

                // Check customer limit, only check when limit is set and customer id is set
                if ((item.DataRow.CustomerLimit > 0 && customerId == Guid.Empty) || item.DataRow.CustomerLimit > 0 && item.GetCustomerUsageCount(customerId) - context.ReservedCount + currentOrderPromotionCount >= item.DataRow.CustomerLimit)
                {
                    continue;
                }

                // First do simply checks that will take little time and save us from wasting processor time
                // -----------------------------------------------------------------------------------------
                if (!filter.IncludeInactive)
                {
                    // Skip if start date is past now
                    if (!item.Campaign.IsActive)
                    {
                        continue;
                    }

                    if (item.Campaign.StartDate > FrameworkContext.Current.CurrentDateTime)
                    {
                        continue;
                    }

                    // Skip if end date is in the past
                    if (item.Campaign.EndDate < FrameworkContext.Current.CurrentDateTime)
                    {
                        continue;
                    }

                    // Skip if start date is past now
                    if (item.DataRow.StartDate > FrameworkContext.Current.CurrentDateTime)
                    {
                        continue;
                    }

                    // Skip if end date is in the past
                    if (item.DataRow.EndDate < FrameworkContext.Current.CurrentDateTime)
                    {
                        continue;
                    }

                    // Check promotion status
                    if (!item.DataRow.Status.Equals(PromotionStatus.Active))
                    {
                        continue;
                    }
                }

                if (!filter.IncludeCoupons)
                {
                    // Check coupons
                    string couponCode = item.DataRow.CouponCode;
                    if (!String.IsNullOrEmpty(couponCode))
                    {
                        if (coupons == null)
                        {
                            continue;
                        }

                        bool foundCoupon = false;
                        foreach (string coupon in coupons)
                        {
                            if (couponCode.Equals(coupon, StringComparison.OrdinalIgnoreCase))
                            {
                                foundCoupon = true;
                            }
                        }

                        if (!foundCoupon)
                        {
                            continue;
                        }
                    }
                }

                // Check catalog / node / entry filtering
                if (context.TargetEntriesSet.Entries.Count > 0)
                {
                    bool isValid = true;
                    foreach (PromotionEntry entry in context.TargetEntriesSet.Entries)
                    {
                        string catalogEntryId = entry.CatalogEntryCode;
                        string catalogNodeId  = entry.CatalogNodeCode;
                        string catalogName    = entry.CatalogName;

                        if (!String.IsNullOrEmpty(catalogEntryId) || !String.IsNullOrEmpty(catalogNodeId) || !String.IsNullOrEmpty(catalogName))
                        {
                            PromotionDto.PromotionConditionRow[] conditions = item.DataRow.GetPromotionConditionRows();
                            if (conditions != null && conditions.Length > 0)
                            {
                                foreach (PromotionDto.PromotionConditionRow condition in conditions)
                                {
                                    if (!String.IsNullOrEmpty(catalogEntryId) && !condition.IsCatalogEntryIdNull() && !condition.CatalogEntryId.Equals(catalogEntryId))
                                    {
                                        isValid = false;
                                        break;
                                    }
                                    else if (!String.IsNullOrEmpty(catalogNodeId) && !condition.IsCatalogNodeIdNull() && !condition.CatalogNodeId.Equals(catalogNodeId))
                                    {
                                        isValid = false;
                                        break;
                                    }
                                    else if (!String.IsNullOrEmpty(catalogName) && !condition.IsCatalogNameNull() && !condition.CatalogName.Equals(catalogName))
                                    {
                                        isValid = false;
                                        break;
                                    }
                                }

                                if (!isValid)
                                {
                                    continue;
                                }
                            }
                        }
                    }
                    if (!isValid)
                    {
                        continue;
                    }
                }


                // Start doing more expensive checks here
                // -----------------------------------------------------------------------------------------
                if (!filter.IgnoreSegments)
                {
                    // Check customer segments, customer should belong to a segment for promotion to apply
                    CampaignDto.CampaignSegmentRow[] segmentRows = item.Campaign.GetCampaignSegmentRows();

                    // if there are no segments defined assume it applies to everyone
                    if (segmentRows != null && segmentRows.Length > 0)
                    {
                        // customer is not within any segment, so promotion does not apply
                        if (segments == null || segments.Count == 0)
                        {
                            continue;
                        }

                        // start checking segments
                        bool apply = false;
                        foreach (CampaignDto.CampaignSegmentRow row in segmentRows)
                        {
                            if (segments.Contains(row.SegmentId))
                            {
                                // mark promotion as apply and leave loop
                                apply = true;
                                break;
                            }
                        }

                        // if does not apply continue with a next promotion
                        if (!apply)
                        {
                            continue;
                        }
                    }
                }

                if (!filter.IgnoreConditions)
                {
                    // Validate expressions
                    if (item.Expressions.Count > 0)
                    {
                        bool isValid = true;
                        foreach (ExpressionDto.ExpressionRow expression in item.Expressions)
                        {
                            if (!String.IsNullOrEmpty(expression.ExpressionXml))
                            {
                                ValidationResult result = ValidateExpression(expression.ApplicationId.ToString() + "-" + expression.Category + "-" + expression.ExpressionId.ToString(), expression.ExpressionXml, context);
                                if (!result.IsValid)
                                {
                                    isValid = false;
                                    break;
                                }
                            }
                        }

                        if (!isValid)
                        {
                            continue;
                        }
                    }
                }
                else
                {
                    // Create Award manually based on default settings
                    PromotionReward     reward = new PromotionReward(PromotionRewardType.EachAffectedEntry, item.DataRow.OfferAmount, item.DataRow.OfferType == 0 ? PromotionRewardAmountType.Percentage : PromotionRewardAmountType.Value);
                    PromotionItemRecord record = new PromotionItemRecord(context.TargetEntriesSet, context.TargetEntriesSet, reward);
                    record.PromotionItem = context.CurrentPromotion;
                    context.PromotionResult.PromotionRecords.Add(record);
                }

                if (!filter.IgnorePolicy)
                {
                    // Validate store policies
                    if (item.PolicyExpressions.Count > 0)
                    {
                        bool isValid = true;
                        foreach (ExpressionDto.ExpressionRow expression in item.PolicyExpressions)
                        {
                            if (!String.IsNullOrEmpty(expression.ExpressionXml))
                            {
                                ValidationResult result = ValidateExpression(expression.ApplicationId.ToString() + "-" + expression.Category + "-" + expression.ExpressionId.ToString(), expression.ExpressionXml, context);
                                if (!result.IsValid)
                                {
                                    isValid = false;
                                    break;
                                }
                            }
                        }

                        if (!isValid)
                        {
                            // Invalidates all records added during this evaluation sequence
                            context.RejectRecords();
                            continue;
                        }
                    }
                }

                // Commits all records added during this evaluation sequence
                context.CommitRecords();

                // Apply item
                rowIndexes.Add(item.RowIndex);

                // Add item to a group if it is applied
                if (item.DataRow.ExclusivityType.Equals(ExclusionType.GroupLevel))
                {
                    context.ExclusiveGroups.Add(item.DataRow.PromotionGroup);
                }

                // Finish processing if global level item has been added
                if (item.DataRow.ExclusivityType.Equals(ExclusionType.GlobalLevel))
                {
                    break;
                }
            }

            // Assign curren promotion to null
            context.CurrentPromotion = null;

            return(new PromotionItemCollection(promotions, rowIndexes.ToArray()));
        }