public async void Should_benefit_fulfillment_fee_if_discount_is_higher_than_fee() { fixture.Factory.ClearAllEntities(); HttpClient client = fixture.Factory.CreateClient(); Promotion promotion = await new PromotionBuilder() .QualifiedBy(new IsCurrentDayConditionBuilder() .Day(DateTime.Now.Day)) .BenefitBy(new CartAmountOffFulfillmentActionBuilder() .AmountOff(8)) // Fee = 5, Discount = 8 .Build(fixture.Factory); fixture.Factory.AddEntityToList(promotion, CommerceEntity.ListName <Promotion>()); fixture.Factory.AddEntity(promotion); Cart cart = await new CartBuilder() .WithLines(new LineBuilder().Quantity(1).Price(50)) .WithStandardFulfillment() // Default fulfillment fee = 5 .Build(); fixture.Factory.AddEntity(cart); Cart resultCart = await client.GetJsonAsync <Cart>( "api/Carts('Cart01')?$expand=Lines($expand=CartLineComponents($expand=ChildComponents)),Components"); AwardedAdjustment adjustment = resultCart.Adjustments.Single(x => x.AwardingBlock == nameof(CartAmountOffFulfillmentAction)); Assert.Equal(-5, adjustment.Adjustment.Amount); // Subtotal = 50, Tax is 10% = 5, Fulfillment fee = 5 - 5 = 0 Assert.Equal(55, resultCart.Totals.GrandTotal.Amount); }
private void PopulateAdjustmentChildView(EntityView entityView, AwardedAdjustment adjustment, CommercePipelineExecutionContext context) { EntityView entityView1 = new EntityView(); entityView1.EntityId = entityView.EntityId; entityView1.ItemId = adjustment.Name; entityView1.Name = "Adjustment"; // context.GetPolicy<KnownOrderViewsPolicy>().Adjustment; EntityView entityView2 = entityView1; List <ViewProperty> properties1 = entityView2.Properties; ViewProperty viewProperty1 = new ViewProperty(); viewProperty1.Name = "ItemId"; viewProperty1.IsHidden = true; viewProperty1.IsReadOnly = true; viewProperty1.RawValue = (object)adjustment.Name; properties1.Add(viewProperty1); entityView1.AddProperty("Name", adjustment.DisplayName); List <ViewProperty> properties2 = entityView2.Properties; ViewProperty viewProperty2 = new ViewProperty(); viewProperty2.Name = "Type"; viewProperty2.IsReadOnly = true; viewProperty2.RawValue = (object)adjustment.AdjustmentType; properties2.Add(viewProperty2); List <ViewProperty> properties3 = entityView2.Properties; ViewProperty viewProperty3 = new ViewProperty(); viewProperty3.Name = "Adjustment"; viewProperty3.IsReadOnly = true; viewProperty3.RawValue = (object)adjustment.Adjustment; properties3.Add(viewProperty3); List <ViewProperty> properties4 = entityView2.Properties; ViewProperty viewProperty4 = new ViewProperty(); viewProperty4.Name = "Taxable"; viewProperty4.IsReadOnly = true; viewProperty4.RawValue = (object)adjustment.IsTaxable; properties4.Add(viewProperty4); List <ViewProperty> properties5 = entityView2.Properties; ViewProperty viewProperty5 = new ViewProperty(); viewProperty5.Name = "IncludeInGrandTotal"; viewProperty5.IsReadOnly = true; viewProperty5.RawValue = (object)adjustment.IncludeInGrandTotal; properties5.Add(viewProperty5); List <ViewProperty> properties6 = entityView2.Properties; ViewProperty viewProperty6 = new ViewProperty(); viewProperty6.Name = "AwardingBlock"; viewProperty6.IsReadOnly = true; viewProperty6.RawValue = (object)adjustment.AwardingBlock; properties6.Add(viewProperty6); entityView.ChildViews.Add((Model)entityView2); }
public async void Should_benefit_multiple_times_when_within_action_limit() { fixture.Factory.ClearAllEntities(); HttpClient client = fixture.Factory.CreateClient(); Promotion promotion = await new PromotionBuilder() .QualifiedBy(new IsCurrentDayConditionBuilder() .Day(DateTime.Now.Day)) .BenefitBy(new CartEveryXItemsInCategoryPriceDiscountActionBuilder() .AmountOff(10) .ForCategory("Laptops") .ItemsToAward(1) .ItemsToPurchase(2) .ApplyActionTo(ApplicationOrder.Ascending) .ActionLimit(2)) .Build(fixture.Factory); fixture.Factory.AddEntityToList(promotion, CommerceEntity.ListName <Promotion>()); fixture.Factory.AddEntity(promotion); fixture.Factory.AddEntity(new Category { Id = "Laptops".ToEntityId <Category>(), SitecoreId = "435345345" }); Cart cart = await new CartBuilder() .WithLines(new LineBuilder().IdentifiedBy("001").Quantity(1).InCategory("435345345").Price(40), new LineBuilder().IdentifiedBy("002").Quantity(1).InCategory("435345345").Price(50), new LineBuilder().IdentifiedBy("003").Quantity(1).InCategory("435345345").Price(40), new LineBuilder().IdentifiedBy("004").Quantity(1).InCategory("435345345").Price(50)) .Build(); fixture.Factory.AddEntity(cart); Cart resultCart = await client.GetJsonAsync <Cart>( "api/Carts('Cart01')?$expand=Lines($expand=CartLineComponents($expand=ChildComponents)),Components"); CartLineComponent firstLine = resultCart.Lines.Single(x => x.Id == "001"); AwardedAdjustment adjustment = firstLine.Adjustments.Single(x => x.AwardingBlock == nameof(CartEveryXItemsInCategoryPriceDiscountAction)); Assert.Equal(-10, adjustment.Adjustment.Amount); CartLineComponent secondLine = resultCart.Lines.Single(x => x.Id == "003"); AwardedAdjustment secondAdjustment = secondLine.Adjustments.Single(x => x.AwardingBlock == nameof(CartEveryXItemsInCategoryPriceDiscountAction)); Assert.Equal(-10, secondAdjustment.Adjustment.Amount); // Subtotal = 160, Tax is 10% = 16, Fulfillment fee = 5 Assert.Equal(181, resultCart.Totals.GrandTotal.Amount); }
public async void Should_benefit() { fixture.Factory.ClearAllEntities(); HttpClient client = fixture.Factory.CreateClient(); Promotion promotion = await new PromotionBuilder() .QualifiedBy(new IsCurrentDayConditionBuilder() .Day(DateTime.Now.Day)) .BenefitBy(new CartFreeGiftActionBuilder() .Quantity(1) .Gift("MyCatalog|999|")) .Build(fixture.Factory); var catalog = new Catalog { Id = "Entity-Catalog-MyCatalog", FriendlyId = "MyCatalog", Name = "MyCatalog" }; SellableItem sellableItem = new SellableItemBuilder() .IdentifiedBy("001") .Priced(40) .Catalog("MyCatalog") .Build(); SellableItem gift = new SellableItemBuilder() .IdentifiedBy("999") .Catalog("MyCatalog") .Named("Free Gift") .Priced(99) .Build(); Cart cart = await new CartBuilder() .WithLines(new LineBuilder() .IdentifiedBy("001").WithProductId("MyCatalog|001|").Quantity(1).Price(40)) .Build(); fixture.Factory.AddEntities(promotion, gift, sellableItem, catalog, cart); fixture.Factory.AddEntityToList(promotion, CommerceEntity.ListName <Promotion>()); Cart resultCart = await client.GetJsonAsync <Cart>( "api/Carts('Cart01')?$expand=Lines($expand=CartLineComponents($expand=ChildComponents)),Components"); CartLineComponent giftLine = resultCart.Lines.Single(x => x.ItemId == "MyCatalog|999|"); AwardedAdjustment adjustment = giftLine.Adjustments.Single(x => x.AwardingBlock == nameof(CartFreeGiftAction)); Assert.Equal(-99, adjustment.Adjustment.Amount); // Subtotal = 40, Tax is 10% = 4, Fulfillment fee = 5 Assert.Equal(49, resultCart.Totals.GrandTotal.Amount); }
private static decimal GetFulfillmentFee(Cart cart) { if (cart.HasComponent <SplitFulfillmentComponent>()) { return(cart.Lines .Select(line => line.Adjustments.FirstOrDefault(a => a.Name.EqualsOrdinalIgnoreCase("FulfillmentFee"))) .TakeWhile(lineFulfillment => lineFulfillment != null) .Sum(lineFulfillment => lineFulfillment.Adjustment.Amount)); } AwardedAdjustment awardedAdjustment = cart.Adjustments.FirstOrDefault(a => a.Name.EqualsOrdinalIgnoreCase("FulfillmentFee")); return(awardedAdjustment?.Adjustment.Amount ?? 0); }
public async void Should_benefit_in_descending_order_when_category_matches() { fixture.Factory.ClearAllEntities(); HttpClient client = fixture.Factory.CreateClient(); Promotion promotion = await new PromotionBuilder() .QualifiedBy(new IsCurrentDayConditionBuilder() .Day(DateTime.Now.Day)) .BenefitBy(new CartItemsMatchingInCategoryPercentageDiscountActionBuilder() .PercentageOff(50) .ForCategory("Laptops") .Operator(Operator.Equal) .NumberOfProducts(2) .ApplyActionTo(ApplicationOrder.Descending) .ActionLimit(1)) .Build(fixture.Factory); fixture.Factory.AddEntityToList(promotion, CommerceEntity.ListName <Promotion>()); fixture.Factory.AddEntity(promotion); fixture.Factory.AddEntity(new Category { Id = "Laptops".ToEntityId <Category>(), SitecoreId = "435345345" }); Cart cart = await new CartBuilder() .WithLines(new LineBuilder().IdentifiedBy("001").Quantity(1).InCategory("435345345").Price(40), new LineBuilder().IdentifiedBy("002").Quantity(1).InCategory("435345345").Price(50)) .Build(); fixture.Factory.AddEntity(cart); Cart resultCart = await client.GetJsonAsync <Cart>( "api/Carts('Cart01')?$expand=Lines($expand=CartLineComponents($expand=ChildComponents)),Components"); CartLineComponent line = resultCart.Lines.Single(x => x.Id == "002"); AwardedAdjustment adjustment = line.Adjustments.Single(x => x.AwardingBlock == nameof(CartItemsMatchingInCategoryPercentageDiscountAction)); Assert.Equal(-25, adjustment.Adjustment.Amount); // Subtotal = 65, Tax is 10% = 6.5, Fulfillment fee = 5 Assert.Equal(76.5m, resultCart.Totals.GrandTotal.Amount); }
public async void Should_benefit_split_fulfillments() { fixture.Factory.ClearAllEntities(); HttpClient client = fixture.Factory.CreateClient(); Promotion promotion = await new PromotionBuilder() .QualifiedBy(new IsCurrentDayConditionBuilder() .Day(DateTime.Now.Day)) .BenefitBy(new CartAmountOffFulfillmentActionBuilder() .AmountOff(3)) // Fee = 2 * 2, Discount = 3 .Build(fixture.Factory); fixture.Factory.AddEntityToList(promotion, CommerceEntity.ListName <Promotion>()); fixture.Factory.AddEntity(promotion); Cart cart = await new CartBuilder() .WithLines(new LineBuilder() .IdentifiedBy("001") .Quantity(1) .WithStandardFulfillment() // Default fulfillment fee per line item is 2 .Price(50), new LineBuilder() .IdentifiedBy("002") .Quantity(1) .WithStandardFulfillment() // Default fulfillment fee per line item is 2 .Price(50)) .WithSplitFulfillment() .Build(); fixture.Factory.AddEntity(cart); Cart resultCart = await client.GetJsonAsync <Cart>( "api/Carts('Cart01')?$expand=Lines($expand=CartLineComponents($expand=ChildComponents)),Components"); AwardedAdjustment adjustment = resultCart.Adjustments.Single(x => x.AwardingBlock == nameof(CartAmountOffFulfillmentAction)); Assert.Equal(-3, adjustment.Adjustment.Amount); // Subtotal = 100, Tax is 10% = 10, Fulfillment fee = 2*2 - 3 = 1 Assert.Equal(111, resultCart.Totals.GrandTotal.Amount); }
public void Execute_WithProperties( IRuleValue <string> fulfillmentOptionName, IRuleValue <decimal> amountOff, Cart cart, CommerceContext commerceContext, IRuleExecutionContext context, string fulfillmentOptionNameValue, FulfillmentComponent fulfillmentComponent) { /********************************************** * Arrange **********************************************/ const decimal FULFILLMENT_FEE = MINIMUM_AMOUNT_OFF + 10m; cart.Adjustments.Clear(); var awardedAdjustment = new AwardedAdjustment() { Name = "FulfillmentFee" }; awardedAdjustment.Adjustment = new Money(FULFILLMENT_FEE); cart.Adjustments.Add(awardedAdjustment); cart.Lines.ForEach(l => l.Adjustments.Clear()); cart.SetComponent(fulfillmentComponent); context.Fact <CommerceContext>().ReturnsForAnyArgs(commerceContext); commerceContext.AddObject(cart); var globalPricingPolicy = commerceContext.GetPolicy <GlobalPricingPolicy>(); globalPricingPolicy.ShouldRoundPriceCalc = false; fulfillmentOptionName.Yield(context).ReturnsForAnyArgs(fulfillmentOptionNameValue); amountOff.Yield(context).ReturnsForAnyArgs(MINIMUM_AMOUNT_OFF); var propertiesModel = new PropertiesModel(); propertiesModel.Properties.Add("PromotionId", "id"); propertiesModel.Properties.Add("PromotionCartText", "carttext"); propertiesModel.Properties.Add("PromotionText", "text"); commerceContext.AddObject(propertiesModel); var commander = Substitute.For <CommerceCommander>(Substitute.For <IServiceProvider>()); var command = Substitute.For <GetFulfillmentMethodsCommand>( Substitute.For <IFindEntityPipeline>(), Substitute.For <IGetCartFulfillmentMethodsPipeline>(), Substitute.For <IGetCartLineFulfillmentMethodsPipeline>(), Substitute.For <IGetFulfillmentMethodsPipeline>(), Substitute.For <IServiceProvider>()); var fulfillmentMethod = new FulfillmentMethod() { FulfillmentType = fulfillmentOptionNameValue }; command.Process(commerceContext).ReturnsForAnyArgs(new List <FulfillmentMethod>() { fulfillmentMethod }.AsEnumerable()); commander.When(x => x.Command <GetFulfillmentMethodsCommand>()).DoNotCallBase(); commander.Command <GetFulfillmentMethodsCommand>().Returns(command); var action = new CartShippingOptionAmountOffAction(commander) { FulfillmentOptionName = fulfillmentOptionName, AmountOff = amountOff }; /********************************************** * Act **********************************************/ Action executeAction = () => action.Execute(context); /********************************************** * Assert **********************************************/ executeAction.Should().NotThrow <Exception>(); cart.Lines.SelectMany(l => l.Adjustments).Should().HaveCount(0); cart.Adjustments.Should().NotBeEmpty(); cart.Adjustments.LastOrDefault().Should().NotBeNull(); cart.Adjustments.LastOrDefault().Should().BeOfType <CartLevelAwardedAdjustment>(); cart.Adjustments.LastOrDefault().Name.Should().Be("text"); cart.Adjustments.LastOrDefault().DisplayName.Should().Be("carttext"); cart.Adjustments.LastOrDefault().AdjustmentType.Should().Be(commerceContext.GetPolicy <KnownCartAdjustmentTypesPolicy>().Discount); cart.Adjustments.LastOrDefault().AwardingBlock.Should().Contain(nameof(CartShippingOptionAmountOffAction)); cart.Adjustments.LastOrDefault().IsTaxable.Should().BeFalse(); cart.Adjustments.LastOrDefault().Adjustment.CurrencyCode.Should().Be(commerceContext.CurrentCurrency()); cart.Adjustments.LastOrDefault().Adjustment.Amount.Should().Be(-MINIMUM_AMOUNT_OFF); cart.HasComponent <MessagesComponent>().Should().BeTrue(); }
public void Execute_AmountOffLessThanFulfillmentFee_False( IRuleValue <string> fulfillmentOptionName, IRuleValue <decimal> amountOff, Cart cart, CommerceContext commerceContext, IRuleExecutionContext context, string fulfillmentOptionNameValue, FulfillmentComponent fulfillmentComponent) { /********************************************** * Arrange **********************************************/ const decimal FULFILLMENT_FEE = 10m; const decimal AMOUNT_OFF = FULFILLMENT_FEE + 1m; cart.Adjustments.Clear(); var awardedAdjustment = new AwardedAdjustment() { Name = "FulfillmentFee" }; awardedAdjustment.Adjustment = new Money(FULFILLMENT_FEE); cart.Adjustments.Add(awardedAdjustment); cart.Lines.ForEach(l => l.Adjustments.Clear()); cart.SetComponent(fulfillmentComponent); context.Fact <CommerceContext>().ReturnsForAnyArgs(commerceContext); commerceContext.AddObject(cart); fulfillmentOptionName.Yield(context).ReturnsForAnyArgs(fulfillmentOptionNameValue); amountOff.Yield(context).ReturnsForAnyArgs(AMOUNT_OFF); var commander = Substitute.For <CommerceCommander>(Substitute.For <IServiceProvider>()); var command = Substitute.For <GetFulfillmentMethodsCommand>( Substitute.For <IFindEntityPipeline>(), Substitute.For <IGetCartFulfillmentMethodsPipeline>(), Substitute.For <IGetCartLineFulfillmentMethodsPipeline>(), Substitute.For <IGetFulfillmentMethodsPipeline>(), Substitute.For <IServiceProvider>()); var fulfillmentMethod = new FulfillmentMethod() { FulfillmentType = fulfillmentOptionNameValue }; command.Process(commerceContext).ReturnsForAnyArgs(new List <FulfillmentMethod>() { fulfillmentMethod }.AsEnumerable()); commander.When(x => x.Command <GetFulfillmentMethodsCommand>()).DoNotCallBase(); commander.Command <GetFulfillmentMethodsCommand>().Returns(command); var action = new CartShippingOptionAmountOffAction(commander) { FulfillmentOptionName = fulfillmentOptionName, AmountOff = amountOff }; /********************************************** * Act **********************************************/ Action executeAction = () => action.Execute(context); /********************************************** * Assert **********************************************/ executeAction.Should().NotThrow <Exception>(); command.Received().Process(commerceContext); cart.Lines.SelectMany(l => l.Adjustments).Should().BeEmpty(); cart.Adjustments.Should().HaveCount(2, "The cart adjustments should only contain the fulfillment fee and shipping discount adjustments."); var adjustment = cart.Adjustments.LastOrDefault(); adjustment.Adjustment.Amount.Should().Be(-FULFILLMENT_FEE, "Adjustment amount should match the AmountOff rule."); }