<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Commerce Server/Engine Tips]]></title><description><![CDATA[Tips&Tricks for Sitecore Commerce Server and Sitecore Commerce Engine]]></description><link>https://commerceservertips.com/</link><image><url>https://commerceservertips.com/favicon.png</url><title>Commerce Server/Engine Tips</title><link>https://commerceservertips.com/</link></image><generator>Ghost 2.25</generator><lastBuildDate>Sun, 08 Mar 2026 02:34:07 GMT</lastBuildDate><atom:link href="https://commerceservertips.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Creating an index for Gift cards in Sitecore XC 10]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Sitecore XC stores entities in SQL server in a JSON format. It is possible to query the data that is stored in JSON, but it is a little more difficult and there really is nothing in the XC framework that helps you.<br>
The reason there is no support for flexible</p>]]></description><link>https://commerceservertips.com/creating-an-index-for-gift-cards/</link><guid isPermaLink="false">6079d001bcf14323c011ed18</guid><category><![CDATA[Sitecore Commerce]]></category><category><![CDATA[SOLR]]></category><dc:creator><![CDATA[Erwin Werkman]]></dc:creator><pubDate>Mon, 19 Apr 2021 17:46:27 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1582582542002-504761a4c8e6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fGdpZnQlMjBjYXJkfGVufDB8fHx8MTYxODU5NjA1OQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1582582542002-504761a4c8e6?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fGdpZnQlMjBjYXJkfGVufDB8fHx8MTYxODU5NjA1OQ&ixlib=rb-1.2.1&q=80&w=1080" alt="Creating an index for Gift cards in Sitecore XC 10"><p>Sitecore XC stores entities in SQL server in a JSON format. It is possible to query the data that is stored in JSON, but it is a little more difficult and there really is nothing in the XC framework that helps you.<br>
The reason there is no support for flexible querying is that XC gives you the option to index entities in SOLR which gives you really flexible querying.</p>
<p>In XC 10 there are a number of entities that are indexed out-of-the-box:</p>
<ul>
<li>Catalog items (Category, Sellable Item, Catalog)</li>
<li>Orders</li>
<li>Customer</li>
<li>Price Cards</li>
<li>Promotions</li>
</ul>
<p>The catalog items, orders and customer indexes are mainly there to facilitate searching from the Business tools. The price cards and promotions indexes are used by the business tools as well, but also play a major part in the operations of the Shops role: they are used to get the latest prices and promotions, so you need to make sure these are always up to date, otherwise your promotion or price card will not be applied.</p>
<p>But what if you want to index another identity for instance the gift card entity. What do you need to do to make gift cards searchable?</p>
<p>In this article I will go into the different steps and will find out it is surprisingly easy to do.</p>
<blockquote>
<p>Note that this article is specifically about SOLR.</p>
</blockquote>
<blockquote>
<p>You can find the accompanying repository here: <a href="https://github.com/ewerkman/indexed-giftcard">https://github.com/ewerkman/indexed-giftcard</a></p>
</blockquote>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="step1createanewcoreinsolr">Step 1: Create a new core in SOLR</h2>
<blockquote>
<p>Note: this might not be the best way to create new SOLR cores. Ask someone that actually knows about SOLR for advice :-).</p>
</blockquote>
<p>Every index in XC 10 uses two cores, one active one and one for rebuilding the index. For your new index:</p>
<ol>
<li>Create two new cores in SOLR by copying them from an existing core, for instance the <code>OrdersScope</code> and <code>OrdersScope-Rebuild</code> folders. Copy them to <code>GiftcardsScope</code> and <code>GiftcardsScope-Rebuild</code> respectively.</li>
<li>Delete all files from the <code>data</code> folder in the <code>GiftcardsScope</code> and <code>GiftcardsScope-Rebuild</code> folders. When you do a full index, this folder will be filled again with the correct data.</li>
<li>Change the name of the cores by editing the <code>core.properties</code> file in both core. Open <code>core.properties</code> and change name to <code>GiftcardsScope</code> and <code>GiftcardsScope-Rebuild</code> respectively.</li>
<li>In each core, the <code>conf</code> folder contains the configuration of the core. A file called <code>managed-schema</code> contains the schema for the new core. You need to take out some of the fields that are specific to the index you copied the configuration from and replace them with fields specific for the gift card index.<br>
The GitHub repository contains the file that you should end up with for my example:<br>
<a href="https://github.com/ewerkman/indexed-giftcard/blob/main/solr/managed-schema">https://github.com/ewerkman/indexed-giftcard/blob/main/solr/managed-schema</a>.</li>
</ol>
<p>The most important part there is this section:</p>
<pre><code class="language-xml">    &lt;!-- CommerceEngine Order Unique Id Field --&gt;
    &lt;field name=&quot;entityuniqueid&quot; type=&quot;string&quot; indexed=&quot;true&quot; stored=&quot;true&quot; required=&quot;true&quot; /&gt;
    &lt;field name=&quot;entityid&quot; type=&quot;string&quot; indexed=&quot;true&quot; stored=&quot;true&quot; required=&quot;true&quot; /&gt;
    &lt;field name=&quot;entityversion&quot; type=&quot;pint&quot; indexed=&quot;true&quot; stored=&quot;true&quot; /&gt;
    
    &lt;!-- CommerceEngine Order --&gt;
    &lt;field name=&quot;giftcardcode&quot; type=&quot;string&quot; indexed=&quot;true&quot; stored=&quot;true&quot; /&gt;
    &lt;field name=&quot;activationdate&quot; type=&quot;pdate&quot; indexed=&quot;true&quot; stored=&quot;true&quot; /&gt;
    &lt;field name=&quot;balance&quot; type=&quot;pfloat&quot; indexed=&quot;true&quot; stored=&quot;true&quot; /&gt;
    &lt;field name=&quot;originalamount&quot; type=&quot;pfloat&quot; indexed=&quot;true&quot; stored=&quot;true&quot; /&gt;
    &lt;field name=&quot;artifactstoreid&quot; type=&quot;string&quot; indexed=&quot;true&quot; stored=&quot;true&quot; /&gt;
    &lt;field name=&quot;datecreated&quot; type=&quot;pdate&quot; indexed=&quot;true&quot; stored=&quot;true&quot; /&gt;
    &lt;field name=&quot;dateupdated&quot; type=&quot;pdate&quot; indexed=&quot;true&quot; stored=&quot;true&quot; /&gt;
</code></pre>
<p>Here the fields specific to the gift card index are defined such as <code>giftcardcode</code> and <code>balance</code>. You want this index to contain all the fields you want to query on.</p>
<ol start="5">
<li>You can now restart SOLR and your new cores should be visible in the admin interface.</li>
</ol>
<h2 id="step2configurethenewindex">Step 2: Configure the new index</h2>
<p>Next you need to configure the new index in XC. You can find all of the necessary configuration in this file:</p>
<p><a href="https://github.com/ewerkman/indexed-giftcard/blob/main/policies/Plugin.Giftcard.PolicySet.json">https://github.com/ewerkman/indexed-giftcard/blob/main/policies/Plugin.Giftcard.PolicySet.json</a></p>
<p>There are 3 things you need to configure:</p>
<h3 id="fullindexminion">FullIndexMinion</h3>
<p>This is the minion responsible for doing a full index. You configure it by:</p>
<ul>
<li>specifying the <code>Sitecore.Commerce.Plugin.Search.FullIndexMinion</code> as the minion to run;</li>
<li>configuring the <code>GiftCards</code> list as the list to watch for indexing;</li>
<li>configuring <code>Sitecore.Commerce.Plugin.GiftCards.GiftCard</code> as the entity to index;</li>
</ul>
<p>Note that the full index minion is not configured to run on a set interval. You only run it if necessary, for instance by using Postman to start the minion.</p>
<h3 id="incrementalindexminion">IncrementalIndexMinion</h3>
<p>The incremental index minion is run on a schedule (by default, every 5 minutes) and watches the <code>GiftcardsIndex</code> list. If something changes on a <code>GiftCard</code> it is automatically added to the specified list. The incremental index minion goes through this list and updates the index.</p>
<h3 id="searchscopepolicy">SearchScopePolicy</h3>
<p>The <code>SearchScopePolicy</code> configures the index:</p>
<pre><code class="language-json"> {
    &quot;$type&quot;: &quot;Sitecore.Commerce.Plugin.Search.SearchScopePolicy, Sitecore.Commerce.Plugin.Search&quot;,
    &quot;Name&quot;: &quot;GiftcardsScope&quot;,
    &quot;CurrentIndexName&quot;: &quot;GiftcardsScope&quot;,
    &quot;SwitchOnRebuild&quot;: true,
    &quot;SwitchOnRebuildReset&quot;: false,
    &quot;SwitchOnRebuildClearPreviousIndex&quot;: true,
    &quot;SwitchOnRebuildPrimaryIndexName&quot;: &quot;GiftcardsScope&quot;,
    &quot;SwitchOnRebuildSecondaryIndexName&quot;: &quot;GiftcardsScope-Rebuild&quot;,
    &quot;IncrementalListName&quot;: &quot;GiftcardsIndex&quot;,
    &quot;DeletedListName&quot;: &quot;DeletedGiftcardsIndex&quot;,
    &quot;FullListNames&quot;: [
        &quot;Giftcards&quot;
    ],
    &quot;EntityTypeNames&quot;: {
        &quot;$type&quot;: &quot;System.Collections.Generic.List`1[[System.String, mscorlib]], mscorlib&quot;,
        &quot;$values&quot;: [
            &quot;Sitecore.Commerce.Plugin.GiftCards.GiftCard&quot;
        ]
    },
    &quot;ResultDetailsTags&quot;: {
        &quot;$type&quot;: &quot;System.Collections.Generic.List`1[[Sitecore.Commerce.Core.Tag, Sitecore.Commerce.Core]], mscorlib&quot;,
        &quot;$values&quot;: [
            {
                &quot;$type&quot;: &quot;Sitecore.Commerce.Core.Tag, Sitecore.Commerce.Core&quot;,
                &quot;Name&quot;: &quot;GiftcardTable&quot;
            }
        ]
    }
}
</code></pre>
<p>Here you specify which cores to use, if you want to do a switch-on-rebuild, which list to use for the incremental index and which one for deleted entities etc.</p>
<h3 id="indexablepolicy">IndexablePolicy</h3>
<p>In the <code>IndexablePolicy</code> you configure which properties are indexed.</p>
<h2 id="step3createablocktoturnanentityintoadocument">Step 3: Create a block to turn an entity into a document</h2>
<p>The last step is to create a block that you add to the <code>IFullIndexMinionPipeline</code> and <code>IIncrementalIndexMinionPipeline</code> to create the SOLR document based on a <code>GiftCard</code> entity.<br>
You can find the code for this block here: <a href="https://github.com/ewerkman/indexed-giftcard/blob/main/src/SitecoreServices.Commerce.Plugin.Index/Pipelines/Blocks/InitializeGiftcardsIndexingViewBlock.cs">InitializeGiftcardsIndexingViewBlock.cs</a></p>
<p>This block takes a list of <code>GiftCard</code> entities and for each gift card it adds the properties to be indexed to an <code>EntityView</code>.</p>
<h2 id="howdoiquerytheindex">How do I query the index?</h2>
<p>The <a href="https://github.com/ewerkman/indexed-giftcard/blob/main/src/SitecoreServices.Commerce.Plugin.Index/Commands/GetGiftCardsCommand.cs">repository</a> contains a sample command that show how you can query the index:</p>
<pre><code class="language-csharp">// Filter order on activation date
var filterQuery = new FilterQuery(new AndFilterNode(new LessThanFilterNode(
        new FieldNameFilterNode(&quot;activationdate&quot;),
        new FieldValueFilterNode(endDate
                .ToString(SearchConstants.DateTimeSearchFormat, CultureInfo.InvariantCulture),
            FilterNodeValueType.Date)),
    new GreaterThanFilterNode(new FieldNameFilterNode(&quot;activationdate&quot;),
        new FieldValueFilterNode(
            startDate
                .ToString(SearchConstants.DateTimeSearchFormat, CultureInfo.InvariantCulture),
            FilterNodeValueType.Date))));

var scope = SearchScopePolicy.GetPolicyByType(commerceContext, commerceContext.Environment,
    typeof(GiftCard));

var searchResults = await _commander.Command&lt;SearchEntitiesCommand&gt;()
    .Process&lt;GiftCard&gt;(commerceContext, scope.Name, new SearchQuery(), filterQuery)
    .ConfigureAwait(false);
