Create a Campaign, Discount programmatically – Episerver Commerce

We had a scenario that customer wants to generate a coupon dynamically and use the coupon as credit for the user. The code as everything else in Episerver is quite straight forward:

The first step is to create a sales campaign:

        private SalesCampaign EnsureCampaignCreated()
        {
            var campaign = contentRepository
                .GetChildren<SalesCampaign>(SalesCampaignFolder.CampaignRoot)?
                .FirstOrDefault(a => a.Name == "Credit");
            
            if (campaign == null)
            {
                campaign = contentRepository
                    .GetDefault<SalesCampaign>(SalesCampaignFolder.CampaignRoot);
                campaign.Name = "Credit";
                campaign.Created = DateTime.UtcNow;
                campaign.IsActive = true;
                campaign.ValidFrom = DateTime.Today;
                campaign.ValidUntil = DateTime.Today.AddYears(1);
                var campaignContentRefrence = contentRepository.Save(campaign, SaveAction.Publish, AccessLevel.NoAccess);

                campaign = contentRepository.Get<SalesCampaign>(campaignContentRefrence);
            }

            return campaign;
        }

As you can see we just make sure the campaign is being created and our key in this stage is ‘Campaign Name’ which may be not good enough for some scenarios but for us is enough. Second I’m loading ALL campaigns and iterate through them to find a specific campaign. Again in our scenario, it will not that much effect but if the commerce you are dealing with has MANY campaigns you can use LoaderOptione as below:

Next step is to create a discount:

        private string CreateDiscountPromotion(ContentReference campaignLink, string orderNumber, Money couponAmount)
        {
            var catalougLink = siteSettingsService.StartPage.DefaultCatalogue;

            var couponCode = $"CREDIT-{Guid.NewGuid().ToString().Replace("-", "").Substring(6)}";

            var promotion = contentRepository.GetDefault<BuyQuantityGetItemDiscount>(campaignLink);
            promotion.IsActive = true;
            promotion.Name = $"CREDIT-{orderNumber}";
            promotion.Coupon.Code = couponCode;
            promotion.Condition.Items = new List<ContentReference>() { catalougLink };
            promotion.Condition.RequiredQuantity = 1;
            promotion.Condition.MatchRecursive = true;
            promotion.DiscountTarget.Items = new List<ContentReference>() { catalougLink };
            promotion.Discount.UseAmounts = true;
            promotion.Discount.Amounts = new List<Money>() { couponAmount };

            contentRepository.Save(promotion, SaveAction.Publish, AccessLevel.NoAccess);

            return couponCode;
        }

As you can see generating a discount is very straight forward. I used ‘BuyQuantityGetItemDiscount’ which fits for my purpose but you can use any out of the box or custom discount you want. Discount which I used ‘BuyQuantityGetItemDiscount’ require to define what catalog item can this discount can be applied. I’m including all current site catalog (I’m getting it from start page which is custom property) but you can use any other method (e.g. iterate through all catalogs and include all catalogs). There is a flag ‘MatchRecursive’ which is set to true, means all descendants of selected items will include in promotion.

So finally we need to call these method full code as bellow:

        private string CreateCreditVoucher(string orderNumber, Money couponAmount)
        {
            var campaign = EnsureCampaignCreated();

            return CreateDiscountPromotion(campaign.ContentLink, orderNumber, couponAmount);
        }

        private SalesCampaign EnsureCampaignCreated()
        {
            var campaign = contentRepository
                .GetChildren<SalesCampaign>(SalesCampaignFolder.CampaignRoot)?
                .FirstOrDefault(a => a.Name == "Credit");
            
            if (campaign == null)
            {
                campaign = contentRepository
                    .GetDefault<SalesCampaign>(SalesCampaignFolder.CampaignRoot);
                campaign.Name = "Credit";
                campaign.Created = DateTime.UtcNow;
                campaign.IsActive = true;
                campaign.ValidFrom = DateTime.Today;
                campaign.ValidUntil = DateTime.Today.AddYears(1);
                var campaignContentRefrence = contentRepository.Save(campaign, SaveAction.Publish, AccessLevel.NoAccess);

                campaign = contentRepository.Get<SalesCampaign>(campaignContentRefrence);
            }

            return campaign;
        }

        private string CreateDiscountPromotion(ContentReference campaignLink, string orderNumber, Money couponAmount)
        {
            var catalougLink = siteSettingsService.StartPage.DefaultCatalogue;

            var couponCode = $"CREDIT-{Guid.NewGuid().ToString().Replace("-", "").Substring(6)}";

            var promotion = contentRepository.GetDefault<BuyQuantityGetItemDiscount>(campaignLink);
            promotion.IsActive = true;
            promotion.Name = $"CREDIT-{orderNumber}";
            promotion.Coupon.Code = couponCode;
            promotion.Condition.Items = new List<ContentReference>() { catalougLink };
            promotion.Condition.RequiredQuantity = 1;
            promotion.Condition.MatchRecursive = true;
            promotion.DiscountTarget.Items = new List<ContentReference>() { catalougLink };
            promotion.Discount.UseAmounts = true;
            promotion.Discount.Amounts = new List<Money>() { couponAmount };
            
            contentRepository.Save(promotion, SaveAction.Publish, AccessLevel.NoAccess);

            return couponCode;
        }

