<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><language>en</language><title>Blog posts by Joel Yourstone</title> <link>https://world.optimizely.com/blogs/joel-yourstone/</link><description></description><ttl>60</ttl><generator>Optimizely World</generator><item> <title>Running promotions - in the future</title>            <link>https://www.avensia.com/joel-yourstone/running-promotions-in-the-future/</link>            <description>
                    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/prismjs@1.22.0/components/prism-core.min.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/prismjs@1.22.0/plugins/autoloader/prism-autoloader.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;  var link = document.createElement(&quot;link&quot;);
  link.type = &quot;text/css&quot;;
  link.rel = &quot;stylesheet&quot;;
  link.href = &quot;https://cdn.jsdelivr.net/npm/prismjs@1.22.0/themes/prism-okaidia.css&quot;;
  document.head.appendChild(link);
&lt;/script&gt;
&lt;p&gt;&lt;em&gt;While making a tool that could index hundreds of thousands new prices within minutes during a big promotion activation, we had to figure out how to run promotions but for a future timestamp! It has been immensely useful during this year’s black week. Here’s how we approached it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you just want to look at the code, go ahead and scroll down!&lt;/em&gt;
&lt;/p&gt;
&lt;p&gt;I work with a client that has thousands of promotions. Most of them are not active, but there is still an impressive amount of active promotions. &lt;a href=&quot;/link/0ce061802d5944859831027129d61d4e.aspx&quot;&gt;Here’s some of the reasons as to why I recommend against ever deleting a promotion&lt;/a&gt; that has been in use. It’s very fun and challenging to find all side effects with this and we’ve had our share of bug fixes and/or feature requests from this client that’s been embedded in Epi Commerce.
&lt;/p&gt;
&lt;h2&gt;The problem&lt;/h2&gt;
&lt;p&gt;Running a B2C ecommerce site can be a lot about promotions and in our case it is. They run different promotions now and then, which might affect big chunks of the assortment, sometimes the entire assortment. Of course they want the prices to be adjusted as soon as the promotion becomes active, which the promotion engine and the cart solves wonderfully. But when it comes to the price in product listings, it is not viable in real-time to run the promotion engine for every product in the product list, hence we use an indexed value. This means we have to run a product indexer before the prices are changed. &lt;strong&gt;As a full index currently takes 2h for 200k+ products, the prices on product listing pages are shown 2 hours after the promotion is active.&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;When the business model relies on having scheduled promotions in marketing (an example: 20% off everything starting Black Friday 00:00), it does not thrive when customers see the wrong price in the listings for several hours.
&lt;/p&gt;
&lt;p&gt;So me and my colleague &lt;a href=&quot;https://www.linkedin.com/in/olatsandstrom/&quot; target=&quot;_blank&quot;&gt;Ola Sandstr&#246;m&lt;/a&gt; put our brains together and theorized a solution. What if we can prepare a result of an index with prices coming from a future timestamp? So when that time comes, we can simply push all of the pre calculated prices to the product index, having the heavy lifting already done in the past.
&lt;/p&gt;
&lt;p&gt;There is one big &lt;strong&gt;risk &lt;/strong&gt;with this approach, that the prices we pre-calculate mismatch what the real prices would be, if we spent the 2 hours at the time and figuring out what they would be. We tried to outline every scenario this would happen:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;The price of a SKU is changed in any way, after we’ve done the pre-calculation but before the time set for the pre-calculation.
&lt;/li&gt;&lt;li&gt;Any promotion has their data changed that would affect the price in the same time span as above. Note that you can set up the entire schedule and promotion structure ahead of time with ValidFrom and ValidUntil.
&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Both price changes and promotion changes can be carefully managed, so we and the client agreed that these risks could be entirely mitigated with simple human instructions. But to be safe, we never entirely trust our pre-calculated value and we start a normal indexing just as we did before and when that is done it will flush those changes to the product index. So there is a window of around 2 hours where we show the pre-calculated prices and after that the normal indexing job catches up and overwrites, hopefully with the exact same data.
&lt;/p&gt;
&lt;p&gt;To make this all work, it requires the following components
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;A way of running the promotion engine pretending to be in the future
&lt;/li&gt;&lt;li&gt;A way of storing and managing pre-calculated price lists connected to a certain timestamp
&lt;/li&gt;&lt;li&gt;A way of only changing some properties in an indexed product while keeping all other values the same. This is needed so other changes to the indexed product can happen and be reflected, even after we’ve applied the pre-calculated price changes. Stock, content or any other change not related to discounts or prices should not be stale because of the pre-calculated price.
&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;In this blog post, I’ll only talk about “A way of running the promotion engine pretending to be in the future”. I might add more posts to cover the other parts as well.
&lt;/p&gt;
&lt;h2&gt;The solution&lt;/h2&gt;

