For example, you may want to manage information about the brands that are sold on your site and use that information to organize how product information is displayed.

This is part 1 in the series and will show you how to add an new entity named “Brand” to the system and allow business users to create, update and delete brands. Adding a new entity is really simple, this post focuses more on how to manage this entity from within the business tools.

For instance, you may be selling sports caps and want to store information about the brands you sell such as 47 brand or New Era. The brands will have their own descriptions, images and be related to the SellableItems. Eventually you may use this information to create a “Brand” landing page on the front end of the site (later posts will show this).

Most of the time it doesn’t make sense to store information regarding your SellableItems by duplicating that information on each SellableItem. The better approach is to create a new entity that stores this information and relate it to the SellableItem (basic relational database stuff really).

The following is covered in this post :-

  1. Add new entity to the engine
  2. Add the UI components to the Business Tools to allow users to manage new Brands.

Future posts will build on this foundation including how to add the new brand entity as content within the CMS.

The end result will look like the image at the start of the post.

Step 1 – Create new plugin

  • In the Commerce Engine SDK create a new plugin and reference it from the engine
  • Add the following code to create the new entity and the pipelines

In folder “Entities” create…

using System;
using System.Collections.Generic;
using Sitecore.Commerce.Core;
namespace Sitecore.Services.Examples.Entities.Entities
{
public class Brand : CommerceEntity
{
public Brand()
{
Components = new List<Component>();
DateCreated = DateTime.UtcNow;
DateUpdated = DateCreated;
}
public Brand(string id) : this()
{
Id = id;
}
public string Description { get; set; }
}
}

view raw
Brand.cs
hosted with ❤ by GitHub

In folder named “Pipelines\Blocks” create…

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Sitecore.Commerce.Core;
using Sitecore.Commerce.EntityViews;
using Sitecore.Framework.Pipelines;
using Sitecore.Services.Examples.Entities.Entities;
namespace Sitecore.Services.Examples.Entities.Pipelines.Blocks
{
[PipelineDisplayName("DoActionAddBrandBlock")]
public class DoActionAddBrandBlock : PipelineBlock<EntityView, EntityView, CommercePipelineExecutionContext>
{
private readonly CommerceCommander _commerceCommander;
public DoActionAddBrandBlock(CommerceCommander commerceCommander)
{
_commerceCommander = commerceCommander;
}
public override async Task<EntityView> Run(EntityView entityView, CommercePipelineExecutionContext context)
{
var action = entityView?.Action;
if (string.IsNullOrEmpty(action) || !entityView.Action.Equals("AddBrand", StringComparison.OrdinalIgnoreCase))
{
return await Task.FromResult(entityView);
}
var brand = new Brand();
var displayNameProperty = entityView.Properties.FirstOrDefault(p => p.Name.Equals("DisplayName", StringComparison.OrdinalIgnoreCase));
if(displayNameProperty?.Value != null)
{
brand.DisplayName = displayNameProperty.Value.ToString();
}
var nameProperty = entityView.Properties.FirstOrDefault(p => p.Name.Equals("Name", StringComparison.OrdinalIgnoreCase));
if (nameProperty?.Value != null)
{
brand.Name = nameProperty.Value.ToString();
brand.Id = CommerceEntity.IdPrefix<Brand>() + brand.Name;
}
var descriptionProperty = entityView.Properties.FirstOrDefault(p => p.Name.Equals("Description", StringComparison.OrdinalIgnoreCase));
if (descriptionProperty?.Value != null)
{
brand.Description = descriptionProperty.Value.ToString();
}
//TODO : Create a command for persisting brand
var result = await _commerceCommander.Pipeline<IPersistEntityPipeline>().Run(new PersistEntityArgument(brand), context);
if (result.Entity != null && string.IsNullOrEmpty(result.Entity.Id) != true)
{
var list = new List<string>
{
(result.Entity as Brand)?.Id
};
await _commerceCommander.Pipeline<IAddListEntitiesPipeline>().Run(new ListEntitiesArgument(list, CommerceEntity.ListName<Brand>()), context);
}
return await Task.FromResult(entityView);
}
}
}