Discount and promotion has far more features that I haven’t included, you can read more about it HERE

“Some” best practices I learned on commerce personalization

The concept of personalization is a wide range. I’m working on a project that needs personalization and started to learn. One cool thing I learn from “Foundation” project (Foundation is a very good sample of all Episerver offerings) is how to build a code to use Episerver Tracking Commerce. Again I’m learning and please advise me if I can improve any part of it 🙂

Please read through how to install tracking Nuget package from here

The next step is to create a separate project in your solution and add a commerce tracking package to the solution. Then add a class as below:

public interface ICommerceTrackingService
    {
        Task<TrackingResponseData> TrackOrder(HttpContextBase httpContext, IPurchaseOrder order);
    }

    public class CommerceTrackingService: ICommerceTrackingService
    {
        private readonly IContextModeResolver contextModeResolver;
        private readonly TrackingDataFactory trackingDataFactory;
        private readonly ITrackingService trackingService;
        private readonly ServiceAccessor<IContentRouteHelper> contentRouteHelperAccessor;

        public CommerceTrackingService(IContextModeResolver contextModeResolver,
            TrackingDataFactory trackingDataFactory,
            ITrackingService trackingService,
            ServiceAccessor<IContentRouteHelper> contentRouteHelperAccessor)
        {
            this.contextModeResolver = contextModeResolver;
            this.trackingDataFactory = trackingDataFactory;
            this.trackingService = trackingService;
            this.contentRouteHelperAccessor = contentRouteHelperAccessor;
        }

        public async Task<TrackingResponseData> TrackOrder(HttpContextBase httpContext, IPurchaseOrder order)
        {
            if (contextModeResolver.CurrentMode != ContextMode.Default)
            {
                return null;
            }

            var trackingData = trackingDataFactory.CreateOrderTrackingData(order, httpContext);
            return await trackingService.TrackAsync(trackingData, httpContext, contentRouteHelperAccessor().Content);
        }
    }

I put both interface and class in the same file to simplify the post but if you want you can separate them.

Next step is to change you StructureMap to scan new project (this is optional depending on your StructureMap structure)

            context.StructureMap().Configure(config =>
            {
                config.Scan(scan =>
                {
                    scan.TheCallingAssembly();
                    scan.Assembly("EPiServer.Sample.Commerce.Personalization");
                    scan.WithDefaultConventions();
                    scan.LookForRegistries();
                });
            });

The next step is to call the track order. We want to do that when the order is being placed. So after cart converted to purchase order you can call your TrackOrder function:

commerceTrackingService.TrackOrder(httpContext, purchaseOrder);

This will push all relevant data from order to tracking engine to help the recommendation engine to get a better understanding of the user behavior.

I recommend to checkout “Foundation” project that has a very comprehensive sample.

How to pass configuration to your custom editor in EPiServer

Recently I’m working on a project which has a requirement. We need focal point selector for an image. So the idea is you pick a focal point and application based on requested image size crop the image based on selected focal point. So I store the focal point (x,y) as a comma separated in string.

public class ImageFile : ImageData
{
	[UIHint("FocalPoint")]
	public virtual string FocalPoint { get; set; }
}

Now we want make the EPiServer custom editor named “FocalPoint”. There are many post about this so I ignore this part but what I need to pass current image URL to be passed to my editor. So for doing this we need EditorDescriptor as below:


	[EditorDescriptorRegistration(TargetType = typeof(string), UIHint = "FocalPoint")]
	public class FocalPointEditorDescriptor : EditorDescriptor
	{
		public FocalPointEditorDescriptor()
		{
			this.ClientEditingClass = "alloy.editors.FocalPointSelector";
		}
		public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable attributes)
		{
			base.ModifyMetadata(metadata, attributes);
		}
	}

Now we want to change the code to pass current image URL to editor, so editor can load the image and user can select the focal point. Code below is a magic which need to be injected into ModifyMetadata method.


			var contentDataMetadata = metadata as ContentDataMetadata;
			if (contentDataMetadata != null)
			{
				var imageFile = contentDataMetadata.OwnerContent as IContentMedia;
				if (imageFile != null)
				{
					metadata.EditorConfiguration["imageURL"] = UrlResolver.Current.GetUrl(imageFile.ContentLink);
				}
			}

And now we can access the value in our edit in JS:


define([
	"dojo/_base/connect",
	"dojo/_base/declare",
	"dijit/_CssStateMixin",
	"dijit/_Widget",
	"dijit/_TemplatedMixin",
	"dijit/_WidgetsInTemplateMixin",
	"epi/shell/widget/_ValueRequiredMixin",
	"dojo/dom",
	"dojo/on",
	"dojo/text!./templates/FocalPointSelector.html"],

	function (
		connect,
		declare,
		_CssStateMixin,
		_Widget,
		_TemplatedMixin,
		_WidgetsInTemplateMixin,
		_ValueRequiredMixin,
		dom,
		on,
		template) {

		return declare(
			"ras.editors.FocalPointSelector", [
				_Widget,
				_TemplatedMixin,
				_WidgetsInTemplateMixin,
				_CssStateMixin,
				_ValueRequiredMixin],
			{
				templateString: template,
				selector: null,
				intermediateChanges: false,
				image: null,
				value: null,
				onChange: function (value) { },
				postCreate: function () {
					this.image = dojo.create("img", { "style": "width:100%;height:100%", "src": this.imageURL }, this.domNode);
				}
			});
	});

And you can see “imageURL” is being passed to our javascript code and we can use it.

WCF RESTful .Net 4.0

In this post we want to introduce the project that hosts the WCF RESTful service and a client which is consuming the service with AJAX and jQeury. You need to have VS2010 and .Net 4.0 and being sure that you select the “.Net 4.0” when you want to create projects. So first step is creating new blank solution, then create new “WCF Service Application” and named it as “Server”:

Now have your service the only change that you need to apply is on web.config file. Let’s change it as below:

[cc lang=”XML” width=”100%”]

















[/cc]

Visual Studio has created the sample service, operation contract and data contract for you that should fine for our sample and your server for hosting RESTful service has been completed. Next step is to setup the client. You need to ensure that you have internet connection while loading the test client. Create new HTML file and place the code below as a content:

[cc lang=”HTML” width=”100%”]








[/cc]
Now run your service by pressing the “F5” in Visual Studio and open an “Internet Explorer” and open the created HTML file. Now the HTML code will contact the RESTful service and pass two values to the service (as a JSON object) and show the result value from server.

Easy, first WCF fully server and client

In this post I’m going to show how you easy steps to how you can create a server/client application base on WCF. Calling and receiving information from server to client and reverse. First of all create new solution called “ServiceHost”. Open Visual Studio 2010 and click on “File”->”New Project”:

And top section select “.Net Framework 4” and then from the “Installed Templates” select “Other Project Types” and then “Visual Studio Solutions”. On the right hand side select “Bank Solution”. Select the “Location” and solution name. Finally click on “OK”:

Now from the “File” menu click on “Add” and select “NewProject”:

From the list select “Class Library” and change the name to be “ServiceLibrary”:

 

From the solution explorer add a reference “System.ServiceModel” to the “ServiceLibrary” project.

Then:

 

From the “Solution Explorer” deleted automatically generated class file called “Class1.cs” and create new interface file called “ICalculator”:

 

Change the interface to become a public interface. Add extra using “System.ServiceModel” to the header, add “ServiceContract” attribute above the interface. Add new method called as below with “OperationContract” attribute.

Now create new class file called “Calculator”. Inherit the class from the “ICalculator” interface.

Now implement the interface as below:

 

From the menu “File”->”Add”->”New Project”, create a new console application project called “Server”:

And add the reference of “System.ServiceModel” to new created project called “Server”. Add another reference to “ServiceLibrary” project. Then build the project.

Now add new application configuration file to “Server” project.

Then:

Now build your project and you should get successful message from compiler. Now we need to configure the application to host the WCF service. So we need to change the app.config file first and introduce the service and configure it properly. For doing that open the “app.config” and change it to be like this:

 

This is the time for setting up your server runner. Open the “Solution Explorer”->”Server”->”Program.cs” and change it as below:

 

If you are using “Windows 7” or “Windows VISTA” when you want to start the application it may show you this message:

You can check the resolution here or easily run the “cmd.exe” in administrator mode and replace the command below and change the computer name and current user:

“netsh http add urlacl url=http://+:8082/ user={Computer Name}\{Current User}”

Now sever is ready and it is a time for building the client application. Add new “Console Application” project to your solution called “Client”. Then we need to add the reference of “Server” service to “Client” application. For doing that first run the server “Console Application” and make sure that service is running properly. For checking the server open an IE and change the address to http://localhost:8082 and should get the message as below:

 

After making sure that the service is working perfectly, for adding the “Service Reference” to “Client” application, right click on “Client” project and select “Add Service Reference”.

Submit the http://localhost:8082 to the address field and press the “Go” button”:

Then change the “Namespace” to “CalculatorClient” and press the “OK” button. Then open the “Client”->”Program.cs” and change it as below:

Make sure that the “Server” is running and then run the “Client”. You can the result as below:

 

You can download the sample from here