&lt;p&gt;Essentially, we need to do something like this (pseudo code)
&lt;/p&gt;
&lt;pre data-src=&quot;prism.js&quot; class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;using (pretendToBeInTheFuture) {
   var rewards = _promotionEngine.Evaluate(a product)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which is exactly what we did! (real code :D)
&lt;/p&gt;
&lt;pre data-src=&quot;prism.js&quot; class=&quot;language-cs&quot;&gt;&lt;code class=&quot;language-cs&quot;&gt;using (new RunPromotionsAtSpecificDate((DateTime) timestamp))
{
    var rewards = _promotionEngine.Evaluate(skuReference);
    CalculateAndStorePrice(skuReference, rewards, timestamp);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I found out while digging in the promotion engine that one of the first steps is to load all promotions that &lt;em&gt;could&lt;/em&gt; give us a reward (if you are interested in knowing this process I wrote about it in &lt;a href=&quot;/link/68b236dc1b5c437caa62b56976962f97.aspx&quot;&gt;a forum thread&lt;/a&gt;). This is where we filter out promotions and campaigns who has a start date that is in the future, hence not active for us. The class responsible is &lt;code class=&quot;language-markup&quot;&gt;PromotionEngineContentLoader&lt;/code&gt; and method is &lt;code class=&quot;language-cs&quot;&gt;GetEvaluablePromotionsInPriorityOrder(IMarket market)&lt;/code&gt;. This method (aside from adding caching layer) calls a private &lt;code class=&quot;language-cs&quot;&gt;GetEvaluablePromotions(IMarket market)&lt;/code&gt;. This method essentially does this:
&lt;/p&gt;
&lt;pre data-src=&quot;prism.js&quot; class=&quot;language-cs&quot;&gt;&lt;code class=&quot;language-cs&quot;&gt;var campaigns = _contentLoader.GetChildren&amp;lt;SalesCampaign&amp;gt;(GetCampaignFolderRoot())
    .Where(c =&amp;gt; _campaignInfoExtractor.IsCampaignActive(c) &amp;&amp; IsValidMarket(c, market))
    .ToDictionary(x =&amp;gt; x.ContentLink);
var promotions = GetPromotions(campaigns.Keys)
    .Where(x =&amp;gt; IsActive(x, campaigns))
    .OrderBy(x =&amp;gt; x.Priority)
    .ToList();
return promotions;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What we see is that somewhere in here, we call a sub function that will filter based on the current time, but for both promotions and campaigns. So we need to make sure we do the same logic for both. &lt;code class=&quot;language-cs&quot;&gt;IsActive&lt;/code&gt; will in the end call &lt;code class=&quot;language-cs&quot;&gt;GetEffectiveStatus(promotion, campaign)&lt;/code&gt; on the same &lt;code class=&quot;language-cs&quot;&gt;CampaignInfoExtractor&lt;/code&gt;. And both of these calls will ultimately call &lt;code class=&quot;language-cs&quot;&gt;GetStatusFromDates(DateTime validFrom, DateTime validUntil)&lt;/code&gt;
&lt;/p&gt;
&lt;p&gt;As you might figure, this is where we want to change the logic. So what we want to do is to inherit this class, make Structuremap use this class whenever it’s injecting &lt;code class=&quot;language-cs&quot;&gt;CampaignInfoExtractor&lt;/code&gt; and only override the &lt;code class=&quot;language-cs&quot;&gt;GetStatusFromDates&lt;/code&gt; method by so:
&lt;/p&gt;
&lt;pre data-src=&quot;prism.js&quot; class=&quot;language-cs&quot;&gt;&lt;code class=&quot;language-cs&quot;&gt;private override CampaignItemStatus GetStatusFromDates(DateTime validFrom, DateTime validUntil)
{
    var now = GetNowOrFutureTimestamp();
    if (validFrom &amp;gt; now)
        return CampaignItemStatus.Pending;
    return !(validUntil &amp;gt; now) ? CampaignItemStatus.Expired : CampaignItemStatus.Active;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the added thing and magic here is the newly added &lt;code class=&quot;language-cs&quot;&gt;GetNowOrFutureTimestamp()&lt;/code&gt; call. To figure out what that method should look like, we need to look at where we store the future timestamp, which was the disposable &lt;code class=&quot;language-cs&quot;&gt;RunPromotionsAtSpecificDate&lt;/code&gt; mentioned earlier.
&lt;/p&gt;
&lt;p&gt;This is how we built it
&lt;/p&gt;
&lt;pre data-src=&quot;prism.js&quot; class=&quot;language-cs&quot;&gt;&lt;code class=&quot;language-cs&quot;&gt;public class RunPromotionsAtSpecificDate : IDisposable
{
    public RunPromotionsAtSpecificDate(DateTime dateTime)
    {
        PromotionEngineContentLoaderDate.DateToExecuteUtc = dateTime;
    }
    public void Dispose()
    {
        PromotionEngineContentLoaderDate.DateToExecuteUtc = DateTime.MinValue;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With a supporting class:
&lt;/p&gt;
&lt;pre data-src=&quot;prism.js&quot; class=&quot;language-cs&quot;&gt;&lt;code class=&quot;language-cs&quot;&gt;public class PromotionEngineContentLoaderDate
{
    public static DateTime DateToExecuteUtc { get; set; }
    public static bool HasSetDate =&amp;gt; DateToExecuteUtc != DateTime.MinValue;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you see, we use &lt;code class=&quot;language-cs&quot;&gt;DateTime.MinValue&lt;/code&gt; to represent that there is no future value set, that the &lt;code class=&quot;language-cs&quot;&gt;GetStatusFromDates&lt;/code&gt; should use the “real now ” value, as it worked before. We also use static values to have a global accessible value. This might not be optional. We only took this simple route because:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;All our jobs run in an isolated service.
&lt;/li&gt;&lt;li&gt;Only 1 job is responsible for running the promotion engine, so this static value would only be used for that job
&lt;/li&gt;&lt;li&gt;We run multi-threaded, but all threads are either working with the same future time or the present time, never parallel threads working with different promotion work sets.
&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;If you use this solution, make sure you don’t get any multi-threading issues and make sure the logic of future is isolated to the task you want to run in the future, nothing else.&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;Now we can create the simple &lt;code class=&quot;language-cs&quot;&gt;GetNowOrFutureTimestamp()&lt;/code&gt; method that would look like
&lt;/p&gt;
&lt;pre class=&quot;language-cs&quot;&gt;&lt;code class=&quot;language-cs&quot;&gt;return PromotionEngineContentLoaderDate.HasSetDate ? PromotionEngineContentLoaderDate.DateToExecuteUtc : DateTime.Now;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that’s it. Very simple in the end, but has been an extremely powerful tool for us and our client with all the different major assortment price changes during black week and cyber monday. The code is found below. If you have any questions don’t hesitate to ask! And let me know if you are interested in the other topics as well that made the entire solution:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;A way of storing and managing pre-calculated price lists connected to a certain timestamp
&lt;/li&gt;&lt;li&gt;A way of only changing some properties in an indexed product while keeping all other values the same. This is needed so other changes to the indexed product can happen and be reflected, even after we’ve applied the pre-calculated price changes. Stock, content or any other change not related to discounts or prices should not be stale because of the pre-calculated price.
&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;The code&lt;/h2&gt;
&lt;p&gt;Not much code, but very powerful depending on what you want to do with it!&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/JoelYourstone/33705e6e5d647a617c9a25a9aa477a4d.js&quot;&gt;&lt;/script&gt;
                </description>            <guid>https://www.avensia.com/joel-yourstone/running-promotions-in-the-future/</guid>            <pubDate>Tue, 01 Dec 2020 14:08:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Feedback on prioritization in Marketing UI</title>            <link>https://www.avensia.com/joel-yourstone/feedback-prioritization-marketing-ui/</link>            <description>
                    &lt;p&gt;Last year around this time I did an &lt;a href=&quot;/link/77280c4269754771a52f336010865133.aspx&quot;&gt;extensive forum post&lt;/a&gt; about UX in the new (newest) Marketing UI. I’m glad to say a lot of these pains have been fixed, including:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;New filters in the side menu and the ability to create custom filters. &lt;a href=&quot;/link/90cf7447afe7461b8d03f5fe6b10958b.aspx&quot;&gt;Documentation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Promotions loading incrementally, they now load first when you expand the Campaign&lt;/li&gt;&lt;li&gt;Instead of loading all promotions in the prioritization view, the client loads them in chunks, depending on the window size&lt;/li&gt;&lt;li&gt;An added search bar to be able to find discounts&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;While all of that is really good, there are still some things that could be improved and some areas that still aren’t very good UX. The interaction design bits from my old post still applies, such as single clicking on a Campaign does&amp;hellip; something? It gets a border. Apart from that, nothing, you need to double click to expand the child promotions, which is to me (and many others I’ve seen) a bit hidden.&lt;/p&gt;
&lt;p&gt;But today I’ll focus on the &lt;strong&gt;prioritization view&lt;/strong&gt;, which I think is in most crucial need of some fixup. For those who do not know how to find this view, as it is not extremely clear, it’s by clicking the small icon in the upper right corner while at the Campaign list view. &lt;img src=&quot;https://i.imgur.com/Xc1hoBV.png&quot; width=&quot;274&quot; height=&quot;228&quot; /&gt;&lt;/p&gt;
&lt;p&gt;So, here goes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Important note&lt;/strong&gt;: I work with clients that work with Promotions a lot. All the screenshots and measurements in this post will be from a specific customer that has 1400 inactive and active promotions. While one could argue that there are a lot of inactive that could be deleted, by the nature promotions works for purchase orders, you don’t really want to change/delete them too much. The environment is Commerce 11.2, so it’s not the latest. However I couldn’t find any relevant changes in the changelog regarding this view. But there might be some things that’s already fixed. &lt;span style=&quot;font-size: 70%;&quot;&gt;Did I hear a Quan yelling “always update to latest version” from a rooftop afar just now? :)&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Searching for promotions&lt;/h2&gt;
&lt;p&gt;When an admin wants to go and change priority of a specific promotion, to maybe solve a prioritization issue with another promotion, they will struggle if they have ~80 promotions or more, and the struggle scales with the number of (inactive+active) promotions.&lt;/p&gt;
&lt;p&gt;While the Campaign list view has gotten a search bar implemented, the prioritization view has not. However, you could argue that you could simply search in page via the browser (Ctrl/Cmd+F) which would be handy if it wasn’t for virtualization of the list.&lt;/p&gt;
&lt;h3&gt;Virtualized list&lt;/h3&gt;
&lt;p&gt;With lack of a better name, I’ll call it this throughout the post and since I’ll refer to it several times, I thought I should explain what it is.&lt;/p&gt;
&lt;p&gt;To make the list more performant in the browser, i.e. while scrolling, drag’n’dropping, etc, not all items in the list are rendered to the HTML DOM. You’re only looking at a &lt;em&gt;window&lt;/em&gt; of the list. That window is (I believe, haven’t looked at the code) roughly the amount of items that fits in 150-200% of your screen height. The amount of items in the window won’t change, but while scrolling you still get normal scrolling behaviour. So while scrolling down, the items in the top of the window are removed and items in the bottom are added, giving the illusion that the full list is there but in fact it’s virtualized in memory. Performance thinking, I like it! However, some thinks needs to be dealt with differently when having this virtualized list approach.&lt;/p&gt;
&lt;h3&gt;Not searchable&lt;/h3&gt;
&lt;p&gt;Seeing as only a portion of the list is actually in the HTML DOM, only that portion is searchable by the browser search. If you have 1400 promotions and your job is to prioritize promotion X at least over promotion Y, that job becomes quite hard. People are creative however, so the people who work in this has some workarounds including putting the screen in portrait mode and zooming out as much as you can, so the items in the current “window” increases a lot. Or abusing the fact that if you change any priority of any promotion, the virtualized list feature is removed and the entire list is written to the DOM.&lt;/p&gt;
&lt;h2&gt;Dragging and dropping performance&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/lI0ISKm.png&quot; width=&quot;585&quot; height=&quot;28&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This picture isn’t very nice to look at when working with browser performance. It is not great when the browser gives you warnings in the log that a click event took more than 5 seconds. While this happens, the entire UI is frozen, you can’t click or interact on anything. This is a screenshot when dragging and then dropping, the mouse up event being when you drop the promotion. The virtualize implementation of that list should generally take care of that so it isn’t expensive to modify/interact with the existing list items, but somewhere somehow it doesn’t. Dragging items when the total number of promotions is big is not a very pleasant experience, regardless of how many list items the browser renders in the window.&lt;/p&gt;
&lt;p&gt;Another thing with dragging when it comes to the big list that would still be something troublesome even if the frame rate would stay at 60, is that if you have to prioritize a promotion very differently. For example, when you create a new promotion, that promotion is prioritized furthest down in the list. If you want this promotion to have the highest priority, you will have to drag and drop very far. For me, a drag and drop feature thrives when you can drag and drop the items within the viewport. Whenever you have to work with lists that go outside of the viewport, maybe drag and drop isn’t the best method to move items in that list.&lt;/p&gt;
&lt;h2&gt;Dropping an item scrolls you to the top&lt;/h2&gt;
&lt;p&gt;Another thing adding to the bad experience drag and dropping discounts in the prioritization view is that whenever you drop an item after dragged it, after the couple of seconds waiting for the mouse up event to be handled, you are scrolled to the top. If you for example are moving a promotion from #655 to #653, you will have to do a lot of scrolling to verify the changes you just did.&lt;/p&gt;
&lt;h2&gt;Dropping an item kills the virtualization&lt;/h2&gt;
&lt;p&gt;Whenever you change the prioritization of promotions, just dropping without saving, the entire list is rendered to the HTML DOM. The virtualization feature is gone. I’m sure there was a reason for this, but regardless the result is that the browser window becomes incredibly laggy. Drag and dropping even more promotions in this phase, you’ll have a bad time. This in combination with being scrolled to top and not searchable/filterable, makes it even harder to find the promotion you just change.&lt;/p&gt;
&lt;p&gt;To somewhat illustrate the struggle we can take a look at a profiler to see what we can produce. &lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/MbZAZUn.png&quot; width=&quot;496&quot; height=&quot;99&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I highlighted an area where I scrolled in the profiling results. The green bars in the top are frames, meaning we get 1 frame in roughly 130 ms, which is about 8fps, not great. Meanwhile, since Javascript is single threaded, you can clearly see the effects on my quite strong CPU when I started scrolling, between the two thick blue lines:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/snkjtsb.png&quot; width=&quot;602&quot; height=&quot;584&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;(Bug) Entire list is sometimes not rendered&lt;/h2&gt;
&lt;p&gt;When testing around with this interface (with a large number of items, as stated before) I found that sometimes when the entire list is printed to the DOM (as described above), not the entire list is actually printed. It feels like the renderer just gives up someway along the line and returns whatever items it has processed. I couldn’t reproduce this very often on my work computer with my 2560x1600 screen and powerful processor, but on a less powerful ultrabook with 1920x1080 (125% zoom, Windows) I could reproduce it all the time. Easily enough: &lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Go to the prioritization view with a quite large promotion list (&amp;gt;1000?)&lt;/li&gt;&lt;li&gt;Note the amount of promotions&lt;/li&gt;&lt;li&gt;Reorder any promotion&lt;/li&gt;&lt;li&gt;Wait for the UI to become responsive&lt;/li&gt;&lt;li&gt;Note the amount of promotions (can be less than previous count)&lt;/li&gt;&lt;/ol&gt;
&lt;h2&gt;The list is reloaded on successful save&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;img src=&quot;https://i.imgur.com/aINV5uC.png&quot; width=&quot;541&quot; height=&quot;76&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Whenever you have made a change and clicked on the blue save button and the server responds with OK, the entire list is reloaded again. This means the browser has to remove all the DOM elements (without serialized list, since that is removed if you reorder any promotion), show the “Loading discounts” text and as soon as the server request comes back it has to write to the DOM again. Again, there might be some technical reason for this but the user experience is the one taking the fall.&lt;/p&gt;
&lt;h2&gt;Add exclusions popup freezes the UI while loading&lt;/h2&gt;
&lt;p&gt;Whenever you want to add some exclusion, you click the plus next to a promotion and get prompted with a list of which promotions you want to add to be excluded. When testing this with the big amount of promotions, I had to wait several seconds before I could see the popup.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/4i27H6G.png&quot; width=&quot;602&quot; height=&quot;409&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If you take a look at the image (where I’ve censured the promotion names by background-color: black), it took a whole 10 seconds for the UI to become responsive again, we managed to render 1 frame in 9822ms, quite far from the goal of 1 frame per 16.67 ms (60 fps). And when I looked at a trace what the browser was doing, I found a very expensive forEach loop that did tons and tons of work in a very big iteration, presumably all the promotions.&lt;/p&gt;

&lt;p&gt;The following I could reproduce every time when I wrote it, but I can&#39;t anymore. Will keep it here and keep looking at it.&lt;/p&gt;
&lt;h2 style=&quot;text-decoration: line-through;&quot;&gt;Dragging and dropping “across windows” &lt;/h2&gt;
&lt;p style=&quot;text-decoration: line-through;&quot;&gt;Another thing I found when playing around was that whenever I moved a promotion just a few steps, within the currently rendered window, the request payload when clicking save will only contain 1-2 promotions. Presumably the promotion moved and maybe the promotion in the previous order. However, when dragging a promotion so far that the virtualized list will load another window, dropping it there and then clicking save, the request payload will contain all promotions in between the old and new priority order. That feels to me like something’s wrong, why would we need to send all of this data to the server, when the server did just fine just knowing the 1-2 promotions before? It can cause a lot of data being transferred with that request, which in turn will lead to more time spent deserializing and ultimately a slower Save-request. It can also lead to quite terrific results, which I will explain in the next section:&lt;/p&gt;
&lt;h2 style=&quot;text-decoration: line-through;&quot;&gt;(Bug) Dragging an item too far will make it unsavable&lt;/h2&gt;
&lt;p style=&quot;text-decoration: line-through;&quot;&gt;With the previous section in mind, those promotions in the prioritization view are quite big JSON objects when serialized! If you drag a promotion, say, 500 items up and try to save, you’ll get a JSON deserialization exception telling you that the thing to serialize is too big to serialize. Even 1000 serialized objects shouldn’t be too large to send, but as I said I think they are quite heavy, some colleagues of mine found that they had response sizes that were over 100mb &lt;strong&gt;for just one window load&lt;/strong&gt; in the prioritization view. They think the cause of that is that they have over 1000 shipping methods, and all of them are serialized on each (shipping?) promotion. But I will skip that dilemma from this list, as they will sort it out on their side together with Epi! But this exception is definitely something to be looked at. How to reproduce:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Drag a promotion from the bottom of a big virtualized list&lt;/li&gt;&lt;li&gt;Drop it at the top&lt;/li&gt;&lt;li&gt;Save&lt;/li&gt;&lt;/ol&gt;

&lt;h2&gt;Suggestions&lt;/h2&gt;
&lt;p&gt;So far I’ve mostly stated things I think needs improvement, without providing that many suggestions in how to solve them. I don’t have suggestions for all things I pointed out above, but here goes.&lt;/p&gt;
&lt;h3&gt;Add alternative to drag and dropping for reordering promotions&lt;/h3&gt;
&lt;p&gt;Currently the only way of reordering a promotion is by dragging and dropping. While this is a very good way of doing it when there are a small amount of promotions or when it’s just a small movement, I think other options is needed for the other cases. It simply isn’t nice drag and dropping something far, it takes far too much time.&lt;/p&gt;
&lt;p&gt;Perhaps a button that lets you input a priority order number that will move the promotion to that priority number? Or Move to top/Move to Bottom buttons.&lt;/p&gt;
&lt;h3&gt;Add a search/filter feature&lt;/h3&gt;
&lt;p&gt;To resolve the issue of finding a promotion, the same search/filter input feature as in the main screen of Marketing could be used here. Seeing as we can’t use the browser in page search. &lt;img src=&quot;https://i.imgur.com/W9tZbiV.png&quot; width=&quot;602&quot; height=&quot;343&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Doesn’t need to be much more advanced than the mockup image above. It gets a bit more tricky what you would want to see as results. We don’t want to filter like we do on the main Marketing page, because if we found our promotion there would be nowhere to drag and drop it. Maybe we get a drop down list as a result of this search and when you select the one you want you are scrolled to it and it’s highlighted?&lt;/p&gt;
&lt;h3&gt;Don’t show irrelevant promotions unless requested&lt;/h3&gt;
&lt;p&gt;I think this is the biggest point and a “low hanging fruit”. Currently the list is always every promotion, regardless if they are active or not. Seeing as the promotion engine will remove all the promotions that are inactive from evaluation quite early in the process, before the prioritization/exclusions come into play, having this list filled with inactive is in 90% of the cases useless. However, there are still valid use cases for seeing inactive, such as just creating a new promotion and having it Scheduled and want to set it up before it go live. But just like the main Marketing view, we could use a facet system for “active”, “inactive” and “scheduled” and have it default to “active” only. In the most common use case, the performance will be much better seeing as probably only a minority of the promotions are active, and that minority will become a bigger minority over time, as more and more promotions expire and go inactive.&lt;/p&gt;
&lt;h3&gt;Keep the “add exclusion” popup rendered&lt;/h3&gt;
&lt;p&gt;One way of solving the 10 sec freeze when we click on the plus to add exclusions would be to not having to render that item every time, but simply render it once sometime after the page loaded and then simply clear all the checkboxes whenever it is closed or finished. Or simply optimize the render function for the promotion list in memory to the checkbox hierarchy in the DOM, maybe there are some cheap things one can do there.&lt;/p&gt;
&lt;h3&gt;Trust the frontend with the list&lt;/h3&gt;
&lt;p&gt;Whenever you reorder something and then save, there is no apparent need to refresh the entire list and it can be very expensive changing the DOM that much, as described. I’d say to just remove the refreshing of the list, trust the frontend that when you move something and then save, the list is identical as if you would reload the entire page again.&lt;/p&gt;
&lt;h2&gt;Finally&lt;/h2&gt;
&lt;p&gt;It’s always nice contributing/giving feedback in different ways. Blog post/forum threads are one, actual code is another way! It would be amazing if those who want to contribute can get more channels to do so!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;*COUGH*&lt;/strong&gt; PRs! &lt;strong&gt;*COUGH*&lt;/strong&gt;&lt;/p&gt;
                </description>            <guid>https://www.avensia.com/joel-yourstone/feedback-prioritization-marketing-ui/</guid>            <pubDate>Sat, 12 May 2018 09:11:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Feedback on prioritization in Marketing UI</title>            <link>https://www.avensia.com/joel-yourstone/feedback-prioritization-marketing-ui/</link>            <description>
                    &lt;p&gt;Last year around this time I did an &lt;a href=&quot;/link/77280c4269754771a52f336010865133.aspx&quot;&gt;extensive forum post&lt;/a&gt; about UX in the new (newest) Marketing UI. I’m glad to say a lot of these pains have been fixed, including:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;New filters in the side menu and the ability to create custom filters. &lt;a href=&quot;/link/90cf7447afe7461b8d03f5fe6b10958b.aspx&quot;&gt;Documentation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Promotions loading incrementally, they now load first when you expand the Campaign&lt;/li&gt;&lt;li&gt;Instead of loading all promotions in the prioritization view, the client loads them in chunks, depending on the window size&lt;/li&gt;&lt;li&gt;An added search bar to be able to find discounts&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;While all of that is really good, there are still some things that could be improved and some areas that still aren’t very good UX. The interaction design bits from my old post still applies, such as single clicking on a Campaign does&amp;hellip; something? It gets a border. Apart from that, nothing, you need to double click to expand the child promotions, which is to me (and many others I’ve seen) a bit hidden.&lt;/p&gt;
&lt;p&gt;But today I’ll focus on the &lt;strong&gt;prioritization view&lt;/strong&gt;, which I think is in most crucial need of some fixup. For those who do not know how to find this view, as it is not extremely clear, it’s by clicking the small icon in the upper right corner while at the Campaign list view. &lt;img src=&quot;https://i.imgur.com/Xc1hoBV.png&quot; width=&quot;274&quot; height=&quot;228&quot; /&gt;&lt;/p&gt;
&lt;p&gt;So, here goes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Important note&lt;/strong&gt;: I work with clients that work with Promotions a lot. All the screenshots and measurements in this post will be from a specific customer that has 1400 inactive and active promotions. While one could argue that there are a lot of inactive that could be deleted, by the nature promotions works for purchase orders, you don’t really want to change/delete them too much. The environment is Commerce 11.2, so it’s not the latest. However I couldn’t find any relevant changes in the changelog regarding this view. But there might be some things that’s already fixed. &lt;span style=&quot;font-size: 70%;&quot;&gt;Did I hear a Quan yelling “always update to latest version” from a rooftop afar just now? :)&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Searching for promotions&lt;/h2&gt;
&lt;p&gt;When an admin wants to go and change priority of a specific promotion, to maybe solve a prioritization issue with another promotion, they will struggle if they have ~80 promotions or more, and the struggle scales with the number of (inactive+active) promotions.&lt;/p&gt;
&lt;p&gt;While the Campaign list view has gotten a search bar implemented, the prioritization view has not. However, you could argue that you could simply search in page via the browser (Ctrl/Cmd+F) which would be handy if it wasn’t for virtualization of the list.&lt;/p&gt;
&lt;h3&gt;Virtualized list&lt;/h3&gt;
&lt;p&gt;With lack of a better name, I’ll call it this throughout the post and since I’ll refer to it several times, I thought I should explain what it is.&lt;/p&gt;
&lt;p&gt;To make the list more performant in the browser, i.e. while scrolling, drag’n’dropping, etc, not all items in the list are rendered to the HTML DOM. You’re only looking at a &lt;em&gt;window&lt;/em&gt; of the list. That window is (I believe, haven’t looked at the code) roughly the amount of items that fits in 150-200% of your screen height. The amount of items in the window won’t change, but while scrolling you still get normal scrolling behaviour. So while scrolling down, the items in the top of the window are removed and items in the bottom are added, giving the illusion that the full list is there but in fact it’s virtualized in memory. Performance thinking, I like it! However, some thinks needs to be dealt with differently when having this virtualized list approach.&lt;/p&gt;
&lt;h3&gt;Not searchable&lt;/h3&gt;
&lt;p&gt;Seeing as only a portion of the list is actually in the HTML DOM, only that portion is searchable by the browser search. If you have 1400 promotions and your job is to prioritize promotion X at least over promotion Y, that job becomes quite hard. People are creative however, so the people who work in this has some workarounds including putting the screen in portrait mode and zooming out as much as you can, so the items in the current “window” increases a lot. Or abusing the fact that if you change any priority of any promotion, the virtualized list feature is removed and the entire list is written to the DOM.&lt;/p&gt;
&lt;h2&gt;Dragging and dropping performance&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/lI0ISKm.png&quot; width=&quot;585&quot; height=&quot;28&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This picture isn’t very nice to look at when working with browser performance. It is not great when the browser gives you warnings in the log that a click event took more than 5 seconds. While this happens, the entire UI is frozen, you can’t click or interact on anything. This is a screenshot when dragging and then dropping, the mouse up event being when you drop the promotion. The virtualize implementation of that list should generally take care of that so it isn’t expensive to modify/interact with the existing list items, but somewhere somehow it doesn’t. Dragging items when the total number of promotions is big is not a very pleasant experience, regardless of how many list items the browser renders in the window.&lt;/p&gt;
&lt;p&gt;Another thing with dragging when it comes to the big list that would still be something troublesome even if the frame rate would stay at 60, is that if you have to prioritize a promotion very differently. For example, when you create a new promotion, that promotion is prioritized furthest down in the list. If you want this promotion to have the highest priority, you will have to drag and drop very far. For me, a drag and drop feature thrives when you can drag and drop the items within the viewport. Whenever you have to work with lists that go outside of the viewport, maybe drag and drop isn’t the best method to move items in that list.&lt;/p&gt;
&lt;h2&gt;Dropping an item scrolls you to the top&lt;/h2&gt;
&lt;p&gt;Another thing adding to the bad experience drag and dropping discounts in the prioritization view is that whenever you drop an item after dragged it, after the couple of seconds waiting for the mouse up event to be handled, you are scrolled to the top. If you for example are moving a promotion from #655 to #653, you will have to do a lot of scrolling to verify the changes you just did.&lt;/p&gt;
&lt;h2&gt;Dropping an item kills the virtualization&lt;/h2&gt;
&lt;p&gt;Whenever you change the prioritization of promotions, just dropping without saving, the entire list is rendered to the HTML DOM. The virtualization feature is gone. I’m sure there was a reason for this, but regardless the result is that the browser window becomes incredibly laggy. Drag and dropping even more promotions in this phase, you’ll have a bad time. This in combination with being scrolled to top and not searchable/filterable, makes it even harder to find the promotion you just change.&lt;/p&gt;
&lt;p&gt;To somewhat illustrate the struggle we can take a look at a profiler to see what we can produce. &lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/MbZAZUn.png&quot; width=&quot;496&quot; height=&quot;99&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I highlighted an area where I scrolled in the profiling results. The green bars in the top are frames, meaning we get 1 frame in roughly 130 ms, which is about 8fps, not great. Meanwhile, since Javascript is single threaded, you can clearly see the effects on my quite strong CPU when I started scrolling, between the two thick blue lines:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/snkjtsb.png&quot; width=&quot;602&quot; height=&quot;584&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;(Bug) Entire list is sometimes not rendered&lt;/h2&gt;
&lt;p&gt;When testing around with this interface (with a large number of items, as stated before) I found that sometimes when the entire list is printed to the DOM (as described above), not the entire list is actually printed. It feels like the renderer just gives up someway along the line and returns whatever items it has processed. I couldn’t reproduce this very often on my work computer with my 2560x1600 screen and powerful processor, but on a less powerful ultrabook with 1920x1080 (125% zoom, Windows) I could reproduce it all the time. Easily enough: &lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Go to the prioritization view with a quite large promotion list (&amp;gt;1000?)&lt;/li&gt;&lt;li&gt;Note the amount of promotions&lt;/li&gt;&lt;li&gt;Reorder any promotion&lt;/li&gt;&lt;li&gt;Wait for the UI to become responsive&lt;/li&gt;&lt;li&gt;Note the amount of promotions (can be less than previous count)&lt;/li&gt;&lt;/ol&gt;
&lt;h2&gt;The list is reloaded on successful save&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;img src=&quot;https://i.imgur.com/aINV5uC.png&quot; width=&quot;541&quot; height=&quot;76&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Whenever you have made a change and clicked on the blue save button and the server responds with OK, the entire list is reloaded again. This means the browser has to remove all the DOM elements (without serialized list, since that is removed if you reorder any promotion), show the “Loading discounts” text and as soon as the server request comes back it has to write to the DOM again. Again, there might be some technical reason for this but the user experience is the one taking the fall.&lt;/p&gt;
&lt;h2&gt;Add exclusions popup freezes the UI while loading&lt;/h2&gt;
&lt;p&gt;Whenever you want to add some exclusion, you click the plus next to a promotion and get prompted with a list of which promotions you want to add to be excluded. When testing this with the big amount of promotions, I had to wait several seconds before I could see the popup.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/4i27H6G.png&quot; width=&quot;602&quot; height=&quot;409&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If you take a look at the image (where I’ve censured the promotion names by background-color: black), it took a whole 10 seconds for the UI to become responsive again, we managed to render 1 frame in 9822ms, quite far from the goal of 1 frame per 16.67 ms (60 fps). And when I looked at a trace what the browser was doing, I found a very expensive forEach loop that did tons and tons of work in a very big iteration, presumably all the promotions.&lt;/p&gt;

&lt;p&gt;The following I could reproduce every time when I wrote it, but I can&#39;t anymore. Will keep it here and keep looking at it.&lt;/p&gt;
&lt;h2 style=&quot;text-decoration: line-through;&quot;&gt;Dragging and dropping “across windows” &lt;/h2&gt;
&lt;p style=&quot;text-decoration: line-through;&quot;&gt;Another thing I found when playing around was that whenever I moved a promotion just a few steps, within the currently rendered window, the request payload when clicking save will only contain 1-2 promotions. Presumably the promotion moved and maybe the promotion in the previous order. However, when dragging a promotion so far that the virtualized list will load another window, dropping it there and then clicking save, the request payload will contain all promotions in between the old and new priority order. That feels to me like something’s wrong, why would we need to send all of this data to the server, when the server did just fine just knowing the 1-2 promotions before? It can cause a lot of data being transferred with that request, which in turn will lead to more time spent deserializing and ultimately a slower Save-request. It can also lead to quite terrific results, which I will explain in the next section:&lt;/p&gt;
&lt;h2 style=&quot;text-decoration: line-through;&quot;&gt;(Bug) Dragging an item too far will make it unsavable&lt;/h2&gt;
&lt;p style=&quot;text-decoration: line-through;&quot;&gt;With the previous section in mind, those promotions in the prioritization view are quite big JSON objects when serialized! If you drag a promotion, say, 500 items up and try to save, you’ll get a JSON deserialization exception telling you that the thing to serialize is too big to serialize. Even 1000 serialized objects shouldn’t be too large to send, but as I said I think they are quite heavy, some colleagues of mine found that they had response sizes that were over 100mb &lt;strong&gt;for just one window load&lt;/strong&gt; in the prioritization view. They think the cause of that is that they have over 1000 shipping methods, and all of them are serialized on each (shipping?) promotion. But I will skip that dilemma from this list, as they will sort it out on their side together with Epi! But this exception is definitely something to be looked at. How to reproduce:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Drag a promotion from the bottom of a big virtualized list&lt;/li&gt;&lt;li&gt;Drop it at the top&lt;/li&gt;&lt;li&gt;Save&lt;/li&gt;&lt;/ol&gt;

&lt;h2&gt;Suggestions&lt;/h2&gt;
&lt;p&gt;So far I’ve mostly stated things I think needs improvement, without providing that many suggestions in how to solve them. I don’t have suggestions for all things I pointed out above, but here goes.&lt;/p&gt;
&lt;h3&gt;Add alternative to drag and dropping for reordering promotions&lt;/h3&gt;
&lt;p&gt;Currently the only way of reordering a promotion is by dragging and dropping. While this is a very good way of doing it when there are a small amount of promotions or when it’s just a small movement, I think other options is needed for the other cases. It simply isn’t nice drag and dropping something far, it takes far too much time.&lt;/p&gt;
&lt;p&gt;Perhaps a button that lets you input a priority order number that will move the promotion to that priority number? Or Move to top/Move to Bottom buttons.&lt;/p&gt;
&lt;h3&gt;Add a search/filter feature&lt;/h3&gt;
&lt;p&gt;To resolve the issue of finding a promotion, the same search/filter input feature as in the main screen of Marketing could be used here. Seeing as we can’t use the browser in page search. &lt;img src=&quot;https://i.imgur.com/W9tZbiV.png&quot; width=&quot;602&quot; height=&quot;343&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Doesn’t need to be much more advanced than the mockup image above. It gets a bit more tricky what you would want to see as results. We don’t want to filter like we do on the main Marketing page, because if we found our promotion there would be nowhere to drag and drop it. Maybe we get a drop down list as a result of this search and when you select the one you want you are scrolled to it and it’s highlighted?&lt;/p&gt;
&lt;h3&gt;Don’t show irrelevant promotions unless requested&lt;/h3&gt;
&lt;p&gt;I think this is the biggest point and a “low hanging fruit”. Currently the list is always every promotion, regardless if they are active or not. Seeing as the promotion engine will remove all the promotions that are inactive from evaluation quite early in the process, before the prioritization/exclusions come into play, having this list filled with inactive is in 90% of the cases useless. However, there are still valid use cases for seeing inactive, such as just creating a new promotion and having it Scheduled and want to set it up before it go live. But just like the main Marketing view, we could use a facet system for “active”, “inactive” and “scheduled” and have it default to “active” only. In the most common use case, the performance will be much better seeing as probably only a minority of the promotions are active, and that minority will become a bigger minority over time, as more and more promotions expire and go inactive.&lt;/p&gt;
&lt;h3&gt;Keep the “add exclusion” popup rendered&lt;/h3&gt;
&lt;p&gt;One way of solving the 10 sec freeze when we click on the plus to add exclusions would be to not having to render that item every time, but simply render it once sometime after the page loaded and then simply clear all the checkboxes whenever it is closed or finished. Or simply optimize the render function for the promotion list in memory to the checkbox hierarchy in the DOM, maybe there are some cheap things one can do there.&lt;/p&gt;
&lt;h3&gt;Trust the frontend with the list&lt;/h3&gt;
&lt;p&gt;Whenever you reorder something and then save, there is no apparent need to refresh the entire list and it can be very expensive changing the DOM that much, as described. I’d say to just remove the refreshing of the list, trust the frontend that when you move something and then save, the list is identical as if you would reload the entire page again.&lt;/p&gt;
&lt;h2&gt;Finally&lt;/h2&gt;
&lt;p&gt;It’s always nice contributing/giving feedback in different ways. Blog post/forum threads are one, actual code is another way! It would be amazing if those who want to contribute can get more channels to do so!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;*COUGH*&lt;/strong&gt; PRs! &lt;strong&gt;*COUGH*&lt;/strong&gt;&lt;/p&gt;
                </description>            <guid>https://www.avensia.com/joel-yourstone/feedback-prioritization-marketing-ui/</guid>            <pubDate>Sat, 12 May 2018 07:11:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Why isn&#39;t my promotion showing?</title>            <link>http://avensia.com/joel-yourstone/why-isnt-my-promotion-showing/</link>            <description>
                    &lt;p&gt;A question I often get internally and see a lot in the Commerce forum is a version of the title of this blog post. A person has a promotion, could be built in, could be custom, could be manual, etc. and same person is scratching heads over why the promotion
    hasn’t been applied on the checkout page (or in the cart). So, I’ll try to make a checklist from my experience why a promotion is not applying.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;I’m sure I will add more cases as time goes, but this is what I can come up with so far. There are a lot of promotion specific cases where it won’t be applied, but if you believe it should be applied, check this list before looking at the promotion processor
    itself.
&lt;/p&gt;
&lt;p&gt;
    &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(&#39;.dev-explainations&#39;).toggle()&quot;&gt;Expand all explainations&lt;/a&gt;
&lt;/p&gt;
&lt;h2&gt;The common cases&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;The promotion isn’t active&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;Super easy to check this. The Marketing UI will give hints fading your Promotion or entire Campaign if it’s inactive, but check the following fields on both the Promotion and Campaign (if the Scheduling is set as “Same as the campaign”).&lt;br /&gt;
            &lt;img src=&quot;https://lh3.googleusercontent.com/DpsiRT4K04XG8DR-oEZiXbmVCq0qAoOPnEBK08Ntw8Vfo5Luks-9IJVgbku0h3s95c6buhV1my3gTkAG286VwPeS0lNkQDulCRH8HT9eCZc7edsvnkfx_lkAa8-Ttsa8oKM8BR3b&quot; width=&quot;512&quot; height=&quot;331&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The promotion has a coupon code&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;Perhaps you missed or forgot that you entered a coupon code for the promotion, that you haven’t applied to the cart? If it has a coupon code which the cart doesn’t have, the promotion engine will filter it out before starting the evaluation.&lt;br /&gt;
            &lt;img src=&quot;https://lh6.googleusercontent.com/9fs2gDajNKjzkv_ie2DAHpm_kOMWN453xyhex6YD2cfuuSaSFvkDPpOc82du-ESNn5fCOKc_qx9re8Lt8BVct_Ct5qyYtrCvY384VX9EwnCJwFJYw5FiPi4eeAsOfY-0DQGfzPvS&quot; width=&quot;451&quot; height=&quot;165&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The market of the SalesCampaign doesn’t match the cart&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt; SalesCampaign can be restricted to specific Markets, or “All” (as for built in features at least, you can extend this to allow several but not all). Make sure your cart has the same market.&lt;br /&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/RQzDZRaAqJnkcPzGvUTNDz-WYUa1SbHdXlWDJKcvj408TLhLOIIrVd_48ZkHYALcKF-lM_a2zaHWZZriKhp3Zd0zSTQV-N4xqACKfBj59gfFheeGNGD9YWbMztOuEbVy3cFU0gwc&quot; width=&quot;472&quot; height=&quot;140&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;There is a Visitor group selected that your customer doesn’t have&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;On SalesCampaigns, you can have all Promotion children to only get applied if a certain Visitor Group has been applied. Make sure the Visitor Group criterion is fulfilled, if there is any set on the SalesCampaign.&lt;br /&gt;&lt;img src=&quot;https://lh4.googleusercontent.com/fDZhfY-5g2AxIuoY8Mv-7ugzjf6EMsDcAzQ7-UXzvpA2io0fyTgkoxO3qhof78P5Y1EJaQu9q1QM-EITpRaoll7Rv3Tp_kQ8w2BvFghsCooNPW37G9aqZaFKBiDOkO73Yn-iRd6c&quot; width=&quot;517&quot; height=&quot;143&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;br /&gt;&lt;em&gt;P.S. Don’t build a visitor group like that D.S.&lt;/em&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;You’ve reached a Redemption Limit&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt; Promotion level, you can set some redemption limits per order per customer and total overall usage. If you set one of these values and used the promotion in purchases more than that value, your promotion will not be selected due to this.&lt;br /&gt;
            &lt;img src=&quot;https://lh5.googleusercontent.com/CKZweVrnrzrcMf3c-8Ke9ZWCPEc9J28k0sjn1g4YNF-ora3154iC2j2lkGTuls6V8ljYNt-XsnYaYtsT3fL0ehp1NPoFoORY1JX8zWWw_Ba3TmV7lfqVJ0PeGlMcDgr5rpYL8ZY_&quot; width=&quot;467&quot; height=&quot;237&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
    &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;A bit less common cases&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Another Promotion is excluding yours&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;You can set up that some promotions shouldn’t be combined. Effectively, taking priority into account, there might be another Promotion with higher priority that excludes yours. You can see all the priority and exclusions clicking on this button
            &lt;img src=&quot;https://lh5.googleusercontent.com/tnsOowHR7h6w4DpkGpSNgVfkYhivOvimPD1JzUDOXMmfF2LNfo5eZeZeUc0hZIseUOKattbpiP797mGMR_sXcFh2hpexz4lbocwennSB106erQBbmUhABpBfA-0HF9gGPVGX7OOn&quot; width=&quot;38&quot; height=&quot;31&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt; when you’re in the Commerce -&amp;gt; Marketing start page. Put your promotion in the top priority if you want to test if that’s the issue.&lt;br /&gt;
            &lt;img src=&quot;https://lh5.googleusercontent.com/5nNdLwxk2u_CEKjQaxVPIbgqmhxQ-Oi9LRAqbdOp994oSz50MYbQd_MLjCw5FInGokS26R_um-2UCFEpvRQbCJesSkkfrkT9DFi8tRnwquu8XLNf0ezOiWrKVjdmgc8izLi92myC&quot; width=&quot;311&quot; height=&quot;93&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The Evaluation of the promotion fails&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;This one is a bit harder to help with, as it differs from Promotion to Promotion, what implementation of the PromotionProcessor exists. If it’s your custom Promotion, you have a much easier as you can just debug the Processor you wrote.&lt;br /&gt;Note
            that before running the Evaluate method, it will run a CanBeFulfilled method on your processor. If it doesn’t exist it will fallback to the CanBeFulfilled on the PromotionProcessorBase that simply returns true. But if you have implemented
            a CanBeFulfilled method to make it performant, make sure that one returns true as well. If you are inheriting from some pre built processor, such as GetItemDiscountProcessorBase, that one will have a built in CanBeFulfilled with some logic.&lt;br /&gt;Another
            thing I can recommend checking here is if you have a “Spend Amount” condition in your promotion, or a “Recieve Discount Amount” in the reward, make sure you have a value in the &lt;strong&gt;same currency&lt;/strong&gt; as the cart. Otherwise it won’t
            be fulfilled as it doesn’t know how much to give you.&lt;/p&gt;
    &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The rare cases&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;(Entry level only) You have an item that should fulfill condition but is excluded by an entry filter&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;Quite rare this would happen by accident, but I have done it to myself and felt quite stupid for a moment. Make sure that if you’ve set up &lt;a href=&quot;/link/c874b7d7594a4d818c00bc87c2036105.aspx&quot;&gt;one of these&lt;/a&gt;            your items are not affected by that excluder.&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;(Manual promotion only) You are applying the promotions in the wrong way&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;If you want to apply a manual promotion (creating a PromotionInformation object in memory and applying that one ,see &lt;a href=&quot;/link/6f4db3d220c44dd19931002499c0b3c4.aspx&quot;&gt;this forum post &lt;/a&gt;for
            info on how to do it), you can’t run promotions using the IPromotionEngine directly. So you can’t do _promotionEngine.Run(cart); you need to do call (IOrderGroup)cart.ApplyDiscounts(); Reason for this is that the PromotionEngine clears all
            the PromotionInformation objects from the OrderForm before starting to process promotions, so you’d lose your manual promotion if you’ve added it. What the extension method ApplyDiscounts does is that it will extract the manual promotions
            from the orderform, storing them safely. Then it will run the promotion engine and after it’s done will manually apply the manual promotions. This is quite quirky in my opinion and creates bugs like &lt;a href=&quot;/link/9f16c3ffe25045c0bce541d45d0c3184.aspx&quot;&gt;this one&lt;/a&gt;,
            but there you have it.&lt;/p&gt;
    &lt;/li&gt;
&lt;/ul&gt;
                </description>            <guid>http://avensia.com/joel-yourstone/why-isnt-my-promotion-showing/</guid>            <pubDate>Sat, 10 Feb 2018 13:59:07 GMT</pubDate>           <category>Blog post</category></item><item> <title>Why isn&#39;t my promotion showing?</title>            <link>http://www.avensia.com/joel-yourstone/why-isnt-my-promotion-showing/</link>            <description>
                    &lt;p&gt;A question I often get internally and see a lot in the Commerce forum is a version of the title of this blog post. A person has a promotion, could be built in, could be custom, could be manual, etc. and same person is scratching heads over why the promotion
    hasn’t been applied on the checkout page (or in the cart). So, I’ll try to make a checklist from my experience why a promotion is not applying.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;I’m sure I will add more cases as time goes, but this is what I can come up with so far. There are a lot of promotion specific cases where it won’t be applied, but if you believe it should be applied, check this list before looking at the promotion processor
    itself.
&lt;/p&gt;
&lt;p&gt;
    &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(&#39;.dev-explainations&#39;).toggle()&quot;&gt;Expand all explainations&lt;/a&gt;
&lt;/p&gt;
&lt;h2&gt;The common cases&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;The promotion isn’t active&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;Super easy to check this. The Marketing UI will give hints fading your Promotion or entire Campaign if it’s inactive, but check the following fields on both the Promotion and Campaign (if the Scheduling is set as “Same as the campaign”).&lt;br /&gt;
            &lt;img src=&quot;https://lh3.googleusercontent.com/DpsiRT4K04XG8DR-oEZiXbmVCq0qAoOPnEBK08Ntw8Vfo5Luks-9IJVgbku0h3s95c6buhV1my3gTkAG286VwPeS0lNkQDulCRH8HT9eCZc7edsvnkfx_lkAa8-Ttsa8oKM8BR3b&quot; width=&quot;512&quot; height=&quot;331&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The promotion has a coupon code&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;Perhaps you missed or forgot that you entered a coupon code for the promotion, that you haven’t applied to the cart? If it has a coupon code which the cart doesn’t have, the promotion engine will filter it out before starting the evaluation.&lt;br /&gt;
            &lt;img src=&quot;https://lh6.googleusercontent.com/9fs2gDajNKjzkv_ie2DAHpm_kOMWN453xyhex6YD2cfuuSaSFvkDPpOc82du-ESNn5fCOKc_qx9re8Lt8BVct_Ct5qyYtrCvY384VX9EwnCJwFJYw5FiPi4eeAsOfY-0DQGfzPvS&quot; width=&quot;451&quot; height=&quot;165&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The market of the SalesCampaign doesn’t match the cart&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt; SalesCampaign can be restricted to specific Markets, or “All” (as for built in features at least, you can extend this to allow several but not all). Make sure your cart has the same market.&lt;br /&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/RQzDZRaAqJnkcPzGvUTNDz-WYUa1SbHdXlWDJKcvj408TLhLOIIrVd_48ZkHYALcKF-lM_a2zaHWZZriKhp3Zd0zSTQV-N4xqACKfBj59gfFheeGNGD9YWbMztOuEbVy3cFU0gwc&quot; width=&quot;472&quot; height=&quot;140&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;There is a Visitor group selected that your customer doesn’t have&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;On SalesCampaigns, you can have all Promotion children to only get applied if a certain Visitor Group has been applied. Make sure the Visitor Group criterion is fulfilled, if there is any set on the SalesCampaign.&lt;br /&gt;&lt;img src=&quot;https://lh4.googleusercontent.com/fDZhfY-5g2AxIuoY8Mv-7ugzjf6EMsDcAzQ7-UXzvpA2io0fyTgkoxO3qhof78P5Y1EJaQu9q1QM-EITpRaoll7Rv3Tp_kQ8w2BvFghsCooNPW37G9aqZaFKBiDOkO73Yn-iRd6c&quot; width=&quot;517&quot; height=&quot;143&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;br /&gt;&lt;em&gt;P.S. Don’t build a visitor group like that D.S.&lt;/em&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;You’ve reached a Redemption Limit&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt; Promotion level, you can set some redemption limits per order per customer and total overall usage. If you set one of these values and used the promotion in purchases more than that value, your promotion will not be selected due to this.&lt;br /&gt;
            &lt;img src=&quot;https://lh5.googleusercontent.com/CKZweVrnrzrcMf3c-8Ke9ZWCPEc9J28k0sjn1g4YNF-ora3154iC2j2lkGTuls6V8ljYNt-XsnYaYtsT3fL0ehp1NPoFoORY1JX8zWWw_Ba3TmV7lfqVJ0PeGlMcDgr5rpYL8ZY_&quot; width=&quot;467&quot; height=&quot;237&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
    &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;A bit less common cases&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You have disabled the promotion type showing up in the Marketing UI&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
&lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;
You can, by code, disable some promotion types. Maybe because you don&#39;t want the marketers to be able to create them, or that your code base doesn&#39;t support it. You do it in your initialization by using a PromotionTypeHandler. If you don&#39;t see the promotion type you want to create in the UI, head over to the &lt;a href=&quot;/link/b4fcab01c2844550bcede3d32e5ba883.aspx&quot;&gt;documentation of hiding promotion types&lt;/a&gt; and then check if you are doing exactly that.
&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Another Promotion is excluding yours&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;You can set up that some promotions shouldn’t be combined. Effectively, taking priority into account, there might be another Promotion with higher priority that excludes yours. You can see all the priority and exclusions clicking on this button
            &lt;img src=&quot;https://lh5.googleusercontent.com/tnsOowHR7h6w4DpkGpSNgVfkYhivOvimPD1JzUDOXMmfF2LNfo5eZeZeUc0hZIseUOKattbpiP797mGMR_sXcFh2hpexz4lbocwennSB106erQBbmUhABpBfA-0HF9gGPVGX7OOn&quot; width=&quot;38&quot; height=&quot;31&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt; when you’re in the Commerce -&amp;gt; Marketing start page. Put your promotion in the top priority if you want to test if that’s the issue.&lt;br /&gt;
            &lt;img src=&quot;https://lh5.googleusercontent.com/5nNdLwxk2u_CEKjQaxVPIbgqmhxQ-Oi9LRAqbdOp994oSz50MYbQd_MLjCw5FInGokS26R_um-2UCFEpvRQbCJesSkkfrkT9DFi8tRnwquu8XLNf0ezOiWrKVjdmgc8izLi92myC&quot; width=&quot;311&quot; height=&quot;93&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The Evaluation of the promotion fails&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;This one is a bit harder to help with, as it differs from Promotion to Promotion, what implementation of the PromotionProcessor exists. If it’s your custom Promotion, you have a much easier as you can just debug the Processor you wrote.&lt;br /&gt;Note
            that before running the Evaluate method, it will run a CanBeFulfilled method on your processor. If it doesn’t exist it will fallback to the CanBeFulfilled on the PromotionProcessorBase that simply returns true. But if you have implemented
            a CanBeFulfilled method to make it performant, make sure that one returns true as well. If you are inheriting from some pre built processor, such as GetItemDiscountProcessorBase, that one will have a built in CanBeFulfilled with some logic.&lt;br /&gt;Another
            thing I can recommend checking here is if you have a “Spend Amount” condition in your promotion, or a “Recieve Discount Amount” in the reward, make sure you have a value in the &lt;strong&gt;same currency&lt;/strong&gt; as the cart. Otherwise it won’t
            be fulfilled as it doesn’t know how much to give you.&lt;/p&gt;
    &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The rare cases&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;(Entry level only) You have an item that should fulfill condition but is excluded by an entry filter&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;Quite rare this would happen by accident, but I have done it to myself and felt quite stupid for a moment. Make sure that if you’ve set up &lt;a href=&quot;/link/c874b7d7594a4d818c00bc87c2036105.aspx&quot;&gt;one of these&lt;/a&gt;            your items are not affected by that excluder.&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;(Manual promotion only) You are applying the promotions in the wrong way&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;If you want to apply a manual promotion (creating a PromotionInformation object in memory and applying that one ,see &lt;a href=&quot;/link/6f4db3d220c44dd19931002499c0b3c4.aspx&quot;&gt;this forum post &lt;/a&gt;for
            info on how to do it), you can’t run promotions using the IPromotionEngine directly. So you can’t do _promotionEngine.Run(cart); you need to do call (IOrderGroup)cart.ApplyDiscounts(); Reason for this is that the PromotionEngine clears all
            the PromotionInformation objects from the OrderForm before starting to process promotions, so you’d lose your manual promotion if you’ve added it. What the extension method ApplyDiscounts does is that it will extract the manual promotions
            from the orderform, storing them safely. Then it will run the promotion engine and after it’s done will manually apply the manual promotions. This is quite quirky in my opinion and creates bugs like &lt;a href=&quot;/link/9f16c3ffe25045c0bce541d45d0c3184.aspx&quot;&gt;this one&lt;/a&gt;,
            but there you have it.&lt;/p&gt;
    &lt;/li&gt;
&lt;/ul&gt;
                </description>            <guid>http://www.avensia.com/joel-yourstone/why-isnt-my-promotion-showing/</guid>            <pubDate>Sat, 10 Feb 2018 13:59:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Why isn&#39;t my promotion showing?</title>            <link>https://www.avensia.com/joel-yourstone/why-isnt-my-promotion-showing/</link>            <description>
                    &lt;p&gt;A question I often get internally and see a lot in the Commerce forum is a version of the title of this blog post. A person has a promotion, could be built in, could be custom, could be manual, etc. and same person is scratching heads over why the promotion
    hasn’t been applied on the checkout page (or in the cart). So, I’ll try to make a checklist from my experience why a promotion is not applying.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;I’m sure I will add more cases as time goes, but this is what I can come up with so far. There are a lot of promotion specific cases where it won’t be applied, but if you believe it should be applied, check this list before looking at the promotion processor
    itself.
&lt;/p&gt;
&lt;p&gt;
    &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(&#39;.dev-explainations&#39;).toggle()&quot;&gt;Expand all explainations&lt;/a&gt;
&lt;/p&gt;
&lt;h2&gt;The common cases&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;The promotion isn’t active&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;Super easy to check this. The Marketing UI will give hints fading your Promotion or entire Campaign if it’s inactive, but check the following fields on both the Promotion and Campaign (if the Scheduling is set as “Same as the campaign”).&lt;br /&gt;
            &lt;img src=&quot;https://lh3.googleusercontent.com/DpsiRT4K04XG8DR-oEZiXbmVCq0qAoOPnEBK08Ntw8Vfo5Luks-9IJVgbku0h3s95c6buhV1my3gTkAG286VwPeS0lNkQDulCRH8HT9eCZc7edsvnkfx_lkAa8-Ttsa8oKM8BR3b&quot; width=&quot;512&quot; height=&quot;331&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The promotion has a coupon code&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;Perhaps you missed or forgot that you entered a coupon code for the promotion, that you haven’t applied to the cart? If it has a coupon code which the cart doesn’t have, the promotion engine will filter it out before starting the evaluation.&lt;br /&gt;
            &lt;img src=&quot;https://lh6.googleusercontent.com/9fs2gDajNKjzkv_ie2DAHpm_kOMWN453xyhex6YD2cfuuSaSFvkDPpOc82du-ESNn5fCOKc_qx9re8Lt8BVct_Ct5qyYtrCvY384VX9EwnCJwFJYw5FiPi4eeAsOfY-0DQGfzPvS&quot; width=&quot;451&quot; height=&quot;165&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The market of the SalesCampaign doesn’t match the cart&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt; SalesCampaign can be restricted to specific Markets, or “All” (as for built in features at least, you can extend this to allow several but not all). Make sure your cart has the same market.&lt;br /&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/RQzDZRaAqJnkcPzGvUTNDz-WYUa1SbHdXlWDJKcvj408TLhLOIIrVd_48ZkHYALcKF-lM_a2zaHWZZriKhp3Zd0zSTQV-N4xqACKfBj59gfFheeGNGD9YWbMztOuEbVy3cFU0gwc&quot; width=&quot;472&quot; height=&quot;140&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;There is a Visitor group selected that your customer doesn’t have&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;On SalesCampaigns, you can have all Promotion children to only get applied if a certain Visitor Group has been applied. Make sure the Visitor Group criterion is fulfilled, if there is any set on the SalesCampaign.&lt;br /&gt;&lt;img src=&quot;https://lh4.googleusercontent.com/fDZhfY-5g2AxIuoY8Mv-7ugzjf6EMsDcAzQ7-UXzvpA2io0fyTgkoxO3qhof78P5Y1EJaQu9q1QM-EITpRaoll7Rv3Tp_kQ8w2BvFghsCooNPW37G9aqZaFKBiDOkO73Yn-iRd6c&quot; width=&quot;517&quot; height=&quot;143&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;br /&gt;&lt;em&gt;P.S. Don’t build a visitor group like that D.S.&lt;/em&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;You’ve reached a Redemption Limit&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt; Promotion level, you can set some redemption limits per order per customer and total overall usage. If you set one of these values and used the promotion in purchases more than that value, your promotion will not be selected due to this.&lt;br /&gt;
            &lt;img src=&quot;https://lh5.googleusercontent.com/CKZweVrnrzrcMf3c-8Ke9ZWCPEc9J28k0sjn1g4YNF-ora3154iC2j2lkGTuls6V8ljYNt-XsnYaYtsT3fL0ehp1NPoFoORY1JX8zWWw_Ba3TmV7lfqVJ0PeGlMcDgr5rpYL8ZY_&quot; width=&quot;467&quot; height=&quot;237&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
    &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;A bit less common cases&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You have disabled the promotion type showing up in the Marketing UI&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
&lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;
You can, by code, disable some promotion types. Maybe because you don&#39;t want the marketers to be able to create them, or that your code base doesn&#39;t support it. You do it in your initialization by using a PromotionTypeHandler. If you don&#39;t see the promotion type you want to create in the UI, head over to the &lt;a href=&quot;/link/b4fcab01c2844550bcede3d32e5ba883.aspx&quot;&gt;documentation of hiding promotion types&lt;/a&gt; and then check if you are doing exactly that.
&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Another Promotion is excluding yours&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;You can set up that some promotions shouldn’t be combined. Effectively, taking priority into account, there might be another Promotion with higher priority that excludes yours. You can see all the priority and exclusions clicking on this button
            &lt;img src=&quot;https://lh5.googleusercontent.com/tnsOowHR7h6w4DpkGpSNgVfkYhivOvimPD1JzUDOXMmfF2LNfo5eZeZeUc0hZIseUOKattbpiP797mGMR_sXcFh2hpexz4lbocwennSB106erQBbmUhABpBfA-0HF9gGPVGX7OOn&quot; width=&quot;38&quot; height=&quot;31&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt; when you’re in the Commerce -&amp;gt; Marketing start page. Put your promotion in the top priority if you want to test if that’s the issue.&lt;br /&gt;
            &lt;img src=&quot;https://lh5.googleusercontent.com/5nNdLwxk2u_CEKjQaxVPIbgqmhxQ-Oi9LRAqbdOp994oSz50MYbQd_MLjCw5FInGokS26R_um-2UCFEpvRQbCJesSkkfrkT9DFi8tRnwquu8XLNf0ezOiWrKVjdmgc8izLi92myC&quot; width=&quot;311&quot; height=&quot;93&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The Evaluation of the promotion fails&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;This one is a bit harder to help with, as it differs from Promotion to Promotion, what implementation of the PromotionProcessor exists. If it’s your custom Promotion, you have a much easier as you can just debug the Processor you wrote.&lt;br /&gt;Note
            that before running the Evaluate method, it will run a CanBeFulfilled method on your processor. If it doesn’t exist it will fallback to the CanBeFulfilled on the PromotionProcessorBase that simply returns true. But if you have implemented
            a CanBeFulfilled method to make it performant, make sure that one returns true as well. If you are inheriting from some pre built processor, such as GetItemDiscountProcessorBase, that one will have a built in CanBeFulfilled with some logic.&lt;br /&gt;Another
            thing I can recommend checking here is if you have a “Spend Amount” condition in your promotion, or a “Recieve Discount Amount” in the reward, make sure you have a value in the &lt;strong&gt;same currency&lt;/strong&gt; as the cart. Otherwise it won’t
            be fulfilled as it doesn’t know how much to give you.&lt;/p&gt;
    &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The rare cases&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;(Entry level only) You have an item that should fulfill condition but is excluded by an entry filter&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;Quite rare this would happen by accident, but I have done it to myself and felt quite stupid for a moment. Make sure that if you’ve set up &lt;a href=&quot;/link/c874b7d7594a4d818c00bc87c2036105.aspx&quot;&gt;one of these&lt;/a&gt;            your items are not affected by that excluder.&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;(Manual promotion only) You are applying the promotions in the wrong way&lt;/strong&gt;&lt;br /&gt;
        &lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;
        &lt;p class=&quot;dev-explainations&quot; style=&quot;display: none&quot;&gt;If you want to apply a manual promotion (creating a PromotionInformation object in memory and applying that one ,see &lt;a href=&quot;/link/6f4db3d220c44dd19931002499c0b3c4.aspx&quot;&gt;this forum post &lt;/a&gt;for
            info on how to do it), you can’t run promotions using the IPromotionEngine directly. So you can’t do _promotionEngine.Run(cart); you need to do call (IOrderGroup)cart.ApplyDiscounts(); Reason for this is that the PromotionEngine clears all
            the PromotionInformation objects from the OrderForm before starting to process promotions, so you’d lose your manual promotion if you’ve added it. What the extension method ApplyDiscounts does is that it will extract the manual promotions
            from the orderform, storing them safely. Then it will run the promotion engine and after it’s done will manually apply the manual promotions. This is quite quirky in my opinion and creates bugs like &lt;a href=&quot;/link/9f16c3ffe25045c0bce541d45d0c3184.aspx&quot;&gt;this one&lt;/a&gt;,
            but there you have it.&lt;/p&gt;
    &lt;/li&gt;
&lt;/ul&gt;
                </description>            <guid>https://www.avensia.com/joel-yourstone/why-isnt-my-promotion-showing/</guid>            <pubDate>Sat, 10 Feb 2018 13:59:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Performance, it’s a mindset</title>            <link>http://avensia.com/joel-yourstone/performance-its-a-mindset/</link>            <description>
                    &lt;p&gt;I’ve read a lot of blogs recently about performance for (mostly) commerce solutions. This time of year it is crucial, as it is the yearly organic load test for almost all people selling&amp;nbsp;things online. I’m of course talking about the combination of Black Friday (weekend/week), Cyber Monday&amp;nbsp;and the Christmas&amp;nbsp;sales. This period of roughly one month can completely decimate an ecommerce vendor if they are not performant enough. Not only talking about completely being down, but as well that conversion rate is proportional to how fast your website is. I.e. people are too lazy to wait another second and will go to another competitor in another tab instead.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;So who determines if a site is slow or fast? Is it the Newrelic APM threshold thingy? Google PageSpeed Insights? No. &lt;strong&gt;It’s the customer&lt;/strong&gt;. &lt;/p&gt;
&lt;p&gt;That maybe makes it harder, some might think, but for me I believe it’s easier to work with. It is subjective. But also quite objective. UX designers and performance geeks are all quite agreed on what ultimately is a nice &lt;strong&gt;experience &lt;/strong&gt;on a web page. Pure performance metrics absolutely has its part here, but more importantly the user’s perception of performance.&lt;/p&gt;
&lt;p&gt;There are a lot of great people talking about performance, here again especially around this time of the year, targeting the backend part of the application, which to be fair is the part you need to have under control to avoid downtime. Noteworthy mention is &lt;a href=&quot;http://vimvq1987.com/episerver/performance/&quot;&gt;Quan Mai’s blog&lt;/a&gt;, he writes a lot about performance, especially in the data layer of a site (i.e. SQL Server stuff). But I see far too few posts about performance on an architectural level. Can we challenge the architecture we have to make something more performant? Or can we even just make something that &lt;strong&gt;seems&lt;/strong&gt; more performant?&lt;/p&gt;
&lt;h2&gt;Fake it til you make it&lt;/h2&gt;
&lt;p&gt;On several Epi Commerce implementations we’ve made at Avensia, I’d like to say that we’ve had this phrase in mind when it comes to performance a lot. Of course we take the needed steps in order to have a performant system generally, but I think we’ve done quite a lot of work with the perceived performance. &lt;/p&gt;
&lt;p&gt;An example that we’ve tried successfully to implement:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://lh4.googleusercontent.com/gmlz48WH2t1liSR0GZ2rxyeC_-XZ1SqK6nvbnyr8zIYR4FVklx-OiU0Z-QHYEYhaGqdLvMK6W6Sae4fwtM_KFF8-KnhOb-aGyBz9_RueKa_rTiriA-qdEI-IjpvnkFxBKFZRNsi8&quot; width=&quot;602&quot; height=&quot;352&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When we are on a category page and see a list with products. Once we click a product, the expected behaviour from the user is that they will load and see the product page. And that’s what we’ll do, but in this case, we can be smart about the data management. We already partially have the data that we’re displaying on the category page. In the small product card on the category page, we have the price(s), an image and the title of the product. That’s essentially 80% of the mobile viewport on the top of the product page. So why not just show that data instantly? &lt;/p&gt;
&lt;p&gt;When a user clicks on the product card, we take the data we have and instantly renders the product page with this data. For the user, the page load is &lt;strong&gt;instantaneous&lt;/strong&gt;. However we know that it’s not the real truth, we haven’t loaded everything yet. But changes are that we will have enough time to download and render the full product page in the background while the user reacts to the new screen of info, so when they start to scroll down, it’s all there!&lt;/p&gt;
&lt;p&gt;Page load is not ultra fast, maybe 100-200ms. But we are &lt;strong&gt;buying time&lt;/strong&gt; to have a chance of “fooling” the user to believe it was 0-4ms (time to render the new HTML). This is a perfect example of a “fake it til you make it” approach that really works. I can say that this performance is better than the one we had before, even though the numbers are exactly the same. Because again, performance is partly about perception!&lt;/p&gt;
&lt;p&gt;There are tons of other things you can do with data management and on an architectural level to work with this brand of performance. This said, we can’t just bypass the other performance work. We can’t fake it all the way. There’s a reason that we’re getting 100-200ms product page load at 20 simultaneous user as well as the same page load at 3000 simultaneous users. And that’s not perceived!&lt;/p&gt;
&lt;h2&gt;Truth it til you make it&lt;/h2&gt;
&lt;p&gt;I’m writing a blog post about Epi Commerce performance, I have to touch performance that isn’t just perception as well. Again, I won’t go much into detail, there are tons of other posts that already explain a lot, but here goes some of my personal favourites.&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Maintain your indexes&lt;/strong&gt;&lt;strong&gt;&lt;br /&gt;&lt;/strong&gt;Or make sure someone else does it for you. You don’t need application level insight to maintain one, however you do need application level insight to create one. So looking out for indexes that should be created is a job for the developer!&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Don’t load things more than necessary&lt;/strong&gt;&lt;strong&gt;&lt;br /&gt;&lt;/strong&gt;This is super important, but still I can say more than half (if not way more) of Epi Commerce sites don’t follow through on this point. One clear example is the cart. When do you need to reload the cart and re-calculate promotions? If you can properly answer this question and that answer maps 1:1 to your implementation you’ve done a great job! &lt;/li&gt;&lt;li&gt;&lt;strong&gt;Delegate heavy things that the user don’t need direct feedback on&lt;/strong&gt;&lt;br /&gt;An example - the user adds something to the cart. Is there anything the user wants in return more than the item added to the cart? I can only think of one thing, an updated subtotal in the cart that have run promotions with the new item in the cart. But all other parts, as in actually creating the item in the cart in frontend should probably be done instantly, and only a small spinner on the cart total. The user don’t need to wait with everything for the AddToCart response, that thinking can be done in the background.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Make sure your hardware has the correct dimension in the correct places&lt;/strong&gt;&lt;strong&gt;&lt;br /&gt;&lt;/strong&gt;Hardware is a bit tricky with Epi Commerce in my mind. In theory, the system only has one bottle neck, which is the database. So make sure you have the processors/disks/memory needed for your needs. But also make sure that all scalable parts of your system are easy to scale and aren’t under dimensioned, forcing you to scale unnecessarily much.&lt;br /&gt;Another thing to mention is that the cache is completely memory based, so if you’re not using a separate cache server like Redis, you’ll want quite a lot of memory on each web front. You don’t want cache that’s evicted too early due to memory limitations!&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Finding things that cost a lot fetching or managing can be tricky, but smart use of performance tools will help here. I could talk a lot about specific findings we’ve done with SQL Profiler and New Relic APM Thread profiling, but to keep this post’s topic I’ll do that in a separate blog post.&lt;/p&gt;
&lt;p&gt;These points have all come from experience. Some bad experiences, some good, but you live and you learn!&lt;/p&gt;
&lt;h2&gt;Combining the two approaches&amp;hellip;&lt;/h2&gt;
&lt;p&gt;&amp;hellip; makes you have a different mindset when it comes to performance. Performance is architecture, architecture is performance. Even if you are a frontend developer, it’s good to have the performance mindset to think data management and have solutions for where to store and manage your data.&lt;/p&gt;
&lt;p&gt;Performance really is a mindset that one needs to have as a developer and continually challenge and improve. I’m far away from being an expert in these fields, but I try to embrace this mindset as much as I can, to try to create performant cutting edge solutions!&lt;/p&gt;
                </description>            <guid>http://avensia.com/joel-yourstone/performance-its-a-mindset/</guid>            <pubDate>Thu, 04 Jan 2018 17:34:05 GMT</pubDate>           <category>Blog post</category></item><item> <title>Performance, it’s a mindset</title>            <link>http://www.avensia.com/joel-yourstone/performance-its-a-mindset/</link>            <description>
                    &lt;p&gt;I’ve read a lot of blogs recently about performance for (mostly) commerce solutions. This time of year it is crucial, as it is the yearly organic load test for almost all people selling&amp;nbsp;things online. I’m of course talking about the combination of Black Friday (weekend/week), Cyber Monday&amp;nbsp;and the Christmas&amp;nbsp;sales. This period of roughly one month can completely decimate an ecommerce vendor if they are not performant enough. Not only talking about completely being down, but as well that conversion rate is proportional to how fast your website is. I.e. people are too lazy to wait another second and will go to another competitor in another tab instead.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;So who determines if a site is slow or fast? Is it the Newrelic APM threshold thingy? Google PageSpeed Insights? No. &lt;strong&gt;It’s the customer&lt;/strong&gt;. &lt;/p&gt;
&lt;p&gt;That maybe makes it harder, some might think, but for me I believe it’s easier to work with. It is subjective. But also quite objective. UX designers and performance geeks are all quite agreed on what ultimately is a nice &lt;strong&gt;experience &lt;/strong&gt;on a web page. Pure performance metrics absolutely has its part here, but more importantly the user’s perception of performance.&lt;/p&gt;
&lt;p&gt;There are a lot of great people talking about performance, here again especially around this time of the year, targeting the backend part of the application, which to be fair is the part you need to have under control to avoid downtime. Noteworthy mention is &lt;a href=&quot;http://vimvq1987.com/episerver/performance/&quot;&gt;Quan Mai’s blog&lt;/a&gt;, he writes a lot about performance, especially in the data layer of a site (i.e. SQL Server stuff). But I see far too few posts about performance on an architectural level. Can we challenge the architecture we have to make something more performant? Or can we even just make something that &lt;strong&gt;seems&lt;/strong&gt; more performant?&lt;/p&gt;
&lt;h2&gt;Fake it til you make it&lt;/h2&gt;
&lt;p&gt;On several Epi Commerce implementations we’ve made at Avensia, I’d like to say that we’ve had this phrase in mind when it comes to performance a lot. Of course we take the needed steps in order to have a performant system generally, but I think we’ve done quite a lot of work with the perceived performance. &lt;/p&gt;
&lt;p&gt;An example that we’ve tried successfully to implement:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://lh4.googleusercontent.com/gmlz48WH2t1liSR0GZ2rxyeC_-XZ1SqK6nvbnyr8zIYR4FVklx-OiU0Z-QHYEYhaGqdLvMK6W6Sae4fwtM_KFF8-KnhOb-aGyBz9_RueKa_rTiriA-qdEI-IjpvnkFxBKFZRNsi8&quot; width=&quot;602&quot; height=&quot;352&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When we are on a category page and see a list with products. Once we click a product, the expected behaviour from the user is that they will load and see the product page. And that’s what we’ll do, but in this case, we can be smart about the data management. We already partially have the data that we’re displaying on the category page. In the small product card on the category page, we have the price(s), an image and the title of the product. That’s essentially 80% of the mobile viewport on the top of the product page. So why not just show that data instantly? &lt;/p&gt;
&lt;p&gt;When a user clicks on the product card, we take the data we have and instantly renders the product page with this data. For the user, the page load is &lt;strong&gt;instantaneous&lt;/strong&gt;. However we know that it’s not the real truth, we haven’t loaded everything yet. Chances are that we will have enough time to download and render the full product page in the background while the user reacts to the new screen of info, so when they start to scroll down, it’s all there!&lt;/p&gt;
&lt;p&gt;Page load is not ultra fast, maybe 100-200ms. But we are &lt;strong&gt;buying time&lt;/strong&gt; to have a chance of “fooling” the user to believe it was 0-4ms (time to render the new HTML). This is a perfect example of a “fake it til you make it” approach that really works. I can say that this performance is better than the one we had before, even though the numbers are exactly the same. Because again, performance is partly about perception!&lt;/p&gt;
&lt;p&gt;There are tons of other things you can do with data management and on an architectural level to work with this brand of performance. This said, we can’t just bypass the other performance work. We can’t fake it all the way. There’s a reason that we’re getting 100-200ms product page load at 20 simultaneous user as well as the same page load at 3000 simultaneous users. And that’s not perceived!&lt;/p&gt;
&lt;h2&gt;Truth it til you make it&lt;/h2&gt;
&lt;p&gt;I’m writing a blog post about Epi Commerce performance, I have to touch performance that isn’t just perception as well. Again, I won’t go much into detail, there are tons of other posts that already explain a lot, but here goes some of my personal favourites.&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Maintain your indexes&lt;/strong&gt;&lt;strong&gt;&lt;br /&gt;&lt;/strong&gt;Or make sure someone else does it for you. You don’t need application level insight to maintain one, however you do need application level insight to create one. So looking out for indexes that should be created is a job for the developer!&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Don’t load things more than necessary&lt;/strong&gt;&lt;strong&gt;&lt;br /&gt;&lt;/strong&gt;This is super important, but still I can say more than half (if not way more) of Epi Commerce sites don’t follow through on this point. One clear example is the cart. When do you need to reload the cart and re-calculate promotions? If you can properly answer this question and that answer maps 1:1 to your implementation you’ve done a great job! &lt;/li&gt;&lt;li&gt;&lt;strong&gt;Delegate heavy things that the user don’t need direct feedback on&lt;/strong&gt;&lt;br /&gt;An example - the user adds something to the cart. Is there anything the user wants in return more than the item added to the cart? I can only think of one thing, an updated subtotal in the cart that have run promotions with the new item in the cart. But all other parts, as in actually creating the item in the cart in frontend should probably be done instantly, and only a small spinner on the cart total. The user don’t need to wait with everything for the AddToCart response, that thinking can be done in the background.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Make sure your hardware has the correct dimension in the correct places&lt;/strong&gt;&lt;strong&gt;&lt;br /&gt;&lt;/strong&gt;Hardware is a bit tricky with Epi Commerce in my mind. In theory, the system only has one bottle neck, which is the database. So make sure you have the processors/disks/memory needed for your needs. But also make sure that all scalable parts of your system are easy to scale and aren’t under dimensioned, forcing you to scale unnecessarily much.&lt;br /&gt;Another thing to mention is that the cache is completely memory based, so if you’re not using a separate cache server like Redis, you’ll want quite a lot of memory on each web front. You don’t want cache that’s evicted too early due to memory limitations!&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Finding things that cost a lot fetching or managing can be tricky, but smart use of performance tools will help here. I could talk a lot about specific findings we’ve done with SQL Profiler and New Relic APM Thread profiling, but to keep this post’s topic I’ll do that in a separate blog post.&lt;/p&gt;
&lt;p&gt;These points have all come from experience. Some bad experiences, some good, but you live and you learn!&lt;/p&gt;
&lt;h2&gt;Combining the two approaches&amp;hellip;&lt;/h2&gt;
&lt;p&gt;&amp;hellip; makes you have a different mindset when it comes to performance. Performance is architecture, architecture is performance. Even if you are a frontend developer, it’s good to have the performance mindset to think data management and have solutions for where to store and manage your data.&lt;/p&gt;
&lt;p&gt;Performance really is a mindset that one needs to have as a developer and continually challenge and improve. I’m far away from being an expert in these fields, but I try to embrace this mindset as much as I can, to try to create performant cutting edge solutions!&lt;/p&gt;
                </description>            <guid>http://www.avensia.com/joel-yourstone/performance-its-a-mindset/</guid>            <pubDate>Thu, 04 Jan 2018 17:34:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Performance, it’s a mindset</title>            <link>https://www.avensia.com/joel-yourstone/performance-its-a-mindset/</link>            <description>
                    &lt;p&gt;I’ve read a lot of blogs recently about performance for (mostly) commerce solutions. This time of year it is crucial, as it is the yearly organic load test for almost all people selling&amp;nbsp;things online. I’m of course talking about the combination of Black Friday (weekend/week), Cyber Monday&amp;nbsp;and the Christmas&amp;nbsp;sales. This period of roughly one month can completely decimate an ecommerce vendor if they are not performant enough. Not only talking about completely being down, but as well that conversion rate is proportional to how fast your website is. I.e. people are too lazy to wait another second and will go to another competitor in another tab instead.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;So who determines if a site is slow or fast? Is it the Newrelic APM threshold thingy? Google PageSpeed Insights? No. &lt;strong&gt;It’s the customer&lt;/strong&gt;. &lt;/p&gt;
&lt;p&gt;That maybe makes it harder, some might think, but for me I believe it’s easier to work with. It is subjective. But also quite objective. UX designers and performance geeks are all quite agreed on what ultimately is a nice &lt;strong&gt;experience &lt;/strong&gt;on a web page. Pure performance metrics absolutely has its part here, but more importantly the user’s perception of performance.&lt;/p&gt;
&lt;p&gt;There are a lot of great people talking about performance, here again especially around this time of the year, targeting the backend part of the application, which to be fair is the part you need to have under control to avoid downtime. Noteworthy mention is &lt;a href=&quot;http://vimvq1987.com/episerver/performance/&quot;&gt;Quan Mai’s blog&lt;/a&gt;, he writes a lot about performance, especially in the data layer of a site (i.e. SQL Server stuff). But I see far too few posts about performance on an architectural level. Can we challenge the architecture we have to make something more performant? Or can we even just make something that &lt;strong&gt;seems&lt;/strong&gt; more performant?&lt;/p&gt;
&lt;h2&gt;Fake it til you make it&lt;/h2&gt;
&lt;p&gt;On several Epi Commerce implementations we’ve made at Avensia, I’d like to say that we’ve had this phrase in mind when it comes to performance a lot. Of course we take the needed steps in order to have a performant system generally, but I think we’ve done quite a lot of work with the perceived performance. &lt;/p&gt;
&lt;p&gt;An example that we’ve tried successfully to implement:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://lh4.googleusercontent.com/gmlz48WH2t1liSR0GZ2rxyeC_-XZ1SqK6nvbnyr8zIYR4FVklx-OiU0Z-QHYEYhaGqdLvMK6W6Sae4fwtM_KFF8-KnhOb-aGyBz9_RueKa_rTiriA-qdEI-IjpvnkFxBKFZRNsi8&quot; width=&quot;602&quot; height=&quot;352&quot; style=&quot;border-width: initial; border-style: none; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When we are on a category page and see a list with products. Once we click a product, the expected behaviour from the user is that they will load and see the product page. And that’s what we’ll do, but in this case, we can be smart about the data management. We already partially have the data that we’re displaying on the category page. In the small product card on the category page, we have the price(s), an image and the title of the product. That’s essentially 80% of the mobile viewport on the top of the product page. So why not just show that data instantly? &lt;/p&gt;
&lt;p&gt;When a user clicks on the product card, we take the data we have and instantly renders the product page with this data. For the user, the page load is &lt;strong&gt;instantaneous&lt;/strong&gt;. However we know that it’s not the real truth, we haven’t loaded everything yet. Chances are that we will have enough time to download and render the full product page in the background while the user reacts to the new screen of info, so when they start to scroll down, it’s all there!&lt;/p&gt;
&lt;p&gt;Page load is not ultra fast, maybe 100-200ms. But we are &lt;strong&gt;buying time&lt;/strong&gt; to have a chance of “fooling” the user to believe it was 0-4ms (time to render the new HTML). This is a perfect example of a “fake it til you make it” approach that really works. I can say that this performance is better than the one we had before, even though the numbers are exactly the same. Because again, performance is partly about perception!&lt;/p&gt;
&lt;p&gt;There are tons of other things you can do with data management and on an architectural level to work with this brand of performance. This said, we can’t just bypass the other performance work. We can’t fake it all the way. There’s a reason that we’re getting 100-200ms product page load at 20 simultaneous user as well as the same page load at 3000 simultaneous users. And that’s not perceived!&lt;/p&gt;
&lt;h2&gt;Truth it til you make it&lt;/h2&gt;
&lt;p&gt;I’m writing a blog post about Epi Commerce performance, I have to touch performance that isn’t just perception as well. Again, I won’t go much into detail, there are tons of other posts that already explain a lot, but here goes some of my personal favourites.&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Maintain your indexes&lt;/strong&gt;&lt;strong&gt;&lt;br /&gt;&lt;/strong&gt;Or make sure someone else does it for you. You don’t need application level insight to maintain one, however you do need application level insight to create one. So looking out for indexes that should be created is a job for the developer!&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Don’t load things more than necessary&lt;/strong&gt;&lt;strong&gt;&lt;br /&gt;&lt;/strong&gt;This is super important, but still I can say more than half (if not way more) of Epi Commerce sites don’t follow through on this point. One clear example is the cart. When do you need to reload the cart and re-calculate promotions? If you can properly answer this question and that answer maps 1:1 to your implementation you’ve done a great job! &lt;/li&gt;&lt;li&gt;&lt;strong&gt;Delegate heavy things that the user don’t need direct feedback on&lt;/strong&gt;&lt;br /&gt;An example - the user adds something to the cart. Is there anything the user wants in return more than the item added to the cart? I can only think of one thing, an updated subtotal in the cart that have run promotions with the new item in the cart. But all other parts, as in actually creating the item in the cart in frontend should probably be done instantly, and only a small spinner on the cart total. The user don’t need to wait with everything for the AddToCart response, that thinking can be done in the background.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Make sure your hardware has the correct dimension in the correct places&lt;/strong&gt;&lt;strong&gt;&lt;br /&gt;&lt;/strong&gt;Hardware is a bit tricky with Epi Commerce in my mind. In theory, the system only has one bottle neck, which is the database. So make sure you have the processors/disks/memory needed for your needs. But also make sure that all scalable parts of your system are easy to scale and aren’t under dimensioned, forcing you to scale unnecessarily much.&lt;br /&gt;Another thing to mention is that the cache is completely memory based, so if you’re not using a separate cache server like Redis, you’ll want quite a lot of memory on each web front. You don’t want cache that’s evicted too early due to memory limitations!&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Finding things that cost a lot fetching or managing can be tricky, but smart use of performance tools will help here. I could talk a lot about specific findings we’ve done with SQL Profiler and New Relic APM Thread profiling, but to keep this post’s topic I’ll do that in a separate blog post.&lt;/p&gt;
&lt;p&gt;These points have all come from experience. Some bad experiences, some good, but you live and you learn!&lt;/p&gt;
&lt;h2&gt;Combining the two approaches&amp;hellip;&lt;/h2&gt;
&lt;p&gt;&amp;hellip; makes you have a different mindset when it comes to performance. Performance is architecture, architecture is performance. Even if you are a frontend developer, it’s good to have the performance mindset to think data management and have solutions for where to store and manage your data.&lt;/p&gt;
&lt;p&gt;Performance really is a mindset that one needs to have as a developer and continually challenge and improve. I’m far away from being an expert in these fields, but I try to embrace this mindset as much as I can, to try to create performant cutting edge solutions!&lt;/p&gt;
                </description>            <guid>https://www.avensia.com/joel-yourstone/performance-its-a-mindset/</guid>            <pubDate>Thu, 04 Jan 2018 17:34:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Headless Epi Commerce, worth investing in?</title>            <link>http://www.avensia.com/joel-yourstone/headless-epi-commerce-worth-investing-in/</link>            <description>
                    &lt;p&gt;&lt;a href=&quot;#headless-tldr&quot;&gt;TL;DR Version&lt;/a&gt;&amp;nbsp;(Too Long; Didn&#39;t Read)&lt;/p&gt;
&lt;p&gt;Currently there is a lot of fuzz about “Headless Commerce”. I can’t say I’ve known the term more than a couple of months, however, I’ve worked with the concept for more than a year now. But a couple of months ago, this “buzzword” was introduced to me. Before researching more on the subject, I asked one of my brilliant colleagues what it was about. His response was “the thing we’re doing right now”. Ah. Right. So that must mean we’re doing some things right, no?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;So what is it about?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;In more detail, it’s about decoupling the user interface with the application logic, so that in theory, you can switch user interface entirely and have it working the same way. The&amp;nbsp;&lt;em&gt;user interface&lt;/em&gt;&amp;nbsp;doesn’t necessarily mean the&amp;nbsp;&lt;em&gt;design&lt;/em&gt;. It can be a complete other way a user interacts with your application. For example if you have a headless commerce website, you could implement a mobile app using the same logic, because the logic is not strictly coupled with the frontend of the website. Or even more trendy, make parts of your application usable via Alexa, Google Assistant, Cortana, etc. Voice APIs are here to stay and work exceedingly well with integrations.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Making an app with the existing “site” logic? Why wouldn’t you want to run headless?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;There are many positive effects of running headless and in my opinion they in most cases outweigh the negative effects. But if we are talking about the subject of what can go wrong, there are some mentionable points:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;It requires more from your development team.
Headless architecture is something you need to consider in all features, making sure you are not just catering to one UI. This also means you might need more code in the backend to handle this, whereas in a site project you could rely on site specific addons or pre-built packages. To be successful, I think you’d need a skilled tech lead in a team that’s building a headless ecommerce site.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Maintaining the same UX
One thing that seems to float a round quite much in the headless topic is that the UX for the UIs will generally be worse than if you built one backend for each UI. And while that is sort of true, you can and should work around it. You should make sure the site has the same UX as if it wouldn’t be a headless and the same with all the other UIs. You have all the tools to do it, it just might require a bit more thinking to make it happen.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;More often than not, you cannot use built in functionality if the functionality includes an UI implementation.
What I’m talking about here is features that comes shipped from your headless system that relies on a specific UI. Maybe it relies on javascript to be run, maybe it relies on being able to render a .cshtml-file, etc.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;The biggest benefit of running headless according to me...&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;hellip; is that you are completely free choosing any tools/frameworks/technologies implementing your UIs. I love experimenting with new technologies and evaluate if they would make the UX (counting in performance and all that) better for the end user. In the end, better UX allows you to have higher prices than the competitors, while still having the customers!&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Epi Cms + Commerce&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;So you might want to look into going headless. One of the big selling points is that you can use any CMS together with any Commerce system together with any UI. Why then use both Epi CMS and Commerce? Why not use Storm Commerce that’s built headless? I’m stealing a Powerpoint slide from mentioned brilliant colleague, Anders Ekdahl, to explain why we at Avensia think it’s a great idea.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/r10JLu5BOFquwEjZoOyj-3EYRFokh1Qo30vEBB3n-H32J38nYxAZZdzBI7Qty1rHhXQeQOARDAMJxMwkKgn1JcuQ0BwewGMTZdDh4wyAApjFx0zVSMTTETtGvzViLxQ42sBJbm8R&quot; width=&quot;602&quot; height=&quot;339&quot; style=&quot;border-width: initial; border-style: none; vertical-align: middle; cursor: pointer; max-width: 100%; height: auto; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
&lt;p&gt;So imagine that you have Storm Commerce as the Commerce Platform and Epi CMS as the Publishing Platform, because you wanted to try headless commerce. Storm Commerce will say “we integrate with anything, look at this API that gives you that possibility”. Epi CMS will say “we integrate with anything, look at this API that gives you that possibility”. Great. Perfect headless solution, right?&lt;/p&gt;
&lt;p&gt;The problem comes when we developers take a look at this.&amp;nbsp;&lt;b&gt;Wait, we need to build a complete integration between these two systems?&lt;/b&gt;&amp;nbsp;Of course Storm/Epi will never build the integration themselves because they can’t implement 40 connectors to 40 different systems and keep them up to date. So first you need to build a major stable connection between the systems and&amp;nbsp;&lt;em&gt;then&lt;/em&gt;&amp;nbsp;build an UI on top of that.&lt;/p&gt;
&lt;p&gt;This is where Epi CMS + Commerce shines. They’re&amp;nbsp;&lt;b&gt;already deeply integrated with themselves&lt;/b&gt;&amp;nbsp;and you can make this solution (of course together with some other Epi products) headless:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://lh5.googleusercontent.com/AGSx-W1CwDDGdbQrmRSjbzTMZgpYeCPFjWwZ1mY6BKfnhuAD82WoWCGettTbh2Pxq3HQVqndCY1AcqULCccaNiOPJFHOVE7FTvTCAj0BJ5SvHoQXnScxqwPRnjnAmahS0SGXCRP_&quot; width=&quot;602&quot; height=&quot;333&quot; style=&quot;border-width: initial; border-style: none; vertical-align: middle; cursor: pointer; max-width: 100%; height: auto; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
&lt;p&gt;So using all the benefits that comes from headless architecture, we’ve now gotten rid of a huge integration between two complex systems and can now build our headless implementation of our CMS and ecommerce system. Three words for making the headless layer implementation: make everything JSON!&lt;/p&gt;
&lt;p&gt;&lt;b&gt;So what have we done with it?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;At Avensia, we run a lot of labs testing different frontend technologies together with Epi CMS &amp; Ecommerce (among others). Some of these labs yield a “cool, but not really a use case for it right now”-result, others yield a “let’s use this instantly”-result. One of the “let’s use this instantly”-results lead us to build a Headless layer, named SCOPE, that powers many of our customer projects, including our first implementation of SCOPE,&amp;nbsp;&lt;a href=&quot;https://lyko.se/&quot;&gt;Lyko.se&lt;/a&gt;, that resulted in a site awarded&amp;nbsp;&lt;a href=&quot;http://www.episerver.com/about/news/press-room/pressreleases/lyko-awarded-for-best-nordic-website/&quot;&gt;Best Nordic website &amp; Commerce&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Challenges creating SCOPE&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Making Epi CMS+Commerce didn’t come without challenges and hardships. Developers much more talented than I have spent many hours solving some of the hardships. Since Epi comes with many out of the box solutions that are connected to the web UI, you have to make sure it works with your headless implementation. Things like On Page Editing, routing, promotions, etc must work like out of the box in order to deliver a great system to the content creators.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;What’s coming?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Our headless layer implementation have been relying on the existing usual APIs, the same ones you’d use for a standard implementation of Epi CMS+Commerce. However, Epi is working on a&amp;nbsp;&lt;b&gt;new package/API specifically tailored for going headless&amp;nbsp;&lt;/b&gt;that will be revealed Q1 2018. From what I’ve heard, the first version will have its focus on utilizing the Content system, making it easier to push any type of content to any platform. Not much details have been released, but I look forward to evaluating it in a lab session!&lt;/p&gt;
&lt;p id=&quot;headless-tldr&quot;&gt;&lt;b&gt;TL;DR&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;People saying their systems are headless doesn’t necessarily mean it’s easy to integrate and implement. Epi comes out of the box with an integrated CMS+Commerce platform that you can morph into a headless system.&lt;/p&gt;
                </description>            <guid>http://www.avensia.com/joel-yourstone/headless-epi-commerce-worth-investing-in/</guid>            <pubDate>Tue, 14 Nov 2017 04:53:35 GMT</pubDate>           <category>Blog post</category></item><item> <title>Headless Epi Commerce, worth investing in?</title>            <link>http://avensia.com/joel-yourstone/headless-epi-commerce-worth-investing-in/</link>            <description>
                    &lt;p&gt;&lt;a href=&quot;#headless-tldr&quot;&gt;TL;DR Version&lt;/a&gt;&amp;nbsp;(Too Long; Didn&#39;t Read)&lt;/p&gt;
&lt;p&gt;Currently there is a lot of fuzz about “Headless Commerce”. I can’t say I’ve known the term more than a couple of months, however, I’ve worked with the concept for more than a year now. But a couple of months ago, this “buzzword” was introduced to me. Before researching more on the subject, I asked one of my brilliant colleagues what it was about. His response was “the thing we’re doing right now”. Ah. Right. So that must mean we’re doing some things right, no?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;So what is it about?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;In more detail, it’s about decoupling the user interface with the application logic, so that in theory, you can switch user interface entirely and have it working the same way. The&amp;nbsp;&lt;em&gt;user interface&lt;/em&gt;&amp;nbsp;doesn’t necessarily mean the&amp;nbsp;&lt;em&gt;design&lt;/em&gt;. It can be a complete other way a user interacts with your application. For example if you have a headless commerce website, you could implement a mobile app using the same logic, because the logic is not strictly coupled with the frontend of the website. Or even more trendy, make parts of your application usable via Alexa, Google Assistant, Cortana, etc. Voice APIs are here to stay and work exceedingly well with integrations.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Making an app with the existing “site” logic? Why wouldn’t you want to run headless?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;There are many positive effects of running headless and in my opinion they in most cases outweigh the negative effects. But if we are talking about the subject of what can go wrong, there are some mentionable points:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;It requires more from your development team.
Headless architecture is something you need to consider in all features, making sure you are not just catering to one UI. This also means you might need more code in the backend to handle this, whereas in a site project you could rely on site specific addons or pre-built packages. To be successful, I think you’d need a skilled tech lead in a team that’s building a headless ecommerce site.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Maintaining the same UX
One thing that seems to float a round quite much in the headless topic is that the UX for the UIs will generally be worse than if you built one backend for each UI. And while that is sort of true, you can and should work around it. You should make sure the site has the same UX as if it wouldn’t be a headless and the same with all the other UIs. You have all the tools to do it, it just might require a bit more thinking to make it happen.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;More often than not, you cannot use built in functionality if the functionality includes an UI implementation.
What I’m talking about here is features that comes shipped from your headless system that relies on a specific UI. Maybe it relies on javascript to be run, maybe it relies on being able to render a .cshtml-file, etc.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;The biggest benefit of running headless according to me...&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;hellip; is that you are completely free choosing any tools/frameworks/technologies implementing your UIs. I love experimenting with new technologies and evaluate if they would make the UX (counting in performance and all that) better for the end user. In the end, better UX allows you to have higher prices than the competitors, while still having the customers!&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Epi Cms + Commerce&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;So you might want to look into going headless. One of the big selling points is that you can use any CMS together with any Commerce system together with any UI. Why then use both Epi CMS and Commerce? Why not use Storm Commerce that’s built headless? I’m stealing a Powerpoint slide from mentioned brilliant colleague, Anders Ekdahl, to explain why we at Avensia think it’s a great idea.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/r10JLu5BOFquwEjZoOyj-3EYRFokh1Qo30vEBB3n-H32J38nYxAZZdzBI7Qty1rHhXQeQOARDAMJxMwkKgn1JcuQ0BwewGMTZdDh4wyAApjFx0zVSMTTETtGvzViLxQ42sBJbm8R&quot; width=&quot;602&quot; height=&quot;339&quot; style=&quot;border-width: initial; border-style: none; vertical-align: middle; cursor: pointer; max-width: 100%; height: auto; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
&lt;p&gt;So imagine that you have Storm Commerce as the Commerce Platform and Epi CMS as the Publishing Platform, because you wanted to try headless commerce. Storm Commerce will say “we integrate with anything, look at this API that gives you that possibility”. Epi CMS will say “we integrate with anything, look at this API that gives you that possibility”. Great. Perfect headless solution, right?&lt;/p&gt;
&lt;p&gt;The problem comes when we developers take a look at this.&amp;nbsp;&lt;b&gt;Wait, we need to build a complete integration between these two systems?&lt;/b&gt;&amp;nbsp;Of course Storm/Epi will never build the integration themselves because they can’t implement 40 connectors to 40 different systems and keep them up to date. So first you need to build a major stable connection between the systems and&amp;nbsp;&lt;em&gt;then&lt;/em&gt;&amp;nbsp;build an UI on top of that.&lt;/p&gt;
&lt;p&gt;This is where Epi CMS + Commerce shines. They’re&amp;nbsp;&lt;b&gt;already deeply integrated with themselves&lt;/b&gt;&amp;nbsp;and you can make this solution (of course together with some other Epi products) headless:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://lh5.googleusercontent.com/AGSx-W1CwDDGdbQrmRSjbzTMZgpYeCPFjWwZ1mY6BKfnhuAD82WoWCGettTbh2Pxq3HQVqndCY1AcqULCccaNiOPJFHOVE7FTvTCAj0BJ5SvHoQXnScxqwPRnjnAmahS0SGXCRP_&quot; width=&quot;602&quot; height=&quot;333&quot; style=&quot;border-width: initial; border-style: none; vertical-align: middle; cursor: pointer; max-width: 100%; height: auto; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
&lt;p&gt;So using all the benefits that comes from headless architecture, we’ve now gotten rid of a huge integration between two complex systems and can now build our headless implementation of our CMS and ecommerce system. Three words for making the headless layer implementation: make everything JSON!&lt;/p&gt;
&lt;p&gt;&lt;b&gt;So what have we done with it?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;At Avensia, we run a lot of labs testing different frontend technologies together with Epi CMS &amp; Ecommerce (among others). Some of these labs yield a “cool, but not really a use case for it right now”-result, others yield a “let’s use this instantly”-result. One of the “let’s use this instantly”-results lead us to build a Headless layer, named SCOPE, that powers many of our customer projects, including our first implementation of SCOPE,&amp;nbsp;&lt;a href=&quot;https://lyko.se/&quot;&gt;Lyko.se&lt;/a&gt;, that resulted in a site awarded&amp;nbsp;&lt;a href=&quot;http://www.episerver.com/about/news/press-room/pressreleases/lyko-awarded-for-best-nordic-website/&quot;&gt;Best Nordic website &amp; Commerce&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Challenges creating SCOPE&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Making Epi CMS+Commerce didn’t come without challenges and hardships. Developers much more talented than I have spent many hours solving some of the hardships. Since Epi comes with many out of the box solutions that are connected to the web UI, you have to make sure it works with your headless implementation. Things like On Page Editing, routing, promotions, etc must work like out of the box in order to deliver a great system to the content creators.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;What’s coming?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Our headless layer implementation have been relying on the existing usual APIs, the same ones you’d use for a standard implementation of Epi CMS+Commerce. However, Epi is working on a&amp;nbsp;&lt;b&gt;new package/API specifically tailored for going headless&amp;nbsp;&lt;/b&gt;that will be revealed Q1 2018. From what I’ve heard, the first version will have its focus on utilizing the Content system, making it easier to push any type of content to any platform. Not much details have been released, but I look forward to evaluating it in a lab session!&lt;/p&gt;
&lt;p id=&quot;headless-tldr&quot;&gt;&lt;b&gt;TL;DR&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;People saying their systems are headless doesn’t necessarily mean it’s easy to integrate and implement. Epi comes out of the box with an integrated CMS+Commerce platform that you can morph into a headless system.&lt;/p&gt;
                </description>            <guid>http://avensia.com/joel-yourstone/headless-epi-commerce-worth-investing-in/</guid>            <pubDate>Tue, 14 Nov 2017 04:53:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Headless Epi Commerce, worth investing in?</title>            <link>https://www.avensia.com/joel-yourstone/headless-epi-commerce-worth-investing-in/</link>            <description>
                    &lt;p&gt;&lt;a href=&quot;#headless-tldr&quot;&gt;TL;DR Version&lt;/a&gt;&amp;nbsp;(Too Long; Didn&#39;t Read)&lt;/p&gt;
&lt;p&gt;Currently there is a lot of fuzz about “Headless Commerce”. I can’t say I’ve known the term more than a couple of months, however, I’ve worked with the concept for more than a year now. But a couple of months ago, this “buzzword” was introduced to me. Before researching more on the subject, I asked one of my brilliant colleagues what it was about. His response was “the thing we’re doing right now”. Ah. Right. So that must mean we’re doing some things right, no?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;So what is it about?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;In more detail, it’s about decoupling the user interface with the application logic, so that in theory, you can switch user interface entirely and have it working the same way. The&amp;nbsp;&lt;em&gt;user interface&lt;/em&gt;&amp;nbsp;doesn’t necessarily mean the&amp;nbsp;&lt;em&gt;design&lt;/em&gt;. It can be a complete other way a user interacts with your application. For example if you have a headless commerce website, you could implement a mobile app using the same logic, because the logic is not strictly coupled with the frontend of the website. Or even more trendy, make parts of your application usable via Alexa, Google Assistant, Cortana, etc. Voice APIs are here to stay and work exceedingly well with integrations.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Making an app with the existing “site” logic? Why wouldn’t you want to run headless?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;There are many positive effects of running headless and in my opinion they in most cases outweigh the negative effects. But if we are talking about the subject of what can go wrong, there are some mentionable points:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;It requires more from your development team.
Headless architecture is something you need to consider in all features, making sure you are not just catering to one UI. This also means you might need more code in the backend to handle this, whereas in a site project you could rely on site specific addons or pre-built packages. To be successful, I think you’d need a skilled tech lead in a team that’s building a headless ecommerce site.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Maintaining the same UX
One thing that seems to float a round quite much in the headless topic is that the UX for the UIs will generally be worse than if you built one backend for each UI. And while that is sort of true, you can and should work around it. You should make sure the site has the same UX as if it wouldn’t be a headless and the same with all the other UIs. You have all the tools to do it, it just might require a bit more thinking to make it happen.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;More often than not, you cannot use built in functionality if the functionality includes an UI implementation.
What I’m talking about here is features that comes shipped from your headless system that relies on a specific UI. Maybe it relies on javascript to be run, maybe it relies on being able to render a .cshtml-file, etc.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;The biggest benefit of running headless according to me...&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;hellip; is that you are completely free choosing any tools/frameworks/technologies implementing your UIs. I love experimenting with new technologies and evaluate if they would make the UX (counting in performance and all that) better for the end user. In the end, better UX allows you to have higher prices than the competitors, while still having the customers!&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Epi Cms + Commerce&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;So you might want to look into going headless. One of the big selling points is that you can use any CMS together with any Commerce system together with any UI. Why then use both Epi CMS and Commerce? Why not use Storm Commerce that’s built headless? I’m stealing a Powerpoint slide from mentioned brilliant colleague, Anders Ekdahl, to explain why we at Avensia think it’s a great idea.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/r10JLu5BOFquwEjZoOyj-3EYRFokh1Qo30vEBB3n-H32J38nYxAZZdzBI7Qty1rHhXQeQOARDAMJxMwkKgn1JcuQ0BwewGMTZdDh4wyAApjFx0zVSMTTETtGvzViLxQ42sBJbm8R&quot; width=&quot;602&quot; height=&quot;339&quot; style=&quot;border-width: initial; border-style: none; vertical-align: middle; cursor: pointer; max-width: 100%; height: auto; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
&lt;p&gt;So imagine that you have Storm Commerce as the Commerce Platform and Epi CMS as the Publishing Platform, because you wanted to try headless commerce. Storm Commerce will say “we integrate with anything, look at this API that gives you that possibility”. Epi CMS will say “we integrate with anything, look at this API that gives you that possibility”. Great. Perfect headless solution, right?&lt;/p&gt;
&lt;p&gt;The problem comes when we developers take a look at this.&amp;nbsp;&lt;b&gt;Wait, we need to build a complete integration between these two systems?&lt;/b&gt;&amp;nbsp;Of course Storm/Epi will never build the integration themselves because they can’t implement 40 connectors to 40 different systems and keep them up to date. So first you need to build a major stable connection between the systems and&amp;nbsp;&lt;em&gt;then&lt;/em&gt;&amp;nbsp;build an UI on top of that.&lt;/p&gt;
&lt;p&gt;This is where Epi CMS + Commerce shines. They’re&amp;nbsp;&lt;b&gt;already deeply integrated with themselves&lt;/b&gt;&amp;nbsp;and you can make this solution (of course together with some other Epi products) headless:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://lh5.googleusercontent.com/AGSx-W1CwDDGdbQrmRSjbzTMZgpYeCPFjWwZ1mY6BKfnhuAD82WoWCGettTbh2Pxq3HQVqndCY1AcqULCccaNiOPJFHOVE7FTvTCAj0BJ5SvHoQXnScxqwPRnjnAmahS0SGXCRP_&quot; width=&quot;602&quot; height=&quot;333&quot; style=&quot;border-width: initial; border-style: none; vertical-align: middle; cursor: pointer; max-width: 100%; height: auto; transform: rotate(0rad);&quot; /&gt;&lt;/p&gt;
&lt;p&gt;So using all the benefits that comes from headless architecture, we’ve now gotten rid of a huge integration between two complex systems and can now build our headless implementation of our CMS and ecommerce system. Three words for making the headless layer implementation: make everything JSON!&lt;/p&gt;
&lt;p&gt;&lt;b&gt;So what have we done with it?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;At Avensia, we run a lot of labs testing different frontend technologies together with Epi CMS &amp; Ecommerce (among others). Some of these labs yield a “cool, but not really a use case for it right now”-result, others yield a “let’s use this instantly”-result. One of the “let’s use this instantly”-results lead us to build a Headless layer, named SCOPE, that powers many of our customer projects, including our first implementation of SCOPE,&amp;nbsp;&lt;a href=&quot;https://lyko.se/&quot;&gt;Lyko.se&lt;/a&gt;, that resulted in a site awarded&amp;nbsp;&lt;a href=&quot;http://www.episerver.com/about/news/press-room/pressreleases/lyko-awarded-for-best-nordic-website/&quot;&gt;Best Nordic website &amp; Commerce&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Challenges creating SCOPE&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Making Epi CMS+Commerce didn’t come without challenges and hardships. Developers much more talented than I have spent many hours solving some of the hardships. Since Epi comes with many out of the box solutions that are connected to the web UI, you have to make sure it works with your headless implementation. Things like On Page Editing, routing, promotions, etc must work like out of the box in order to deliver a great system to the content creators.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;What’s coming?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Our headless layer implementation have been relying on the existing usual APIs, the same ones you’d use for a standard implementation of Epi CMS+Commerce. However, Epi is working on a&amp;nbsp;&lt;b&gt;new package/API specifically tailored for going headless&amp;nbsp;&lt;/b&gt;that will be revealed Q1 2018. From what I’ve heard, the first version will have its focus on utilizing the Content system, making it easier to push any type of content to any platform. Not much details have been released, but I look forward to evaluating it in a lab session!&lt;/p&gt;
&lt;p id=&quot;headless-tldr&quot;&gt;&lt;b&gt;TL;DR&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;People saying their systems are headless doesn’t necessarily mean it’s easy to integrate and implement. Epi comes out of the box with an integrated CMS+Commerce platform that you can morph into a headless system.&lt;/p&gt;
                </description>            <guid>https://www.avensia.com/joel-yourstone/headless-epi-commerce-worth-investing-in/</guid>            <pubDate>Tue, 14 Nov 2017 04:53:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>New relations API in Commerce 11</title>            <link>http://www.avensia.com/joel-yourstone/new-relations-api-in-commerce-11/</link>            <description>
                    &lt;p&gt;&lt;em&gt;When epi released Commerce 11, it came with a new way of working with Relations. The old way is obsolete. I will go through how we migrated to the new APIs and some gotchas with it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;/link/e2af3d1925564abeb40482c82819328a.aspx&quot;&gt;Commerce 11 Breaking changes&lt;/a&gt; page, you can read the following point:
&lt;/p&gt;
&lt;blockquote&gt;In IRelationRepository, the GetChildren and GetParents methods supercede GetRelationsBySource and GetRelationsByTarget. On the relation objects, the properties Parent and Child serve the same purpose.&lt;/blockquote&gt;
&lt;p&gt;So the big difference is that we need to migrate all our existing &lt;code&gt;GetRelationsBySource&lt;/code&gt; / &lt;code&gt;GetRelationsByTarget&lt;/code&gt; to &lt;code&gt;GetParents&lt;/code&gt; / &lt;code&gt;GetChildren&lt;/code&gt;. However, it is not as easy as it may sound. The old API have been known among developers to be confusing, because depending on what type of relation it is, the source/target differs. I’ll talk about how that played out pre Commerce 11 now, if you already are an expert with this, you can skip this segment.&lt;/p&gt;
&lt;h2&gt;Relations explained&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Note: I sometimes use the term Node and perhaps sometimes Category, but for this post, they are interchangeable. In epi Commerce admin, they are named Category, but in the apis they are nodes.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the API in epi commerce there are two kinds of relations, &lt;strong&gt;NodeRelation &lt;/strong&gt;and &lt;strong&gt;EntryRelation&lt;/strong&gt;. NodeRelations are relations where one side of the relation is a NodeContent (most commonly Category). EntryRelations are relations where both sides are EntryContent (most commonly Variant, Product, Bundle or Package). There are a couple of classes that inherit EntryRelation, that specifies them more, such as &lt;strong&gt;ProductVariation&lt;/strong&gt;, &lt;strong&gt;PackageEntry &lt;/strong&gt;and &lt;strong&gt;BundleEntry&lt;/strong&gt;. In Commerce 11.2, we have as well a NodeEntryRelation in the APIs.&lt;/p&gt;
&lt;p&gt;So if I take a variant as an example. This variant belongs to a product, which is a &lt;strong&gt;EntryRelation &lt;/strong&gt;(more specifically &lt;strong&gt;ProductVariation&lt;/strong&gt;) since both variant and products are EntryContent. The variant was also created in a category, &lt;strong&gt;NodeRelation &lt;/strong&gt;between the variant and the category, as well as have been linked to 4 additional categories (&lt;strong&gt;NodeRelation &lt;/strong&gt;again).&lt;/p&gt;
&lt;p&gt;And if we do the same thing for a Category, that will only be &lt;strong&gt;NodeRelation&lt;/strong&gt;s. Nodes have relations to other nodes (a category can be in several categories) and nodes have relations to entries (an entry exists in or is linked to a node). There is a dangerous quirk here, one of the “node relations” is not actually a &lt;strong&gt;NodeRelation&lt;/strong&gt;. The parent of the node (where the node was created), is not a relation, but simply stored in a “Parent” field. But all other node relations to the node will be &lt;strong&gt;NodeRelations&lt;/strong&gt;. This might sound confusing, but later on I’ll try to show you the ramifications with this, how this is played out.&lt;/p&gt;
&lt;h2&gt;New Relations API explained&lt;/h2&gt;
&lt;p&gt;Episerver has presented dobby with a gift! Dobby is free [of having to remember and understand the different Target/Source depending on if it’s a node or entry, or coming up with some “product is always the target” chant]!&lt;/p&gt;
&lt;p&gt;The new APIs are much more semantic, using GetParents and GetChildren. And on the Relation objects, you can access the Parents and Children properties.&lt;/p&gt;
&lt;h2&gt;Comparing old API vs. new API vs. IContentLoader&lt;/h2&gt;
&lt;p&gt;Why do I compare with IContentLoader, weren’t we only talking about Relations? Yes, but seeing as the IContentLoader has a GetChildren method, I thought I could include it as well, as that one is not the same as any of the new/old relation apis.&lt;/p&gt;
&lt;p&gt;The variable x in these is always a ContentReference for the APIs, so the first column in the tables we assumed that you used the contentloader to fetch the content &lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;(IContentLoader.Get&amp;lt;IContent&amp;gt;(x))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; and hence get the type of the content.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Also note that I never specify in detail what Relation type I want to get, I always send in &amp;lt;Relation&amp;gt;. You can specify it closer, such as &lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;IRelationRepository.GetParents&amp;lt;ProductVariation&amp;gt;((VariationContent) x)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; and I will only get the parent product and not the parent nodes (see table).&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;N = Nodes&lt;br /&gt;
P = Products&lt;br /&gt;
V = Variation&lt;br /&gt;
LN = Linked Nodes&lt;/p&gt;
&lt;h3&gt;Commerce 11 Relations&lt;/h3&gt;
&lt;style type=&quot;text/css&quot;&gt;
.relations-table {
  width: 120%;
}
.relations-table td {
  text-align: center !important;
}
.relations-table td {
  width: 40%;
}
.relations-table td:first-child {
  font-family: &#39;Courier New&#39;;
  width: 150px;
}
.relation-list ol{
  margin-left: 40px;
}
.relation-list p {
  margin: 0;
}
&lt;/style&gt;
&lt;table class=&quot;relations-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetParents(x)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetChildren(x)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is ProductContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N + P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display:none&quot;&gt;You get all the nodes this product is in, as well as the products if another product has this product as variant.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;V(/P)&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display:none&quot;&gt;You get all ProductVariant relations. The trick here is that a product can have a product as variation as well, hence the (/P). But that product is not the “parent” product.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is VariationContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N + P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Same as the GetParents for ProductContent, I will get all nodes and all product this variant belongs to.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;-&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is NodeContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets the additional nodes for x. So x has a parent node, the one it was created in, but you won’t get that parent here. Instead, you’ll get nodes this node has been linked to (All properties -&amp;gt; Belongs to in the admin UI)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;V + P + LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets all the variants and products that is linked to the category. You will also get nodes that have specifically linked this node as a parent node. See it as you will get the child node in the node to node relation. But you will not get nodes that are created in the parent node, only linked ones. If you take &lt;img src=&quot;http://qs.lc/jorel/zl47g.png&quot; /&gt;&lt;br /&gt; as an example, “Mens Jackets” was created under “Mens”, but were linked to “Womens Dresses” as well. “Subcategory womens..” was created in “Womens Dresses”. If you do GetChildren on “Womens Dresses” you will get all the products, variants and the “Mens Jackets”, but NOT the “Subcategory womens”.&lt;/p&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;Commerce &amp;lt; 11 Relations&lt;/h3&gt;
&lt;table class=&quot;relations-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetRelationsBySource(x)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetRelationsByTarget(x)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is ProductContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N + V(/P)&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;You get all nodes and all ProductVariant relations. The trick here is that a product can have a product as variation as well, hence the (/P). But that product is not the “parent” product.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;A product can belong to another product, if so, you will get the parent product here.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is VariationContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets all the nodes the variant belongs to. Note that the variant can belong to different/additional nodes than the product.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets the product that the variant belongs to.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is NodeContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets the additional nodes for x. So x has a parent node, the one it was created in, but you won’t get that parent here. Instead, you’ll get nodes this node has been linked to (All properties -&amp;gt; Belongs to in the admin UI)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;V &amp;nbsp;+ P + LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets all the variants and products that is linked to the category. You will also get nodes that have specifically linked this node as a parent node. See it as you will get the child node in the node to node relation. But you will not get nodes that are created in the parent node, only linked ones. If you take &lt;img src=&quot;http://qs.lc/jorel/zl47g.png&quot; /&gt; &lt;br /&gt; as an example, “Mens Jackets” was created under “Mens”, but were linked to “Womens Dresses” as well. “Subcategory womens..” was created in “Womens Dresses”. If you do GetRelationsByTarget on “Womens Dresses” you will get all the products, variants and the “Mens Jackets”, but NOT the “Subcategory womens”.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;IContentLoader&lt;/h3&gt;
&lt;table class=&quot;relations-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetChildren(x)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is ProductContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;-&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is VariationContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;-&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is NodeContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;V + P + N&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;This will get everything “below” the node. Compared to GetRelationsByTarget it will also get the sub categories that were created in this node.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;Migrating to the new API&lt;/h2&gt;
&lt;p&gt;You can use the table above to try to understand what has changed and where you need to change something, or you can use the following logic applying the old API on top of the new API.&lt;/p&gt;
&lt;ul class=&quot;relation-list&quot;&gt;
&lt;li&gt;&lt;p&gt;GetRelationsBySource switches on the ContentType&lt;/p&gt;&lt;/li&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogEntry, it will GetParents&amp;lt;NodeRelation&amp;gt; as well as GetChildren&amp;lt;Relation&amp;gt; and return that to you.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogNode, it will GetParents&amp;lt;Relation&amp;gt; and return that to you.&lt;/p&gt;&lt;/li&gt;
  &lt;/ol&gt;
&lt;li&gt;&lt;p&gt;GetRelationsByTarget also switches on the ContentType&lt;/p&gt;&lt;/li&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogEntry, it will GetParents&amp;lt;Relation&amp;gt; and return that to you&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogNode, it will GetChildren&amp;lt;Relation&amp;gt; and return that to you&lt;/p&gt;&lt;/li&gt;
  &lt;/ol&gt;
&lt;/ul&gt;
&lt;p&gt;So in conclusion, if you had any GetRelationsBySource/GetRelationsByTarget that included EntryRelations, you will need to think which method you’d want to use depending on the input and desired output. If they only include NodeRelations, you can just translate GetRelationsBySource to GetParents and GetRelationsByTarget to GetChildren.&lt;/p&gt;
&lt;h2&gt;Primary category for Entries&lt;/h2&gt;
&lt;p&gt;A thing that was changed in the API as well was how you determine which category is the primary category for a CatalogEntry. Epi explained this pretty well at their &lt;a href=&quot;/link/e2af3d1925564abeb40482c82819328a.aspx&quot;&gt;Breaking Changes Commerce 11 document&lt;/a&gt; so I will just unceremoniously copy&amp;paste it here.&lt;/p&gt;
&lt;blockquote&gt;In previous versions, a catalog entry&#39;s home category, used for the content&#39;s ParentLink and when rendering default hierarchical URLs, was determined by the relation with the lowest SortOrder. This version introduces a separate field for marking one relation as the primary relation, used as home category.&lt;/blockquote&gt;
&lt;p&gt;Hopefully this post didn’t confuse you more, it was just notes on a deep dive on Relations we did when we upgraded to Commerce 11.&lt;/p&gt;
                </description>            <guid>http://www.avensia.com/joel-yourstone/new-relations-api-in-commerce-11/</guid>            <pubDate>Wed, 06 Sep 2017 18:01:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>New relations API in Commerce 11</title>            <link>http://www.avensia.com/index.php/joel-yourstone/new-relations-api-in-commerce-11/</link>            <description>
                    &lt;p&gt;&lt;em&gt;When epi released Commerce 11, it came with a new way of working with Relations. The old way is obsolete. I will go through how we migrated to the new APIs and some gotchas with it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;/link/e2af3d1925564abeb40482c82819328a.aspx&quot;&gt;Commerce 11 Breaking changes&lt;/a&gt; page, you can read the following point:
&lt;/p&gt;
&lt;blockquote&gt;In IRelationRepository, the GetChildren and GetParents methods supercede GetRelationsBySource and GetRelationsByTarget. On the relation objects, the properties Parent and Child serve the same purpose.&lt;/blockquote&gt;
&lt;p&gt;So the big difference is that we need to migrate all our existing &lt;code&gt;GetRelationsBySource&lt;/code&gt; / &lt;code&gt;GetRelationsByTarget&lt;/code&gt; to &lt;code&gt;GetParents&lt;/code&gt; / &lt;code&gt;GetChildren&lt;/code&gt;. However, it is not as easy as it may sound. The old API have been known among developers to be confusing, because depending on what type of relation it is, the source/target differs. I’ll talk about how that played out pre Commerce 11 now, if you already are an expert with this, you can skip this segment.&lt;/p&gt;
&lt;h2&gt;Relations explained&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Note: I sometimes use the term Node and perhaps sometimes Category, but for this post, they are interchangeable. In epi Commerce admin, they are named Category, but in the apis they are nodes.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the API in epi commerce there are two kinds of relations, &lt;strong&gt;NodeRelation &lt;/strong&gt;and &lt;strong&gt;EntryRelation&lt;/strong&gt;. NodeRelations are relations where one side of the relation is a NodeContent (most commonly Category). EntryRelations are relations where both sides are EntryContent (most commonly Variant, Product, Bundle or Package). There are a couple of classes that inherit EntryRelation, that specifies them more, such as &lt;strong&gt;ProductVariation&lt;/strong&gt;, &lt;strong&gt;PackageEntry &lt;/strong&gt;and &lt;strong&gt;BundleEntry&lt;/strong&gt;. In Commerce 11.2, we have as well a NodeEntryRelation in the APIs.&lt;/p&gt;
&lt;p&gt;So if I take a variant as an example. This variant belongs to a product, which is a &lt;strong&gt;EntryRelation &lt;/strong&gt;(more specifically &lt;strong&gt;ProductVariation&lt;/strong&gt;) since both variant and products are EntryContent. The variant was also created in a category, &lt;strong&gt;NodeRelation &lt;/strong&gt;between the variant and the category, as well as have been linked to 4 additional categories (&lt;strong&gt;NodeRelation &lt;/strong&gt;again).&lt;/p&gt;
&lt;p&gt;And if we do the same thing for a Category, that will only be &lt;strong&gt;NodeRelation&lt;/strong&gt;s. Nodes have relations to other nodes (a category can be in several categories) and nodes have relations to entries (an entry exists in or is linked to a node). There is a dangerous quirk here, one of the “node relations” is not actually a &lt;strong&gt;NodeRelation&lt;/strong&gt;. The parent of the node (where the node was created), is not a relation, but simply stored in a “Parent” field. But all other node relations to the node will be &lt;strong&gt;NodeRelations&lt;/strong&gt;. This might sound confusing, but later on I’ll try to show you the ramifications with this, how this is played out.&lt;/p&gt;
&lt;h2&gt;New Relations API explained&lt;/h2&gt;
&lt;p&gt;Episerver has presented dobby with a gift! Dobby is free [of having to remember and understand the different Target/Source depending on if it’s a node or entry, or coming up with some “product is always the target” chant]!&lt;/p&gt;
&lt;p&gt;The new APIs are much more semantic, using GetParents and GetChildren. And on the Relation objects, you can access the Parents and Children properties.&lt;/p&gt;
&lt;h2&gt;Comparing old API vs. new API vs. IContentLoader&lt;/h2&gt;
&lt;p&gt;Why do I compare with IContentLoader, weren’t we only talking about Relations? Yes, but seeing as the IContentLoader has a GetChildren method, I thought I could include it as well, as that one is not the same as any of the new/old relation apis.&lt;/p&gt;
&lt;p&gt;The variable x in these is always a ContentReference for the APIs, so the first column in the tables we assumed that you used the contentloader to fetch the content &lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;(IContentLoader.Get&amp;lt;IContent&amp;gt;(x))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; and hence get the type of the content.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Also note that I never specify in detail what Relation type I want to get, I always send in &amp;lt;Relation&amp;gt;. You can specify it closer, such as &lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;IRelationRepository.GetParents&amp;lt;ProductVariation&amp;gt;((VariationContent) x)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; and I will only get the parent product and not the parent nodes (see table).&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;N = Nodes&lt;br /&gt;
P = Products&lt;br /&gt;
V = Variation&lt;br /&gt;
LN = Linked Nodes&lt;/p&gt;
&lt;h3&gt;Commerce 11 Relations&lt;/h3&gt;
&lt;style type=&quot;text/css&quot;&gt;
.relations-table {
  width: 120%;
}
.relations-table td {
  text-align: center !important;
}
.relations-table td {
  width: 40%;
}
.relations-table td:first-child {
  font-family: &#39;Courier New&#39;;
  width: 150px;
}
.relation-list ol{
  margin-left: 40px;
}
.relation-list p {
  margin: 0;
}
&lt;/style&gt;
&lt;table class=&quot;relations-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetParents(x)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetChildren(x)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is ProductContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N + P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display:none&quot;&gt;You get all the nodes this product is in, as well as the products if another product has this product as variant.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;V(/P)&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display:none&quot;&gt;You get all ProductVariant relations. The trick here is that a product can have a product as variation as well, hence the (/P). But that product is not the “parent” product.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is VariationContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N + P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Same as the GetParents for ProductContent, I will get all nodes and all product this variant belongs to.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;-&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is NodeContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets the additional nodes for x. So x has a parent node, the one it was created in, but you won’t get that parent here. Instead, you’ll get nodes this node has been linked to (All properties -&amp;gt; Belongs to in the admin UI)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;V + P + LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets all the variants and products that is linked to the category. You will also get nodes that have specifically linked this node as a parent node. See it as you will get the child node in the node to node relation. But you will not get nodes that are created in the parent node, only linked ones. If you take &lt;img src=&quot;http://qs.lc/jorel/zl47g.png&quot; /&gt;&lt;br /&gt; as an example, “Mens Jackets” was created under “Mens”, but were linked to “Womens Dresses” as well. “Subcategory womens..” was created in “Womens Dresses”. If you do GetChildren on “Womens Dresses” you will get all the products, variants and the “Mens Jackets”, but NOT the “Subcategory womens”.&lt;/p&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;Commerce &amp;lt; 11 Relations&lt;/h3&gt;
&lt;table class=&quot;relations-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetRelationsBySource(x)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetRelationsByTarget(x)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is ProductContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N + V(/P)&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;You get all nodes and all ProductVariant relations. The trick here is that a product can have a product as variation as well, hence the (/P). But that product is not the “parent” product.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;A product can belong to another product, if so, you will get the parent product here.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is VariationContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets all the nodes the variant belongs to. Note that the variant can belong to different/additional nodes than the product.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets the product that the variant belongs to.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is NodeContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets the additional nodes for x. So x has a parent node, the one it was created in, but you won’t get that parent here. Instead, you’ll get nodes this node has been linked to (All properties -&amp;gt; Belongs to in the admin UI)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;V &amp;nbsp;+ P + LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets all the variants and products that is linked to the category. You will also get nodes that have specifically linked this node as a parent node. See it as you will get the child node in the node to node relation. But you will not get nodes that are created in the parent node, only linked ones. If you take &lt;img src=&quot;http://qs.lc/jorel/zl47g.png&quot; /&gt; &lt;br /&gt; as an example, “Mens Jackets” was created under “Mens”, but were linked to “Womens Dresses” as well. “Subcategory womens..” was created in “Womens Dresses”. If you do GetRelationsByTarget on “Womens Dresses” you will get all the products, variants and the “Mens Jackets”, but NOT the “Subcategory womens”.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;IContentLoader&lt;/h3&gt;
&lt;table class=&quot;relations-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetChildren(x)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is ProductContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;-&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is VariationContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;-&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is NodeContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;V + P + N&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;This will get everything “below” the node. Compared to GetRelationsByTarget it will also get the sub categories that were created in this node.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;Migrating to the new API&lt;/h2&gt;
&lt;p&gt;You can use the table above to try to understand what has changed and where you need to change something, or you can use the following logic applying the old API on top of the new API.&lt;/p&gt;
&lt;ul class=&quot;relation-list&quot;&gt;
&lt;li&gt;&lt;p&gt;GetRelationsBySource switches on the ContentType&lt;/p&gt;&lt;/li&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogEntry, it will GetParents&amp;lt;NodeRelation&amp;gt; as well as GetChildren&amp;lt;Relation&amp;gt; and return that to you.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogNode, it will GetParents&amp;lt;Relation&amp;gt; and return that to you.&lt;/p&gt;&lt;/li&gt;
  &lt;/ol&gt;
&lt;li&gt;&lt;p&gt;GetRelationsByTarget also switches on the ContentType&lt;/p&gt;&lt;/li&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogEntry, it will GetParents&amp;lt;Relation&amp;gt; and return that to you&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogNode, it will GetChildren&amp;lt;Relation&amp;gt; and return that to you&lt;/p&gt;&lt;/li&gt;
  &lt;/ol&gt;
&lt;/ul&gt;
&lt;p&gt;So in conclusion, if you had any GetRelationsBySource/GetRelationsByTarget that included EntryRelations, you will need to think which method you’d want to use depending on the input and desired output. If they only include NodeRelations, you can just translate GetRelationsBySource to GetParents and GetRelationsByTarget to GetChildren.&lt;/p&gt;
&lt;h2&gt;Primary category for Entries&lt;/h2&gt;
&lt;p&gt;A thing that was changed in the API as well was how you determine which category is the primary category for a CatalogEntry. Epi explained this pretty well at their &lt;a href=&quot;/link/e2af3d1925564abeb40482c82819328a.aspx&quot;&gt;Breaking Changes Commerce 11 document&lt;/a&gt; so I will just unceremoniously copy&amp;paste it here.&lt;/p&gt;
&lt;blockquote&gt;In previous versions, a catalog entry&#39;s home category, used for the content&#39;s ParentLink and when rendering default hierarchical URLs, was determined by the relation with the lowest SortOrder. This version introduces a separate field for marking one relation as the primary relation, used as home category.&lt;/blockquote&gt;
&lt;p&gt;Hopefully this post didn’t confuse you more, it was just notes on a deep dive on Relations we did when we upgraded to Commerce 11.&lt;/p&gt;
                </description>            <guid>http://www.avensia.com/index.php/joel-yourstone/new-relations-api-in-commerce-11/</guid>            <pubDate>Wed, 06 Sep 2017 18:01:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>New relations API in Commerce 11</title>            <link>http://www.avensia.com/index.php?p=joel-yourstone/new-relations-api-in-commerce-11</link>            <description>
                    &lt;p&gt;&lt;em&gt;When epi released Commerce 11, it came with a new way of working with Relations. The old way is obsolete. I will go through how we migrated to the new APIs and some gotchas with it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;/link/e2af3d1925564abeb40482c82819328a.aspx&quot;&gt;Commerce 11 Breaking changes&lt;/a&gt; page, you can read the following point:
&lt;/p&gt;
&lt;blockquote&gt;In IRelationRepository, the GetChildren and GetParents methods supercede GetRelationsBySource and GetRelationsByTarget. On the relation objects, the properties Parent and Child serve the same purpose.&lt;/blockquote&gt;
&lt;p&gt;So the big difference is that we need to migrate all our existing &lt;code&gt;GetRelationsBySource&lt;/code&gt; / &lt;code&gt;GetRelationsByTarget&lt;/code&gt; to &lt;code&gt;GetParents&lt;/code&gt; / &lt;code&gt;GetChildren&lt;/code&gt;. However, it is not as easy as it may sound. The old API have been known among developers to be confusing, because depending on what type of relation it is, the source/target differs. I’ll talk about how that played out pre Commerce 11 now, if you already are an expert with this, you can skip this segment.&lt;/p&gt;
&lt;h2&gt;Relations explained&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Note: I sometimes use the term Node and perhaps sometimes Category, but for this post, they are interchangeable. In epi Commerce admin, they are named Category, but in the apis they are nodes.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the API in epi commerce there are two kinds of relations, &lt;strong&gt;NodeRelation &lt;/strong&gt;and &lt;strong&gt;EntryRelation&lt;/strong&gt;. NodeRelations are relations where one side of the relation is a NodeContent (most commonly Category). EntryRelations are relations where both sides are EntryContent (most commonly Variant, Product, Bundle or Package). There are a couple of classes that inherit EntryRelation, that specifies them more, such as &lt;strong&gt;ProductVariation&lt;/strong&gt;, &lt;strong&gt;PackageEntry &lt;/strong&gt;and &lt;strong&gt;BundleEntry&lt;/strong&gt;. In Commerce 11.2, we have as well a NodeEntryRelation in the APIs.&lt;/p&gt;
&lt;p&gt;So if I take a variant as an example. This variant belongs to a product, which is a &lt;strong&gt;EntryRelation &lt;/strong&gt;(more specifically &lt;strong&gt;ProductVariation&lt;/strong&gt;) since both variant and products are EntryContent. The variant was also created in a category, &lt;strong&gt;NodeRelation &lt;/strong&gt;between the variant and the category, as well as have been linked to 4 additional categories (&lt;strong&gt;NodeRelation &lt;/strong&gt;again).&lt;/p&gt;
&lt;p&gt;And if we do the same thing for a Category, that will only be &lt;strong&gt;NodeRelation&lt;/strong&gt;s. Nodes have relations to other nodes (a category can be in several categories) and nodes have relations to entries (an entry exists in or is linked to a node). There is a dangerous quirk here, one of the “node relations” is not actually a &lt;strong&gt;NodeRelation&lt;/strong&gt;. The parent of the node (where the node was created), is not a relation, but simply stored in a “Parent” field. But all other node relations to the node will be &lt;strong&gt;NodeRelations&lt;/strong&gt;. This might sound confusing, but later on I’ll try to show you the ramifications with this, how this is played out.&lt;/p&gt;
&lt;h2&gt;New Relations API explained&lt;/h2&gt;
&lt;p&gt;Episerver has presented dobby with a gift! Dobby is free [of having to remember and understand the different Target/Source depending on if it’s a node or entry, or coming up with some “product is always the target” chant]!&lt;/p&gt;
&lt;p&gt;The new APIs are much more semantic, using GetParents and GetChildren. And on the Relation objects, you can access the Parents and Children properties.&lt;/p&gt;
&lt;h2&gt;Comparing old API vs. new API vs. IContentLoader&lt;/h2&gt;
&lt;p&gt;Why do I compare with IContentLoader, weren’t we only talking about Relations? Yes, but seeing as the IContentLoader has a GetChildren method, I thought I could include it as well, as that one is not the same as any of the new/old relation apis.&lt;/p&gt;
&lt;p&gt;The variable x in these is always a ContentReference for the APIs, so the first column in the tables we assumed that you used the contentloader to fetch the content &lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;(IContentLoader.Get&amp;lt;IContent&amp;gt;(x))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; and hence get the type of the content.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Also note that I never specify in detail what Relation type I want to get, I always send in &amp;lt;Relation&amp;gt;. You can specify it closer, such as &lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;IRelationRepository.GetParents&amp;lt;ProductVariation&amp;gt;((VariationContent) x)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; and I will only get the parent product and not the parent nodes (see table).&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;N = Nodes&lt;br /&gt;
P = Products&lt;br /&gt;
V = Variation&lt;br /&gt;
LN = Linked Nodes&lt;/p&gt;
&lt;h3&gt;Commerce 11 Relations&lt;/h3&gt;
&lt;style type=&quot;text/css&quot;&gt;
.relations-table {
  width: 120%;
}
.relations-table td {
  text-align: center !important;
}
.relations-table td {
  width: 40%;
}
.relations-table td:first-child {
  font-family: &#39;Courier New&#39;;
  width: 150px;
}
.relation-list ol{
  margin-left: 40px;
}
.relation-list p {
  margin: 0;
}
&lt;/style&gt;
&lt;table class=&quot;relations-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetParents(x)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetChildren(x)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is ProductContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N + P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display:none&quot;&gt;You get all the nodes this product is in, as well as the products if another product has this product as variant.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;V(/P)&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display:none&quot;&gt;You get all ProductVariant relations. The trick here is that a product can have a product as variation as well, hence the (/P). But that product is not the “parent” product.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is VariationContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N + P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Same as the GetParents for ProductContent, I will get all nodes and all product this variant belongs to.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;-&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is NodeContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets the additional nodes for x. So x has a parent node, the one it was created in, but you won’t get that parent here. Instead, you’ll get nodes this node has been linked to (All properties -&amp;gt; Belongs to in the admin UI)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;V + P + LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets all the variants and products that is linked to the category. You will also get nodes that have specifically linked this node as a parent node. See it as you will get the child node in the node to node relation. But you will not get nodes that are created in the parent node, only linked ones. If you take &lt;img src=&quot;http://qs.lc/jorel/zl47g.png&quot; /&gt;&lt;br /&gt; as an example, “Mens Jackets” was created under “Mens”, but were linked to “Womens Dresses” as well. “Subcategory womens..” was created in “Womens Dresses”. If you do GetChildren on “Womens Dresses” you will get all the products, variants and the “Mens Jackets”, but NOT the “Subcategory womens”.&lt;/p&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;Commerce &amp;lt; 11 Relations&lt;/h3&gt;
&lt;table class=&quot;relations-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetRelationsBySource(x)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetRelationsByTarget(x)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is ProductContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N + V(/P)&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;You get all nodes and all ProductVariant relations. The trick here is that a product can have a product as variation as well, hence the (/P). But that product is not the “parent” product.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;A product can belong to another product, if so, you will get the parent product here.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is VariationContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets all the nodes the variant belongs to. Note that the variant can belong to different/additional nodes than the product.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets the product that the variant belongs to.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is NodeContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets the additional nodes for x. So x has a parent node, the one it was created in, but you won’t get that parent here. Instead, you’ll get nodes this node has been linked to (All properties -&amp;gt; Belongs to in the admin UI)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;V &amp;nbsp;+ P + LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets all the variants and products that is linked to the category. You will also get nodes that have specifically linked this node as a parent node. See it as you will get the child node in the node to node relation. But you will not get nodes that are created in the parent node, only linked ones. If you take &lt;img src=&quot;http://qs.lc/jorel/zl47g.png&quot; /&gt; &lt;br /&gt; as an example, “Mens Jackets” was created under “Mens”, but were linked to “Womens Dresses” as well. “Subcategory womens..” was created in “Womens Dresses”. If you do GetRelationsByTarget on “Womens Dresses” you will get all the products, variants and the “Mens Jackets”, but NOT the “Subcategory womens”.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;IContentLoader&lt;/h3&gt;
&lt;table class=&quot;relations-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetChildren(x)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is ProductContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;-&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is VariationContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;-&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is NodeContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;V + P + N&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;This will get everything “below” the node. Compared to GetRelationsByTarget it will also get the sub categories that were created in this node.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;Migrating to the new API&lt;/h2&gt;
&lt;p&gt;You can use the table above to try to understand what has changed and where you need to change something, or you can use the following logic applying the old API on top of the new API.&lt;/p&gt;
&lt;ul class=&quot;relation-list&quot;&gt;
&lt;li&gt;&lt;p&gt;GetRelationsBySource switches on the ContentType&lt;/p&gt;&lt;/li&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogEntry, it will GetParents&amp;lt;NodeRelation&amp;gt; as well as GetChildren&amp;lt;Relation&amp;gt; and return that to you.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogNode, it will GetParents&amp;lt;Relation&amp;gt; and return that to you.&lt;/p&gt;&lt;/li&gt;
  &lt;/ol&gt;
&lt;li&gt;&lt;p&gt;GetRelationsByTarget also switches on the ContentType&lt;/p&gt;&lt;/li&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogEntry, it will GetParents&amp;lt;Relation&amp;gt; and return that to you&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogNode, it will GetChildren&amp;lt;Relation&amp;gt; and return that to you&lt;/p&gt;&lt;/li&gt;
  &lt;/ol&gt;
&lt;/ul&gt;
&lt;p&gt;So in conclusion, if you had any GetRelationsBySource/GetRelationsByTarget that included EntryRelations, you will need to think which method you’d want to use depending on the input and desired output. If they only include NodeRelations, you can just translate GetRelationsBySource to GetParents and GetRelationsByTarget to GetChildren.&lt;/p&gt;
&lt;h2&gt;Primary category for Entries&lt;/h2&gt;
&lt;p&gt;A thing that was changed in the API as well was how you determine which category is the primary category for a CatalogEntry. Epi explained this pretty well at their &lt;a href=&quot;/link/e2af3d1925564abeb40482c82819328a.aspx&quot;&gt;Breaking Changes Commerce 11 document&lt;/a&gt; so I will just unceremoniously copy&amp;paste it here.&lt;/p&gt;
&lt;blockquote&gt;In previous versions, a catalog entry&#39;s home category, used for the content&#39;s ParentLink and when rendering default hierarchical URLs, was determined by the relation with the lowest SortOrder. This version introduces a separate field for marking one relation as the primary relation, used as home category.&lt;/blockquote&gt;
&lt;p&gt;Hopefully this post didn’t confuse you more, it was just notes on a deep dive on Relations we did when we upgraded to Commerce 11.&lt;/p&gt;
                </description>            <guid>http://www.avensia.com/index.php?p=joel-yourstone/new-relations-api-in-commerce-11</guid>            <pubDate>Wed, 06 Sep 2017 18:01:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>New relations API in Commerce 11</title>            <link>http://avensia.com/joel-yourstone/new-relations-api-in-commerce-11/</link>            <description>
                    &lt;p&gt;&lt;em&gt;When epi released Commerce 11, it came with a new way of working with Relations. The old way is obsolete. I will go through how we migrated to the new APIs and some gotchas with it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;/link/e2af3d1925564abeb40482c82819328a.aspx&quot;&gt;Commerce 11 Breaking changes&lt;/a&gt; page, you can read the following point:
&lt;/p&gt;
&lt;blockquote&gt;In IRelationRepository, the GetChildren and GetParents methods supercede GetRelationsBySource and GetRelationsByTarget. On the relation objects, the properties Parent and Child serve the same purpose.&lt;/blockquote&gt;
&lt;p&gt;So the big difference is that we need to migrate all our existing &lt;code&gt;GetRelationsBySource&lt;/code&gt; / &lt;code&gt;GetRelationsByTarget&lt;/code&gt; to &lt;code&gt;GetParents&lt;/code&gt; / &lt;code&gt;GetChildren&lt;/code&gt;. However, it is not as easy as it may sound. The old API have been known among developers to be confusing, because depending on what type of relation it is, the source/target differs. I’ll talk about how that played out pre Commerce 11 now, if you already are an expert with this, you can skip this segment.&lt;/p&gt;
&lt;h2&gt;Relations explained&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Note: I sometimes use the term Node and perhaps sometimes Category, but for this post, they are interchangeable. In epi Commerce admin, they are named Category, but in the apis they are nodes.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the API in epi commerce there are two kinds of relations, &lt;strong&gt;NodeRelation &lt;/strong&gt;and &lt;strong&gt;EntryRelation&lt;/strong&gt;. NodeRelations are relations where one side of the relation is a NodeContent (most commonly Category). EntryRelations are relations where both sides are EntryContent (most commonly Variant, Product, Bundle or Package). There are a couple of classes that inherit EntryRelation, that specifies them more, such as &lt;strong&gt;ProductVariation&lt;/strong&gt;, &lt;strong&gt;PackageEntry &lt;/strong&gt;and &lt;strong&gt;BundleEntry&lt;/strong&gt;. In Commerce 11.2, we have as well a NodeEntryRelation in the APIs.&lt;/p&gt;
&lt;p&gt;So if I take a variant as an example. This variant belongs to a product, which is a &lt;strong&gt;EntryRelation &lt;/strong&gt;(more specifically &lt;strong&gt;ProductVariation&lt;/strong&gt;) since both variant and products are EntryContent. The variant was also created in a category, &lt;strong&gt;NodeRelation &lt;/strong&gt;between the variant and the category, as well as have been linked to 4 additional categories (&lt;strong&gt;NodeRelation &lt;/strong&gt;again).&lt;/p&gt;
&lt;p&gt;And if we do the same thing for a Category, that will only be &lt;strong&gt;NodeRelation&lt;/strong&gt;s. Nodes have relations to other nodes (a category can be in several categories) and nodes have relations to entries (an entry exists in or is linked to a node). There is a dangerous quirk here, one of the “node relations” is not actually a &lt;strong&gt;NodeRelation&lt;/strong&gt;. The parent of the node (where the node was created), is not a relation, but simply stored in a “Parent” field. But all other node relations to the node will be &lt;strong&gt;NodeRelations&lt;/strong&gt;. This might sound confusing, but later on I’ll try to show you the ramifications with this, how this is played out.&lt;/p&gt;
&lt;h2&gt;New Relations API explained&lt;/h2&gt;
&lt;p&gt;Episerver has presented dobby with a gift! Dobby is free [of having to remember and understand the different Target/Source depending on if it’s a node or entry, or coming up with some “product is always the target” chant]!&lt;/p&gt;
&lt;p&gt;The new APIs are much more semantic, using GetParents and GetChildren. And on the Relation objects, you can access the Parents and Children properties.&lt;/p&gt;
&lt;h2&gt;Comparing old API vs. new API vs. IContentLoader&lt;/h2&gt;
&lt;p&gt;Why do I compare with IContentLoader, weren’t we only talking about Relations? Yes, but seeing as the IContentLoader has a GetChildren method, I thought I could include it as well, as that one is not the same as any of the new/old relation apis.&lt;/p&gt;
&lt;p&gt;The variable x in these is always a ContentReference for the APIs, so the first column in the tables we assumed that you used the contentloader to fetch the content &lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;(IContentLoader.Get&amp;lt;IContent&amp;gt;(x))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; and hence get the type of the content.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Also note that I never specify in detail what Relation type I want to get, I always send in &amp;lt;Relation&amp;gt;. You can specify it closer, such as &lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;IRelationRepository.GetParents&amp;lt;ProductVariation&amp;gt;((VariationContent) x)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; and I will only get the parent product and not the parent nodes (see table).&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;N = Nodes&lt;br /&gt;
P = Products&lt;br /&gt;
V = Variation&lt;br /&gt;
LN = Linked Nodes&lt;/p&gt;
&lt;h3&gt;Commerce 11 Relations&lt;/h3&gt;
&lt;style type=&quot;text/css&quot;&gt;
.relations-table {
  width: 120%;
}
.relations-table td {
  text-align: center !important;
}
.relations-table td {
  width: 40%;
}
.relations-table td:first-child {
  font-family: &#39;Courier New&#39;;
  width: 150px;
}
.relation-list ol{
  margin-left: 40px;
}
.relation-list p {
  margin: 0;
}
&lt;/style&gt;
&lt;table class=&quot;relations-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetParents(x)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetChildren(x)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is ProductContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N + P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display:none&quot;&gt;You get all the nodes this product is in, as well as the products if another product has this product as variant.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;V(/P)&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display:none&quot;&gt;You get all ProductVariant relations. The trick here is that a product can have a product as variation as well, hence the (/P). But that product is not the “parent” product.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is VariationContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N + P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Same as the GetParents for ProductContent, I will get all nodes and all product this variant belongs to.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;-&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is NodeContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets the additional nodes for x. So x has a parent node, the one it was created in, but you won’t get that parent here. Instead, you’ll get nodes this node has been linked to (All properties -&amp;gt; Belongs to in the admin UI)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;V + P + LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets all the variants and products that is linked to the category. You will also get nodes that have specifically linked this node as a parent node. See it as you will get the child node in the node to node relation. But you will not get nodes that are created in the parent node, only linked ones. If you take &lt;img src=&quot;http://qs.lc/jorel/zl47g.png&quot; /&gt;&lt;br /&gt; as an example, “Mens Jackets” was created under “Mens”, but were linked to “Womens Dresses” as well. “Subcategory womens..” was created in “Womens Dresses”. If you do GetChildren on “Womens Dresses” you will get all the products, variants and the “Mens Jackets”, but NOT the “Subcategory womens”.&lt;/p&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;Commerce &amp;lt; 11 Relations&lt;/h3&gt;
&lt;table class=&quot;relations-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetRelationsBySource(x)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetRelationsByTarget(x)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is ProductContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N + V(/P)&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;You get all nodes and all ProductVariant relations. The trick here is that a product can have a product as variation as well, hence the (/P). But that product is not the “parent” product.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;A product can belong to another product, if so, you will get the parent product here.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is VariationContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets all the nodes the variant belongs to. Note that the variant can belong to different/additional nodes than the product.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets the product that the variant belongs to.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is NodeContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets the additional nodes for x. So x has a parent node, the one it was created in, but you won’t get that parent here. Instead, you’ll get nodes this node has been linked to (All properties -&amp;gt; Belongs to in the admin UI)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;V &amp;nbsp;+ P + LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets all the variants and products that is linked to the category. You will also get nodes that have specifically linked this node as a parent node. See it as you will get the child node in the node to node relation. But you will not get nodes that are created in the parent node, only linked ones. If you take &lt;img src=&quot;http://qs.lc/jorel/zl47g.png&quot; /&gt; &lt;br /&gt; as an example, “Mens Jackets” was created under “Mens”, but were linked to “Womens Dresses” as well. “Subcategory womens..” was created in “Womens Dresses”. If you do GetRelationsByTarget on “Womens Dresses” you will get all the products, variants and the “Mens Jackets”, but NOT the “Subcategory womens”.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;IContentLoader&lt;/h3&gt;
&lt;table class=&quot;relations-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetChildren(x)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is ProductContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;-&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is VariationContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;-&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is NodeContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;V + P + N&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;This will get everything “below” the node. Compared to GetRelationsByTarget it will also get the sub categories that were created in this node.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;Migrating to the new API&lt;/h2&gt;
&lt;p&gt;You can use the table above to try to understand what has changed and where you need to change something, or you can use the following logic applying the old API on top of the new API.&lt;/p&gt;
&lt;ul class=&quot;relation-list&quot;&gt;
&lt;li&gt;&lt;p&gt;GetRelationsBySource switches on the ContentType&lt;/p&gt;&lt;/li&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogEntry, it will GetParents&amp;lt;NodeRelation&amp;gt; as well as GetChildren&amp;lt;Relation&amp;gt; and return that to you.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogNode, it will GetParents&amp;lt;Relation&amp;gt; and return that to you.&lt;/p&gt;&lt;/li&gt;
  &lt;/ol&gt;
&lt;li&gt;&lt;p&gt;GetRelationsByTarget also switches on the ContentType&lt;/p&gt;&lt;/li&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogEntry, it will GetParents&amp;lt;Relation&amp;gt; and return that to you&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogNode, it will GetChildren&amp;lt;Relation&amp;gt; and return that to you&lt;/p&gt;&lt;/li&gt;
  &lt;/ol&gt;
&lt;/ul&gt;
&lt;p&gt;So in conclusion, if you had any GetRelationsBySource/GetRelationsByTarget that included EntryRelations, you will need to think which method you’d want to use depending on the input and desired output. If they only include NodeRelations, you can just translate GetRelationsBySource to GetParents and GetRelationsByTarget to GetChildren.&lt;/p&gt;
&lt;h2&gt;Primary category for Entries&lt;/h2&gt;
&lt;p&gt;A thing that was changed in the API as well was how you determine which category is the primary category for a CatalogEntry. Epi explained this pretty well at their &lt;a href=&quot;/link/e2af3d1925564abeb40482c82819328a.aspx&quot;&gt;Breaking Changes Commerce 11 document&lt;/a&gt; so I will just unceremoniously copy&amp;paste it here.&lt;/p&gt;
&lt;blockquote&gt;In previous versions, a catalog entry&#39;s home category, used for the content&#39;s ParentLink and when rendering default hierarchical URLs, was determined by the relation with the lowest SortOrder. This version introduces a separate field for marking one relation as the primary relation, used as home category.&lt;/blockquote&gt;
&lt;p&gt;Hopefully this post didn’t confuse you more, it was just notes on a deep dive on Relations we did when we upgraded to Commerce 11.&lt;/p&gt;
                </description>            <guid>http://avensia.com/joel-yourstone/new-relations-api-in-commerce-11/</guid>            <pubDate>Wed, 06 Sep 2017 18:01:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>New relations API in Commerce 11</title>            <link>https://www.avensia.com/joel-yourstone/new-relations-api-in-commerce-11/</link>            <description>
                    &lt;p&gt;&lt;em&gt;When epi released Commerce 11, it came with a new way of working with Relations. The old way is obsolete. I will go through how we migrated to the new APIs and some gotchas with it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;/link/e2af3d1925564abeb40482c82819328a.aspx&quot;&gt;Commerce 11 Breaking changes&lt;/a&gt; page, you can read the following point:
&lt;/p&gt;
&lt;blockquote&gt;In IRelationRepository, the GetChildren and GetParents methods supercede GetRelationsBySource and GetRelationsByTarget. On the relation objects, the properties Parent and Child serve the same purpose.&lt;/blockquote&gt;
&lt;p&gt;So the big difference is that we need to migrate all our existing &lt;code&gt;GetRelationsBySource&lt;/code&gt; / &lt;code&gt;GetRelationsByTarget&lt;/code&gt; to &lt;code&gt;GetParents&lt;/code&gt; / &lt;code&gt;GetChildren&lt;/code&gt;. However, it is not as easy as it may sound. The old API have been known among developers to be confusing, because depending on what type of relation it is, the source/target differs. I’ll talk about how that played out pre Commerce 11 now, if you already are an expert with this, you can skip this segment.&lt;/p&gt;
&lt;h2&gt;Relations explained&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Note: I sometimes use the term Node and perhaps sometimes Category, but for this post, they are interchangeable. In epi Commerce admin, they are named Category, but in the apis they are nodes.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the API in epi commerce there are two kinds of relations, &lt;strong&gt;NodeRelation &lt;/strong&gt;and &lt;strong&gt;EntryRelation&lt;/strong&gt;. NodeRelations are relations where one side of the relation is a NodeContent (most commonly Category). EntryRelations are relations where both sides are EntryContent (most commonly Variant, Product, Bundle or Package). There are a couple of classes that inherit EntryRelation, that specifies them more, such as &lt;strong&gt;ProductVariation&lt;/strong&gt;, &lt;strong&gt;PackageEntry &lt;/strong&gt;and &lt;strong&gt;BundleEntry&lt;/strong&gt;. In Commerce 11.2, we have as well a NodeEntryRelation in the APIs.&lt;/p&gt;
&lt;p&gt;So if I take a variant as an example. This variant belongs to a product, which is a &lt;strong&gt;EntryRelation &lt;/strong&gt;(more specifically &lt;strong&gt;ProductVariation&lt;/strong&gt;) since both variant and products are EntryContent. The variant was also created in a category, &lt;strong&gt;NodeRelation &lt;/strong&gt;between the variant and the category, as well as have been linked to 4 additional categories (&lt;strong&gt;NodeRelation &lt;/strong&gt;again).&lt;/p&gt;
&lt;p&gt;And if we do the same thing for a Category, that will only be &lt;strong&gt;NodeRelation&lt;/strong&gt;s. Nodes have relations to other nodes (a category can be in several categories) and nodes have relations to entries (an entry exists in or is linked to a node). There is a dangerous quirk here, one of the “node relations” is not actually a &lt;strong&gt;NodeRelation&lt;/strong&gt;. The parent of the node (where the node was created), is not a relation, but simply stored in a “Parent” field. But all other node relations to the node will be &lt;strong&gt;NodeRelations&lt;/strong&gt;. This might sound confusing, but later on I’ll try to show you the ramifications with this, how this is played out.&lt;/p&gt;
&lt;h2&gt;New Relations API explained&lt;/h2&gt;
&lt;p&gt;Episerver has presented dobby with a gift! Dobby is free [of having to remember and understand the different Target/Source depending on if it’s a node or entry, or coming up with some “product is always the target” chant]!&lt;/p&gt;
&lt;p&gt;The new APIs are much more semantic, using GetParents and GetChildren. And on the Relation objects, you can access the Parents and Children properties.&lt;/p&gt;
&lt;h2&gt;Comparing old API vs. new API vs. IContentLoader&lt;/h2&gt;
&lt;p&gt;Why do I compare with IContentLoader, weren’t we only talking about Relations? Yes, but seeing as the IContentLoader has a GetChildren method, I thought I could include it as well, as that one is not the same as any of the new/old relation apis.&lt;/p&gt;
&lt;p&gt;The variable x in these is always a ContentReference for the APIs, so the first column in the tables we assumed that you used the contentloader to fetch the content &lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;(IContentLoader.Get&amp;lt;IContent&amp;gt;(x))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; and hence get the type of the content.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Also note that I never specify in detail what Relation type I want to get, I always send in &amp;lt;Relation&amp;gt;. You can specify it closer, such as &lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;IRelationRepository.GetParents&amp;lt;ProductVariation&amp;gt;((VariationContent) x)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; and I will only get the parent product and not the parent nodes (see table).&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;N = Nodes&lt;br /&gt;
P = Products&lt;br /&gt;
V = Variation&lt;br /&gt;
LN = Linked Nodes&lt;/p&gt;
&lt;h3&gt;Commerce 11 Relations&lt;/h3&gt;
&lt;style type=&quot;text/css&quot;&gt;
.relations-table {
  width: 120%;
}
.relations-table td {
  text-align: center !important;
}
.relations-table td {
  width: 40%;
}
.relations-table td:first-child {
  font-family: &#39;Courier New&#39;;
  width: 150px;
}
.relation-list ol{
  margin-left: 40px;
}
.relation-list p {
  margin: 0;
}
&lt;/style&gt;
&lt;table class=&quot;relations-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetParents(x)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetChildren(x)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is ProductContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N + P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display:none&quot;&gt;You get all the nodes this product is in, as well as the products if another product has this product as variant.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;V(/P)&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display:none&quot;&gt;You get all ProductVariant relations. The trick here is that a product can have a product as variation as well, hence the (/P). But that product is not the “parent” product.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is VariationContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N + P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Same as the GetParents for ProductContent, I will get all nodes and all product this variant belongs to.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;-&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is NodeContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets the additional nodes for x. So x has a parent node, the one it was created in, but you won’t get that parent here. Instead, you’ll get nodes this node has been linked to (All properties -&amp;gt; Belongs to in the admin UI)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;V + P + LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets all the variants and products that is linked to the category. You will also get nodes that have specifically linked this node as a parent node. See it as you will get the child node in the node to node relation. But you will not get nodes that are created in the parent node, only linked ones. If you take &lt;img src=&quot;http://qs.lc/jorel/zl47g.png&quot; /&gt;&lt;br /&gt; as an example, “Mens Jackets” was created under “Mens”, but were linked to “Womens Dresses” as well. “Subcategory womens..” was created in “Womens Dresses”. If you do GetChildren on “Womens Dresses” you will get all the products, variants and the “Mens Jackets”, but NOT the “Subcategory womens”.&lt;/p&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;Commerce &amp;lt; 11 Relations&lt;/h3&gt;
&lt;table class=&quot;relations-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetRelationsBySource(x)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetRelationsByTarget(x)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is ProductContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N + V(/P)&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;You get all nodes and all ProductVariant relations. The trick here is that a product can have a product as variation as well, hence the (/P). But that product is not the “parent” product.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;A product can belong to another product, if so, you will get the parent product here.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is VariationContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;N&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets all the nodes the variant belongs to. Note that the variant can belong to different/additional nodes than the product.&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;P&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets the product that the variant belongs to.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is NodeContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets the additional nodes for x. So x has a parent node, the one it was created in, but you won’t get that parent here. Instead, you’ll get nodes this node has been linked to (All properties -&amp;gt; Belongs to in the admin UI)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;V &amp;nbsp;+ P + LN&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;Gets all the variants and products that is linked to the category. You will also get nodes that have specifically linked this node as a parent node. See it as you will get the child node in the node to node relation. But you will not get nodes that are created in the parent node, only linked ones. If you take &lt;img src=&quot;http://qs.lc/jorel/zl47g.png&quot; /&gt; &lt;br /&gt; as an example, “Mens Jackets” was created under “Mens”, but were linked to “Womens Dresses” as well. “Subcategory womens..” was created in “Womens Dresses”. If you do GetRelationsByTarget on “Womens Dresses” you will get all the products, variants and the “Mens Jackets”, but NOT the “Subcategory womens”.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;IContentLoader&lt;/h3&gt;
&lt;table class=&quot;relations-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;GetChildren(x)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is ProductContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;-&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is VariationContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;-&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;x is NodeContent&lt;/p&gt;&lt;/td&gt;&lt;td&gt;
&lt;/td&gt;&lt;td&gt;&lt;p&gt;V + P + N&lt;/p&gt;&lt;a href=&quot;javascript:void(0)&quot; onclick=&quot;$(this.nextElementSibling).toggle()&quot;&gt;Explaination&lt;/a&gt;&lt;p style=&quot;display: none&quot;&gt;This will get everything “below” the node. Compared to GetRelationsByTarget it will also get the sub categories that were created in this node.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;Migrating to the new API&lt;/h2&gt;
&lt;p&gt;You can use the table above to try to understand what has changed and where you need to change something, or you can use the following logic applying the old API on top of the new API.&lt;/p&gt;
&lt;ul class=&quot;relation-list&quot;&gt;
&lt;li&gt;&lt;p&gt;GetRelationsBySource switches on the ContentType&lt;/p&gt;&lt;/li&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogEntry, it will GetParents&amp;lt;NodeRelation&amp;gt; as well as GetChildren&amp;lt;Relation&amp;gt; and return that to you.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogNode, it will GetParents&amp;lt;Relation&amp;gt; and return that to you.&lt;/p&gt;&lt;/li&gt;
  &lt;/ol&gt;
&lt;li&gt;&lt;p&gt;GetRelationsByTarget also switches on the ContentType&lt;/p&gt;&lt;/li&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogEntry, it will GetParents&amp;lt;Relation&amp;gt; and return that to you&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt;If the ContentType is CatalogNode, it will GetChildren&amp;lt;Relation&amp;gt; and return that to you&lt;/p&gt;&lt;/li&gt;
  &lt;/ol&gt;
&lt;/ul&gt;
&lt;p&gt;So in conclusion, if you had any GetRelationsBySource/GetRelationsByTarget that included EntryRelations, you will need to think which method you’d want to use depending on the input and desired output. If they only include NodeRelations, you can just translate GetRelationsBySource to GetParents and GetRelationsByTarget to GetChildren.&lt;/p&gt;
&lt;h2&gt;Primary category for Entries&lt;/h2&gt;
&lt;p&gt;A thing that was changed in the API as well was how you determine which category is the primary category for a CatalogEntry. Epi explained this pretty well at their &lt;a href=&quot;/link/e2af3d1925564abeb40482c82819328a.aspx&quot;&gt;Breaking Changes Commerce 11 document&lt;/a&gt; so I will just unceremoniously copy&amp;paste it here.&lt;/p&gt;
&lt;blockquote&gt;In previous versions, a catalog entry&#39;s home category, used for the content&#39;s ParentLink and when rendering default hierarchical URLs, was determined by the relation with the lowest SortOrder. This version introduces a separate field for marking one relation as the primary relation, used as home category.&lt;/blockquote&gt;
&lt;p&gt;Hopefully this post didn’t confuse you more, it was just notes on a deep dive on Relations we did when we upgraded to Commerce 11.&lt;/p&gt;
                </description>            <guid>https://www.avensia.com/joel-yourstone/new-relations-api-in-commerce-11/</guid>            <pubDate>Wed, 06 Sep 2017 18:01:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Promotions &amp; GiftCards</title>            <link>http://www.avensia.com/joel-yourstone/promotions-and-gift-cards/</link>            <description>
                    &lt;p&gt;&lt;em&gt;Note: this is a post regarding the new Promotion System, introduced in Commerce 9.24. I highly recommend using it, if you are not already.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Selling gift cards in an ecommerce shop is a very common thing shop owners want to do, albeit it’s more the B2C shops rather than B2B. Selling gift cards can be very good for business for many reasons (including the notorious “This gift card has expired” where the shop owner gets the income “for free”). &lt;/p&gt;
&lt;h2&gt;So can I sell gift cards with my Episerver Commerce site?&lt;/h2&gt;
&lt;p&gt;Absolutely! A lot of sites already do it. A common way of implementing it is creating a custom episerver content class that inherits VariationContent. I won’t go through an implementation on this in this blog post however, I want to focus on some things that are good to know when &lt;strong&gt;combining selling gift cards and promotions&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You might think “since it’s just a normal variant, it will work perfectly with promotions, just like my other variants”. And you are right! The promotion system works in a way that all items you add to the cart are affected, regardless of what created them. But when it comes to gift cards, you might not want every discount in your system to affect the price. Let’s take a common discount “20% off everything!”:&lt;/p&gt;
&lt;h2&gt;The Loophole&lt;/h2&gt;
&lt;p&gt;
&lt;strong&gt;Initial setup&lt;/strong&gt;&lt;br /&gt;
Have 5 gift cards in your cart worth $10 a piece -&amp;gt; &lt;br /&gt;
You now have 5 giftcards having spent $40 (due to the 20% discount)
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Loop&lt;/strong&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Step 1: Buy 5 gift cards, pay $40 with 4 x $10 gift card&lt;/li&gt;
&lt;li&gt;Step 2: Receive the new gift cards and go back to step 1&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Each iteration of the steps will grant you &lt;strong&gt;1 free gift card worth $10&lt;/strong&gt;, seeing as you only pay with the gift cards you purchased initially with the $40.&lt;/p&gt;
&lt;p&gt;You can quickly realize this seems bad for business, especially if you have digital gift cards that are sent instantly via mail. This time tomorrow ebay will be filled your newly created currency, which will quickly inflate seeing as you’re doing the “Print more bills in order to have more money!”-thing that historically doesn’t really work out&amp;hellip; All from one person who likes loopholes and have the massive amount of initial investment of $40.&lt;/p&gt;
&lt;h2&gt;How can we prevent this?&lt;/h2&gt;
&lt;p&gt;As far as I’ve seen, Epi doesn’t have a built in way of excluding specific line items from the promotion evaluation (Would be great if such a feature was introduced!) so we’ll have to hack around it.&lt;/p&gt;
&lt;p&gt;So, normally you want to have somewhere in your cart loading/updating flow&lt;br /&gt;&lt;/p&gt;
&lt;pre&gt;
&lt;code class=&quot;cs&quot;&gt;((IPromotionEngine)_promotionEngine).Run((ICart) cart);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But we want to add some hacky logic to exclude gift cards, so instead of that line, create a method that&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Removes the gift card line items from the cart&lt;/li&gt;&lt;li&gt;Run the promotions on the cart without gift card line items&lt;/li&gt;&lt;li&gt;Catches the discounts applied and applies them to the existing cart&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;Like such:&lt;/p&gt;
&lt;pre&gt;
&lt;code class=&quot;cs&quot;&gt;public void RunPromotions(ICart cart)
{
    // Presuming you have a meta field on line item that tells you
    // if the line item is a gift card or not
    if (cart.GetAllLineItems().All(li =&amp;gt; !li.GetIsGiftCard()))
    {
        ((IPromotionEngine) _promotionEngine).Run(cart);
        return;
    }
    // We must exclude at least one giftcard. To do this, 
    // perform all calculations on a clone of the cart and 
    // copy the result to the original cart
    var clone = (ICart)((IDeepCloneable)cart).DeepClone();
            
    // Note that this implementation is working around always only having 
    // 1 OrderForm per Cart and 1 Shipment per Cart.
    // It can be wise use several Shipments, but we do that once
    // we create a PurchaseOrder of it
    var cloneForm = clone.Forms.First();
    var cloneShipment = cloneForm.Shipments.First();
    foreach (var li in cloneShipment.LineItems.Where(li =&amp;gt; li.GetIsGiftCard()).ToList())
    {
        cloneShipment.LineItems.Remove(li);
    }
    _promotionEngine.Run(clone);
    var cartForm = cart.Forms.First();
    var cartShipment = cartForm.Shipments.First();
    // Set the shipping discount from the clone
    var shipmentDiscount = cloneShipment.TryGetDiscountValue(x =&amp;gt; x.ShipmentDiscount);
    cartShipment.TrySetDiscountValue(x =&amp;gt; x.ShipmentDiscount, shipmentDiscount);
    // Set the entry discounts from the clone
    var cartLineItems = cartShipment.LineItems.ToDictionary(li =&amp;gt; li.LineItemId);
    foreach (var cloneLineItem in cloneShipment.LineItems)
    {
        ILineItem cartLineItem;
        if (cartLineItems.TryGetValue(cloneLineItem.LineItemId, out cartLineItem))
        {
            cartLineItem.TrySetDiscountValue(
                x =&amp;gt; x.EntryAmount,
                cloneLineItem.GetEntryDiscount()
            );
            cartLineItem.TrySetDiscountValue(
                x =&amp;gt; x.OrderAmount, 
                cloneLineItem.TryGetDiscountValue(x =&amp;gt; x.OrderAmount)
            );
        }
        else
        {
            // Rare case, but you can make it happen that you&#39;ll
            // get a gift from the clone, but not the ordinary cart
            cartShipment.LineItems.Add(cloneLineItem);
        }
    }
    foreach (var giftToRemove in cartShipment.LineItems.Where(
        li =&amp;gt; li.IsGift &amp;&amp; 
        !cloneShipment.LineItems.Any(cl =&amp;gt; cl.LineItemId == li.LineItemId))
        .ToList())
    {
        // Remove eventual gifts you got from the cart with the gift cards
        cartShipment.LineItems.Remove(giftToRemove);
    }
    // Remove promotions from the orderfrom and re add them from the clone form
    cartForm.Promotions.Clear();
    foreach (var p in cloneForm.Promotions)
        cartForm.Promotions.Add(p);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is a &lt;em&gt;very&lt;/em&gt; hacky way of doing it, but it works. Ebay will not be filled with your gift cards, the loopholey person that had $40 still has $40, the universe it at peace.&lt;/p&gt;
&lt;p&gt;If you have any comments/&lt;em&gt;BETTER SOLUTIONS&lt;/em&gt;/questions about this post, feel free to leave a comment below. And with a comment below I mean send me a mail to &lt;a href=&quot;mailto:joel.yourstone@avensia.com&quot;&gt;joel.yourstone@avensia.com&lt;/a&gt; and I’ll edit it into the post because I haven’t fixed any plugin that does blog comments yet...&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
                </description>            <guid>http://www.avensia.com/joel-yourstone/promotions-and-gift-cards/</guid>            <pubDate>Fri, 18 Aug 2017 14:00:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Promotions &amp; GiftCards</title>            <link>http://www.avensia.com/index.php/joel-yourstone/promotions-and-gift-cards/</link>            <description>
                    &lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&amp;nbsp;&lt;/strong&gt;If you have &amp;gt; Commerce 10.1 there is a feature for this that solves the issue much more gracefully!&amp;nbsp;I haven&#39;t evaluated it yet, but will do and update this blog post!&amp;nbsp;&lt;a href=&quot;/link/c874b7d7594a4d818c00bc87c2036105.aspx&quot;&gt;http://world.episerver.com/documentation/developer-guides/commerce/marketing/exclude-products-from-promotions/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/link/c874b7d7594a4d818c00bc87c2036105.aspx&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: this is a post regarding the new Promotion System, introduced in Commerce 9.24. I highly recommend using it, if you are not already.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Selling gift cards in an ecommerce shop is a very common thing shop owners want to do, albeit it’s more the B2C shops rather than B2B. Selling gift cards can be very good for business for many reasons (including the notorious “This gift card has expired” where the shop owner gets the income “for free”). &lt;/p&gt;
&lt;h2&gt;So can I sell gift cards with my Episerver Commerce site?&lt;/h2&gt;
&lt;p&gt;Absolutely! A lot of sites already do it. A common way of implementing it is creating a custom episerver content class that inherits VariationContent. I won’t go through an implementation on this in this blog post however, I want to focus on some things that are good to know when &lt;strong&gt;combining selling gift cards and promotions&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You might think “since it’s just a normal variant, it will work perfectly with promotions, just like my other variants”. And you are right! The promotion system works in a way that all items you add to the cart are affected, regardless of what created them. But when it comes to gift cards, you might not want every discount in your system to affect the price. Let’s take a common discount “20% off everything!”:&lt;/p&gt;
&lt;h2&gt;The Loophole&lt;/h2&gt;
&lt;p&gt;
&lt;strong&gt;Initial setup&lt;/strong&gt;&lt;br /&gt;
Have 5 gift cards in your cart worth $10 a piece -&amp;gt; &lt;br /&gt;
You now have 5 giftcards having spent $40 (due to the 20% discount)
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Loop&lt;/strong&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Step 1: Buy 5 gift cards, pay $40 with 4 x $10 gift card&lt;/li&gt;
&lt;li&gt;Step 2: Receive the new gift cards and go back to step 1&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Each iteration of the steps will grant you &lt;strong&gt;1 free gift card worth $10&lt;/strong&gt;, seeing as you only pay with the gift cards you purchased initially with the $40.&lt;/p&gt;
&lt;p&gt;You can quickly realize this seems bad for business, especially if you have digital gift cards that are sent instantly via mail. This time tomorrow ebay will be filled with your newly created currency, which will quickly inflate seeing as you’re doing the “Print more bills in order to have more money!”-thing that historically doesn’t really work out&amp;hellip; All from one person who likes loopholes and have the massive amount of initial investment of $40.&lt;/p&gt;
&lt;h2&gt;How can we prevent this?&lt;/h2&gt;
&lt;p&gt;As far as I’ve seen, Epi doesn’t have a built in way of excluding specific line items from the promotion evaluation (Would be great if such a feature was introduced!) so we’ll have to hack around it.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you&#39;re running &amp;gt; Commerce 10.1. use&amp;nbsp;&lt;/em&gt;&lt;a href=&quot;/link/c874b7d7594a4d818c00bc87c2036105.aspx&quot;&gt;http://world.episerver.com/documentation/developer-guides/commerce/marketing/exclude-products-from-promotions/&lt;/a&gt;.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;So, normally you want to have somewhere in your cart loading/updating flow&lt;br /&gt;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;((IPromotionEngine)_promotionEngine).Run((ICart) cart);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But we want to add some hacky logic to exclude gift cards, so instead of that line, create a method that&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Removes the gift card line items from the cart&lt;/li&gt;&lt;li&gt;Run the promotions on the cart without gift card line items&lt;/li&gt;&lt;li&gt;Catches the discounts applied and applies them to the existing cart&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;Like such:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public void RunPromotions(ICart cart)
{
    // Presuming you have a meta field on line item that tells you
    // if the line item is a gift card or not
    if (cart.GetAllLineItems().All(li =&amp;gt; !li.GetIsGiftCard()))
    {
        ((IPromotionEngine) _promotionEngine).Run(cart);
        return;
    }
    // We must exclude at least one giftcard. To do this, 
    // perform all calculations on a clone of the cart and 
    // copy the result to the original cart
    var clone = (ICart)((IDeepCloneable)cart).DeepClone();
            
    // Note that this implementation is working around always only having 
    // 1 OrderForm per Cart and 1 Shipment per Cart.
    // It can be wise use several Shipments, but we do that once
    // we create a PurchaseOrder of it
    var cloneForm = clone.Forms.First();
    var cloneShipment = cloneForm.Shipments.First();
    foreach (var li in cloneShipment.LineItems.Where(li =&amp;gt; li.GetIsGiftCard()).ToList())
    {
        cloneShipment.LineItems.Remove(li);
    }
    _promotionEngine.Run(clone);
    var cartForm = cart.Forms.First();
    var cartShipment = cartForm.Shipments.First();
    // Set the shipping discount from the clone
    var shipmentDiscount = cloneShipment.TryGetDiscountValue(x =&amp;gt; x.ShipmentDiscount);
    cartShipment.TrySetDiscountValue(x =&amp;gt; x.ShipmentDiscount, shipmentDiscount);
    // Set the entry discounts from the clone
    var cartLineItems = cartShipment.LineItems.ToDictionary(li =&amp;gt; li.LineItemId);
    foreach (var cloneLineItem in cloneShipment.LineItems)
    {
        ILineItem cartLineItem;
        if (cartLineItems.TryGetValue(cloneLineItem.LineItemId, out cartLineItem))
        {
            cartLineItem.TrySetDiscountValue(
                x =&amp;gt; x.EntryAmount,
                cloneLineItem.GetEntryDiscount()
            );
            cartLineItem.TrySetDiscountValue(
                x =&amp;gt; x.OrderAmount, 
                cloneLineItem.TryGetDiscountValue(x =&amp;gt; x.OrderAmount)
            );
        }
        else
        {
            // Rare case, but you can make it happen that you&#39;ll
            // get a gift from the clone, but not the ordinary cart
            cartShipment.LineItems.Add(cloneLineItem);
        }
    }
    foreach (var giftToRemove in cartShipment.LineItems.Where(
        li =&amp;gt; li.IsGift &amp;&amp; 
        !cloneShipment.LineItems.Any(cl =&amp;gt; cl.LineItemId == li.LineItemId))
        .ToList())
    {
        // Remove eventual gifts you got from the cart with the gift cards
        cartShipment.LineItems.Remove(giftToRemove);
    }
    // Remove promotions from the orderfrom and re add them from the clone form
    cartForm.Promotions.Clear();
    foreach (var p in cloneForm.Promotions)
        cartForm.Promotions.Add(p);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is a &lt;em&gt;very&lt;/em&gt; hacky way of doing it, but it works. Ebay will not be filled with your gift cards, the loopholey person that had $40 still has $40, the universe it at peace.&lt;/p&gt;
&lt;p&gt;If you have any comments/&lt;em&gt;BETTER SOLUTIONS&lt;/em&gt;/questions about this post, feel free to leave a comment below.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
                </description>            <guid>http://www.avensia.com/index.php/joel-yourstone/promotions-and-gift-cards/</guid>            <pubDate>Fri, 18 Aug 2017 14:00:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Promotions &amp; GiftCards</title>            <link>http://www.avensia.com/index.php?p=joel-yourstone/promotions-and-gift-cards</link>            <description>
                    &lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&amp;nbsp;&lt;/strong&gt;If you have &amp;gt; Commerce 10.1 there is a feature for this that solves the issue much more gracefully!&amp;nbsp;I haven&#39;t evaluated it yet, but will do and update this blog post!&amp;nbsp;&lt;a href=&quot;/link/c874b7d7594a4d818c00bc87c2036105.aspx&quot;&gt;http://world.episerver.com/documentation/developer-guides/commerce/marketing/exclude-products-from-promotions/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/link/c874b7d7594a4d818c00bc87c2036105.aspx&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: this is a post regarding the new Promotion System, introduced in Commerce 9.24. I highly recommend using it, if you are not already.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Selling gift cards in an ecommerce shop is a very common thing shop owners want to do, albeit it’s more the B2C shops rather than B2B. Selling gift cards can be very good for business for many reasons (including the notorious “This gift card has expired” where the shop owner gets the income “for free”). &lt;/p&gt;
&lt;h2&gt;So can I sell gift cards with my Episerver Commerce site?&lt;/h2&gt;
&lt;p&gt;Absolutely! A lot of sites already do it. A common way of implementing it is creating a custom episerver content class that inherits VariationContent. I won’t go through an implementation on this in this blog post however, I want to focus on some things that are good to know when &lt;strong&gt;combining selling gift cards and promotions&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You might think “since it’s just a normal variant, it will work perfectly with promotions, just like my other variants”. And you are right! The promotion system works in a way that all items you add to the cart are affected, regardless of what created them. But when it comes to gift cards, you might not want every discount in your system to affect the price. Let’s take a common discount “20% off everything!”:&lt;/p&gt;
&lt;h2&gt;The Loophole&lt;/h2&gt;
&lt;p&gt;
&lt;strong&gt;Initial setup&lt;/strong&gt;&lt;br /&gt;
Have 5 gift cards in your cart worth $10 a piece -&amp;gt; &lt;br /&gt;
You now have 5 giftcards having spent $40 (due to the 20% discount)
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Loop&lt;/strong&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Step 1: Buy 5 gift cards, pay $40 with 4 x $10 gift card&lt;/li&gt;
&lt;li&gt;Step 2: Receive the new gift cards and go back to step 1&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Each iteration of the steps will grant you &lt;strong&gt;1 free gift card worth $10&lt;/strong&gt;, seeing as you only pay with the gift cards you purchased initially with the $40.&lt;/p&gt;
&lt;p&gt;You can quickly realize this seems bad for business, especially if you have digital gift cards that are sent instantly via mail. This time tomorrow ebay will be filled with your newly created currency, which will quickly inflate seeing as you’re doing the “Print more bills in order to have more money!”-thing that historically doesn’t really work out&amp;hellip; All from one person who likes loopholes and have the massive amount of initial investment of $40.&lt;/p&gt;
&lt;h2&gt;How can we prevent this?&lt;/h2&gt;
&lt;p&gt;As far as I’ve seen, Epi doesn’t have a built in way of excluding specific line items from the promotion evaluation (Would be great if such a feature was introduced!) so we’ll have to hack around it.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you&#39;re running &amp;gt; Commerce 10.1. use&amp;nbsp;&lt;/em&gt;&lt;a href=&quot;/link/c874b7d7594a4d818c00bc87c2036105.aspx&quot;&gt;http://world.episerver.com/documentation/developer-guides/commerce/marketing/exclude-products-from-promotions/&lt;/a&gt;.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;So, normally you want to have somewhere in your cart loading/updating flow&lt;br /&gt;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;((IPromotionEngine)_promotionEngine).Run((ICart) cart);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But we want to add some hacky logic to exclude gift cards, so instead of that line, create a method that&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Removes the gift card line items from the cart&lt;/li&gt;&lt;li&gt;Run the promotions on the cart without gift card line items&lt;/li&gt;&lt;li&gt;Catches the discounts applied and applies them to the existing cart&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;Like such:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public void RunPromotions(ICart cart)
{
    // Presuming you have a meta field on line item that tells you
    // if the line item is a gift card or not
    if (cart.GetAllLineItems().All(li =&amp;gt; !li.GetIsGiftCard()))
    {
        ((IPromotionEngine) _promotionEngine).Run(cart);
        return;
    }
    // We must exclude at least one giftcard. To do this, 
    // perform all calculations on a clone of the cart and 
    // copy the result to the original cart
    var clone = (ICart)((IDeepCloneable)cart).DeepClone();
            
    // Note that this implementation is working around always only having 
    // 1 OrderForm per Cart and 1 Shipment per Cart.
    // It can be wise use several Shipments, but we do that once
    // we create a PurchaseOrder of it
    var cloneForm = clone.Forms.First();
    var cloneShipment = cloneForm.Shipments.First();
    foreach (var li in cloneShipment.LineItems.Where(li =&amp;gt; li.GetIsGiftCard()).ToList())
    {
        cloneShipment.LineItems.Remove(li);
    }
    _promotionEngine.Run(clone);
    var cartForm = cart.Forms.First();
    var cartShipment = cartForm.Shipments.First();
    // Set the shipping discount from the clone
    var shipmentDiscount = cloneShipment.TryGetDiscountValue(x =&amp;gt; x.ShipmentDiscount);
    cartShipment.TrySetDiscountValue(x =&amp;gt; x.ShipmentDiscount, shipmentDiscount);
    // Set the entry discounts from the clone
    var cartLineItems = cartShipment.LineItems.ToDictionary(li =&amp;gt; li.LineItemId);
    foreach (var cloneLineItem in cloneShipment.LineItems)
    {
        ILineItem cartLineItem;
        if (cartLineItems.TryGetValue(cloneLineItem.LineItemId, out cartLineItem))
        {
            cartLineItem.TrySetDiscountValue(
                x =&amp;gt; x.EntryAmount,
                cloneLineItem.GetEntryDiscount()
            );
            cartLineItem.TrySetDiscountValue(
                x =&amp;gt; x.OrderAmount, 
                cloneLineItem.TryGetDiscountValue(x =&amp;gt; x.OrderAmount)
            );
        }
        else
        {
            // Rare case, but you can make it happen that you&#39;ll
            // get a gift from the clone, but not the ordinary cart
            cartShipment.LineItems.Add(cloneLineItem);
        }
    }
    foreach (var giftToRemove in cartShipment.LineItems.Where(
        li =&amp;gt; li.IsGift &amp;&amp; 
        !cloneShipment.LineItems.Any(cl =&amp;gt; cl.LineItemId == li.LineItemId))
        .ToList())
    {
        // Remove eventual gifts you got from the cart with the gift cards
        cartShipment.LineItems.Remove(giftToRemove);
    }
    // Remove promotions from the orderfrom and re add them from the clone form
    cartForm.Promotions.Clear();
    foreach (var p in cloneForm.Promotions)
        cartForm.Promotions.Add(p);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is a &lt;em&gt;very&lt;/em&gt; hacky way of doing it, but it works. Ebay will not be filled with your gift cards, the loopholey person that had $40 still has $40, the universe it at peace.&lt;/p&gt;
&lt;p&gt;If you have any comments/&lt;em&gt;BETTER SOLUTIONS&lt;/em&gt;/questions about this post, feel free to leave a comment below.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
                </description>            <guid>http://www.avensia.com/index.php?p=joel-yourstone/promotions-and-gift-cards</guid>            <pubDate>Fri, 18 Aug 2017 14:00:00 GMT</pubDate>           <category>Blog post</category></item></channel>
</rss>