allBlogsList

Creating a Customer Specific Discount Plugin - Sitecore Commerce Engine


You may have a use case where you need a customer service operative to set a discount for a specific customer by going into that customer profile and setting a specific percentage off for every purchase until it is updated or set to zero.

Step 1
Login to Sitecore and go to BizTools
We will create a views template in composer and assign it to all customer entity.
You may name your template the way you like, I named mine “CustomerDiscount” and later added a decimal field named “Discount” and assigned it to Customer Entity

Composer Image

Step 2
I created a commerce user and added a discount value to the Discount field under the created CustomerDiscount view.

Discount View

I proceeded to updating Commerce template in Sitecore content editor so that the composer template is synced(Only needed to do this once after creating or updating the template). 

Step 3
Under the Commerce Engine Solution for your site, create a new .NET Core project and name it as you wish. I named mine “Sitecore.Commerce.Plugin.CustomerSpecificDiscount”.
I am using Visual Studio 2017 and my .Net framework is 4.6.2
Step 4
Add Sitecore Nugget 
“Sitecore.Commerce.Core 2.2.29”
“Sitecore.Commerce.Plugin.Catalog 2.2.46”
“Sitecore.Commerce.Plugin.Customers 2.2.10”
“Sitecore.Commerce.Plugin.Views 2.2.29”

Step 5
Add a folder name Pipelines and under that folder, add a Blocks folder
Under the blocks folder, add a public class for customer discount adjustment and name it as you wish. I named mine “CustomerSpecificDiscountBlock” I also followed the naming convention and ended the name with Block.
Inherit from “ PipelineBlock<Cart, Cart, CommercePipelineExecutionContext>”
Implement the required Run method with its parameters.

In the run method, we need to ensure the cart parameter is not null
Check that the owner of the cart is logged in
If the owner is logged in, we need to check that it has a CustomerDiscount view. 
If it has, we need to check if it has reasonable value set for the discount. The value must be above zero and less than 100. 
If it has, we simply create an adjustment of type Discount and apply it to the cart.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Sitecore.Commerce.Core;
using Sitecore.Commerce.EntityViews;
using Sitecore.Commerce.EntityViews.Commands;
using Sitecore.Commerce.Plugin.Carts;
using Sitecore.Commerce.Plugin.Customers;
using Sitecore.Commerce.Plugin.Pricing;
using Sitecore.Framework.Conditions;
using Sitecore.Framework.Pipelines;

namespace Sitecore.Commerce.Plugin.CustomerSpecificDiscount.Pipelines.Blocks
{
    public class CustomerSpecificDiscountBlock : PipelineBlock<cart,>
    {
        private readonly GetEntityViewCommand _getEntityViewCommand;
        public CustomerSpecificDiscountBlock(GetEntityViewCommand getEntityViewCommand)
        {
            _getEntityViewCommand = getEntityViewCommand;
        }
        public override async Task Run(Cart arg, CommercePipelineExecutionContext context)
        {
            // if cart is null
            Condition.Requires(arg).IsNotNull($"{(object)(this.Name)}: Cart cannot be null.");

            // cartlines cannot be null
            Condition.Requires<ilist>(arg.Lines).IsNotNull<ilist>($"{(object)(this.Name)}: The cart's lines cannot be null");

            if (!arg.Lines.Any()) { return arg; }

            // get contact from Cart. If contact is null return. If contact is not registered, return. else get customer View and check for the child view with discount field
            var contactComponent = arg.GetComponent();

            if (!contactComponent.IsRegistered)
            {
                return arg;
            }

            // Get the customer master entity view
            var entityView = await _getEntityViewCommand.Process(context.CommerceContext, contactComponent.ShopperId, new int?(arg.EntityVersion), context.GetPolicy().Master, "", "");

            if (entityView != null && entityView.ChildViews.Any())
            {
                // if no discount field return arg
                var customerDiscountView = entityView.ChildViews.FirstOrDefault(x => x.Name == "CustomerDiscount") as EntityView;
                if (customerDiscountView == null)
                {
                    return arg;
                }

                // if discount field is blank, return arg
                var properties = customerDiscountView.Properties;
                var discountProperty = properties.FirstOrDefault(x => x.DisplayName == "Discount");
                if (discountProperty == null)
                {
                    return arg;
                }


                var discount = GetFirstDecimalFromString(discountProperty.Value);
                if (discount <= 0 || discount >= 100)
                {
                    return arg;
                }

                // if discount field is not blank and has a value, try get its decimal value.  if successful, add adjustment to cart.
                var disountAmount = GetFirstDecimalFromString(arg.Totals.SubTotal.ToString()) *
                                    (100.00m - discount);

                var currencyCode = context.CommerceContext.CurrentCurrency();
                if (!arg.Adjustments.Any(a =>
                    a.Name.Equals("CustomerDiscount", StringComparison.OrdinalIgnoreCase)))
                {
                    arg.Adjustments.Add(new CartLevelAwardedAdjustment
                    {
                        Name = "CustomerDiscount",
                        DisplayName = "CustomerDiscount",
                        Adjustment = new Money(currencyCode, -1 * disountAmount),
                        AdjustmentType = context.GetPolicy().Discount,
                        AwardingBlock = this.Name,
                        IsTaxable = false
                    });
                }
            }

            return arg;
        }

        /// 
        /// 
        /// 
        /// 
        /// 
        private decimal GetFirstDecimalFromString(string str)
        {
            if (string.IsNullOrEmpty(str)) return 0.00M;
            var decList = Regex.Split(str, @"[^0-9\.]+").Where(c => c != "." && c.Trim() != "").ToList();
            var decimalVal = decList.Any() ? decList.FirstOrDefault() : string.Empty;

            if (string.IsNullOrEmpty(decimalVal)) return 0.00M;
            decimal decimalResult = 0;
            decimal.TryParse(decimalVal, out decimalResult);
            return decimalResult;
        }
    }
}

</ilist</ilist</cart,>

Step 6
Under Sitecore.Commerce.Engine, add reference to your new plugin project and under the file “SitecoreServicesConfigurationExtensions.cs” add the line below under “ICalculateCartPipeline”
.Add<CustomerSpecificDiscountBlock>().Before<CalculateCartTotalsBlock>()
just after .Add<CalculateCartTotalsBlock>()

    public static class SitecoreServiceConfigurationExtensions
    {
        /// 
        /// The configure commerce pipelines.
        /// 
        /// 
        /// The sitecore services configuration.
        /// 
        /// 
        /// The .
        /// 
        public static ISitecoreServicesConfiguration ConfigureCommercePipelines(this ISitecoreServicesConfiguration services)
        {
            services.Pipelines(config => config
                .ConfigurePipeline(builder => builder
                    .Add().After())

                .ConfigurePipeline(builder => builder
                    .Add()
                    .Add()
                    .Add().After()
                    .Add()
                    .Add()
                    .Add())

               .ConfigurePipeline(builder => builder
                    .Add()
                    .Add()
                    .Add().After()
                    .Add()
                    .Add()
                    .Add()
                    .Add().Before()
                    .Add()
                    .Add())

                .ConfigurePipeline(builder =>
                    builder.Add().After()));

            return services;
        }
    }


The plugin is now ready to put to a test.

Add it to your solution and try it out.

You may down load the sample plugin from Github here: Github Link