</code></pre>
<p>It's easy: create a query, get the relevant scope you want to query and search for entities. This will automatically give you a list of entities.</p>
<h2 id="insummary">In Summary</h2>
<p>Creating an index for entities gives you much more flexibility in querying.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Note to self: Changing the url of the Business Tools in Launchpad]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>I keep forgetting the location of the item you need to change the link of the <em>Business Tools</em> button in Launchpad, so this post is mostly a reminder for myself but I hope others will also benefit.</p>
<p>You can find it here:</p>
<pre><code>/sitecore/client/Applications/Launchpad/PageSettings/Buttons/Commerce/BusinessTools</code></pre>]]></description><link>https://commerceservertips.com/note-to-self-changing-the-url-of-the-business-tools-in-launchpad/</link><guid isPermaLink="false">5fbe24f7bcf14323c011ecf7</guid><dc:creator><![CDATA[Erwin Werkman]]></dc:creator><pubDate>Wed, 25 Nov 2020 09:40:25 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1587815280777-90694ad8c4ac?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1587815280777-90694ad8c4ac?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Note to self: Changing the url of the Business Tools in Launchpad"><p>I keep forgetting the location of the item you need to change the link of the <em>Business Tools</em> button in Launchpad, so this post is mostly a reminder for myself but I hope others will also benefit.</p>
<p>You can find it here:</p>
<pre><code>/sitecore/client/Applications/Launchpad/PageSettings/Buttons/Commerce/BusinessTools
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[4 things you should not forget when setting up the minions role]]></title><description><![CDATA[<p>Recently I had to set up a Sitecore Commerce Minions role manually. I thought it was simple, because in the past I had already found out that you need to make sure you <a href="https://commerceservertips.com/quick-little-tip-configure-the-minion-role-to-be-alwaysrunning/">set the application pool to <code>AlwaysRunning</code></a>.  So I did.</p><p>Unfortunately, I found out that even though it</p>]]></description><link>https://commerceservertips.com/4-things-you-should-not-forget-when-setting-up-the-minions-role/</link><guid isPermaLink="false">5e62b716bcf14323c011ebc9</guid><category><![CDATA[sitecore commerce 9]]></category><category><![CDATA[minions]]></category><dc:creator><![CDATA[Erwin Werkman]]></dc:creator><pubDate>Sat, 07 Mar 2020 11:04:12 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1515041219749-89347f83291a?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1515041219749-89347f83291a?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="4 things you should not forget when setting up the minions role"><p>Recently I had to set up a Sitecore Commerce Minions role manually. I thought it was simple, because in the past I had already found out that you need to make sure you <a href="https://commerceservertips.com/quick-little-tip-configure-the-minion-role-to-be-alwaysrunning/">set the application pool to <code>AlwaysRunning</code></a>.  So I did.</p><p>Unfortunately, I found out that even though it was set to <code>AlwaysRunning</code>, the minions didn't start automatically. And worse, if I started the Minions role manually, it would often die after 20 minutes. So, something was wrong.</p><p>Time to dive into the SIF scripts that are used to install the commerce environment on my development machine. There I found out three things:</p><ul><li>On the application pool, you need to set the <code>Start Mode</code> to <code>AlwaysRunning</code>. I already knew that.</li><li>Make sure that <code>Regular Time Interval (minutes)</code> in the <em>Recycle</em> section is set to 0. Should have known that.</li><li>In the advanced settings of the minions website, set <code>Preload Enabled</code> to <code>true</code>. Also didn't know that.</li></ul><p>Looking at the SIF script, this should be it, right? </p><p>Unfortunately, no. Even though my manually configured Minions settings were now exactly the same as my out-of-the-box version, it would not start automatically.</p><p>Looking for more information on how to set up application pools that are always running I found this piece of documentation on the Microsoft site: <em><a href="https://docs.microsoft.com/en-us/iis/get-started/whats-new-in-iis-8/iis-80-application-initialization">IIS 8.0 Application Initialization</a></em>. <br>That page contains amongst others, the pre-requisites for application initialization:</p><blockquote>The Application Initialization feature requires IIS 8.0 to be installed. In addition, the Application Initialization feature within the IIS "Application Development" sub-feature needs to be installed.</blockquote><!--kg-card-begin: image--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://commerceservertips.com/content/images/2020/03/image-2.png" class="kg-image" alt="4 things you should not forget when setting up the minions role"><figcaption>Make sure you have the Application Initialization feature installed.</figcaption></figure><!--kg-card-end: image--><p>I checked the server I was installing the minions role on and indeed, the Application Initialization feature was not installed. After installation, the minion started up automatically. </p><h2 id="in-summary">In summary</h2><p>For the minions to run automatically you have to make sure:</p><ol><li>the <em>Application Initialization</em> feature has been installed;</li><li>the <code>Start Mode</code> of the application pool is set to <code>AlwaysRunning</code>;</li><li>in the Advanced Settings of the application pool, make sure that <code>Regular Time Interval (minutes)</code> in the <em>Recycle</em> section is set to 0 (in general, make sure there are no recycle settings set that would recycle the application pool under normal circumstance). </li><li><code>Preload Enabled</code> is set to <code>true</code> in the advanced settings of the minions website.</li></ol>]]></content:encoded></item><item><title><![CDATA[Personalizing your commerce site]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Sitecore has great personalization features. Personalization in Sitecore is based on the content you visit and the actions you perform on the website.<br>
You use content profiling to gain a better understanding of the interest of your visitor. You assign profile cards to your content items to indicate which &quot;</p>]]></description><link>https://commerceservertips.com/personalizing-your-catalog/</link><guid isPermaLink="false">5e009aecbcf14323c011e9d5</guid><dc:creator><![CDATA[Erwin Werkman]]></dc:creator><pubDate>Sun, 05 Jan 2020 16:55:35 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1560269507-68b9732d35c5?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1560269507-68b9732d35c5?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Personalizing your commerce site"><p>Sitecore has great personalization features. Personalization in Sitecore is based on the content you visit and the actions you perform on the website.<br>
You use content profiling to gain a better understanding of the interest of your visitor. You assign profile cards to your content items to indicate which &quot;type&quot; of user the content is interesting for.<br>
You can use the outcome of content profiling for a particular visitor and any other information you have (like the visitor's location, contents of the cart etc.) to personalize the site.</p>
<h2 id="personalizationconditionsforcommerce">Personalization conditions for Commerce</h2>
<p>Out of the box, you get the following conditions in Sitecore XP when you install the Sitecore Commerce Connect Core package:</p>
<h4 id="cartconditions">Cart conditions</h4>
<ul>
<li>where the quantity of all products in user's cart compares to specific value</li>
<li>where the user's cart total compares to specific value</li>
<li>where the quantity of product product id in user's cart compares to specific value</li>
</ul>
<h4 id="stockcountconditions">Stock count conditions</h4>
<ul>
<li>Current Product has stock count compares to specific stock count</li>
<li>Current Product has stock count compares to specific stock count in location equals to specific location</li>
<li>Product with ID specific product id has stock count compares to specific stock count</li>
<li>Product with ID specific product id has stock count compares to specific stock count in location equals to specific location</li>
</ul>
<h4 id="stockstatusconditions">Stock status conditions</h4>
<ul>
<li>Current product has stock status equals to specific stock status</li>
<li>Current product has stock status equals to specific stock status in location equals to specific location</li>
</ul>
<p>You can add your own conditions by implementing them in the normal way as you would a Sitecore XP condition. But be aware that if you need to call the commerce engine to get information for your condition, your condition could be costly with regards to performance. So make sure the data you are working with is cached in some way.</p>
<h2 id="howdoescontentprofilingworkonacatalog">How does content profiling work on a catalog?</h2>
<p>Content profiling in Sitecore XP basically works by labelling your content with values that tell which audience it is interesting for (and I know I'm simplifying). For example, if you have a company website, you have different audiences: potential customers, people that are already a customer, people looking for a job at your company etc.</p>
<p>By labelling your content with the intendend audience you can probably determine which kind of visitor is on your site by looking at the content she visits. If she is looking at content that is labelled for potential customers the likelyhood that your visitor is a potential customer goes up. You can use that information that personalize the site, for instance by adding banners or showing content on the homepage targeted at your visitor's audience group.</p>
<p>You can do this with &quot;normal&quot; content, but you can do exactly the same with your products (as they are also content).</p>
<p>You &quot;label&quot; your content for different audiences by assigning profile cards or set profile keys on your content. To assign profile cards to a catalog item you do the same as you would do for any normal content item. You click the <em>Edit Profile Cards</em> button for a sellable item or category and specify a profile card. Easy.</p>
<p>Now you just have to do this for the hundreds, thousands or hunderds of thousands of other products you have in your catalog.</p>
<p><img src="https://commerceservertips.com/content/images/2019/12/giphy-1-.gif" alt="Personalizing your commerce site"></p>
<p>Obviously, doing this by hand doesn't scale. So, how can we scale this up?</p>
<h2 id="scalingpersonalization">Scaling personalization</h2>
<p>If we want personalization to scale we need some way to automate the assignment of profile cards to catalog items.</p>
<p>Profile cards are saved on a sellable item or category in the <code>ExternalSettingsComponent</code>. The <code>ExternalSettingsComponent</code> is the place where all Sitecore XP related settings are saved. They are saved in a serialized JSON format.</p>
<p>The <code>ExternalSettingsComponent</code> has one property called <code>Settings</code> which is used to store the Sitecore XP settings serialized as JSON.</p>
<p>The structure of the contents of <code>Settings</code> is as follows:</p>
<pre><code>    {
        &quot;&lt;sitecore node id&gt;&quot;: { 
            &quot;&lt;language 1&gt;&quot;: {
                    &lt;language specific settings for language 1&gt;
                },
            &quot;&lt;language 2&gt;&quot;: {
                    &lt;language specific settings for language 1&gt;
                },
            &quot;shared&quot;: {
                &lt;shared settings&gt;
            },
        &quot;&lt;sitecore node id&gt;&quot;: { 
            &quot;&lt;language 1&gt;&quot;: {
                    &lt;language specific settings for language 1&gt;
                },
            &quot;&lt;language 2&gt;&quot;: {
                    &lt;language specific settings for language 1&gt;
                },
            &quot;shared&quot;: {
                &lt;shared settings&gt;
            }
            
    }
</code></pre>
<p>You might be wondering why there are two sitecore nodes (represented by two sitecore node id entries)? Well you have to realize that a sellable item has a different sitecore id based on the location of the item in the category tree. So the sitecore id of sellable item A in category B is different from the same sellable item A in category C.</p>
<p>The unique Sitecore id for a node is generated based on the sitecore id assigned to the sellable item by the engine concatenated with the sitecore id assigned by the engine on the category (using a pipe symbol to separate them) which is then hashed using MD5.</p>
<p>This also means that if you set the profile card for one location of a sellable item in the category structure, you will also need to set it for any other location. This does mean that your profile card configuration can be category (so context) specific which is a good thing and gives you more flexibility.</p>
<p>The profile cards are not language specific so are stored in the &quot;shared&quot; key.</p>
<h3 id="automatingsettingprofilecards">Automating setting profile cards</h3>
<p>How can we automate setting of profile cards? We first need to know which calls the data provider does to the commerce engine to retrieve sellable items. After some investigation using Fiddler, you can see it is doing calls to <code>https://localhost:5000/api/GetCatalogItemAllLanguages(...)</code> (this is on XC 9.2, depending on which version you have, you may have a different call).</p>
<p>This call executes a 'GetCatalogItemConnectCommand', which calls the <code>IGetSellableItemConnectPipeline</code> to retrieve the sellable item. Notice that the commands and pipelines that have <code>Connect</code> in their name are there specifically for the Commerce Data Provider in Sitecore XP.</p>
<p>So now we know we can extend the <code>IGetSellableItemConnectPipeline</code> to add a block that includes logic to set the profile card information. For this proof-of-concept, we're not going to <strong>save</strong> the profile information on the sellable item but depending on your needs you could do that as well. As saving is an expensive operation, just make sure you only do this once when retrieving the item for the first time.</p>
<p>You can find all the code for this example in this repository: <a href="https://github.com/ewerkman/Sitecore.Commerce.Plugin.ProfileCards">https://github.com/ewerkman/Sitecore.Commerce.Plugin.ProfileCards</a></p>
<p>This repository contains two projects:</p>
<ul>
<li>One (Sitecore.Commerce.Plugin.ProfileCards) for a plugin that extends the <code>IGetSellableItemConnectPipeline</code> with a block that parses the tracking field in  the external settings component and puts the data into a nice model and then calls a custom pipeline called <code>ISetTrackingFieldPipeline</code> for each parent category;</li>
<li>Another plugin (Sitecore.Commerce.Plugin.ProfileCards.Sample) that is a sample implementation which adds a block to <code>ISetTrackingFieldPipeline</code>, which sets the profile card each time the pipeline is called.</li>
</ul>
<h3 id="sitecorecommercepluginprofilecards">Sitecore.Commerce.Plugin.ProfileCards</h3>
<p>This plugin is not really complicated:</p>
<ul>
<li>It contains ,odels so we can represent the information that is in the tracking field in the external settings component in an easier to use way.</li>
<li>Defines a custom pipeline called <code>ISetTrackingFieldPipeline</code>;</li>
<li>It extends the <code>IGetSellableItemConnectPipeline</code> with a block called <code>SetTrackingFieldBlock</code> that parses the external settings component on a sellable item, deserializes the XML in the __Tracking field using the models. It then calls the custom pipeline <code>ISetTrackingFieldPipeline</code> providing it with the sellable item and the tracking model. The result of that pipeline is then again serialized into the __Tracking field and set on the <code>ExternalSettingsComponent</code>.</li>
</ul>
<h3 id="sitecorecommercepluginprofilecardssample">Sitecore.Commerce.Plugin.ProfileCards.Sample</h3>
<p>This is a sample plugin that shows you how to use the functionality in the <code>Sitecore.Commerce.Plugin.ProfileCards</code> plugin. It adds a block called <code>SetInterestProfileCardBlock</code> to the <code>ISetTrackingFieldPipeline</code>, which sets the <em>Interest</em> profile for any sellable item that is run through the pipeline. Of course this is a little bit basic functionality, but as you have the sellable item information available in the <code>TrackingFieldArgument.SellableItem</code> property you can act on the information you have there to set the correct profile information.</p>
<h2 id="conclusion">Conclusion</h2>
<p>This is a sample and simple implementation, but you can imagine how you can extend this. The sample is now only for sellable items but it is possible to extend this to categories and catalogs.</p>
<p>One thing to consider is that while you can have different values for the profile  based on the category a sellable item is in, you cannot change the profile based on any customer specific information as the information on a sellable item is cached and not specific for a user.</p>
<p>Happy profiling!</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[When deserialization goes wrong: Using the migration pipeline]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>As you know (hopefully), Sitecore XC stores all its entities as Json-serialized strings in the database. This makes it really flexible, as any components and policies you add to an entity are just serialized and stored. And if you need to change the entity, you deserialize it and the entity</p>]]></description><link>https://commerceservertips.com/using-the-entity-migration-pipeline/</link><guid isPermaLink="false">5d91ea51bcf14323c011e90a</guid><dc:creator><![CDATA[Erwin Werkman]]></dc:creator><pubDate>Sun, 06 Oct 2019 16:30:45 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1496761523829-6ee70d7928aa?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1496761523829-6ee70d7928aa?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="When deserialization goes wrong: Using the migration pipeline"><p>As you know (hopefully), Sitecore XC stores all its entities as Json-serialized strings in the database. This makes it really flexible, as any components and policies you add to an entity are just serialized and stored. And if you need to change the entity, you deserialize it and the entity is again available to you to work with.</p>
<p>That is until you introduce something breaking. For instance, you decide to change the name of a component. Now, any serialized instance using the old name, cannot be deserialized. Now you have a problem, because how do you fix this?</p>
<h3 id="ientitymigrationpipelinetotherescue"><code>IEntityMigrationPipeline</code> to the rescue!</h3>
<p>Fortunately, there is a solution. The <code>FindEntityPipeline</code> is responsible for retrieving an entity from the database. Within that pipeline, the <code>DeserializeEntityBlock</code> does the deserialization of the entity. If, during deserialization, a <code>JsonSerializationException</code> is thrown, the <code>IEntityMigrationPipeline</code> is run. It takes a <code>FindEntityArgument</code> as its input and returns the translated entity (if possible) as its output. This is what it looks like in my version of the Commerce Engine:</p>
<p><img src="https://commerceservertips.com/content/images/2019/10/entitymigrationpipeline.PNG" alt="When deserialization goes wrong: Using the migration pipeline"></p>
<p>The <code>IEntityMigrationPipeline</code> starts with the <code>FindEntityJsonBlock</code>. This block retrieves the entity from the database and puts the Json as a string in the <code>SerializedEntity</code> property of the <code>FindEntityArgument</code>. Any block after the <code>FindEntityJsonBlock</code> can be used to &quot;fix&quot; the Json so it can be deserialized and returned as the correct entity.</p>
<p>If you need an example on how to do this, the Commerce SDK contains a project called <code>Plugin.Sample.Upgrade</code> which contains <code>PatchEnvironmentJsonBlock</code>. This block patches the environment Json so that the type <code>Sitecore.Commerce.Plugin.Customers.Cs.ProfilesSqlPolicy</code> is changed to <code>Plugin.Sample.Customers.CsMigration.ProfilesSqlPolicy</code>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[A simple payment method - part 2]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>In <a href="https://commerceservertips.com/a-simple-payment-method/">&quot;A simple payment method&quot;</a> I implemented a really simple payment method. But there was one piece missing: If you created an actual order with the simple payment method you would see that the status of the payment would remain &quot;pending&quot;. In this part, we will</p>]]></description><link>https://commerceservertips.com/a-simple-payment-method-part-2/</link><guid isPermaLink="false">5d4c6a39bcf14323c011e7c4</guid><dc:creator><![CDATA[Erwin Werkman]]></dc:creator><pubDate>Fri, 09 Aug 2019 16:48:38 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1516467508483-a7212febe31a?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1516467508483-a7212febe31a?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="A simple payment method - part 2"><p>In <a href="https://commerceservertips.com/a-simple-payment-method/">&quot;A simple payment method&quot;</a> I implemented a really simple payment method. But there was one piece missing: If you created an actual order with the simple payment method you would see that the status of the payment would remain &quot;pending&quot;. In this part, we will settle the actual payment (in a really simple way) and also implement a way to do a simple refund.</p>
<p>Before we do some coding, you need to know about an entity called <code>SalesActivity</code>:</p>
<blockquote>
<p>&quot;A SalesActivity represents an instance of a charge or credit that occurs related to an Order.<br>
When you provide your payment information and place the order, it is often the case that the actual charge to your payment instrument doesn't occur right away. We return back the Order confirmation and process the payment asynchronously (after validating in the PendingOrders minion, that the item can actually be delivered).<br>
This may be due to buying a backorder or preorder or some other situation (fraud alert, inventory issue, etc) which causes you to delay settling the charge. When the order is settled, a SalesActivity is generated, representing that charge.<br>
It is also often the case where multiple SalesActivities are generated during the lifecycle of an Order. For example, if the order is split and charges are done at two different times, a SalesActivity is generated, when each Order piece is fulfilled.<br>
It is also possible to have a negative SalesActivity. If an Order is placed and then the customer subsequently returns all of some of the items in the order, when the customer is refunded, a SalesActivity with a negative number is generated.<br>
These can be thought of as Debits and Credits in a Ledger and represents the history of charges/credits related to a specific order. The sum of the SalesActivities should match the GrandTotal of the Order.&quot;</p>
</blockquote>
<p>(Source: <a href="https://sitecore.stackexchange.com/questions/14136/xc9-sales-activity-definiton">https://sitecore.stackexchange.com/questions/14136/xc9-sales-activity-definiton</a>)</p>
<h3 id="aboutasalesactivity">About a sales activity</h3>
<p>So, how is a <code>SalesActivity</code> created? This is done, in the <code>IPendingOrdersMinionPipeline</code>. It contains a block called <code>CreateOrderSalesActivitiesBlock</code> which does the following for each payment method:</p>
<ul>
<li>It creates a <code>SalesActivity</code> entity;</li>
<li>It sets the <code>Amount</code> of the sales activity to the amount paid with that payment method;</li>
<li>It sets the <code>PaymentStatus</code> of the sales activity to <code>Pending</code>;</li>
<li>It adds the payment component from the order</li>
<li>It adds the sales activity to a list of sales activities called <code>OrderSalesActivities</code>;</li>
<li>It adds the sales activity to the <code>SettleSalesActivities</code> list;</li>
</ul>
<p>The <code>SettleSalesActivities</code> list is monitored by the <code>SettleSalesActivitiesMinion</code>. It runs every 5 minutes (of course you change that) and takes all the sales activities from the list and runs them through the <code>ISettleSalesActivityPipeline</code>. If you need to check whether an order has been paid, this is the place to do it. The Braintree plugin adds two blocks to this pipeline to check the payment and update the order.</p>
<p>The last block in the <code>ISettleSalesActivitiesPipeline</code> is the <code>MoveAndPersistSalesActivityBlock</code>. It will move the SalesActivity to a different list based on it's status:</p>
<ul>
<li>Pending: sales activity remains on the <code>SettleSalesActivities</code> list;</li>
<li>Settled: sales activity moves to the <code>SettledSalesActivities</code> list;</li>
<li>Problem: sales activity moves to the <code>ProblemSalesActivities</code> list;</li>
</ul>
<blockquote>
<p>Note that in Sitecore Commerce 9.2, the Settlement Minion has been merged into the Released minion, to overcome risk of concurrency issues between the two minions. The <a href="https://dev.sitecore.net/Downloads/Sitecore%20Commerce/92/Sitecore%20Experience%20Commerce%2092%20Initial%20Release/Release%20Notes">release notes</a> contain more information (look for TFS No. 325520)</p>
</blockquote>
<h3 id="settlingasimplepayment">Settling a simple payment</h3>
<p>For my simple payment method I added a <code>SettleSimplePaymentBlock</code>. It's <code>Run</code> method looks like this:</p>
<pre><code class="language-csharp">public override Task&lt;SalesActivity&gt; Run(SalesActivity arg, CommercePipelineExecutionContext context)
{
    Condition.Requires(arg).IsNotNull($&quot;{this.Name}: The order cannot be null.&quot;);

    var salesActivity = arg;
    var knownSalesActivityStatuses = context.GetPolicy&lt;KnownSalesActivityStatusesPolicy&gt;();
    if (!salesActivity.HasComponent&lt;SimplePaymentComponent&gt;()
        || !salesActivity.PaymentStatus.Equals(knownSalesActivityStatuses.Pending, 
            StringComparison.OrdinalIgnoreCase))
    {
        return Task.FromResult(salesActivity);
    }

    var payment = salesActivity.GetComponent&lt;SimplePaymentComponent&gt;();

    // Perform logic to check whether the payment was settled
    var settled = PaymentHasBeenSettled();

    // Settle sales activity
    if (settled)
    {
        context.Logger.LogInformation($&quot;{this.Name} - Payment succeeded: {payment.Id}&quot;);
        salesActivity.PaymentStatus = knownSalesActivityStatuses.Settled;
    }

    return Task.FromResult(salesActivity);
}
</code></pre>
<p>This code:</p>
<ul>
<li>First checks whether the sales activity is in the correct status (it should be pending);</li>
<li>As I don't have anywhere to check whether the payment has been settled I randomly decide whether the payment has been settled or not;</li>
<li>If payment has been settled, it will change the status to <code>Settled</code>;</li>
</ul>
<p>The block is added to the <code>ISettleSalesActivityPipeline</code>:</p>
<pre><code>.ConfigurePipeline&lt;ISettleSalesActivityPipeline&gt;(c =&gt; 
    c.Add&lt;SettleSimplePaymentBlock&gt;().Before&lt;MoveAndPersistSalesActivityBlock&gt;())
</code></pre>
<p>Note that the <code>MoveAndPersistSalesActivityBlock</code> moves the sales activity from the <code>SettleSalesActivities</code> list to the <code>SettledSalesActivities</code> list (if the status is <code>Settled</code>').</p>
<h3 id="asimplerefund">A simple refund</h3>
<p>There will be situations where a customer returns all or part of an order. Sitecore Commerce has returns functionality out-of-the-box. Of course, you also need to return all or part of the payment.</p>
<p>Once the item has been received, the <code>IRefundPaymentsPipeline</code> is run, so a refund can be started. For our simple payment method we are going to add a simple refund by adding a block to this pipeline:</p>
<pre><code class="language-csharp">public async override Task&lt;OrderPaymentsArgument&gt; Run(OrderPaymentsArgument arg, 
    CommercePipelineExecutionContext context)
{
    Condition.Requires(arg).IsNotNull($&quot;{this.Name} The arg can not be null&quot;);
    Condition.Requires(arg.Order).IsNotNull($&quot;{this.Name} The order can not be null&quot;);
    Condition.Requires(arg.Payments).IsNotNull($&quot;{this.Name} The payments can not be null&quot;);

    var order = arg.Order;
    if (!order.HasComponent&lt;SimplePaymentComponent&gt;())
    {
        return arg;
    }
                
    if (!order.Status.Equals(context.GetPolicy&lt;KnownOrderStatusPolicy&gt;().Completed, 
        StringComparison.OrdinalIgnoreCase))
    {
        var invalidOrderStateMessage = $&quot;{this.Name}: Expected order in '{context.GetPolicy&lt;KnownOrderStatusPolicy&gt;().Completed}' status but order was in '{order.Status}' status&quot;;
        await context.CommerceContext.AddMessage(
                context.GetPolicy&lt;KnownResultCodes&gt;().ValidationError,
                &quot;InvalidOrderState&quot;,
                new object[] { context.GetPolicy&lt;KnownOrderStatusPolicy&gt;().Completed, order.Status },
                invalidOrderStateMessage);
        return null;
    }

    var existingPayment = order.GetComponent&lt;SimplePaymentComponent&gt;();
    var paymentToRefund = arg.Payments.FirstOrDefault(p =&gt; 
        p.Id.Equals(existingPayment.Id, StringComparison.OrdinalIgnoreCase)) as SimplePaymentComponent;
    if (paymentToRefund == null)
    {
        return arg;
    }
    
    if (existingPayment.Amount.Amount &lt; paymentToRefund.Amount.Amount)
    {
        await context.CommerceContext.AddMessage(
            context.GetPolicy&lt;KnownResultCodes&gt;().Error,
            &quot;IllegalRefundOperation&quot;,
            new object[] { order.Id, existingPayment.Id }, 
            &quot;Order Simple Payment amount is less than refund amount&quot;);
        return null;
    }

    // Perform logic to reverse the actual payment
    
    if (existingPayment.Amount.Amount == paymentToRefund.Amount.Amount)
    {
        // Remove the existingPayment from the order since the entire amount is refunded
        order.Components.Remove(existingPayment);
    }
    else
    {
        // Reduce the existing existingPayment in the order
        existingPayment.Amount.Amount -= paymentToRefund.Amount.Amount;
    }

    await this.GenerateSalesActivity(order, existingPayment, paymentToRefund, context);

    return arg;
}
</code></pre>
<p>This block does the following:</p>
<ul>
<li>It checks whether the order has a <code>SimplePaymentComponent</code>. If not, the block doesn't have to do anything and just returns;</li>
<li>Next, it checks whether the order is in the correct state (Completed);</li>
<li>It checks if the amount to refund is equal to or smaller than the actual amount paid. If it's not, it will report an error.</li>
<li>It then retrieves the existing payment, retrieves the payment to refund and then does the actual refunding. In our simple refund example, this just consists of creating a sales activity to note the refund and changing the amount of the original payment, but you can imagine in other scenarios you will order a payment provider to do a full or partial refund;</li>
</ul>
<p>The block is added to the <code>IRefundPaymentsPipeline</code>:</p>
<pre><code>.ConfigurePipeline&lt;IRefundPaymentsPipeline&gt;(c =&gt; 
    c.Add&lt;RefundSimplePaymentBlock&gt;().Before&lt;PersistOrderBlock&gt;())
</code></pre>
<h3 id="asimpleconclusion">A simple conclusion</h3>
<p>This blog post and <a href="https://commerceservertips.com/a-simple-payment-method/">the previous one</a> show you how to create a new payment method in Sitecore Commerce. Admittedly, it's a really simple one and your own implementations will probably be more complicated if you have to talk to a payment provider, but I hope these two blog posts give you the basis to successfully implement your own!</p>
<p>You can find the source code of this blog post on GitHub: <a href="https://github.com/ewerkman/Commerce.SimplePayment">https://github.com/ewerkman/Commerce.SimplePayment</a></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[A simple payment method]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Sitecore Commerce comes with two payment methods out of the box: Federated Payment using Braintree as the payment provider and a gift card payment method. During development you often have a need for a payment method for which you don't need to enter any information and that just allows you</p>]]></description><link>https://commerceservertips.com/a-simple-payment-method/</link><guid isPermaLink="false">5d3aaadfbcf14323c011e5dc</guid><dc:creator><![CDATA[Erwin Werkman]]></dc:creator><pubDate>Thu, 01 Aug 2019 08:35:59 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1459257831348-f0cdd359235f?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1459257831348-f0cdd359235f?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="A simple payment method"><p>Sitecore Commerce comes with two payment methods out of the box: Federated Payment using Braintree as the payment provider and a gift card payment method. During development you often have a need for a payment method for which you don't need to enter any information and that just allows you to save the cart as an order. In this post I'm going to create the simplest payment method I could create that can be processed by the commerce engine. Note that this doesn't involve any third parties so there are no connections to payment providers, just adding a payment method to a cart so you can save it as an order.</p>
<p>You can find all the code on Github: <a href="https://github.com/ewerkman/Commerce.SimplePayment">https://github.com/ewerkman/Commerce.SimplePayment</a></p>
<h2 id="addingapaymentmethodtothesitecorecommercecontrolpanel">Adding a payment method to the Sitecore Commerce Control panel</h2>
<p>In Sitecore Commerce Control Panel, go to: Sitecore &gt; Commerce &gt; Commerce Control Panel &gt; Shared Settings &gt; Payment Options and add a new payment option called Simple. Note the Item ID of the newly created item, you're going to need it later.</p>
<p>Next go to your Storefront settings (Sitecore &gt; Commerce &gt; Commerce Control Panel &gt; Storefront Settings &gt; Storefronts &gt; <em>Your storefront</em> &gt; Payment Configuration and add the newly created payment method under <em>Payment Options</em>. Save it.</p>
<h2 id="theenginepart">The engine part</h2>
<p>Let's start with extending the engine. So how are payment methods processed by the engine?</p>
<p>Some things to know:</p>
<ul>
<li>You can add multiple payment methods to a cart. So you can add a credit card payment and a gift card payment to a cart.</li>
<li>You add a payment to cart by adding a component that inherits from <code>Sitecore.Commerce.Plugin.Payments.PaymentComponent</code>. It has two properties you need to set: <code>Amount</code> and <code>PaymentMethod</code>. <code>Amount</code> is of type <code>Money</code> and is the amount paid for this method. <code>PaymentMethod</code> is an <code>EntityReference</code>. The <code>PaymentMethod</code> references the ID of the Payment Method you define in the Sitecore Commerce Control Panel.</li>
<li>When you turn a cart into an order, the out-of-the-box engine takes all the payment methods, adds up all the paid amounts and checks if it is equal to the total amount of the order. If it's not, the cart is <em>not</em> turned into an order.</li>
</ul>
<h3 id="coding">Coding</h3>
<p>For the engine I created a new plugin. Let's go through it:</p>
<h4 id="simplepaymentcomponent">SimplePaymentComponent</h4>
<p>The <code>SimplePaymentComponent</code> inherits from <code>PaymentComponent</code> and, because we ant to keep it simple, is an empty class.</p>
<pre><code class="language-csharp">public class SimplePaymentComponent : PaymentComponent
{
}
</code></pre>
<h4 id="commandscontroller">CommandsController</h4>
<p>A <code>CommandsController</code> class that inherits from <code>CommerceController</code> and contains one method called <code>AddSimplePayment</code> that takes a cart and a payment as it's input.</p>
<pre><code class="language-csharp">[HttpPut]
[Route(&quot;AddSimplePayment()&quot;)]
public async Task&lt;IActionResult&gt; AddSimplePayment([FromBody] ODataActionParameters value)
{
    if (!this.ModelState.IsValid || value == null)
    {
        return (IActionResult)new BadRequestObjectResult(this.ModelState);
    }

    if (!value.ContainsKey(&quot;cartId&quot;) || 
        (string.IsNullOrEmpty(value[&quot;cartId&quot;]?.ToString()) || 
        !value.ContainsKey(&quot;payment&quot;)) || 
        string.IsNullOrEmpty(value[&quot;payment&quot;]?.ToString()))
    {
        return (IActionResult)new BadRequestObjectResult((object)value);
    }

    string cartId = value[&quot;cartId&quot;].ToString();

    var paymentComponent = JsonConvert.DeserializeObject&lt;SimplePaymentComponent&gt;(value[&quot;payment&quot;].ToString());
    var command = this.Command&lt;AddPaymentsCommand&gt;();
    await command.Process(this.CurrentContext, cartId, new List&lt;PaymentComponent&gt; { paymentComponent });

    return new ObjectResult(command);
}
</code></pre>
<p>This method:</p>
<ul>
<li>does some checking of it's input;</li>
<li>retrieves the cart id;</li>
<li>deserializes the <code>payment</code> part of it's input to a <code>SimplePaymentComponent</code> instance;</li>
<li>executes the <code>AddPaymentsCommmand</code> to add the payments to the cart;</li>
</ul>
<h4 id="configuration">Configuration</h4>
<p>Lastly, we need to configure the OData model by implementing a <code>ConfigureServiceApiBlock</code>.</p>
<pre><code class="language-csharp">public override Task&lt;ODataConventionModelBuilder&gt; Run(ODataConventionModelBuilder modelBuilder, CommercePipelineExecutionContext context)
{
    Condition.Requires(modelBuilder).IsNotNull($&quot;{this.Name}: The argument cannot be null.&quot;);

    modelBuilder.AddEntityType(typeof(SimplePaymentComponent));

    ActionConfiguration addSimplePaymentConfiguration = modelBuilder.Action(&quot;AddSimplePayment&quot;);
    addSimplePaymentConfiguration.Parameter&lt;string&gt;(&quot;cartId&quot;);
    addSimplePaymentConfiguration.Parameter&lt;SimplePaymentComponent&gt;(&quot;payment&quot;);
    addSimplePaymentConfiguration.ReturnsFromEntitySet&lt;CommerceCommand&gt;(&quot;Commands&quot;);

    return Task.FromResult(modelBuilder);
}
</code></pre>
<p>And we implement <code>ConfigureSitecore</code> to add this pipeline block to the <code>ConfigureServiceApiPipeline</code>:</p>
<pre><code class="language-csharp">public class ConfigureSitecore : IConfigureSitecore
{
    public void ConfigureServices(IServiceCollection services)
    {
        var assembly = Assembly.GetExecutingAssembly();
        services.RegisterAllPipelineBlocks(assembly);

        services.Sitecore().Pipelines(config =&gt; config
            .ConfigurePipeline&lt;IConfigureServiceApiPipeline&gt;(configure =&gt; 
            configure.Add&lt;ConfigureServiceApiBlock&gt;())
            );

        services.RegisterAllCommands(assembly);
    }
}
</code></pre>
<h2 id="thewebsitepart">The website part</h2>
<p>In the website part we create the code to integrate our new payment method into Commerce Connect. Let's start by creating the code to add a payment to the cart. This involves creating an instance of a class that inherits from <code>PaymentInfo</code> and creating a request to add it to the cart:</p>
<pre><code class="language-csharp">var simplePaymentInfo = new SimplePaymentInfo();
simplePaymentInfo.PaymentMethodID = &quot;D652AC0F-C832-450B-BBDD-2CBBDA95E3CC&quot;; 
simplePaymentInfo.Amount = cart.Total.Amount;
simplePaymentInfo.PartyID = billingAddress.ExternalId;

var payments = new List&lt;PaymentInfo&gt;();
payments.Add(simplePaymentInfo);

var addPaymentInfoRequest = new AddPaymentInfoRequest(cart, payments);
var addPaymentInfoResult = cartServiceProvider.AddPaymentInfo(addPaymentInfoRequest);
</code></pre>
<p>What does <code>SimplePaymentInfo</code> look like? Again, it's a simple class:</p>
<pre><code class="language-csharp">using Sitecore.Commerce.Entities.Carts;

namespace Web.SimplePayment.Entities
{
    public class SimplePaymentInfo : PaymentInfo
    {
        public decimal Amount { get; set; }
    }
}
</code></pre>
<p>If you run this code, you will see that you get an error when executing the <code>cartServiceProvider.AddPaymentInfo(addPaymentInfoRequest);</code> code and the error indicates that SimplePaymentInfo is an unsupported payment type.</p>
<p>So, what happens under the covers is that Sitecore Commerce Connect executes the <code>commerce.carts.addPaymentInfo</code> pipeline (in Sitecore XP). Out of the box this pipeline consists of one processor: <code>Sitecore.Commerce.Engine.Connect.Pipelines.Carts.AddPaymentInfoToCart</code>.</p>
<p>If you take sneak dotPeek at the code for this processor, you will notice it only supports two payment methods: Federated and Giftcard. It even goes so far to  declare an error if you added a different payment method.</p>
<p>We can handle our own payment method by adding a processor to this pipeline and handling any payment that is of type <code>SimplePaymentInfo</code>.</p>
<p>The code for the processor that handles adding a simple payment method to the cart looks like this:</p>
<pre><code class="language-csharp">public class AddSimplePaymentToCart
{
    public void Process(ServicePipelineArgs args)
    {
        AddPaymentInfoRequest request;
        AddPaymentInfoResult result;

        request = args.Request as AddPaymentInfoRequest;
        result = args.Result as AddPaymentInfoResult;

        if( request.Payments.Any(p =&gt; p is SimplePaymentInfo))
        {
            var cart = request.Cart;
            var container = EngineConnectUtility.GetShopsContainer(shopName: cart.ShopName, customerId: cart.CustomerId);

            foreach (var payment in request.Payments.Where(p =&gt; p is SimplePaymentInfo).Select(p =&gt; TranslatePayment((SimplePaymentInfo) p)).ToList())
            {
                var command = Proxy.DoCommand(container.AddSimplePayment(cart.ExternalId, payment));
                result.HandleCommandMessages(command);                   
            }

            // Remove the SimplePaymentInfo payments from the list of payments, so they are not evaluated by other processors
            request.Payments = request.Payments.Where(p =&gt; !(p is SimplePaymentInfo)).ToList();
        }

    }

    private SimplePaymentComponent TranslatePayment(SimplePaymentInfo paymentInfo)
    {
        return new SimplePaymentComponent()
        {
            Id = Guid.NewGuid().ToString(&quot;N&quot;, CultureInfo.InvariantCulture),
            PaymentMethod = new EntityReference { EntityTarget = paymentInfo.PaymentMethodID },
            Amount = Money.CreateMoney(paymentInfo.Amount)
        };
    }
}
</code></pre>
<p>First we check if there are any payment methods of the type we can handle (<code>SimplePaymentInfo</code>) in the list of payment methods that is added.</p>
<pre><code>if( request.Payments.Any(p =&gt; p is SimplePaymentInfo))
</code></pre>
<p>If there are, we loop through the payments of type <code>SimplePaymentInfo</code>, translate each SimplePaymentInfo to a SimplePaymentComponent that Commerce Engine understands and send it to the engine, where it will be picked up by the CommandsController we created earlier.</p>
<p>Finally, we remove any payment from the list of payments so it's not processed by the other pipelines.</p>
<h4 id="configuringthepipeline">Configuring the pipeline</h4>
<p>We add the processor created to the <code>commerce.carts.addPaymentInfo</code> pipeline using the following configuration:</p>
<pre><code class="language-xml">&lt;configuration xmlns:patch=&quot;http://www.sitecore.net/xmlconfig/&quot;&gt;
  &lt;sitecore&gt;
    &lt;pipelines&gt;
      &lt;commerce.carts.addPaymentInfo&gt;        
        &lt;processor type=&quot;Web.SimplePayment.Pipelines.AddSimplePaymentToCart,Web.SimplePayment&quot; 
                   patch:before=&quot;processor[@type='Sitecore.Commerce.Engine.Connect.Pipelines.Carts.AddPaymentInfoToCart, Sitecore.Commerce.Engine.Connect']&quot; /&gt;    
     &lt;/commerce.carts.addPaymentInfo&gt;
    &lt;/pipelines&gt;
  &lt;/sitecore&gt;
&lt;/configuration&gt;
</code></pre>
<h3 id="addingthesimplepaymentmethodtoyourcart">Adding the Simple payment method to your cart</h3>
<p>You add the simple payment method to the cart using this code:</p>
<pre><code>var simplePaymentInfo = new SimplePaymentInfo();
simplePaymentInfo.PaymentMethodID = &quot;XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX&quot;; // Replace this with the id of the Simple payment method you added in the Commerce Control Panel. Be careful to REMOVE the { and }
simplePaymentInfo.Amount = cart.Total.Amount;
simplePaymentInfo.PartyID = billingAddress.ExternalId;

payments.Add(simplePaymentInfo);

var addPaymentInfoRequest = new AddPaymentInfoRequest(cart, payments);
var addPaymentInfoResult = cartServiceProvider.AddPaymentInfo(addPaymentInfoRequest);
</code></pre>
<p>The <code>PaymentMethodID</code> should be set to ID the payment method you created earlier in the Sitecore Commerce Control Panel. When setting the <code>PaymentMethodID</code> be careful not to include the <code>{</code> and <code>}</code> (I spent some time trying to figure out why my payment method wouldn't work).</p>
<h2 id="summary">Summary</h2>
<p>This is the simplest payment method I could add. You are probably going to use a payment provider, in which case you can use the PaymentComponent to save specific information on the payment, like a transaction id etc.</p>
<p>(Don't forget to read the exciting next part: <a href="https://commerceservertips.com/a-simple-payment-method-part-2/">A Simple Payment Method - Part 2</a>)</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Don't install Plumber locally: you can now use the hosted version!]]></title><description><![CDATA[Now you don't have to install Plumber on your own machine anymore: just use the hosted version of Plumber!]]></description><link>https://commerceservertips.com/dont-install-plumber-use-the-free-hosted-version/</link><guid isPermaLink="false">5d277178bcf14323c011e578</guid><category><![CDATA[Plumber]]></category><category><![CDATA[sitecore commerce 9]]></category><dc:creator><![CDATA[Erwin Werkman]]></dc:creator><pubDate>Sat, 20 Jul 2019 10:10:24 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1558494949-ef010cbdcc31?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1558494949-ef010cbdcc31?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Don't install Plumber locally: you can now use the hosted version!"><p>In the last couple of months I have wondered how I could make installing Plumber easier. I tried using a Chocolatey but thought it was too complicated, I tried Powershell and that worked but installing locally has so many variables that I was afraid it still needed customization. And both options meant there was something else to maintain.</p>
<p>Then I suddenly realized that you don't <strong>need</strong> to install Plumber locally (duh!): you just need to be able to access Plumber and be able to change the configuration of Plumber. Because Plumber is a single page application that talks directly to your local Sitecore Commerce Engine it completely runs in your browser.</p>
<p>Now, one thing needed to change and that was how you configure Plumber. Up till now you did that in a <code>config.json</code> file you hosted locally, but of course with a hosted version that wouldn't work.</p>
<p>So I added a Settings dialog to Plumber:</p>
<p><img src="https://commerceservertips.com/content/images/2019/07/plumber-settings.png" alt="Don't install Plumber locally: you can now use the hosted version!"></p>
<p>There you can set up the url for the Commerce Engine you want to connect to and the url for your Identity Server and change the Identity Server client id. Settings are saved in a cookie.</p>
<p>Now there are still two things you need to do locally:</p>
<ul>
<li>Configure Identity Server to allow Plumber to connect;</li>
<li>Configure your Commerce Engine to allow Plumber to connect;</li>
</ul>
<p>Because hosted Plumber is always coming from the same domain, setting that up is not difficult.</p>
<p>And one other advantage is that you always have the latest version of Plumber.</p>
<p>One disadvantage: it doesn't work with Sitecore Commerce 8.2.1 (but you should be upgrading anyway ;-))</p>
<p>So, now it's even easier to install and use Plumber!</p>
<p>You can access hosted Plumber at <a href="https://vwr.plumber-sc.com">https://vwr.plumber-sc.com</a></p>
<p>Happy plumbing!<br>
<img src="https://commerceservertips.com/content/images/2019/07/mario-small.gif" alt="Don't install Plumber locally: you can now use the hosted version!"></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[The Commerce Business Tools: Creating your own Angular components]]></title><description><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><blockquote>
<p>This post is part 3 in a 3-part series of posts on how to extend the Sitecore Commerce Business Tools. The other parts are:<br>
<a href="https://commerceservertips.com/the-commerce-business-tools-how-does-it-work/">The Commerce Business Tools: How does it work?</a><br>
<a href="https://commerceservertips.com/the-commerce-business-tools-how-to-extend">The Commerce Business Tools: How to extend it?</a></p>
</blockquote>
<h4 id="sowhatifiwantmore">So, what if I want more?</h4>
<p>Extending and adding new</p>]]></description><link>https://commerceservertips.com/the-commerce-business-tools-creating-your-own-angular-components/</link><guid isPermaLink="false">5d187a48023d09187c6f5fd8</guid><category><![CDATA[sitecore commerce 9]]></category><category><![CDATA[bizfx]]></category><category><![CDATA[angular]]></category><dc:creator><![CDATA[Erwin Werkman]]></dc:creator><pubDate>Wed, 10 Jul 2019 22:00:00 GMT</pubDate><media:content url="https://images.unsplash.com/reserve/oIpwxeeSPy1cnwYpqJ1w_Dufer%20Collateral%20test.jpg?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><blockquote>
<img src="https://images.unsplash.com/reserve/oIpwxeeSPy1cnwYpqJ1w_Dufer%20Collateral%20test.jpg?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="The Commerce Business Tools: Creating your own Angular components"><p>This post is part 3 in a 3-part series of posts on how to extend the Sitecore Commerce Business Tools. The other parts are:<br>
<a href="https://commerceservertips.com/the-commerce-business-tools-how-does-it-work/">The Commerce Business Tools: How does it work?</a><br>
<a href="https://commerceservertips.com/the-commerce-business-tools-how-to-extend">The Commerce Business Tools: How to extend it?</a></p>
</blockquote>
<h4 id="sowhatifiwantmore">So, what if I want more?</h4>
<p>Extending and adding new entity views and adding actions is nice, but what if I want to do something more exciting? What if I want to offer the user a different way to input data?</p>
<p>Well, now we’re coming in uncharted territories and as you know, there be dragons…</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-30-at-16.33.27.png" alt="The Commerce Business Tools: Creating your own Angular components"></p>
<p>But, we’re developers and not afraid of dragons. So let’s face some dragons…</p>
<p>When you install Sitecore Commerce you will notice that it consists of a large number of zips. Each zip represents a part of Sitecore Commerce. You have the Commerce SDK, Identity Server, the SXA components for Storefront, two zips with images and some packages that deal with Sitecore Commerce Connect.</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-30-at-16.34.27.png" alt="The Commerce Business Tools: Creating your own Angular components"></p>
<p>There are also two packages that start with Sitecore.BizFx. One of them ends with SDK. Software Development Kit.</p>
<p>Now, for a developer something ending with SDK sounds intriguing and exciting. Because an SDK should mean that it is something you can build upon, something you can extend.</p>
<p>When you install Sitecore Commerce, the zip that contains the Business Tools SDK is not used, it’s just that other zip that gets installed into an IIS site and a button is added to the dashboard so you can access them.</p>
<p>The zip with the SDK is not touched during installation, so it remains a bit of a mystery. If you look through the documentation there is also not too much information on what is in the zip.</p>
<p>But…there is a README</p>
<p>The SDK contains everything you need to build the Business tools yourself. If you follow the README (and you need to follow the instructions, otherwise it won’t work) you will have it all up and running in no time.</p>
<p>You need to:</p>
<ul>
<li>Unzip the SDK into a folder;</li>
<li>Run:
<ul>
<li><code>npm install speak-ng-bcl-0.8.0.tgz</code></li>
<li><code>npm install speak-styling-0.9.0-r00078.tgz</code></li>
<li><code>npm install @sitecore/bizfx</code></li>
</ul>
</li>
<li>Run: <code>npm install</code></li>
</ul>
<p>Once you have done all this you do:</p>
<p><code>ng serve</code></p>
<p>Wait a little and you will have an instance running of the Business Tools. It will listen on port 4200 so don’t forget to stop the IIS site running on the same port.</p>
<p>The nice thing is now that you can just start changing or adding stuff and it will automatically recompile which makes developing very easy.</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-30-at-16.39.33.png" alt="The Commerce Business Tools: Creating your own Angular components"></p>
<p>The business tools use Angular. Angular is an application framework, much like React or VUE that helps you create applications that run in a browser. The latest versions of Angular use Typescript instead of Javascript for developing.</p>
<p>I don’t know too much about Angular and I am also not a Javascript or Typescript expert. Whenever I am in a situation like this I use a highly scientific method of developing where I basically throw code at the wall and see what sticks.</p>
<p>The source code of the business tools look quite simple:</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-30-at-16.41.19-1.png" alt="The Commerce Business Tools: Creating your own Angular components"></p>
<!--kg-card-end: markdown--><!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p>Every Angular application has an app folder which is where you do most of your development work.</p>
<p>The app folder contains a components folder. Components are an important part of Angular:</p>
<blockquote>
<p>“A component controls a patch of screen called a view.”</p>
</blockquote>
<p>A component is the way to display things in the browser.</p>
<p>The components folder in the business tools contains two subfolders: actions and views.</p>
<p>Let’s start with the views folder, as it contains the components to render the different views. As you may remember, when creating an EntityView you can specify a UIHint. Examples of these UIHints are TableView, ListView or FlatView.</p>
<p>The UIHint determines which one of these components is rendered.</p>
<p>The <code>actions</code> folder contains the components to render things like edit boxes, a date-time editor, a media picker etc. So this is the place where you will find components that deal with editing data.</p>
<p><img src="https://commerceservertips.com/content/images/2019/07/Screenshot-2019-07-04-at-21.19.30.png" alt="The Commerce Business Tools: Creating your own Angular components"></p>
<p>This is also where we will concentrate our efforts to extend the business tools. As an example I wanted to add a component that allows you to add tags, but with a better UI than the standard tag editor.</p>
<p>Now of course, I am not going to create this component myself, but rather use the power of the internet to find me a prebuild component.</p>
<p>After some searching I found a component called Selectize.js (<a href="https://selectize.github.io/selectize.js/">https://selectize.github.io/selectize.js/</a>) which fitted what I wanted to do. You can supply a list of items that can be used in a dropdown and it allows multi-select.</p>
<p>If you are using a framework like Angular you cannot just add the javascript to an HTML page and expect it to work. If you want it to work you will need to provide some plumbing around it.</p>
<p>Luckily there is a cornucopia of packages available for Angular for all kinds of components so a little Google search brought up a Angularized version of Selectize called ng-selectize (<a href="https://www.npmjs.com/package/ng-selectize">https://www.npmjs.com/package/ng-selectize</a>).</p>
<p>I followed the instructions to add the component to angular which involves:</p>
<p>Installing it using npm:</p>
<pre><code>npm i --save ng-selectize jquery selectize
</code></pre>
<p>Adding references to stylesheets to the file .angular-cli.json;</p>
<pre><code>&quot;../node_modules/selectize/dist/css/selectize.css&quot;,
&quot;../node_modules/selectize/dist/css/selectize.{your chosen theme}.css&quot;
</code></pre>
<p>Adding references to scripts to that same file</p>
<pre><code>&quot;../node_modules/jquery/dist/jquery.min.js&quot;,
&quot;../node_modules/ng-selectize/selectize/selectize.standalone.js&quot; 
</code></pre>
<p>And importing the component as a module by adding the following lines to app.module.ts:</p>
<pre><code>import {NgSelectizeModule} from 'ng-selectize';
imports: [..., NgSelectizeModule, ...],
</code></pre>
<p>Now you can add a new component. You do this with an Angular command line generator:</p>
<pre><code>ng generate component selectize
</code></pre>
<p>In this line, <code>selectize</code> is the name of the component I want to generate.</p>
<p>This will create a folder called <code>selectize</code> in the <code>apps</code> folder and creates all the plumbing you need for the new component. It will also automatically add the component to <code>app.module.ts</code> so you are all set to start using your component.</p>
<p>A component in Angular consists of:</p>
<ul>
<li>A stylesheet;</li>
<li>An HTML template;</li>
<li>A specification file you can use for testing the component;</li>
<li>A “code-behind” file that contains the logic behind the template;</li>
</ul>
<p>Now we need some place to add the new component to the existing business tools.</p>
<p>Remember that the business tools are data-driven and we want our new component to display when a user edits a sellable item. We can specify how a property on a sellable item should be rendered by setting the UIType property of the ViewProperty to true.</p>
<p>In the commerce engine code we change the creation of the EntityView so that the UIType is set to Selectize:</p>
<pre><code class="language-csharp">targetView.Properties.Add(new ViewProperty
{
    Name = nameof(FeaturesComponent.FeatureList),
    RawValue = component.FeatureList?.ToArray&lt;string&gt;(),
    IsReadOnly = !isEditView,
    IsRequired = true,
    UiType = isEditView ? &quot;Selectize&quot; : &quot;Tags&quot;,
    Policies = new List&lt;Policy&gt; { selectizeConfig },
    OriginalType = &quot;List&quot;
});
</code></pre>
<p>Note that we only use Selectize when the view is in edit mode, otherwise we use a standard way of rendering called Tags.</p>
<p><strong>What did we do up till now?</strong></p>
<ul>
<li>We unpacked the Business Tools SDK;</li>
<li>Installed a number of npm packages;</li>
<li>We have created a new component called Selectize;</li>
<li>We have changed the plugin that creates the views to edit the component to use Selectize as the UIHint;</li>
</ul>
<p><strong>But when we go to the Business tools now, we don’t see any changes. The <em>Features</em> field is still rendered as a text box.</strong></p>
<p>Apparently, just supplying a <code>UIType</code> with the name of the component, does not automatically mean it will <em>use</em> the component.</p>
<p>So, how do we render the component? For that I looked into the source code of the Business Tool and I found the answer in file with the name: <code>sc-bizfx-actionproperty.component.html</code></p>
<p>This is a template for a component and it is used to render a property on an edit form. This component is rendered for each property that is in the view.</p>
<p>If you look at the code it starts with the following HTML:</p>
<pre><code class="language-html">&lt;div *ngIf=&quot;property &amp;&amp; !property.IsHidden&quot;&gt;

  &lt;div [formGroup]=&quot;actionForm&quot; class=&quot;action-property-container&quot;&gt;

    &lt;div [ngSwitch]=&quot;property.UiType&quot;&gt;

      &lt;div *ngSwitchCase=&quot;'Autocomplete'&quot;&gt;
        &lt;label for=&quot;property-{{property.Name}}&quot;&gt;{{property.DisplayName}}&lt;/label&gt;
        &lt;sc-bizfx-autocomplete [property]=&quot;property&quot;&gt;&lt;/sc-bizfx-autocomplete&gt;
      &lt;/div&gt;
</code></pre>
<p>As you can see, the last line starts a switch statement in the Angular template which is based on the UIType property.</p>
<p>It then contains a case for each UIType it can render. In the example above you see how it renders a property for which the UIType is Autocomplete and it also contains a case for each type it knows how to render.</p>
<p>Apparently this is the place to add our new selectize component.</p>
<p>So what I did is add a ngSwitchCase</p>
<pre><code class="language-html">      &lt;div *ngSwitchCase=&quot;'DownloadCsv'&quot;&gt;
        &lt;button scIconButton=&quot;secondary&quot; id=&quot;download-action&quot; type=&quot;button&quot; (click)=&quot;downloadCsv()&quot;&gt;
          &lt;sc-icon icon=&quot;download&quot; size=&quot;small&quot;&gt;&lt;/sc-icon&gt;
        &lt;/button&gt;
      &lt;/div&gt;

      &lt;div *ngSwitchCase=&quot;'Selectize'&quot;&gt;
        &lt;app-selectize&gt;&lt;/app-selectize&gt;
      &lt;/div&gt;

      &lt;div *ngSwitchDefault&gt;

        &lt;div [ngSwitch]=&quot;property.OriginalType&quot;&gt;
</code></pre>
<p>I added my <code>Selectize</code> case just before the <code>ngSwitchDefault</code> statement. <code>ngSwitchDefault</code> is used when the <code>UIType</code> is not caught by any of the <code>ngSwitchCase</code> statements. It then looks at the <code>OriginalType</code> property to see how it should render the property.</p>
<p>So, I refresh the browser and see what the result is</p>
<p><img src="https://commerceservertips.com/content/images/2019/07/Screenshot-2019-07-04-at-21.50.37.png" alt="The Commerce Business Tools: Creating your own Angular components"></p>
<p>Ok, I have a result!</p>
<p>Now what I want to do is show my selectize component. I change the template to use my component:</p>
<pre><code class="language-html">    &lt;ng-selectize&gt;&lt;/ng-selectize&gt;
</code></pre>
<p>I refresh the browser and edit the Features component and now I see this:</p>
<p><img src="https://commerceservertips.com/content/images/2019/07/Screenshot-2019-07-04-at-21.52.15.png" alt="The Commerce Business Tools: Creating your own Angular components"></p>
<p>Victory! My new component is visible, but of course it doesn’t do anything yet.</p>
<p>So, how do I hook this up?</p>
<p>Looking at the documentation for the Selectize component I can supply a configuration object and I can supply the options the user can select from.</p>
<pre><code class="language-html">&lt;ng-selectize [config]=&quot;...&quot; [options] = &quot;...&quot; 
[placeholder] = &quot;...&quot;  {other-attributes}&gt;&lt;/ng-selectize&gt;
</code></pre>
<p>So first, let’s see if we can get that to work by supplying some dummy options.</p>
<p>I added the following code to the component:</p>
<pre><code class="language-javascript">export class SelectizeComponent implements OnInit {

 config: any = {
   plugins: ['dropdown_direction', 'remove_button'],
   dropdownDirection: 'down',
   labelField: 'DisplayName',
   valueField: 'Name',
   searchField: ['Name'],
   maxItems: 10
 };

 options: any = [{Name:&quot;option1&quot;, DisplayName:&quot;Option 1&quot;}];

 constructor() { }

 ngOnInit() {
 }
}
</code></pre>
<p>And changed the template to:</p>
<pre><code class="language-html">&lt;ng-selectize [config]=&quot;config&quot; [options]=&quot;options&quot;&gt;&lt;/ng-selectize&gt;
</code></pre>
<p>And this was the result:</p>
<p><img src="https://commerceservertips.com/content/images/2019/07/bizfx-1.gif" alt="The Commerce Business Tools: Creating your own Angular components"></p>
<p>As you can see, some result but not completely visible.</p>
<p>I am not a css expert, but after trying some stuff I settled on changing the template to:</p>
<pre><code class="language-html">&lt;div class=&quot;form-group&quot;&gt;
 &lt;ng-selectize [config]=&quot;config&quot; [options]=&quot;options&quot;&gt;&lt;/ng-selectize&gt;
&lt;/div&gt;
</code></pre>
<p>And adding the following line to the style sheet for the component:</p>
<pre><code class="language-css">.form-group { height: 200px; }
</code></pre>
<p>Now the result looked like this:</p>
<p><img src="https://commerceservertips.com/content/images/2019/07/bizfx-2.gif" alt="The Commerce Business Tools: Creating your own Angular components"></p>
<p>So, now I know how to add options and to configure the selectize component to do what I want. But how can I set and get the value of the selectize component?</p>
<p>If you look at the other components used in the SDK, you can see that they get their input using a property attribute. The markup for the autocomplete control looks like this for example:</p>
<pre><code class="language-html">&lt;sc-bizfx-autocomplete [property]=&quot;property&quot;&gt;&lt;/sc-bizfx-autocomplete&gt;
</code></pre>
<p>If you have a look at the code for the auto-complete component you will see this:</p>
<pre><code class="language-javascript">export class ScBizFxAutocompleteComponent extends ScBizFxBaseService implements OnInit, OnDestroy {
   /**
   * Defines the property to render
   */
   @Input() property: ScBizFxProperty;
</code></pre>
<p>The property attribute is connected to the property variable specified in the class. <code>@Input()</code> indicates this variable should gets its value from the property specified in the markup.</p>
<p>The <code>ScBizFxProperty</code> type is directly tied to the property we supply from the commerce engine when we create the entity view, so it gets all the information including the value and the UIType.</p>
<p>So I add the following line to my class:</p>
<pre><code class="language-javascript">    @Input() property: ScBizFxProperty;
</code></pre>
<p>That gave me the following errors when Angular tried to compile my code:</p>
<pre><code>ERROR in C:/workspace/bizfx-extension/src/bizfx/src/app/selectize-2/selectize-2.component.ts (11,4): Cannot find name 'Input'.
ERROR in C:/workspace/bizfx-extension/src/bizfx/src/app/selectize-2/selectize-2.component.ts (11,22): Cannot find name 'ScBizFxProperty'.
ERROR in C:/workspace/bizfx-extension/src/bizfx/src/app/selectize-2/selectize-2.component.ts (11,4): Cannot find name 'Input'.
ERROR in C:/workspace/bizfx-extension/src/bizfx/src/app/selectize-2/selectize-2.component.ts (11,22): Cannot find name 'ScBizFxProperty'.
</code></pre>
<p>So, not good. I get the feeling I need to import some stuff. And sure enough after adding the following lines everything worked compiled again:</p>
<pre><code class="language-javascript">import { Component, OnInit, Input } from '@angular/core';
import { ScBizFxProperty } from '@sitecore/bizfx';
</code></pre>
<p>Next question: How do I get the input from the selectize component into my component. This took some experimentation as I was not sure how this should work. After some trial and a lot of error I found that if I changed my markup to look like this:</p>
<pre><code class="language-html">&lt;ng-selectize [config]=&quot;config&quot; [options]=&quot;options&quot; [(ngModel)]='items'&gt;&lt;/ng-selectize&gt;
</code></pre>
<p>And adding the following property to my class:</p>
<pre><code class="language-javascript">export class SelectizeComponent implements OnInit {
 items = [];
 @Input() property: ScBizFxProperty;
</code></pre>
<p>the values the user entered in the selectize component were put in the items array.</p>
<p>So, now I have a way to get the value into my component and away from the Selectize component. Now I need to tie it all together.</p>
<p>I first started to see if I could get the value out of the component. I looked again for inspiration in the SDK and found that the tags component that comes with the SDK uses something called a FormGroup to send the value back:</p>
<pre><code class="language-javascript">@Input() actionForm: FormGroup;
</code></pre>
<p>And it used the following code to convert the array to a string (because we cannot send back an array to the commerce engine):</p>
<p>``javascript<br>
onInputBlurred(): void {<br>
const current = [];<br>
this.items.forEach(item =&gt; {<br>
current.push(item.value);<br>
});<br>
this.actionForm.controls[this.property.Name].setValue(JSON.stringify(current));<br>
}</p>
<pre><code>
So I made some changes.

First I added the actionForm input to my class:

```javascript
    @Input() actionForm: FormGroup;
</code></pre>
<p><code>FormGroup</code> is a type you need to import so I added:</p>
<pre><code class="language-javascript">import { FormGroup } from '@angular/forms';
</code></pre>
<p>Then I changed the switch case for my component to this:</p>
<pre><code class="language-html">&lt;div *ngSwitchCase=&quot;'Selectize'&quot;&gt;
       &lt;app-selectize [property]=&quot;property&quot; [actionForm]=&quot;actionForm&quot;&gt;&lt;/app-selectize&gt;
     &lt;/div&gt;
</code></pre>
<p>And finally I added code to turn the items array I get from the selectize component to a JSON string that the Business Tools and the commerce engine code can work with:</p>
<pre><code class="language-javascript">onInputBlurred(): void {   this.actionForm.controls[this.property.Name].setValue(JSON.stringify(this.items));
 }
</code></pre>
<p>So, now I can retrieve the input of the Selectize component and send it back to the Business Tools and Commerce Engine. What I still need to do is set the initial value of the Selectize component. To do that, I changed the code of my ngOnInit method to look like this:</p>
<pre><code class="language-javascript"> ngOnInit() {
   this.items = JSON.parse(this.property.Value);
 }
</code></pre>
<p>This takes the value property which is a stringified piece of JSON we get from the Commerce Engine and parses it back to normal JSON and sets the items array to this value.</p>
<p><img src="https://commerceservertips.com/content/images/2019/07/bizfx-3.gif" alt="The Commerce Business Tools: Creating your own Angular components"></p>
<h4 id="addingalistofoptions">Adding a list of options</h4>
<p>Now we know how to edit things, but how do we add a list of options for the user to select?</p>
<p>One way of course would be just to hard code that list in the component. That would maybe be an option, but not a very good one. The best way to do this of course would be to have the commerce engine add a policy to the property.</p>
<p><img src="https://commerceservertips.com/content/images/2019/07/Screenshot-2019-07-04-at-22.27.09.png" alt="The Commerce Business Tools: Creating your own Angular components"></p>
<p>I created a policy called <code>SelectizeConfigPolicy</code> and this policy can be used to configure the Selectize component. It is added by the commerce engine when creating the entity view.<br>
For maximum flexibility it gets the available options from Sitecore XP, which makes it easier for users to add new options.</p>
<p><img src="https://commerceservertips.com/content/images/2019/07/Screenshot-2019-07-04-at-22.28.29.png" alt="The Commerce Business Tools: Creating your own Angular components"></p>
<p>To retrieve these options, you have to create a pipeline with some blocks to retrieve the values.</p>
<p><img src="https://commerceservertips.com/content/images/2019/07/Screenshot-2019-07-04-at-22.29.33.png" alt="The Commerce Business Tools: Creating your own Angular components"></p>
<p>It starts by trying to retrieve the options from the cache. If it cannot find it in the cache, the next block will retrieve it from Sitecore and the last block will put it in the cache for the next time.</p>
<p>This pipeline is used to retrieve the options and add them to the policy and the policy is added to the ViewProperty, which is sent to the Business Tools.</p>
<p>In my SelectizeComponent I need to take the policy from the property and use it to configure my component.</p>
<p>This code will do just that:</p>
<pre><code class="language-csharp">var availableSelectionsPolicy : any;

   availableSelectionsPolicy = this.property.Policies
           .find(p =&gt; p['@odata.type'] === '#Plugin.Sample.Notes.Policies.SelectizeConfigPolicy');
   if(availableSelectionsPolicy)
   {
     this.options = availableSelectionsPolicy.Options;
     this.placeholder = availableSelectionsPolicy.Placeholder;
   }
</code></pre>
<p>What can you do with all this knowledge?</p>
<ul>
<li>Well, the next time your customer asks you to create a management interface to update the status of an order, use your knowledge to add the specific actions your customer might need to the existing order interface.</li>
<li>Your customer might want to be able to send a custom e-mail about an order and needs some way to trigger it.</li>
<li>These are all things you can easily do by extending the Business Tools.</li>
</ul>
<blockquote>
<p>You can find the code for everything discussed in these articles on GitHub: <a href="https://github.com/ewerkman/bizfx-extension">https://github.com/ewerkman/bizfx-extension</a></p>
</blockquote>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[The Commerce Business Tools: How to extend it?]]></title><description><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><blockquote>
<p>This post is part 2 of a 3-part series of posts on how to extend the Sitecore Commerce Business Tools. The other parts are:<br>
<a href="https://commerceservertips.com/the-commerce-business-tools-how-does-it-work/">The Commerce Business Tools: How does it work?</a><br>
<a href="https://commerceservertips.com/the-commerce-business-tools-how-to-extend/the-commerce-business-tools-creating-your-own-angular-components">The Commerce Business Tools: Creating your own Angular components</a></p>
</blockquote>
<h3 id="howdoiextendit">How do I extend it?</h3>
<p>In <a href="https://commerceservertips.com/the-commerce-business-tools-how-does-it-work">Commerce Business Tools:</a></p>]]></description><link>https://commerceservertips.com/the-commerce-business-tools-how-to-extend/</link><guid isPermaLink="false">5d187a48023d09187c6f5fd9</guid><category><![CDATA[bizfx]]></category><category><![CDATA[sitecore commerce 9]]></category><dc:creator><![CDATA[Erwin Werkman]]></dc:creator><pubDate>Wed, 10 Jul 2019 06:01:04 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1508450859948-4e04fabaa4ea?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><blockquote>
<img src="https://images.unsplash.com/photo-1508450859948-4e04fabaa4ea?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="The Commerce Business Tools: How to extend it?"><p>This post is part 2 of a 3-part series of posts on how to extend the Sitecore Commerce Business Tools. The other parts are:<br>
<a href="https://commerceservertips.com/the-commerce-business-tools-how-does-it-work/">The Commerce Business Tools: How does it work?</a><br>
<a href="https://commerceservertips.com/the-commerce-business-tools-how-to-extend/the-commerce-business-tools-creating-your-own-angular-components">The Commerce Business Tools: Creating your own Angular components</a></p>
</blockquote>
<h3 id="howdoiextendit">How do I extend it?</h3>
<p>In <a href="https://commerceservertips.com/the-commerce-business-tools-how-does-it-work">Commerce Business Tools: how does it work?</a> you learned how the business tools work and which pipelines are involved to customize the tools.<br>
The next question will be: how do you extend the business tools?</p>
<p>Well, that depends on what you want to do.</p>
<p>For most scenarios you don’t need to touch the BizFx SDK because you can do it by extending the engine.</p>
<p>About naming stuff</p>
<ul>
<li>Editable in the Commerce Control Panel</li>
<li>Look in the <code>Commerce Engine Setting</code> &gt; <code>Commerce Terms</code> &gt; <code>Business Tools</code> &gt; <code>View Names</code> / <code>View Action Names</code> / <code>View Property Names</code></li>
</ul>
<h3 id="howdoiextendanexistingview">How do I extend an existing view?</h3>
<p>Now you know which pipelines are involved in the process and how entity views work, the next question is of course: how can I extend this?</p>
<p>We’ll start with the easiest way to extend the business tools and that is by adding an additional property to an existing view.</p>
<p>Let’s say we want the creation date and last update date of a sellable item to show in the Summary view.</p>
<p>At the moment this view, looks like this:</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-07-at-17.22.24.png" alt="The Commerce Business Tools: How to extend it?"></p>
<p>We want to add the date the sellable item was and last updated date to this view.</p>
<p>You might think that one way to do this is, is to create a subclass of the block that creates the view and extend this. That is certainly possible, but not the best way to do this.</p>
<p>Instead what we will do is create a new PipelineBlock called <code>ExtendSellableItemDetailsViewBlock</code> and add this to the <code>IGetEntityViewPipeline</code>.</p>
<pre><code class="language-csharp">public class ExtendSellableItemDetailsViewBlock : PipelineBlock&lt;EntityView, EntityView, CommercePipelineExecutionContext&gt;
</code></pre>
<p>Remember that the <code>IGetEntityViewPipeline</code> gets called for each view requested by the business tools, so it contains all the blocks responsible for creating <em>all the views</em>. Each block has to decide by itself whether it needs to add something to the requested view.</p>
<p>So the first thing the <code>ExtendSellableItemDetailsViewBlock</code> has to do is decide whether the requested view is the one it wants to add something to. It does this by checking whether the name of the requested view is <code>Master</code>:</p>
<pre><code class="language-csharp">Condition.Requires(entityView).IsNotNull($&quot;{this.Name}: The argument can not be null&quot;);
var policy = context.GetPolicy&lt;KnownCatalogViewsPolicy&gt;();
EntityViewArgument request = context.CommerceContext.GetObject&lt;EntityViewArgument&gt;();

if (string.IsNullOrEmpty(request?.ViewName) || !request.ViewName.Equals(policy.Master, StringComparison.OrdinalIgnoreCase))
{
	return Task.FromResult(entityView);
}

</code></pre>
<p>and whether the requested entity is a sellable item:</p>
<pre><code class="language-csharp">if (!(request.Entity is SellableItem) || !string.IsNullOrEmpty(request.ForAction))
{
  return Task.FromResult(entityView);
}
</code></pre>
<p>If it is, it will add two properties to the view, one with the created date:</p>
<pre><code class="language-csharp">var dateCreatedProperty = new ViewProperty() { 
    DisplayName = &quot;Date Created&quot;,
    Name = nameof(sellableItem.DateCreated),
    IsReadOnly = true,
    RawValue = sellableItem.DateCreated
};
entityView.Properties.Add(dateCreatedProperty);
</code></pre>
<p>and one with the last update date of the sellable item.</p>
<pre><code class="language-csharp">var dateUpdatedProperty = new ViewProperty() {
  DisplayName = &quot;Date Updated&quot;,
  Name = nameof(sellableItem.DateUpdated),
  IsReadOnly = true,
  RawValue = sellableItem.DateUpdated
};

entityView.Properties.Add(dateUpdatedProperty);

return Task.FromResult(entityView);
</code></pre>
<p>Now we only need to add the new block to the <code>IGetEntityViewPipeline</code> after the block that creates the original summary view (<code>GetSellableItemDetailsViewBlock</code>). The code looks like the code below and we add it to a <code>ConfigureSitecore</code> class:</p>
<pre><code class="language-csharp">services.Sitecore().Pipelines(config =&gt;
   config.ConfigurePipeline&lt;IGetEntityViewPipeline&gt;(c =&gt;
      c.Add&lt;ExtendSellableItemDetailsViewBlock&gt;().After&lt;GetSellableItemDetailsViewBlock&gt;()
   ) 
);
</code></pre>
<blockquote>
<p>Something to note about the <code>GetEntityViewPipeline</code>: At the end of <code>GetEntityViewPipeline</code> it calls the <code>IFormatEntityPipeline</code> which will do some magic. If you add your block <em>after</em> this pipeline, you might not see anything if you only added stuff to the <code>RawValue</code> property because that pipeline makes sure the <code>Value</code> property is filled.</p>
</blockquote>
<p>After we run the Commerce Engine, the end result looks like this:</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-07-at-17.34.26.png" alt="The Commerce Business Tools: How to extend it?"></p>
<p>So now we have added two properties to an existing view.</p>
<p>Remember how you do this:</p>
<ul>
<li>Create a new pipeline block;</li>
<li>Find the name of the view you want to extend;</li>
<li>Add the properties you want to add to this view;</li>
<li>Finally add the new block to the <code>IGetEntityViewPipeline</code>, making sure you add the block after the block that creates the view you want to extend;</li>
</ul>
<p>You will find <a href="https://github.com/ewerkman/bizfx-extension/blob/master/src/engine/Plugin.Sample.SellableItem/Pipelines/Blocks/ExtendSellableItemDetailsViewBlock.cs">the code for this block here</a>.</p>
<h3 id="howdoiaddanewchildview">How do I add a new (child) view?</h3>
<p>Adding a child view builds on what we did for adding a property to an existing view.</p>
<p>Let’s say you want to display the data of a component you added. It’s a component that merchandisers can use to add notes and warranty information to a sellable item.</p>
<p>The code for the component is simple and looks like this:</p>
<pre><code class="language-csharp">namespace Plugin.Sample.Notes.Components
{
    using Sitecore.Commerce.Core;

    public class NotesComponent : Component
    {
        public string InternalNotes { get; set; } = string.Empty;
        public string WarrantyInformation { get; set; } = string.Empty;
    }
}
</code></pre>
<p>We want to display the data in this component on the entity view page for a sellable item.</p>
<p>Again, we create a new block, this time we call it <code>GetNotesViewBlock</code>.</p>
<pre><code class="language-csharp">[PipelineDisplayName(nameof(GetNotesViewBlock))]
public class GetNotesViewBlock : PipelineBlock&lt;EntityView, EntityView, CommercePipelineExecutionContext&gt;
{
    protected ViewCommander Commander { get; set; }

    public GetNotesViewBlock(ViewCommander commander)
        : base(null)
    {
        this.Commander = commander;
    }

    public override Task&lt;EntityView&gt; Run(EntityView entityView, 
	CommercePipelineExecutionContext context)
    {
        Condition.Requires(entityView).IsNotNull($&quot;{this.Name}: The argument can not be null&quot;);
	.
	.
	.
</code></pre>
<p>As with the previous block we created, first thing we need to do is check if the request we receive is for the entity view we want to extend. The standards views are called Master most of the time. Of course, this doesn’t give you enough information to determine whether this is the right view to extend. So the other thing you need to check is the type of the entity.</p>
<p>You can do this using the following code:</p>
<pre><code class="language-csharp">// Make sure that we target the correct views
if (!isMasterView &amp;&amp; !isConnectView &amp;&amp; !isNotesView)
{
    return Task.FromResult(entityView);
}

var request = this.Commander.CurrentEntityViewArgument(context.CommerceContext);
// Only proceed if the current entity is a sellable item
if (!(request.Entity is SellableItem))
{
   return Task.FromResult(arg);
}
</code></pre>
<p>We check the view name and check whether the request is for a sellable item entity.</p>
<p>If all conditions are met, we create a new child entity view:</p>
<pre><code class="language-csharp"> var view = new EntityView
               {
                    Name = notesViewsPolicy.Notes,
                    DisplayName = notesViewsPolicy.Notes,
                    EntityId = entityView.EntityId,
                    ItemId = variationId,
                    EntityVersion = entityView.EntityVersion
                };

  entityView.ChildViews.Add(view);
</code></pre>
<p>We create a new child entity view and set it’s properties, like the id of the Entity we are targeting and the version of that entity.</p>
<p>Then we add the newly created view to the childviews of the entity view.</p>
<p>Next we retrieve the NotesComponent from the Sellable item and use this to add the properties we want to display to this new view:</p>
<pre><code class="language-csharp">var component = sellableItem.GetComponent&lt;NotesComponent&gt;(variationId);

targetView.Properties.Add(new ViewProperty
{
    Name = nameof(NotesComponent.WarrantyInformation),
    RawValue = component.WarrantyInformation,
    IsReadOnly = !isEditView,
    IsRequired = false
});

targetView.Properties.Add(new ViewProperty
{
    Name = nameof(NotesComponent.InternalNotes),
    RawValue = component.InternalNotes,
    IsReadOnly = !isEditView,
    IsRequired = false
});

return Task.FromResult(entityView);
</code></pre>
<p>And don’t forget to add it to the <code>IGetEntityViewPipeline</code>.</p>
<pre><code>services.Sitecore().Pipelines(config =&gt;
    config.ConfigurePipeline&lt;IGetEntityViewPipeline&gt;(c =&gt;
           c.Add&lt;GetNotesViewBlock&gt;().After&lt;GetSellableItemDetailsViewBlock&gt;()
    )
);
</code></pre>
<p>Then, restart the commerce engine and see if the new view is visible in the merchandising manager.</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-30-at-15.36.13.png" alt="The Commerce Business Tools: How to extend it?"></p>
<p>As you can see, the Notes view has been added displaying the notes that were added to the sellable item.</p>
<p>In summary, to add a new view:</p>
<ul>
<li>Create a new block</li>
<li>Find the master view you want to extend</li>
<li>Add an entity view as a child view</li>
<li>Add properties to that entity view</li>
<li>Add block to <code>IGetEntityViewPipeline</code></li>
</ul>
<p>You can find <a href="https://github.com/ewerkman/bizfx-extension/blob/master/src/engine/Plugin.Sample.SellableItem/Pipelines/Blocks/GetNotesViewBlock.cs">the code for this block here</a>.</p>
<blockquote>
<p>Discuss item links:</p>
<ul>
<li>EntityLink</li>
<li>ItemLink</li>
<li>SubItemLink</li>
</ul>
</blockquote>
<blockquote>
<p>See also: <a href="http://andrewsutherland.azurewebsites.net/2018/10/02/business-tools-ui-hints-and-ui-types/">http://andrewsutherland.azurewebsites.net/2018/10/02/business-tools-ui-hints-and-ui-types/</a></p>
</blockquote>
<h3 id="howdoicreateanewnavigationitem">How do I create a new navigation item?</h3>
<p>What if you want to add a whole new navigation item or dashboard, for instance because you have created your own entity and want some way to view or edit it.</p>
<p>Well, as with almost everything having to do with the business tools, it’s easy: add a block to a pipeline.</p>
<p>In this case you will have to add a block to the <code>IBizFxNavigationPipeline</code>.</p>
<p>Out-of-the-box the <code>IBizFxNavigationPipeline</code> looks like this:</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-30-at-15.44.37.png" alt="The Commerce Business Tools: How to extend it?"></p>
<p>As you can see, it contains one block for each navigation item/dashboard in the business tools. If you want to add a new dashboard, you just add a block.</p>
<p>The code for this block is quite simple:</p>
<pre><code class="language-csharp">public class GetCartNavigationViewBlock : PipelineBlock&lt;EntityView, EntityView, CommercePipelineExecutionContext&gt;
{
    protected ViewCommander Commander { get; set; }

    public GetCartNavigationViewBlock(ViewCommander commander)
        : base(null)
    {
        this.Commander = commander;
    }

    public override Task&lt;EntityView&gt; Run(EntityView entityView, CommercePipelineExecutionContext context)
    {
        Condition.Requires(entityView).IsNotNull($&quot;{this.Name}: The argument can not be null&quot;);

        EntityView cartsView = new EntityView();

        cartsView.Name = context.GetPolicy&lt;KnownCartViewsPolicy&gt;().CartsDashboard;
        cartsView.ItemId = context.GetPolicy&lt;KnownCartViewsPolicy&gt;().CartsDashboard;
        cartsView.Icon = &quot;luggagecart&quot;;
        cartsView.DisplayRank = 2;

        entityView.ChildViews.Add((Model)cartsView);

        return Task.FromResult&lt;EntityView&gt;(entityView);
    }
}
</code></pre>
<p>First you create a new entity view.</p>
<pre><code class="language-csharp">EntityView cartsView = new EntityView();
</code></pre>
<p>Next, you set the properties of the entity view. You can use the Icon property to specify the icon you want displayed and you can use DisplayRank to specify which position you want the new navigation item to be in.</p>
<pre><code class="language-csharp">cartsView.Name = context.GetPolicy&lt;KnownCartViewsPolicy&gt;().CartsDashboard;
cartsView.ItemId = context.GetPolicy&lt;KnownCartViewsPolicy&gt;().CartsDashboard;
cartsView.Icon = &quot;luggagecart&quot;;
cartsView.DisplayRank = 2;
</code></pre>
<p>Finally you add the created view to the entity view:</p>
<pre><code class="language-csharp">entityView.ChildViews.Add((Model)cartsView);
</code></pre>
<p>The result of the <code>IBizFxNavigationPipeline</code> pipeline is an entity view with a child entity view for each dashboard.</p>
<p>And this is what the result looks like.</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-30-at-15.51.54.png" alt="The Commerce Business Tools: How to extend it?"></p>
<p>You have a new navigation item called Carts which you can then use to create views to display the carts.</p>
<blockquote>
<p>You can set the icon you want to display and set the place you want to display the in.<br>
You might ask: which icons can I use? The icons you can use are in a style sheet in a Speak package: speak/icon-fonts/dist/sitecore-icons.css</p>
</blockquote>
<p>In summary, to create a new navigation item:</p>
<ul>
<li>Create a new block</li>
<li>Add a child view to the entity view for the navigation item</li>
<li>Add the block to the <code>IBizfxNavigationPipeline</code></li>
</ul>
<p>You can find <a href="https://github.com/ewerkman/bizfx-extension/blob/master/src/engine/Plugin.BizFx.Carts/Pipelines/Blocks/GetCartNavigationViewBlock.cs">the code for this block here</a>.</p>
<h3 id="howdoiaddanewaction">How do I add a new action?</h3>
<p>Now we know how to view things, but how do we influence that data? How do we edit a component on a sellable item, change the status of an order or create the inventory for an product.</p>
<p>That's where the <em>actions</em> come in.</p>
<p>Actions are added to the view as a policy, called <code>ActionsPolicy</code>. An <code>ActionsPolicy</code> contains one or more <code>EntityActionView</code> instances.</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-30-at-16.12.02.png" alt="The Commerce Business Tools: How to extend it?"></p>
<p>Actions are added to the Entity Views in the <code>IPopulateEntityViewActionsPipeline</code>.<br>
It contains a block for each feature that needs to add actions to an entity view.</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-30-at-16.13.02.png" alt="The Commerce Business Tools: How to extend it?"></p>
<p>The IPopulateEntityViewActionsPipeline is run after the IGetEntityViewPipeline for <em>each</em> entity view.</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-30-at-16.13.45.png" alt="The Commerce Business Tools: How to extend it?"></p>
<p>To add a new action, you create a new block:</p>
<pre><code class="language-csharp">[PipelineDisplayName(nameof(PopulateCartLineItemsActionsBlock))]
public class PopulateCartLineItemsActionsBlock : PipelineBlock&lt;EntityView, 			EntityView, CommercePipelineExecutionContext&gt;
{
   .
   .
   .
}
</code></pre>
<p>As with the entity views, you only want to add an action to a specific entity view so you need to check the name of the entity view.</p>
<p>The code looks like this:</p>
<pre><code class="language-csharp">public override Task&lt;EntityView&gt; Run(EntityView entityView, CommercePipelineExecutionContext context)
{
    Condition.Requires(entityView).IsNotNull($&quot;{this.Name}: The argument can not be null&quot;);

    var viewsPolicy = context.GetPolicy&lt;KnownFeaturesViewsPolicy&gt;();

    if (string.IsNullOrEmpty(entityView?.Name) || !entityView.Name.Equals(viewsPolicy.Features, StringComparison.OrdinalIgnoreCase))
    {
        return Task.FromResult(entityView);
    }

    var actionPolicy = entityView.GetPolicy&lt;ActionsPolicy&gt;();

    actionPolicy.Actions.Add(
      new EntityActionView
      {
          Name = context.GetPolicy&lt;KnownFeaturesActionsPolicy&gt;().EditFeatures,
          DisplayName = &quot;Edit Sellable Item Features&quot;,
          Description = &quot;Edits the sellable item Features&quot;,
          IsEnabled = true,
          EntityView = entityView.Name,
          Icon = &quot;edit&quot;
      });


    return Task.FromResult(entityView);
}
</code></pre>
<p>First you check if the supplied entity view is the one you want to add your actions to. If that is the case you retrieve the ActionsPolicy and add an EntityActionView to it. Remember to always <strong>add</strong> it and not create a new ActionsPolicy, because there might have been another block that already added an action to the view.</p>
<p>Looking at the properties of an <code>EntityActionView</code> there are some things to note:</p>
<ul>
<li>First, there is the <code>Name</code> property. You will need to act on this name later so it should be unique.</li>
<li>The <code>DisplayName</code> and <code>Description</code> can be localized</li>
<li>Set the <code>RequiresConfirmation</code> to true if you want to create an action that requires confirmation. In that case you can use the <code>ConfirmationMessage</code> property and <code>ConfirmationTerms</code> property to create a meaningful message to the user.</li>
<li><code>EntityView</code> is the name of an <em>EntityView</em> that you want to display in a modal popup when the action is executed. The <code>IGetEntityViewPipeline</code> is run to retrieve the properties for this entity view.</li>
<li>If you don’t need confirmation and you don’t want to show a popup, set <code>EntityView</code> to <code>null</code> and set <code>RequiresConfirmation</code> to <code>false</code>.</li>
</ul>
<p>When you run the engine this will be the result.</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-30-at-16.20.29.png" alt="The Commerce Business Tools: How to extend it?"></p>
<p>You have two new actions.</p>
<p>Executing an action will always lead to one of the following happening:</p>
<ul>
<li>If <code>EntityView</code> is empty, an action is performed by running the <code>IDoActionPipeline</code>;</li>
<li>If <code>RequiresConfirmation</code> is true, a confirmation dialog is shown and if the user clicks OK, the <code>IDoActionPipeline</code> is run;</li>
<li>If EntityView has been set, this entity view is first retrieved using the <code>IGetEntityViewPipeline</code> and after the user has clicked OK, the <code>IDoActionPipeline</code> is run;</li>
</ul>
<p>The IDoActionPipeline is where the action is performed, for example, the changed properties of a sellable item are saved or a cart line is deleted.</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-30-at-16.21.48.png" alt="The Commerce Business Tools: How to extend it?"></p>
<p>The input for the IDoActionPipeline is again an EntityView object, containing the data the user entered in the dialog and the Action property set to the name of the action supplied in the EntityActionView.</p>
<p>The IDoActionPipeline contains a block for each action that can be performed. Again you have to check whether your block needs to do something based on information in the EntityView object.</p>
<p>Here is an example for a block that deletes the line in a cart:</p>
<pre><code class="language-csharp">[PipelineDisplayName(nameof(DoActionDeleteCartLineBlock))]
public class DoActionDeleteCartLineBlock : PipelineBlock&lt;EntityView, EntityView, CommercePipelineExecutionContext&gt;
{
    protected CommerceCommander Commander { get; set; }

    public DoActionDeleteCartLineBlock(CommerceCommander commander)
        : base(null)
    {

        this.Commander = commander;

    }

    public override async Task&lt;EntityView&gt; Run(EntityView entityView, CommercePipelineExecutionContext context)
    {
        Condition.Requires(entityView).IsNotNull($&quot;{this.Name}: The argument can not be null&quot;);

        var knownCartActionsPolicy = context.GetPolicy&lt;KnownCartActionsPolicy&gt;();

        if (string.IsNullOrEmpty(entityView?.Action) ||
            !entityView.Action.Equals(knownCartActionsPolicy.DeleteCartLine, StringComparison.OrdinalIgnoreCase))
        {
            return entityView;
        }

        var updatedCart = await Commander.Command&lt;RemoveCartLineCommand&gt;().Process(context.CommerceContext, entityView.EntityId, entityView.ItemId);

        return entityView;
    }
}
</code></pre>
<p>The <code>Run</code> method first checks whether it needs to react to this request. It checks for the <code>Action</code>. If it is not the correct action, it just returns the EntityView.</p>
<p>If it can handle the request, it will execute the <code>RemoveCartLineCommand</code>, supplying the id of the entity and the itemid, which in this case is the id of the line to delete.</p>
<p>In summary, to handle an action:</p>
<ul>
<li>Add a block to the IPopulateEntityViewActionsPipeline to show the actions</li>
<li>Add a block to the IDoActionPipeline to handle the selected action</li>
</ul>
<p>You can find <a href="https://github.com/ewerkman/bizfx-extension/tree/master/src/engine/Plugin.BizFx.Carts/Pipelines/Blocks">the code for these blocks here</a>.</p>
<p>Create an action that requires confirmation &gt; set requiresconfirmation on Action and don’t set EntityView<br>
If you don’t require confirmation, don’t set an entity view name and set requiresconfirmation to false ?</p>
<h3 id="howdoesauthorizationwork">How does authorization work?</h3>
<p>The business tools give users access to potentially sensitive data, which means we want to make sure not everybody can access that data.</p>
<p>You have already seen that you need to log in to get access to the business tools and that Sitecore Identity Server is used to authenticate you. How does that work?</p>
<p>Once you have successfully logged on, Sitecore Identity Server gives a token to the business tools and this token is subsequently  used to authenticate with the authoring server. This token not only validates that you are who you say you are, but it also contains your roles.</p>
<p>Management of users is done through the Sitecore XP User manager. This is where you assign commerce roles to users. Out-of-the-box, Sitecore Commerce has the following commerce specific roles:</p>
<ul>
<li>Commerce Administrator</li>
<li>Commerce Business User</li>
<li>Customer Service Representative</li>
<li>Customer Service Representative Administrator</li>
<li>Merchandiser</li>
<li>Pricer</li>
<li>Pricer Manager</li>
<li>Promotioner</li>
<li>Promotioner Manager</li>
<li>Relationship Administrator</li>
</ul>
<p>If you want to access the Business tools, you always need at least the <em>Commerce Business User</em> role. Also note that even the <em>admin</em> user needs to be assigned a Commerce role, otherwise you won’t have access.</p>
<h4 id="sohowisdecidedwhatyougetaccessto">So, how is decided what you get access to?</h4>
<p>This is actually a two-step process and it’s all configured by policy sets. There are two policy sets that play a role here.</p>
<p>First there is the <code>ControllerMethodRolesPolicySet</code>, which enforces access to the API by checking if the user has the Commerce Business User role.</p>
<p>Secondly, there is the <code>AccessByRolesPolicySet</code> which you will find in <code>Plugin.AccessByRoles.PolicySet-1.0.0.json</code>.</p>
<p>It contains an <code>ActionsRolesPolicy</code> which contains a list of <code>ActionRoleModels</code><br>
An <code>ActionRoleModel</code> has four string properties:</p>
<ul>
<li><code>View</code></li>
<li><code>Action</code></li>
<li><code>EntityType</code></li>
<li><code>Role</code></li>
</ul>
<p>Here is an example of what an ActionRoleModel entry looks like:</p>
<p>If you want to limit the actions somebody can do, you always specify the view for which the actions have been defined. (So <code>Action</code> always requires a View)</p>
<p>The <code>View</code> property supports wildcards.</p>
<h4 id="howisthisenforced">How is this enforced?</h4>
<p>Remember that the last step in the <code>IGetEntityViewPipeline</code> is to run the <code>IFormatEntityViewPipeline</code>? The <code>IFormatEntityViewPipeline</code> also takes care of <em>authorization</em>, removing any view that you do not have access to. There are a couple of blocks that are responsible for this:</p>
<ul>
<li><code>DisableEntityActionsBlock</code></li>
<li><code>ValidateViewBasedOnRolesBlock</code></li>
<li><code>ValidateViewActionsBasedOnRolesBlock</code></li>
</ul>
<p>If you create new roles, note that you need to add them to the Commerce Administrator role if you want your admin user to still be able to access the parts you are limiting, otherwise he doesn't have access.</p>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[The Commerce Business Tools: How does it work?]]></title><description><![CDATA[This is the first post in a 3 part series on the Sitecore Commerce Business Tools. The first part covers how the Business Tools work.]]></description><link>https://commerceservertips.com/the-commerce-business-tools-how-does-it-work/</link><guid isPermaLink="false">5d187a48023d09187c6f5fd7</guid><category><![CDATA[bizfx]]></category><category><![CDATA[sitecore commerce 9]]></category><dc:creator><![CDATA[Erwin Werkman]]></dc:creator><pubDate>Thu, 04 Jul 2019 20:34:26 GMT</pubDate><media:content url="https://commerceservertips.com/content/images/2019/06/tools.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><blockquote>
<img src="https://commerceservertips.com/content/images/2019/06/tools.jpg" alt="The Commerce Business Tools: How does it work?"><p>This post is part of a 3-part series of posts on how to extend the Sitecore Commerce Business Tools. It contains all the contents I presented at SUGCON EU in London in April 2019 and adds some additional insights.<br>
The other parts are:</p>
<ul>
<li><a href="https://commerceservertips.com/the-commerce-business-tools-how-to-extend">The Commerce Business Tools: How to extend it?</a></li>
<li><a href="https://commerceservertips.com/the-commerce-business-tools-creating-your-own-angular-components">The Commerce Business Tools: Creating your own Angular components</a></li>
</ul>
</blockquote>
<p>The Sitecore Commerce Businesss Tools represent a great way to extend Sitecore Commerce with new functionality and it is easy to do. These blog posts show you how the Business Tools work and how to extend them in different ways.</p>
<h3 id="howdoesitwork">How does it work?</h3>
<p>When I started working on the presentation for SUGCON, the first thing I was wondering was, how do the business tools actually work?</p>
<p>Now there is actually <a href="https://doc.sitecore.com/developers/90/sitecore-experience-commerce/en/commerce-views-service.html">a fair bit of documentation</a> on how the business tools work, but if you look for BizFx or business tools you won’t find it immediately, because it is hidden under the innocent sounding term Commerce Views Service.</p>
<p>There you can read the following:</p>
<blockquote>
<p>“The Commerce Views plugin provides a data-driven mechanism for servicing a dynamic business experience. The views provide a mechanism for narrowing or translating data from core entity storage into a dynamic API that can be directly leveraged by the business experience.”</p>
</blockquote>
<p>Now, I don’t exactly know what that means but it is full of experience and experience is always good.</p>
<p>But what it boils down to is that the output from the views service, drives what the user sees in the business tools.</p>
<p>It is what’s called a <em>data-driven application</em>.</p>
<p>The business tools is a single page application (SPA) and it is written in Angular. It gets its data from the commerce service you connect it to and normally that would be the authoring service.</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-05-at-08.57.30.png" alt="The Commerce Business Tools: How does it work?"></p>
<p>When you start up the business tools for instance by going to <a href="https://localhost:4200">https://localhost:4200</a> and you trace which calls the business tools is doing to the authoring service, you will see the following:</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-05-at-09.02.31.png" alt="The Commerce Business Tools: How does it work?"></p>
<p>If you look closely you will see that these calls are actually done twice: once with the <code>OPTIONS</code> method to check if the call is available and once with the <code>GET</code> method to get the actual data.</p>
<p>These calls are going to the Commerce Engine Authoring role. They retrieve data that is used to drive the user interface</p>
<p>If you look at the data, for instance for the <code>GetNavigationView()</code> call, you will see the following:</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-05-at-09.03.57.png" alt="The Commerce Business Tools: How does it work?"></p>
<p>If you have worked with the commerce engine before you will recognize the OData structure.</p>
<p>These 4 calls, lead to the following UI being displayed in your browser:</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-05-at-09.05.14.png" alt="The Commerce Business Tools: How does it work?"></p>
<p>It’s easy to see which call is responsible for which part of the screen.</p>
<ul>
<li>The first <code>GetNavigationView</code> call renders the navigation in the left sidebar;</li>
<li>The <code>GetEnvironmentsView</code> call renders the list of environments in the upper left corner;</li>
<li>The <code>GetLanguagesView</code> call renders the list of languages;</li>
<li>And finally the second <code>GetNavigationView</code> call renders the main navigation in the center screen;</li>
</ul>
<p>If you look at the structure of the data for each call you will see that they all return an EntityView.</p>
<blockquote>
<p>An EntityView represents “a flattened, dynamic service response focused on supporting a dynamic user experience”.</p>
</blockquote>
<p>An EntityView has:</p>
<ul>
<li>a name</li>
<li>Policies;</li>
<li>Child entity views;</li>
<li>Properties;</li>
<li>UIHint (an indication on how the view should be rendered);</li>
</ul>
<p>If you look at the result of GetNavigationView for instance, it consists of a main view called Navigation and 8 child entity views called:</p>
<ul>
<li>MerchandisingDashboard;</li>
<li>InventoryDashboard;</li>
<li>PricingDashboard;</li>
<li>PromotionsDashboard;</li>
<li>OrdersDashboard;</li>
<li>CustomersDashboard;</li>
<li>RelationshipsDashboard;</li>
<li>ComposerDashboard;</li>
</ul>
<p>Let’s go back to our main dashboard and let’s click on Merchandising.</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-05-at-09.07.09.png" alt="The Commerce Business Tools: How does it work?"></p>
<p>If you look at the URL in the browser, you will see the url looks like this:</p>
<p><code>http://localhost:4200/entityView/MerchandisingDashboard</code></p>
<p>which basically means show the <em>entity view</em> called <code>MerchandisingBoard</code>. In Angular this is called a <em>route</em> and Angular will update the user interface based on the route. Note that changing the url is not doing any calls to the server, because there is nothing listening there for it.</p>
<p>If you have a look at the network panel to see which call  the Business tools are doing, you will see the following;</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-05-at-09.10.46.png" alt="The Commerce Business Tools: How does it work?"></p>
<p>This time, the <code>GetEntityView</code> method is called. What is interesting to know of course, is which parameters are used to call <code>GetEntityView</code>. For <code>GetEntityView</code>, the PUT method is used and the payload looks like this:</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-05-at-09.11.58.png" alt="The Commerce Business Tools: How does it work?"></p>
<p>As you can see the requested view(Name) is called MerchandisingDashboard. You will also note 3 other parameters, which we’ll have a look at later.</p>
<p>If you look at the OData response, you will note that it returns an EntityView called <code>MerchandisingDashboard</code> with two child views:<br>
<code>Search</code>;<br>
<code>Catalogs</code>;<br>
<img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-05-at-09.12.36.png" alt="The Commerce Business Tools: How does it work?"></p>
<p>You will also notice the Catalogs view has two child views, both have the name Summary.  Each child view contains 5 properties, which you see displayed in the table of catalogs:</p>
<ul>
<li>Name;</li>
<li>Display Name;</li>
<li>Price Book Name;</li>
<li>Promotion Book Name;</li>
<li>Default Inventory Set Name;</li>
</ul>
<p>The response we get is rendered like this.</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-05-at-09.13.56.png" alt="The Commerce Business Tools: How does it work?"></p>
<p>You can recognize each entity view and the properties.</p>
<p>Notice that the Catalogs view has two UI elements we haven’t seen before:</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-06-at-21.46.05.png" alt="The Commerce Business Tools: How does it work?"></p>
<p>These two buttons are called <em>actions</em>. The first button lets you create a new catalog and the second can be used to delete a catalog.</p>
<p>Each Entity View can have a number of actions.  Here they are represented as buttons, but if there are more than 3 actions for a view, they are represented as a drop down menu.</p>
<p>We’ll see later how you can add an action to a view.</p>
<p>Let’s click on the first catalog name and see what happens.</p>
<p>First thing you should notice is that the URL now looks like this:</p>
<p><code>http://localhost:4200/entityView/Master/1/Entity-Catalog-Habitat_Master</code></p>
<p>There’s a fair bit of information in this url:</p>
<ul>
<li><code>Master</code> refers to the name view we want to be displayed;</li>
<li><code>1</code> indicates the version of the entity we want to see;</li>
<li><code>Entity-Catalog-Habitat-Master</code> is the id of the entity we want to view;</li>
</ul>
<p>This url renders the following screen:</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-06-at-21.49.07.png" alt="The Commerce Business Tools: How does it work?"></p>
<p>This screen consists of a total of 5 entity views (of which you see 3 in the image).</p>
<p>All elements of an entity view should by now look familiar.</p>
<h4 id="behindthescenes">Behind the scenes</h4>
<p>So, what happens behind the scenes when the business tools do a call to the commerce engine.</p>
<p>If you know a little bit about the commerce engine, you will know there is one concept that is very important which are the <em>pipelines</em>.</p>
<p>Each of the javascript calls we have seen, triggers the running of a pipeline.</p>
<p>For instance, to create the languages view, GetLanguagesView runs the IBizFxLanguagesPipeline:</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-06-at-21.53.37.png" alt="The Commerce Business Tools: How does it work?"></p>
<p>And GetNavigationView() runs the IBizFxNavigationPipeline:</p>
<p><img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-06-at-21.56.25.png" alt="The Commerce Business Tools: How does it work?"></p>
<p>And GetEntityView() runs, you can probably guess the GetEntityViewPipeline.<br>
<img src="https://commerceservertips.com/content/images/2019/06/Screenshot-2019-06-06-at-21.56.59.png" alt="The Commerce Business Tools: How does it work?"></p>
<blockquote>
<p>Now you know which pipelines are involved in the process and how entity views work, the next question is of course: how can I extend this? You can read how to extend it in the <a href="https://commerceservertips.com/the-commerce-business-tools-how-to-extend">next blog post</a>.</p>
</blockquote>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[New version of Plumber Configuration Viewer]]></title><description><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><blockquote>
<p><a href="https://github.com/plumber-sc/plumber-sc/releases/tag/1.1">Plumber Configuration Viewer for Sitecore Commerce version 1.1</a> is out and it includes a new feature to generate a code template for pipeline blocks.</p>
</blockquote>
<h2 id="usingthenewpipelineblockgenerator">Using the new pipeline block generator</h2>
<p>Just navigate to a pipeline in Plumber, click the location where you want to include your new custom pipeline,</p>]]></description><link>https://commerceservertips.com/new-version-of-plumber-configuration-viewer/</link><guid isPermaLink="false">5d187a48023d09187c6f5fd6</guid><category><![CDATA[sitecore commerce 9]]></category><category><![CDATA[Plumber]]></category><dc:creator><![CDATA[Erwin Werkman]]></dc:creator><pubDate>Mon, 13 May 2019 11:24:25 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1530124566582-a618bc2615dc?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><blockquote>
<img src="https://images.unsplash.com/photo-1530124566582-a618bc2615dc?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="New version of Plumber Configuration Viewer"><p><a href="https://github.com/plumber-sc/plumber-sc/releases/tag/1.1">Plumber Configuration Viewer for Sitecore Commerce version 1.1</a> is out and it includes a new feature to generate a code template for pipeline blocks.</p>
</blockquote>
<h2 id="usingthenewpipelineblockgenerator">Using the new pipeline block generator</h2>
<p>Just navigate to a pipeline in Plumber, click the location where you want to include your new custom pipeline, configure the namespace and the name of the new block and copy the code for the block and code to configure the pipeline:</p>
<video controls="controls" width="720" autoplay>
  <source type="video/mp4" src="https://github.com/plumber-sc/website/blob/master/assets/videos/Plumber-Pipeline-Code-Generator.mp4?raw=true">
  <p>Your browser does not support the video element.</p>
</video>
<h2 id="whatelseisnew">What else is new?</h2>
<ul>
<li>Upgraded to the latest version if Vue;</li>
<li>Added better messages when there is something not right with your configuration;</li>
<li>Made some cosmetic changes to the UI;</li>
</ul>
<h2 id="installingupgrading">Installing/Upgrading</h2>
<p>You can get the latest version on Github: <a href="https://github.com/plumber-sc/plumber-sc/releases/tag/1.1">https://github.com/plumber-sc/plumber-sc/releases/tag/1.1</a></p>
<p>Something to note is that the <code>config.json</code> file that contains your environment specific configuration has moved from the static folder to the root.</p>
<p>Easiest way to install is:</p>
<ul>
<li>Make a backup of your <code>config.json</code> file;</li>
<li>Remove everything in the folder where you installed Plumber;</li>
<li>Unzip the new version and put it's contents in the folder of the previous version of Plumber;</li>
<li>When upgrading, replace the config.json that's in the new version with the backup you created.</li>
</ul>
<blockquote>
<p>Let me know if you like the new feature!</p>
</blockquote>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[SUGCON 2019 Slides -Extending the Sitecore Commerce Business Tools]]></title><description><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>If you could not make it to SUGCON 2019 in London or you were at the conference but missed my talk, you can now download <a href="https://sitecore.box.com/s/5469n49g5yxzu41cg4rzil9gv009q29i">the full presentation including video's and text</a>.</p>
<p>As mentioned in the presentation, <a href="https://github.com/ewerkman/bizfx-extension">sample code is available on GitHub</a></p>
<p>I still intend to do a write-up</p>]]></description><link>https://commerceservertips.com/sugcon-2019-slides-extending-the-sitecore-commerce-business-tools/</link><guid isPermaLink="false">5d187a48023d09187c6f5fd5</guid><category><![CDATA[sitecore commerce 9]]></category><category><![CDATA[SUGCON]]></category><category><![CDATA[bizfx]]></category><dc:creator><![CDATA[Erwin Werkman]]></dc:creator><pubDate>Mon, 08 Apr 2019 19:44:59 GMT</pubDate><media:content url="https://commerceservertips.com/content/images/2019/04/Tower_Bridge_London.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><img src="https://commerceservertips.com/content/images/2019/04/Tower_Bridge_London.jpg" alt="SUGCON 2019 Slides -Extending the Sitecore Commerce Business Tools"><p>If you could not make it to SUGCON 2019 in London or you were at the conference but missed my talk, you can now download <a href="https://sitecore.box.com/s/5469n49g5yxzu41cg4rzil9gv009q29i">the full presentation including video's and text</a>.</p>
<p>As mentioned in the presentation, <a href="https://github.com/ewerkman/bizfx-extension">sample code is available on GitHub</a></p>
<p>I still intend to do a write-up of the contents. I have some extra material on authentication and localization. Coming soon(ish)...</p>
<p><a href="https://sitecore.box.com/s/5469n49g5yxzu41cg4rzil9gv009q29i"><img src="https://commerceservertips.com/content/images/2019/04/Extending-the-Sitecore-Commerce-Business-Tools.PNG" alt="SUGCON 2019 Slides -Extending the Sitecore Commerce Business Tools"></a></p>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Quick Tip: (remote) debugging the commerce engine]]></title><description><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>This is kind of an embarrassing story: I recently needed to (remote) debug a deployed commerce engine. What do you normally do when you need to debug a deployed website: you set your break-points, attach the debugger in Visual Studio to the <em>w3wp</em> process and wait until the breakpoint indicators</p>]]></description><link>https://commerceservertips.com/quick-tip-remote-debugging-the-commerce-engine/</link><guid isPermaLink="false">5d187a48023d09187c6f5fd4</guid><dc:creator><![CDATA[Erwin Werkman]]></dc:creator><pubDate>Mon, 13 Aug 2018 16:58:29 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>This is kind of an embarrassing story: I recently needed to (remote) debug a deployed commerce engine. What do you normally do when you need to debug a deployed website: you set your break-points, attach the debugger in Visual Studio to the <em>w3wp</em> process and wait until the breakpoint indicators turn red, indicating a successful attach of the debugger.</p>
<p>So I did, but for some reason my breakpoints would not turn red: they stayed disappointingly empty. I tried all kinds of different things like changing the access rights to the remote debugger, reinstalling the remote debugger, turning the firewall off and even installing Visual Studio on the machine I was trying to debug.</p>
<p>Finally, I said to a coworker I still couldn't debug the engine and I showed him what I did. He asked me why I was attaching to the w3wp process instead of connecting to the commerce engine executable. That was the moment when I felt quite stupid: of course I should have attached to the executable and not the the IIS process. What was I thinking off...</p>
<p>So just a quick reminder: <strong>when debugging the engine running &quot;in&quot; IIS, attach to the executable not to w3wp process.</strong></p>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Quick Little Tip: Don't forget to configure the Minion role to be AlwaysRunning]]></title><description><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>The Minion role in Sitecore Commerce is a role that should always be running. It makes sure orders are processed and all housekeeping is done. If you use the SIF installer it will automatically configure a website and application pool in IIS with the correct configuration.</p>
<p>Recently I was working</p>]]></description><link>https://commerceservertips.com/quick-little-tip-configure-the-minion-role-to-be-alwaysrunning/</link><guid isPermaLink="false">5d187a48023d09187c6f5fd3</guid><dc:creator><![CDATA[Erwin Werkman]]></dc:creator><pubDate>Tue, 31 Jul 2018 10:14:43 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>The Minion role in Sitecore Commerce is a role that should always be running. It makes sure orders are processed and all housekeeping is done. If you use the SIF installer it will automatically configure a website and application pool in IIS with the correct configuration.</p>
<p>Recently I was working with a client to setup a development environment. We had already set up an environment using the SIF scripts, but for this environment we created the websites and application pools for the different roles manually. Everything was working well but we noticed the Minions role was not logging any activity. This shouldn't be the case because the Minions should be running all the time. Most Minion jobs are configured to run every 5 minutes.<br>
We had a look at the configuration of the website and the application pool, comparing it to the machine that was working correctly and then we noticed that the start mode of application bool was set to <strong>OnDemand</strong>.</p>
<p>We changed the setting to <strong>AlwaysRunning</strong> and checked the log files, which were once again filling up with log messages.</p>
<h3 id="insummary">In summary</h3>
<p>To configure the application pool to be always running, open IIS Manager, go to the application pool for the Minions role and select Advanced Settings. In the Advanced Settings dialog, make sure the start mode is set to <strong>AlwaysRunning</strong>.</p>
<p><img src="https://res.cloudinary.com/plumber-sc/image/upload/v1533029722/plumber/advancedsettings.png" alt=""></p>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>