Try our conversational search powered by Generative AI!

Visitor group based campaign / promotions [Commerce v. 10.2.3 / CMS v. 10.3.2]



I am currently implementing visitor group based campaigns / promotions on our site, but have gotten some odd results from the promotionEngine.

I have a campaign, which is limited to a single visitor group, this campaign has a single entry promotion (BuyQuantityGetItemDiscount). 

Calling the 
_promotionEngine.GetDiscountPrices( new List { variation.ContentLink }, currentMarket, currentMarket.DefaultCurrency, _referenceConverter, _lineItemCalculator);
yields no results.

But calling the 
_promotionEngine.Evaluate(variation.ContentLink, currentMarket, currentMarket.DefaultCurrency, RequestFulfillmentStatus.Fulfilled)
yields the wanted rewardDescription.

When placing the item in a cart (thus calling the cart.ApplyDiscounts(_promotionEngine, new PromotionEngineSettings()); ) the unit price is correctly calculated if and when being in the correct visitor group, but since I need to show the correct (discounted) price for the current user (based on the users visitor group), i need to get the users discounted price for the variation.


Jun 01, 2017 11:05

You can always use the _promotionEngine.Evaluate call and calculate the discount from the RewardDescription.SavedAmount yourself.

The correct discounted price should be the price minus the saved amount, no?

var discountedPrice = price - _promotionEngine.Evaluate().Sum(r=>r.SavedAmount);
Jun 01, 2017 17:22

Hi Erik

That seems to work, but this leads me to a new question. 

Is there any way to get the discounted price for a variation for a specific visitor group, without actually being in the group? 

To elaborate, we have a scheduled job that replicates our products to ElasticSearch (for searching). In this scheduled job i need to get a specific visitor groups price, as well as the normal price. 

Jun 02, 2017 9:22

I'm sorry Mads, I have nothing but bad news for You.

We have run into the exact same issue, the promotionengine always uses HttpContext.Current when evaluating.

We have a ticket registered with Episerver developer support and they have made a bug for it #COM-4431.

However it is still not visible in the bug list:

So the status is unknown, We do not know when a solution will be forthcoming.

Jun 02, 2017 14:02
<p>Bad news indeed, I have marked your previous answer since it answered the original question! :)<br /><br />Thanks for your time!</p>
Jun 02, 2017 14:09

Erik Norberg

We have a ticket registered with Episerver developer support and they have made a bug for it #COM-4431.
However it is still not visible in the bug list:
So the status is unknown, We do not know when a solution will be forthcoming.

Good to hear! I created a feature request for something similar but got no response.

A way to pass in a contactId or visitor group to the promotion engine would be awesome.

My request:

Edited, Jun 04, 2017 11:44
<p>Hi Erik and Jafet<br /><br />I have just recieved this from Episerver Developer support today:&nbsp;<br /><br /><span>In case you need a&nbsp;</span>workaround<span>&nbsp;</span><span>for your issue, you can create a&nbsp;</span>custom promotion filter&nbsp;<span>of your own.</span><br /><span>Please follow these steps as below:<br /></span><span></span></p> <ol> <li><span>Create a class&nbsp;</span>CustomCampaignVisitorGroupFilter<span>&nbsp;</span><span>inherited from&nbsp;</span>CampaignVisitorGroupFilter<span>&nbsp;</span><span>(reference to the attach file CustomCampaignVisitorGroupFilter.cs)</span><br /><span></span></li> <li><span>Add this line to the method ConfigureContainer in the SiteInitialization class:&nbsp;<br /></span> <pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">services.AddTransient&lt;CampaignVisitorGroupFilter, CustomCampaignVisitorGroupFilter&gt;();</pre> <br /><br /></li> </ol> <p><span>and done, then you can get&nbsp;the discounted&nbsp;price by the method as below:</span></p> <pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false">GetDiscountedPrice(price, promotionEngine, visitorGroupName) { &nbsp;&nbsp;CustomCampaignVisitorGroupFilter.CurrentVisitorGroupName = visitorGroupName; &nbsp;&nbsp;var discountedPrice = price - promotionEngine.Evaluate().Sum(r=&gt;r.SavedAmount); &nbsp;&nbsp;return discountedPrice; }</pre> <p><br /><br /></p> <pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using EPiServer.Commerce.Marketing; using System; using System.Collections.Generic; using System.Linq; using System.Web; using EPiServer.Personalization.VisitorGroups; using EPiServer.ServiceLocation; using EPiServer.Core; namespace EPiServer.Reference.Commerce.Site.CustomPromotionFilter { public class CustomCampaignVisitorGroupFilter : CampaignVisitorGroupFilter { public static string CurrentVisitorGroupName; private readonly IContentLoader _contentLoader; private readonly IVisitorGroupRepository _visitorGroupRepository; public CustomCampaignVisitorGroupFilter(ServiceAccessor&lt;HttpContextBase&gt; httpContextAccessor, IVisitorGroupRepository visitorGroupRepository, IVisitorGroupRoleRepository visitorGroupRoleRepository, IContentLoader contentLoader) : base(httpContextAccessor, visitorGroupRepository, visitorGroupRoleRepository, contentLoader) { _contentLoader = contentLoader; _visitorGroupRepository = visitorGroupRepository; } public override PromotionFilterContext Filter(PromotionFilterContext filterContext) { var shouldFilterCampaign = new Dictionary&lt;ContentReference, bool&gt;(); var cachedVisitorGroupLookup = new Dictionary&lt;Guid, bool&gt;(); foreach (var promotion in filterContext.IncludedPromotions) { var campaignLink = promotion.ParentLink.ToReferenceWithoutVersion(); bool shouldFilter; if (!shouldFilterCampaign.TryGetValue(campaignLink, out shouldFilter)) { shouldFilter = ShouldFilter(campaignLink, cachedVisitorGroupLookup, filterContext); shouldFilterCampaign[campaignLink] = shouldFilter; } if (!shouldFilter) { continue; } filterContext.ExcludePromotion( promotion, FulfillmentStatus.VisitorGroupRequired, filterContext.RequestedStatuses.HasFlag(RequestFulfillmentStatus.NotFulfilled)); } return filterContext; } private bool ShouldFilter(ContentReference campaignLink, IDictionary&lt;Guid, bool&gt; cachedVisitorGroupLookup, PromotionFilterContext filterContext) { SalesCampaign campaign; if (!_contentLoader.TryGet(campaignLink, out campaign)) { return true; } if (campaign.VisitorGroups == null || !campaign.VisitorGroups.Any()) { return false; } foreach (var guid in campaign.VisitorGroups) { bool isValidate; if (!cachedVisitorGroupLookup.TryGetValue(guid, out isValidate)) { var visitorGroup = _visitorGroupRepository.Load(guid); if (CurrentVisitorGroupName == null) { isValidate = true; } else { isValidate = String.Equals(visitorGroup.Name, CurrentVisitorGroupName, StringComparison.OrdinalIgnoreCase); } cachedVisitorGroupLookup[guid] = isValidate; } if (isValidate) { filterContext.AddVisitorGroup(campaignLink, guid); return false; } } return true; } } }</pre> <p>I havent tried this out yet, but wanted to share it with you guys!<br /><br />/Swensson<br /><br /></p>
Jun 15, 2017 15:22

I am not entirely sure but I automatically repel at the sight of them setting a static property for the current visitor group name, it makes me suspect the risk of some really hairy threading issues.

Getting customer specific prices would mean a lot of calls to evaluate with different values of the visitor group...

If you only have the one visitor group I guess you can get it to work thou.

Jun 15, 2017 15:44
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.