Sitecore Experience Commerce: Associating Inventory from Sellable Item and Variant Pages

In this article, we introduce a small custom plugin for Sitecore Commerce Business Tools that enables the inventory association for sellable items and variants directly from the Merchandising Manager pages.

Establishing inventory information associations is an action hosted within the Inventory Manager. Prior to the introduction of inventory indexes in XC 9.3, locating an existing inventory information record for large catalogs via the Inventory Manager can be quite a tedious task with the pagination controls being the only form of search.

The custom plugin adds the Associate Sellable Item to an inventory set action to the Inventory Sets entity view for the Sellable Item and Variant merchanding pages.

A transitional step to select the Inventory Set is added to the modal view, which in the Inventory Manager is driven by the inventory set being viewed.

The final step locks the Inventory Set and Sellable Item fields, as they have already been identified, and if the sellable item already had been associated to the selected inventory set then this would behave like an edit view, keeping the UX at the forefront of this implementation.

Managing existing inventory information records can also be more achieved via the Sellable Item and Variant pages in the Inventory Sets entity view actions, however prior to XC 9.3 a business was required to create a new entity version to enable these controls, unless the EntityVersionsActionsPolicy had been updated to allow these actions to bypass the entity version (as they should). This process was also documented in Enabling Disassociate, Edit, and Transfer Inventory Actions for Published Sellable Items and Variants.

Source Code: Extended Sitecore Commerce Inventory project repository

Sitecore Identity Server: Increasing the Token Lifetime for Local Development

In this article, we will review how to change the authentication token timeout values that force us to log back in to Sitecore or request a new token from Postman. If you are like me, generally working with Sitecore/Sitecore Commerce 10+ hours per day, 6 days a week, it can seem like you are kicked out every 5 minutes. Personally, I set these timeouts to a week (604800 seconds).

Changing the timeouts are not recommended for production instances.

Changing the Timeouts in Sitecore Identity Server

Sitecore Identity Server was first introduced with Sitecore Commerce 9.0.0 and with the release of Sitecore 9.1, Sitecore Identity Server was added to Sitecore authentication process.

Updating the Token Lifetimes in 9.0.X

  1. Open <Sitecore Identity Server root>\wwwroot\appsettings.json.
  2. Under AppSettings.Clients, update the CommerceBusinessTools and Postman API clients for BizFx and Postman applications respectively :-
    1. Update the AccessTokenLifetimeInSeconds and IdentityTokenLifetimeInSeconds from the default 3600 (seconds) to the desired timespan, in seconds.
    2. Save the configuration.
  3. Restart the Sitecore Identity Server so that the updated configuration is consumed on startup.

Updating the Token Lifetimes in 9.3

  1. Open <Sitecore Identity Server root>\Config\production\Sitecore.Commerce.IdentityServer.Host.xml.
  2. Under /Settings/Sitecore/IdentityServer/Clients, update the CommerceClient and PostmanClient for BizFx and Postman applications respectively:-
    1. Update the AccessTokenLifetimeInSeconds and IdentityTokenLifetimeInSeconds from the default 3600 (seconds) to the desired timespan, in seconds.
    2. Save the configuration.
  3. Restart the Sitecore Identity Server so that the updated configuration is consumed on startup.

Sitecore: Using a Dedicated Custom Include Folder for Actual Custom Configuration Files

In this article, we will look at creating our own custom include folder in Sitecore to avoid the ugliness of the folder/file structure that comes with a Sitecore installation and all of its modules.

Include folder contents from default installation of Sitecore with Sitecore XC 9.3

Previously, I covered Managing Commerce Configuration to Align with Helix Principles, however this approach was overly complex and not really developer friendly. Since then I have concluded that the simpler approach would be leave the "custom" folder for the Sitecore integrations and create a dedicated custom folder outside of the "custom" for solution specific custom configurations.

Added benefits to this approach include:

  • When performing an upgrade, a new or updated configuration will not override your custom configurations based on load order.
  • You won't need to revisit the Layers.config file as potentially required with my previously documented approach.
  • For local development, you can comfortablely delete the dedicated custom folder and redeploy, avoiding any stale custom configurations from your current build.

Configuring Layers.config

As the order of the layer nodes in the Layers.confg defines the order in which they load at runtime, all that is required for this approach is to add the new custom folder to Layers.config after the "Custom" layer and ensure your custom solution configuration files are placed in the App_Config/Custom_Include folder within the projects.

...
<layer name="Custom" includeFolder="/App_Config/Include/">
  <loadOrder>
   <add path="Foundation" type="Folder" />
   <add path="Feature" type="Folder" />
   <add path="Project" type="Folder" />
  </loadOrder>