using System;
using System.Threading.Tasks;
using Sitecore.Commerce.Core;
using Sitecore.Commerce.EntityViews;
using Sitecore.Framework.Pipelines;
namespace Sitecore.Services.Examples.Entities.Pipelines.Blocks
{
[PipelineDisplayName("DoActionRemoveBrandBlock")]
public class DoActionRemoveBrandBlock : PipelineBlock<EntityView, EntityView, CommercePipelineExecutionContext>
{
private readonly CommerceCommander _commerceCommander;
public DoActionRemoveBrandBlock(CommerceCommander commerceCommander)
{
_commerceCommander = commerceCommander;
}
public override async Task<EntityView> Run(EntityView entityView, CommercePipelineExecutionContext context)
{
var action = entityView?.Action;
if (string.IsNullOrEmpty(action) || !entityView.Action.Equals("RemoveBrand", StringComparison.OrdinalIgnoreCase))
{
return await Task.FromResult(entityView);
}
else
{
var id = (string.IsNullOrEmpty(entityView.EntityId) ? entityView.ItemId : entityView.EntityId);
if (!string.IsNullOrEmpty(id))
{
//TODO : Add delete command, dont call pipeline directly
await _commerceCommander.Pipeline<IDeleteEntityPipeline>().Run(new DeleteEntityArgument(id), context);
return await Task.FromResult(entityView);
}
else
{
var validationError = context.GetPolicy<KnownResultCodes>().ValidationError;
var objArray = new object[] { "EntityId" };
await context.CommerceContext.AddMessage(validationError, "InvalidOrMissingPropertyValue", objArray, "Invalid or missing value for property 'EntityId'.");
return await Task.FromResult(entityView);
}
}
}
}
}

using System;
using System.Linq;
using System.Threading.Tasks;
using Sitecore.Commerce.Core;
using Sitecore.Commerce.EntityViews;
using Sitecore.Framework.Conditions;
using Sitecore.Framework.Pipelines;
namespace Sitecore.Services.Examples.Entities.Pipelines.Blocks
{
[PipelineDisplayName("EntityViewBlock")]
public class EntityViewBlock : PipelineBlock<EntityView, EntityView, CommercePipelineExecutionContext>
{
public override async Task<EntityView> Run(EntityView entityView, CommercePipelineExecutionContext context)
{
Condition.Requires(entityView).IsNotNull($"{Name}: The argument cannot be null.");
var entityViewArgument = context.CommerceContext.GetObjects<EntityViewArgument>().FirstOrDefault();
var viewName = entityViewArgument?.ViewName;
if (string.IsNullOrEmpty(viewName) || !entityViewArgument.ViewName.Equals("BrandsDashboard", StringComparison.OrdinalIgnoreCase))
{
return await Task.FromResult(entityView);
}
return await Task.FromResult(entityView);
}
}
}

view raw
EntityViewBlock.cs
hosted with ❤ by GitHub

