I am implementing an action that should add a free item to the cart when the user adds another item.

public class GetFreeItemAction : ICartLineAction
    public IRuleValue<string> SellableItemId { get; set; }

    public void Execute(IRuleExecutionContext context)
        var commerceContext = context.Fact<CommerceContext>();

        // this custom method ensures that the ID is in the format CatalogId|ProductId|[VariantId]
        var sellableItemId = SellableItemId.Yield(context).GetCompleteSellableItemId(catalogId);

        var cartLine = new CartLineComponent
            ItemId = sellableItemId,
            Quantity = 1

        cartline.Adjustments?.Add(new CartLineLevelAwardedAdjustment
            Name = "Something",
            DisplayName = "Something else",
            Adjustment = new Money(commerceContext.CurrentCurrency(), cartLine.Totals.SubTotal.Amount * MinusOne),
            AdjustmentType = "Some type",
            IsTaxable = false,
            AwardingBlock = "Some block"

        cartLine.Totals.SubTotal.Amount -= cartLine.Totals.SubTotal.Amount;
        cartLine.GetComponent<MessagesComponent>().AddMessage("Some message code", "Some message");


If I trigger this action from a promotion that triggers when I am adding an item to the cart, a cart line with Some-SellableItem-Id will also be added to the cart - this cart line is persisted. But the changes I make to the adjustments, totals and messages are not persisted to that cart line component. What do I need to do for this to happen?

UPDATE: I have checked the IAddCartLinePipeline to see if there was something in there, that would remove these changes before they were persisted, but I was not able to find anything of that sort. The closest I came was in CalculateCartLinesTotalsBlock (invoked via IAddCartLinePipeline --> ICalculateCartLinesPipeline) that updates Totals.AdjustmentTotal, Totals.GrandTotal and Totals.PaymentTotal on the cart line - but these updates should not revert my changes.

UPDATE 2: Using Plumber I now have a full overview of the IAddCartLinePipeline contains, including other pipelines that are invoked from this. I cannot find any block that should reset my changes on the promotion-added cart line's adjustment, subtotals and messages. Can anyone spot it?

IAddCartLinePipeline, invokes

  • ValidateSellableItemBlock
  • AddCartLineBlock
  • AddContactBlock
  • IPopulateValidateCartPipeline
  • AddCartLineGiftCardBlock
  • AddCartLineDigitalProductBlock
  • AddCartLineWarrantyBlock
  • AddCartLineInstallationBlock
  • ICalculateCartLinesPipeline, invokes
    • ClearCartLinesBlock
    • ClearCartBlock
    • CalculateCartLinesSubTotalsBlock
    • CalculateCartLinesFulfillmentBlock
    • CalculateCartLinesPromotionsBlock, invokes
      • IEvaluatePromotionsQualificationsPipeline
      • IApplyPromotionsBenefitsPipeline, which somewhere down the line invokes RunRuleSetBlock to apply my promotion, which in turn adds the cart line
    • CalculateCartLinesTaxBlock
    • CalculateCartLinesTotalsBlock
  • ICalculateCartPipeline, invokes
    • CalculateCartSubTotalsBlock
    • CalculateCartFulfillmentBlock
    • CalculateCartPromotionsBlock
    • CalculateCartTaxBlock
    • CalculateCartTotalsBlock
    • CalculateCartPaymentsBlock
    • WriteCartTotalsToContextBlock
  • PersistCartBlock
  • 1
    Take a look into github.com/avivasolutionsnl/Nyxie/blob/master/src/… Instead of GiftCard try to use SellableItem Commented Mar 26, 2020 at 11:36
  • @AndreiPaliakou, I have tried the code but it fails for me because of the cloned cart: The AddCartLineCommand persists the temporary cart with the same ID as the existing cart so when the existing cart is about to be persisted, I get a primary key violation in SQL (it tries to save two carts with the same ID). Is it your code and do you know for a fact that it works?
    – ebug
    Commented Mar 31, 2020 at 12:30
  • You don't need to persist temporary cart. You will need temporary cart just to create cart line and then copy this cart line item to your regular cart and then persist your regular cart. Commented Mar 31, 2020 at 12:34
  • @AndreiPaliakou, in the code you referred to, the following line will persist the temporary cart to my knowledge: temporaryCart = AsyncHelper.RunSync(() => _addCommand.Process(commerceContext, temporaryCart, freeGift)); I have tried adding "temp" to the cart id of the temporary cart, and then I don't get the SQL error. But the adjustments on the cart are still not persisted :(
    – ebug
    Commented Mar 31, 2020 at 12:39

1 Answer 1


Yes the Cart.Adjustments and Cart.Totals objects are calculated, so you should not update them directly.

Instead, you can simply add another promotion benefit to your promotion: "Get Cart Subtotal [specific] Amount Off", and set that to the total of your free item?

That will apply at the same time as your GetFreeItem action (which will just add the new cart item), because both are on the same promotion.

  • The GetFreeItem action is actually a dynamic action (Get [specific] item free) so we don't know the price of the item before the action is executed. The code above was a simplified version of the action - I have updated it to reflect the action's dynamic nature.
    – ebug
    Commented Mar 26, 2020 at 6:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.