</layer>
<layer name="Custom_Include" includeFolder="/App_Config/Custom_Include/">
  <loadOrder>
    <add path="Foundation" type="Folder" />
    <add path="Feature" type="Folder" />
    <add path="Project" type="Folder" />
  </loadOrder>
</layer>
...

Add the custom folder after the "Custom" layer entry and include the "Foundation", "Feature", and "Project" folders to manage configuration files using Helix principles.

Custom Include folder contents segregated from the Include folder of the default installation of Sitecore with Sitecore XC 9.3

Summary

No longer will you need to fight for configuration priority over the platform's standard configuration files or soil your solution with 'zzz' prefixed patch files.

Sitecore Experience Commerce: Working with Digital Sellable Items – Part 2

In this article, we look closer at the techincal details behind how digital sellable items are managed in the Commerce Engine, which in turn drives storefront functionality.

For an introduction to digital sellable items and how to configure them, see Working with Digital Sellable Items - Part 1.

Technical Implementation Details

See the Terminology Definitions for further explanation of tag types and Digital Item Tag Types for the default tags available, which are used throughout this article.

Inventory

Digital sellable items are always available for purchase, which I think of as having perpetual inventory. This functionality is driven by the AvailabilityAlwaysPolicy.

During the IGetSellableItemPipeline, the EnsureSellableItemAvailabilityPoliciesBlock replaces the sellable item's AvailabilityPolicy with the AvailabilityAlwaysPolicy where one or more of the sellable item's tags match the digital item tags.

See Digital Item Tags for the full list of default tags.

Plugin.Catalog.IGetSellableItemPipeline
  Plugin.Catalog.PrepGetSellableItemBlock
  Core.IFindEntityPipeline
  Plugin.Catalog.FilterSellableItemVariantsBlock 
  Plugin.Catalog.FormatComposerViewPropertyBlock 
  Plugin.Catalog.IPostGetSellableItemPipeline 
    Plugin.Catalog.GetSellableItemCatalogBlock 
    Plugin.Catalog.ICalculateSellableItemPricesPipeline
    Plugin.Catalog.AddSellableItemToContextBlock
    Plugin.Availability.EnsureSellableItemAvailabilityPoliciesBlock
    Plugin.Availability.IPopulateItemAvailabilityPipeline

Inventory Rules - Standalone Sellable Item

Summary: The AvailabilityAlwaysPolicy (perpetual inventory) is dependent on digital item tags being configured on the sellable item.

Inventory Rules - Sellable Items with Variants

Summary: The AvailabilityAlwaysPolicy (perpetual inventory) is dependent on the digital item tags being configured on the sellable item, which propagates to all variants. Configuring digital item tags on the variants will not have any effect, therefore perpetual inventory will either apply to all variants or no variants.

Cart Lines

When adding an item to the cart, the IAddCartLinePipeline runs, with the notable registered pipeline block or pipeline is IPopulateValidateCartPipeline, which syncs cart lines with the data from the sellable item entities. In part this is achieved with by calling the IGetSellableItemPipeline (more detail above in Inventory) for each cart line, which is important as the subsequent blocks that utilise the presence of AvailabilityAlwaysPolicy.

Plugin.Carts.IAddCartLinePipeline
  Plugin.Catalog.ValidateSellableItemBlock
  Plugin.Carts.AddCartLineBlock
  Plugin.Carts.AddContactBlock
  Plugin.Carts.IPopulateValidateCartPipeline
  Plugin.GiftCards.AddCartLineGiftCardBlock
  Plugin.DigitalItems.AddCartLineDigitalProductBlock
  Plugin.DigitalItems.AddCartLineWarrantyBlock
  Plugin.DigitalItems.AddCartLineInstallationBlock
  Plugin.Carts.ICalculateCartLinesPipeline
  Plugin.Carts.ICalculateCartPipeline
  Plugin.Carts.PersistCartBlock

These subsequent pipeline blocks, AddCartLineDigitalProductBlock, AddCartLineWarrantyBlock, and AddCartLineInstallationBlock, determine if the newly added cart line is a digital item type by comparing the cart line tags against its respective set of digital item type tag lists in conjunction with the AvailabilityAlwaysPolicy. Similarly, the AddCartLineGiftCardBlock also compares cart line tags against gift card tags, but ignores the presence of the AvailabilityAlwaysPolicy.

If the cart line has been identified as a digital sellable item, it will be split the cart line out into single quantity lines, so that each digital sellable item can have unique fulfillment details, coming up in the checkout's delivery step.