using System;
using System.Linq;
using System.Threading.Tasks;
using Sitecore.Commerce.Core;
using Sitecore.Commerce.EntityViews;
using Sitecore.Framework.Conditions;
using Sitecore.Framework.Pipelines;
using Sitecore.Services.Examples.Entities.Entities;
namespace Sitecore.Services.Examples.Entities.Pipelines.Blocks
{
[PipelineDisplayName("GetBrandDetailsViewBlock")]
public class GetBrandDetailsViewBlock : PipelineBlock<EntityView, EntityView, CommercePipelineExecutionContext>
{
public override Task<EntityView> Run(EntityView entityView, CommercePipelineExecutionContext context)
{
Condition.Requires(entityView).IsNotNull($"{Name}: The argument cannot be null");
var viewArg = context.CommerceContext.GetObject<EntityViewArgument>();
var entityViewArgument = viewArg;
var viewName = entityViewArgument?.ViewName;
if (string.IsNullOrEmpty(viewName) || !viewArg.ViewName.Equals("Details", StringComparison.OrdinalIgnoreCase) && !viewArg.ViewName.Equals("Master", StringComparison.OrdinalIgnoreCase))
{
return Task.FromResult(entityView);
}
if (!viewArg.ForAction.Equals("AddBrand", StringComparison.OrdinalIgnoreCase) || !viewArg.ViewName.Equals("Details", StringComparison.OrdinalIgnoreCase))
{
var isEdit = viewArg.ForAction.Equals("EditBrand", StringComparison.OrdinalIgnoreCase);
if (!(viewArg.Entity is Brand) || !isEdit && !string.IsNullOrEmpty(viewArg.ForAction))
{
return Task.FromResult(entityView);
}
var brand = (Brand) viewArg.Entity;
if (!viewArg.ViewName.Equals("Master", StringComparison.OrdinalIgnoreCase))
{
return Task.FromResult(entityView);
}
var properties = entityView.Properties;
var viewProperty = properties.FirstOrDefault(p => p.Name.Equals("Name", StringComparison.OrdinalIgnoreCase));
if (viewProperty != null)
{
var viewProperty1 = viewProperty;
viewProperty1.RawValue = brand.Name;
var viewProperties = entityView.Properties;
var rawValue = viewProperties.FirstOrDefault(p => p.Name.Equals("DisplayName", StringComparison.OrdinalIgnoreCase));
if (rawValue == null)
{
var properties1 = entityView.Properties;
var viewProperty2 = new ViewProperty
{
Name = "DisplayName",
RawValue = viewProperty.RawValue
};
properties1.Add(viewProperty2);
}
else
{
rawValue.RawValue = viewProperty.RawValue;
}
}
var detailsView = new EntityView
{
EntityId = entityView.EntityId,
Name = "Details"
};
entityView.ChildViews.Add(detailsView);
//Save
}
else
{
var nameProperty = new ViewProperty
{
Name = "Name",
UiType = "string"
};
entityView.Properties.Add(nameProperty);
var displayNameProperty = new ViewProperty
{
Name = "Displayname",
UiType = "string"
};
entityView.Properties.Add(displayNameProperty);
var descriptionProperty = new ViewProperty
{
Name = "Description",
UiType = "string"
};
entityView.Properties.Add(descriptionProperty);
}
return Task.FromResult(entityView);
}
}
}

using System.Threading.Tasks;
using Sitecore.Commerce.Core;
using Sitecore.Commerce.EntityViews;
using Sitecore.Framework.Conditions;
using Sitecore.Framework.Pipelines;
namespace Sitecore.Services.Examples.Entities.Pipelines.Blocks
{
[PipelineDisplayName("GetBrandsNavigationView")]
public class GetBrandsNavigationViewBlock : PipelineBlock<EntityView, EntityView, CommercePipelineExecutionContext>
{
public override Task<EntityView> Run(EntityView entityView, CommercePipelineExecutionContext context)
{
Condition.Requires(entityView).IsNotNull($"{Name}: The argument cannot be null");
entityView.ChildViews.Add(new EntityView()
{
Name = "BrandsDashboard",//TODO : get from policy e.g. context.GetPolicy<KnownRelationshipViewsPolicy>().RelationshipsDashboard,
ItemId = "BrandsDashboard",//TODO : get from policy e.g. context.GetPolicy<KnownRelationshipViewsPolicy>().RelationshipsDashboard,
DisplayName = "Brands",
Icon = "link",
DisplayRank = 9
});
return Task.FromResult(entityView);
}
}
}