Plugin.Carts.IAddCartLinePipeline
  Plugin.Catalog.ValidateSellableItemBlock
  Plugin.Carts.AddCartLineBlock
  Plugin.Carts.AddContactBlock
  Plugin.Carts.IPopulateValidateCartPipeline
  Plugin.GiftCards.AddCartLineGiftCardBlock
  Plugin.DigitalItems.AddCartLineDigitalProductBlock
  Plugin.DigitalItems.AddCartLineWarrantyBlock
  Plugin.DigitalItems.AddCartLineInstallationBlock
  Plugin.Carts.ICalculateCartLinesPipeline
  Plugin.Carts.ICalculateCartPipeline
  Plugin.Carts.PersistCartBlock

Cart Line Rules - Standalone Sellable Item

Summary: Configuring a sellable item with one or more digital item type tags and one or more digital item tags (triggering perpetual inventory) will split cart lines into single quantity lines. Where one or more gift card tags are configured to a sellable item, this will also split cart lines and does not require a digital item tag configured.

Cart Line Rules - Sellable Items with Variants

Summary: Similar to sellable item configurations, the only difference for configuring sellable items with variants is that the digital item tags or gift card tags need to apply to the variants themselves.

Consideration: Variants inherit its parent sellable item's tags if not configured, however if a tag is configured on a variant it will not inherit the tags from the parent sellable item.

* includes tags inherited from parent sellable item

Cart Fulfillment Option Types

For the checkout's delivery step, the IGetCartFulfillmentOptionsPipeline is executed within the GetCartFulfillmentOptions API, returning the available fulfillment option types for the storefront.

Plugin.Fulfillment.IGetCartFulfillmentOptionsPipeline
  Plugin.Fulfillment.FilterCartFulfillmentOptionsBlock

The key pipeline block is the FilterCartFulfillmentOptionsBlock, which roughly provides the following logic.

  1. Retrieves all fulfillment option types associated to the storefront.
  2. Removes the "SplitShipping" (Deliver Items Individually - split shipment) type if there is only one cart line.
  3. Removes the physical fulfillment option type, "ShipToMe" (Ship To Address), where a cart contains digital sellable items, identified by the AvailabilityAlwaysPolicy.
  4. Removes the digital fulfillment option type, "Digital" (Digital Delivery), where a cart contains physical sellable items.

The storefront's fulfillment options are located in the Sitecore Content Editor at /sitecore/Commerce/Commerce Control Panel/Storefront Settings/Storefronts/<storefront>/Fulfillment Configuration.

Cart Fulfillment Rules - Standalone Sellable Item

Summary: The digital fulfillment option types are dependent on the presence of the AvailabilityAlwaysPolicy (perpetual inventory) on the sellable item, rather than comparing the digital item type or gift card tags as one would expect.

Cart Fulfillment Rules - Sellable Items with Variants

Summary: The digital fulfillment option types are dependent on the presence of the AvailabilityAlwaysPolicy (perpetual inventory) on the sellable item, rather than comparing the digital item type or gift card tags on the variants as one would expect.

Consideration: This is also another instance where the parent sellable item drives the behaviour of all of its variants, so if you selling books, for example, and wanted to sell the digital PDF version, there is a customisation here to think about.

Cart Line Fulfillment Option Types

Where the split shipping option type is selected during the checkout's delivery step, each cart line will require their own fulfillment option type, of which the available types per cart line are determined via the GetCartLineFulfillmentOptions API.

Plugin.Fulfillment.IGetCartLineFulfillmentOptionsPipeline
  Plugin.Fulfillment.FilterCartLineFulfillmentOptionsBlock

The key pipeline block in this pipeline is the FilterCartLineFulfillmentOptionsBlock, which has similar logic to the FilterCartFulfillmentOptionsBlock, but focuses on each cart line in isolation.

  1. Retrieves all fulfillment option types associated to the storefront.
  2. Removes the "SplitShipping" / (Deliver Items Individually - split shipment) type as this option is not applicable for individual line items.
  3. Removes the physical fulfillment option type, "ShipToMe" (Ship To Address), where the cart line is a digital sellable item, identified by the "entitlement" tag.
  4. Removes the digital fulfillment option type, "Digital" (Digital Delivery), where the cart line is a physical sellable item.

You may have noticed that the "entitlement" tag is being utilised instead of the AvailabilityAlwaysPolicy as per the FilterCartFulfillmentOptionsBlock. Although the "entitlement" tag does exist in the Digital Item Tags, effectually meaning that the sellable item will still contain the AvailablityAlwaysPolicy, it's important note to take for identifying how digital sellable items need to be configured, so we don't run into any surprises in the storefront.

Cart Line Fulfillment Rules - Standalone Sellable Item

Summary: The digital fulfillment option types for cart lines are dependent on the presence of the "entitlement" tag on the sellable item, which by default is equivalent to the AvailabilityAlwaysPolicy, rather than comparing the digital item type or gift card tags as one would expect.

Cart Line Fulfillment Rules - Sellable Items with Variants

Summary: The digital fulfillment option types for cart lines are dependent on the presence of the "entitlement" tag on the variant, which by default is equivalent to the AvailabilityAlwaysPolicy, rather than comparing the digital item type or gift card tags as one would expect.

* includes tags inherited from parent sellable item

Entitlements

Entitlements are provisioned during the released order minion. The GenerateOrderEntitlementsBlock is the central point for creating the entitlements, calling the IProvisionEntitlementsPipeline to handle each digital item type.

Plugin.Orders.IReleasedOrdersMinionPipeline
  Plugin.Fulfillment.GenerateOrderShipmentBlock
  Plugin.Fulfillment.GenerateOrderLinesShipmentBlock
  Plugin.Entitlements.GenerateOrderEntitlementsBlock
  Plugin.FaultInjection.MinionFaultBlock
  Plugin.FaultInjection.SettlePaymentFaultBlock
  Plugin.Sample.Payments.Braintree.SettleOrderSalesActivitiesBlock
  Plugin.Orders.MoveReleasedOrderBlock
Plugin.Entitlements.IProvisionEntitlementsPipeline
  Plugin.Entitlements.ProvisionEntitlementsBlock
  Plugin.GiftCards.ProvisionGiftCardEntitlementsBlock
  Plugin.DigitalItems.ProvisionInstallationEntitlementsBlock
  Plugin.DigitalItems.ProvisionDigitalProductEntitlementsBlock
  Plugin.DigitalItems.ProvisionWarrantyEntitlementsBlock

For orders to receive entitlements, both the AvailabilityAlwaysPolicy and a tag from their respective digital item type must be present, this includes the gift card entitlements, which in previous areas didn't validate against the AvailabilityAlwaysPolicy.

Once the Entitlement entity has been created it is then associated to the order as an Entity Reference.

If the order was placed by a registered customer, the entitlement will also be associated to the order as an Entity Reference.

Entitlement Rules - Standalone Sellable Item

Summary: Both the AvailabilityAlwaysPolicy and a digital item type tag will need to be associated to the sellable item to generate the respective entitlement and create an entity reference to it on the order.

Entitlement Rules - Sellable Items with Variants

Summary: The AvailabilityAlwaysPolicy must be present on the parent sellable item, while a digital item type tag will need to be associated to the variant to generate the respective entitlement and create an entity reference to it on the order.

* includes tags inherited from parent sellable item

Entitlement Rules - Customer Entitlements

Summary: Piggybacking off of the base entitlement rules above, if an entitlement has been created for an order and the customer was registered at the time the order was placed, the respective digital item type entitlement will be added to the Customer commerce entity.

Appendix

Terminology Definitions

  • Digital item tags: Tags configured in DigitalItemTagsPolicy.TagList.
  • Digital type tags: Any of the digital product tags, installation tags, or warranty tags.
  • Gift card tags: Tags configured in GiftCardTagsPolicy.TagList.
  • Digital product tags: Tags configured in KnownEntitlementsTags.DigitalProductTags.
  • Installation tags: Tags configured in KnownEntitlementsTags.InstallationTags.
  • Warranty tags: Tags configured in KnownEntitlementsTags.WarrantyTags.

Summary

We have covered how digital sellable items are treated throughout the Sitecore Commerce Engine. These are not limitations of the platform, as you are free to customise the functionality to your specific business requirements, but should be taken into consideration when evaluating business requirements and performing a gap analysis against platform functionality.

Sitecore Experience Commerce: Working with Digital Sellable Items – Part 1

In this article, we will review the difference between physical and digital sellable items, while focusing on the lesser documented digital sellable items. We will also review how to configure digital sellable items in the business manager.

For a more technical details behind the implementation of digital sellable items, see Working with Digital Sellable Items - Part 2.

Sellable Item Classifications

Sellable items can either represent physical or digital items. The following high-level overviews detail some of the discerning factors between the two classifications of sellable items.

Physical Sellable Items

Physical sellable items are any tangible product that a customer can purchase and have delivered. This is the default product type when creating sellable items via the Merchandising Manager in Sitecore Commerce.

Inventory

With physical items, inventory is required to track stock levels and prevent overselling.

Fulfillment Option Types

When purchasing physical items, during the checkout delivery step, a customer typically enters a delivery address of where the items will be delivered to, or selects a 'click and collect' style fulfillment option type where they can pick up the items from.