using System;
using System.Linq;
using System.Threading.Tasks;
using Sitecore.Commerce.Core;
using Sitecore.Commerce.EntityViews;
using Sitecore.Framework.Conditions;
using Sitecore.Framework.Pipelines;
using Sitecore.Services.Examples.Entities.Entities;
namespace Sitecore.Services.Examples.Entities.Pipelines.Blocks
{
[PipelineDisplayName("GetBrandsViewBlock")]
public class GetBrandsViewBlock : PipelineBlock<EntityView, EntityView, CommercePipelineExecutionContext>
{
private readonly IFindEntitiesInListPipeline _findEntitiesInListPipeline;
public GetBrandsViewBlock(IFindEntitiesInListPipeline findEntitiesInListPipeline)
{
_findEntitiesInListPipeline = findEntitiesInListPipeline;
}
public override async Task<EntityView> Run(EntityView entityView, CommercePipelineExecutionContext context)
{
Condition.Requires(entityView).IsNotNull($"{Name}: The argument cannot be null.");
var entityViewArgument = context.CommerceContext.GetObjects<EntityViewArgument>().FirstOrDefault();
var viewName = entityViewArgument?.ViewName;
if (string.IsNullOrEmpty(viewName) || !entityViewArgument.ViewName.Equals("Brands", StringComparison.OrdinalIgnoreCase) && !entityViewArgument.ViewName.Equals("BrandsDashboard", StringComparison.OrdinalIgnoreCase))
{
return await Task.FromResult(entityView);
}
if (!entityViewArgument.ViewName.Equals("BrandsDashboard", StringComparison.OrdinalIgnoreCase))
{
await Task.FromResult(entityView);
}
var brandsView = new EntityView()
{
EntityId = string.Empty,
ItemId = string.Empty,
Name = "All Brands",
DisplayRank = 100
};
entityView.ChildViews.Add(brandsView);
brandsView.UiHint = "Table";
var brandsResult = await _findEntitiesInListPipeline.Run(new FindEntitiesInListArgument(typeof(Brand), CommerceEntity.ListName<Brand>(), 0, 2147483647), context);
foreach (var brandEntity in brandsResult.List.Items)
{
var brand = brandEntity as Brand;
var summaryEntityView = new EntityView()
{
EntityId = brand.Id,
ItemId = brand.Id,
DisplayName = brand.DisplayName,
Name = "Summary"
};
var viewProperties = summaryEntityView.Properties;
var viewProperty = new ViewProperty()
{
Name = "ItemId",
RawValue = brand.Id,
IsHidden = true,
IsReadOnly = true
};
viewProperties.Add(viewProperty);
var displayNameProperty = new ViewProperty()
{
Name = "Name",
RawValue = brand.Name,
IsReadOnly = true,
UiType = "EntityLink"
};
viewProperties.Add(displayNameProperty);
var descriptionProperty = new ViewProperty()
{
Name = "Displayname",
RawValue = brand.DisplayName,
IsReadOnly = true,
UiType = "string"
};
viewProperties.Add(descriptionProperty);
var dateCreatedProperty = new ViewProperty()
{
Name = "DateCreated",
RawValue = brand.DateCreated,
IsReadOnly = true
};
viewProperties.Add(dateCreatedProperty);
var dateUpdated = new ViewProperty()
{
Name = "DateUpdated",
RawValue = brand.DateUpdated,
IsReadOnly = true
};
viewProperties.Add(dateUpdated);
brandsView.ChildViews.Add(summaryEntityView);
}
return await Task.FromResult(entityView);
}
}
}