The SXA Storefront provides the common 'deliver to address' (physical) fulfillment option type.

Digital Sellable Items

Digital sellable items represent non-tangible products that a customer can purchase that entitle the customer to a form of product or service. Examples of digital sellable items include services, such as installation, warranties, subscriptions, digital downloads, online access, and digital currency (gift cards).

Inventory

As digital sellable items are non-tangible, they do not require inventory information to be associated to them. This can also be thought of as having perpetual inventory, therefore always being available for purchase.

Fulfillment Option Types

For digital sellable item purchases, the SXA Storefront provides a digital delivery sample implementation intended for digital gift card purchases, consisting of a recipient email and custom message, so the digital gift card can be personalised and delivered via email to the intended recipient.

Alternately, for other digital item fulfillment option types, custom implementations would be required to handle these scenarios, based on the client's requirements. Some ideas of requirements are as follows:

  • Services: A custom form, potentially with a third party integration, to create a booking system.
  • Warranties: A custom form to register the sellable item that the warranty was purchased for.
  • Subscriptions: A custom form to register the subscription's recipient details.
  • Digital Downloads: A custom form containing requesting the email address to send the digital download link to, or perhaps the user account functionality is customised to provide access to digital downloads.
  • Online access: Similar to digital downloads, the user may be given access to content via their logged in account, which may not require additional details to be taken during the Delivery step, however may require the user to make the purchase via a registered account.

Cart Lines

Digital sellable items are also separated into their own line items, e.g. when adding a digital sellable item with quantity of 2 this will create 2 cart lines with a quantity of 1, rather than 1 cart line with a quantity of 2. This provides the customer with the ability to input unique delivery information for each item during the checkout delivery step.

Entitlements

Another differentiating factor of digital sellable items are entitlements, which are registered to the order and customer account (where users are registered at the time of purchase), so that customer service representatives can view the current state of each entitlement.

Entitlements provides a basic implementation and a great starting point for customisation to meet business requirements.

Order Summary with entitlements
Customer Summary with entitements

Configuring Digital Sellable Items

Digital sellable items are determined by applying the appropriate Digital Sellable Item Tags on the sellable item and its variants. Let's review this together.

In the Business Tools,

  1. Navigate to the Merchandising Manager
  2. Locate an existing new sellable item or create a new sellable item and navigate to the Sellable Item page view
  3. Follow either Configuring Standalone Sellable Items or Configuring Sellable Item With Variants
  4. Ensure the sellable item has a valid list price and/or price card with an active price snapshot, so that the sellable item is purchasable
  5. Publish the sellable item

Configuring Standalone Sellable Items

  1. Add the appropriate tag that represents a Digital Item.
  2. Add the appropriate tag that represents the desired type of Digital Item Type.
    This will likely be the same tag as the Digital Item, therefore not required, however it is good to validate that the tags match, especially if custom tags have been assigned to the digital item and digital item type tag policies.
  3. Add the tag "entitlement".
Sellable item tags

Configuring Sellable Items With Variants

  1. On the sellable item, add the appropriate tag that represents a Digital Item.
  2. On each variant:
    1. Add the appropriate tag that represents the desired type of Digital Item Type.
    2. Add the tag "entitlement".

Note: Technically, the variants inherit tags from the parent sellable item only if no tags have been specified on the variant. Configuring any tags on a variant will remove the inherited tags from the variant, therefore these instructions specify the fool-proof solution.

Sellable item tags
Variant tags

Digital Sellable Item Tags

The following tables contain the default tags that are used to classify sellable items as digital and their digital item types.

Note: Tags for digital sellable items are not treated as case-sensitive.

Digital Item Tags

ClassificationDefault Tags
Digital Itementitlement
service
installation
subscription
digitalsubscription
warranty
onlinetraining
onlinelearning
giftcards

Digital Item Type Tags

TypeDefault Tags
Virtual Gift Cardgiftcards
Digital ProductOnlineTraining
OnlineLearning
Subscription
DigitalSubscription
Warranty Warranty
InstallationInstallation
Service

Working with the BizFx SDK: Preparing the Base Solution for Customisation

In this article, we will look at preparing the BizFx project for customisation, by first aligning the default configuration of the SDK with the configuration that was deployed with the Sitecore Commerce installation, and then reviewing how to build and deploy solution.

Fair warning: I am not an expert in Angular, however the information provided is enough for getting started and performing the bare minimum to align the BizFx SDK for custom solutions.

Creating the BizFx Development Solution

The Sitecore Commerce On Premise package contains the BizFx SDK and the speak files that will be required for the new project.

Extract the contents of the Sitecore.BizFX.SDK.*.*.*.zip into your desired folder location, e.g. C:\projects\, and copy the speak-ng-bcl-*.*.*.tgz and speak-styling-*.*.*-r*****.tgz files into the same folder as the SDK.

The BizFx SDK does come with a README.md file containing some general instructions on preparing the solution for building, however we will highlight the main aspects of these instructions and cover some addition steps for local and production deployments.

In src\assets\config.json we need to copy the values from our local BizFx installation, located by default at <web root>\<BizFx>\assets\config.json, so that when we deploy our new version the configuration isn't corrupted. You'll notice the values that need to be updated are named 'PlaceholderFor<context>'.

{
  "EnvironmentName": "HabitatAuthoring",
  "EngineUri": "PlaceholderForAuthoringUrl",
  "IdentityServerUri": "PlaceholderForIdentityServerUrl",
  "BizFxUri": "PlaceholderForBizFxUrl",
  "Language": "PlaceholderForDefaultLanguage",
  "Currency": "PlaceholderForDefaultCurrency",
  "ShopName": "PlaceholderForDefaultShopName",
  "LanguageCookieName": "selectedLanguage",
  "EnvironmentCookieName": "selectedEnvironment",
  "AutoCompleteTimeout_ms": 300
}

The other value that will need to be updated for projects will be the EnvironmentName, which is used to select the default environment in BizFx.

It is recommended that the LanguageCookieName and EnvironmentCookieName properties remain as their default value as they may only need to be changed for advanced customisations. We will not cover modifying these properties in this article.

Prerequisites for Building

Assuming node installed already, from the BizFx solution folder, open your preferred CLI tool and run the following commands:-

npm config set @speak:registry=https://sitecore.myget.org/F/sc-npm-packages/npm/
npm config set @sitecore:registry=https://sitecore.myget.org/F/sc-npm-packages/npm/
​​​​​​​npm install speak-ng-bcl-0.8.0.tgz
npm install speak-styling-0.9.0-r00078.tgz
npm install @sitecore/bizfx
npm install

Building and Deploying the BizFx Solution

For building the BizFx Angular application, the ng build command will compile into an output folder named dist, defaulting to the workspace folder. Within the dist folder, the sdk will be the equivalent of the <BizFx> website folder in the web root.

For production builds execute the ng build --prod command, which optimises the compiled solution for production deployments.

For more information about the Angular commands see https://angular.io/cli.

To deploy the BizFx solution, copy the contents of the dist/sdk into the <web root>\<BizFx> folder.

Building and Deploying via Gulp

For building and deploying the BizFx solution, I use a gulp script to wrap the angular commands. See the Source Code link at the end of the article to download the script.

If you haven't installed gulp, run the following command:-

​​​​​​​npm install gulp

Running the default gulp command will build the solution, clean out the BizFx folder in the web root and the deploy the solution to the BizFx folder.

As the gulp tasks will be performing operations on system restricted folders, make sure you run the gulp command under Administrator privileges.

Source Code: Ajsuth.BizFx.DeploymentScripts

Sitecore Experience Commerce: Accessing the GetRawEntity API

In this article, we will take a look at why the GetRawEntity api returns a 404 Not Found response for the default admin user in Sitecore Commerce.

Note: The GetRawEntity API is intended for troubleshooting and validation purposes and would be utilised by devops and developer users.

Reviewing the commerce logs we find that the QA role in not a role in the current request.

00064 22:41:06 ERROR CtxMsg.Error.QARoleNotFound: Text=QA is not a role in the current request.
00064 22:41:06 ERROR PipelineAbort:QA is not a role in the current request.

We can resolve this by updating the role memberships assigned the admin user, or any desired user.

  1. In Sitecore, go to the User Manager
  2. Select and edit the desired user
  3. In the Edit User modal,
    1. Select the MEMBER OF tab and edit the roles.
    2. Locate the sitecore\QA role and add it to the selected roles.

Note: If the user has already has received a token from Identity Server, a new token will need to be issued to receive the new role.

Sitecore Experience Commerce: Enabling Disassociate, Edit, and Transfer Inventory Actions for Published Sellable Items and Variants

In this article, we will look at how we can enable the Disassociate Sellable Item from Inventory Set, Edit Sellable Item Inventory and Transfer Inventory actions when viewing sellable item and variant entity views in BizFx.

For entities that have been configured to utilise entity versioning (catalogs, categories, and sellable items), via the VersioningPolicy in the VersioningPolicySet, all actions are disabled by default when the entity has been published.

The actions that are enabled are due to those actions being registered in the EntityVersionsActionsPolicy in the VersioningPolicySet, under the AllowedActions property.