using System;
using System.Linq;
using System.Threading.Tasks;
using Sitecore.Commerce.Core;
using Sitecore.Commerce.EntityViews;
using Sitecore.Framework.Pipelines;
using Sitecore.Services.Examples.Entities.Entities;
namespace Sitecore.Services.Examples.Entities.Pipelines.Blocks
{
[PipelineDisplayName("PopulateBrandsViewActionsBlock")]
public class PopulateBrandsViewActionsBlock : PipelineBlock<EntityView, EntityView, CommercePipelineExecutionContext>
{
private readonly IFindEntitiesInListPipeline _findEntitiesInListPipeline;
public PopulateBrandsViewActionsBlock(IFindEntitiesInListPipeline findEntitiesInListPipeline)
{
_findEntitiesInListPipeline = findEntitiesInListPipeline;
}
public override async Task<EntityView> Run(EntityView entityView, CommercePipelineExecutionContext context)
{
var name = entityView?.Name;
if (string.IsNullOrEmpty(name) || !entityView.Name.Equals("All Brands", StringComparison.OrdinalIgnoreCase))
{
return await Task.FromResult(entityView);
}
var findEntitiesInListArgument = await _findEntitiesInListPipeline.Run(new FindEntitiesInListArgument(typeof(Brand), CommerceEntity.ListName<Brand>(), 0, 9), context);
var policy = entityView.GetPolicy<ActionsPolicy>();
var actions = policy.Actions;
var entityActionView = new EntityActionView()
{
Name = "AddBrand",
DisplayName = "Add Brand",
Description = "Adds a Brand",
IsEnabled = true,
EntityView = "Details",
Icon = "add"
};
actions.Add(entityActionView);
var entityActionViews = policy.Actions;
var empty = new EntityActionView()
{
Name = "RemoveBrand",
DisplayName = "Remove Brand",
Description = "Removes a Brand"
};
var exists = findEntitiesInListArgument?.List.Items.Any() ?? false;
empty.IsEnabled = exists;
empty.EntityView = string.Empty;
empty.RequiresConfirmation = true;
empty.Icon = "delete";
entityActionViews.Add(empty);
return await Task.FromResult(entityView);
}
}
}

In the root of your project add…

using Sitecore.Services.Examples.Entities.Pipelines.Blocks;
namespace Sitecore.Services.Examples.Entities
{
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Commerce.Core;
using Framework.Configuration;
using Sitecore.Framework.Pipelines.Definitions.Extensions;
using Commerce.EntityViews;
using Commerce.Plugin.Catalog;
using Commerce.Plugin.BusinessUsers;
public class ConfigureSitecore : IConfigureSitecore
{
public void ConfigureServices(IServiceCollection services)
{
var assembly = Assembly.GetExecutingAssembly();
services.RegisterAllPipelineBlocks(assembly);
services.Sitecore().Pipelines(config => config
.ConfigurePipeline<IGetEntityViewPipeline>(c => c.Add<EntityViewBlock>().Before<GetSelectCatalogToAssociateItemDefinitionsViewBlock>()));
services.Sitecore().Pipelines(config => config
.ConfigurePipeline<IGetEntityViewPipeline>(c => c.Add<GetBrandsViewBlock>().Before<GetSelectCatalogToAssociateItemDefinitionsViewBlock>()));
services.Sitecore().Pipelines(config => config
.ConfigurePipeline<IGetEntityViewPipeline>(c => c.Add<GetBrandDetailsViewBlock>().Before<GetSelectCatalogToAssociateItemDefinitionsViewBlock>()));
services.Sitecore().Pipelines(config => config
.ConfigurePipeline<IDoActionPipeline>(c => c.Add<DoActionAddBrandBlock>()));
services.Sitecore().Pipelines(config => config
.ConfigurePipeline<IDoActionPipeline>(c => c.Add<DoActionRemoveBrandBlock>()));
services.Sitecore().Pipelines(config => config
.ConfigurePipeline<IBizFxNavigationPipeline>(c => c.Add<GetBrandsNavigationViewBlock>()));
services.Sitecore().Pipelines(config => config
.ConfigurePipeline<IPopulateEntityViewActionsPipeline>(c => c.Add<PopulateBrandsViewActionsBlock>()));
services.RegisterAllCommands(assembly);
}
}
}

view raw
ConfigureSitecore.cs
hosted with ❤ by GitHub

Your solutions should look like this…

Brands project.png

When you launch the business tools you should now be able to manage your brands within the UI.