There is no need to restrict the Inventory Sets actions in the Sellable Item and Variant entity views as they can be executed from within the Inventory Manager.

Note: Inventory association and disassociation actions apply to all entity versions of the sellable items as inventory records don't have strong ties to specific entity versions as inventory is not content.

To enable the Inventory Sets actions, simply add their action names to the AllowedActions, and deploy and bootstrap.

{
    "$type": "Sitecore.Commerce.Plugin.EntityVersions.EntityVersionsActionsPolicy, Sitecore.Commerce.Plugin.EntityVersions",
    "AllowedActions": {
    "$type": "System.Collections.Generic.List`1[[System.String, mscorlib]], mscorlib",
    "$values": [
            "AddEntityVersion",
            "AddCatalog",
            "DeleteCatalog",
            "AddCategory",
            "DeleteCategory",
            "AddSellableItem",
            "DeleteSellableItem",
            "AddBundle",
            "AssociateCategoryToCategoryOrCatalog",
            "AssociateSellableItemToCatalog",
            "AssociateSellableItemToCategory",
            "DisassociateItem",
            "MakePurchasable",
            "DisassociateSellableItemFromInventorySet",
            "EditSellableItemInventory",
            "TransferInventory"
        ]
    }
}

These actions can now be performed, regardless of whether or not the entity has been published.

Sitecore Experience Commerce: Improved Serilog Configurations

In this article, we will look at a few ways to configure the Serilog Logging to improve the ability to troubleshoot via logs.

Introduction

The LoggerConfiguration is defined in Startup.cs in the Commerce Engine project and will be created if the Logging.SerilogLoggingEnabled setting in the config.json file is true. The configuration is as follows:

Log.Logger = new LoggerConfiguration()
				.ReadFrom.Configuration(this.Configuration)
				.Enrich.FromLogContext()
				.Enrich.With(new ScLogEnricher())
				.WriteTo.Async(
					a => a.File(
						$@"{Path.Combine(this._hostEnv.WebRootPath, "logs")}\SCF.{DateTimeOffset.UtcNow:yyyyMMdd}.log.{this._nodeInstanceId}.txt",
						this.GetSerilogLogLevel(),
						"{ThreadId:D5} {Timestamp:HH:mm:ss} {ScLevel} {Message}{NewLine}{Exception}",
						fileSizeLimitBytes: fileSize,
						rollOnFileSizeLimit: true),
					bufferSize: 500)
				.CreateLogger();

With a little investigation into open source Serilog.Sinks.File repo, and an understanding of the Commerce Engine configuration, we can look to improve the logging configuration.

Log File Enhancements

Updating the Log File Name with Local Timezone

When the Commerce Engine initialises, it creates a log file with the the naming convention of SCF.<date>.log.<node instance id>.txt, e.g. SCF.20190902.log.03d49b837f214f55b815ee7adbba5ec.txt.

By default, Serilog uses the DateTime.Now in the outputTemplate, however the Commerce Engine configures the path property with a UTC timestamp. This means that if the date applied to the filename is not a true indication of the system date.

To resolve this, we can simply update the path property to use DateTimeOffset.Now.

Creating a New Log File Each Day

Creating a new log file each day ensures that all log entries for any given log file will pertain to a single day. No more do we have to review the rolling logs spanning multiple days, with a fine-tooth comb, identifying how many cycles of 24 hour time have passed to determine the date of log entries.

We can achieve this by setting the rollingInterval argument to RollingInterval.Day. We will also need to configure an additional period to the path parameter where the extension is specified {this._nodeInstanceId}..txt.

a => a.File(
	 $@"{Path.Combine(this._hostEnv.WebRootPath, "logs")}\SCF.{DateTimeOffset.UtcNow:yyyyMMdd}.log.{this._nodeInstanceId}..txt",
	 this.GetSerilogLogLevel(),
	 "{ThreadId:D5} {Timestamp:HH:mm:ss} {ScLevel} {Message}{NewLine}{Exception}",
	 fileSizeLimitBytes: fileSize,
	 rollOnFileSizeLimit: true,
	 rollingInterval: RollingInterval.Day)

By changing the rollingInterval, Serilog will append a date/time stamp to the file name, prior to the rolling log index, based on the specificity of the interval. For example, setting the rollingInterval to Day will append the date format yyyyMMdd, whereas setting it to the minute will append the date/time format of yyyyMMddHHmm.

This date/time format appended by Serilog when using rolling intervals is hard-coded and cannot be configured.

The additional period will separate the node instance id from the date, and with the change to the rollingInterval parameter, the date will be listed twice in the filename, e.g. SCF.20190902.log.03d49b837f214f55b815ee7adbba5ec1.20190902.txt.

The difference between the two occurences is the first instance is a constant value while the second is dynamic. When specifying the date format in the filename from the previous step this is a constant value that is set during the initialisation of the Commerce Engine, whereas the second date format instance is part of the Serilog library and during the creation of the daily logs will update this value, e.g.

  • SCF.20190902.log.03d49b837f214f55b815ee7adbba5ec1.20190902.txt
  • SCF.20190902.log.03d49b837f214f55b815ee7adbba5ec1.20190903.txt
  • SCF.20190902.log.03d49b837f214f55b815ee7adbba5ec1.20190904.txt

Removing the date from the beginning of the filename will still allow the files to be sorted by filename in descending order while the Commerce Engine has only been initiliased once, however with deployments and manual resets a new guid will be created to represent the node instance id.

Instead, sorting by date modified will provide an accurate timeline, however this won't separate the NodeConfiguration files from the log files as per sorting by filename.

Adding the Date to the outputTemplate Timestamp

If you are a fan of rolling logs spanning multiple days, then having the date specified in the timestamp will be beneficial.

To achieve this, simply update the outputTemplate parameter's Timestamp to include the date in the desired format. e.g. {Timestamp:dd/MM/yy HH:mm:ss}.

00027 30/08/19 11:28:11 INFO Executing action method "Sitecore.Commerce.Plugin.Carts.CartsController.Get (Sitecore.Commerce.Plugin.Carts)" with arguments (["Default4c29a122-a190-44f3-ab30-baa79629155dStorefront"]) - ModelState is Valid

Summary

We looked at some of the most useful configuration enhancments to improve our ability to troubleshoot the Commerce Engine via log files. While there are potentially other configurations to further improve logging, Creating a New Log File Each Day appears to have the most benefits.

Sitecore Experience Commerce: Methods for Logging and Command Messaging

In this article, we will look at the APIs available for logging to the logging framework and applying command messages to the CommerceContext.

The reason for grouping these two subjects together is due to seeing a lot of confusion around these areas when reviewing developers' code in the field; there is some overlap between them, which is often overlooked.

In Sitecore Commerce, logging is based on Microsoft.Extensions.Logging, and the Sitecore Commerce Engine SDK is setup to utilise the SeriLog diagnostic library for logging.

The CommandMessages are flushed to calling CommerceCommands via the completion of the CommandActivity and are included in the response object of CommandsController APIs.

Logging and command messaging occurs within methods of the CommercePipelineExecutionContext and the CommerceContext.

CommercePipelineExecutionContext

LogInfoIf

public void LogInfoIf(bool conditionResult, string info);

Intuitive enough, the LogInfoIf method will log an Information level entry, info, if the conditionResult is met.

Abort

public override void Abort(string reason, object data);

The Abort method will abort the pipeline and will create an Error level log entry if the reason message doesn't contain the magic string "Ok|".

It's also worth noting that this method is intended to abort the executing pipeline first and foremost, and the log entry is secondary. It is not intended solely for the purpose of logging.

Note: The data object would normally return the current CommercePipelineExecutionContext.

CommerceContext

AddDataMessage

public virtual void AddDataMessage(string messageType, string dataMessage);

The AddDataMessage will add the dataMessage to the command messages. It will also add the dataMessage to the logger at the Information level.

Tip: Avoid setting Debug level messages to avoid spamming your local development logs, which are defaulted to the Information level.

AddMessage

public virtual void AddMessage(CommandMessage message);

AddMessage will add a CommandMessage to the command messages.

The message will not invoke the logger.

AddMessage (Alternate)

public virtual Task<string> AddMessage(string code, string commerceTermKey, object[] args, string defaultMessage = null);

The overloaded AddMessage method's logging behaviour is as follows:

  • Message codes of ValidationError or Warning will add a Warning message to the Logger.
  • All exception types will be logged using the LogException method. See LogException for more details.
  • An Error will also be logged as an Error.

For the CommandMessages, the localised message will attempted to be retrieved from Sitecore, using the commerce term key provided, and further formatted/interpolated with the args provided.

LogException

public virtual void LogException(string caller, Exception ex);

The LogException method will log an exception as an error with the exception message and stack trace details.

LogExceptionAndMessage

public virtual void LogExceptionAndMessage(string caller, Exception ex);

The LogExceptionAndMessage logs the exception as per the LogException method, however the exception message will be added to the CommandMessages at the Error level in addition.

Logger

public ILogger Logger { get; }

The Logger exposes the can be utilised to add the standard log-level entries to it, being:

  • LogDebug
  • LogInformation
  • LogWarning
  • LogError
  • LogCritical