0

I am trying to add a new customer record in my Sitecore Commerce through code but I can't find any docs related to it.

I tried using the OData way, but I am getting an error below.

Does anyone know how to do this? Thanks in advance

My Code:

container.AddToCustomers(new Customer()
                {
                    FirstName = firstName,
                    LastName = lastName,
                    UserName = username,
                    Email = email,
                    Password = password
                });

container.SaveChanges();

Error in Commerce Engine Logs:

00249 12:19:28 ERROR ClientCertificateValidationMiddleware: Message = No action match template 'Customers' in 'CustomersController'
System.NotSupportedException: No action match template 'Customers' in 'CustomersController'
   at Microsoft.AspNetCore.OData.Routing.Conventions.DefaultODataRoutingConvention.SelectAction(RouteContext routeContext)
   at Microsoft.AspNetCore.OData.Routing.ODataActionSelector.SelectCandidates(RouteContext context)
   at Microsoft.AspNetCore.Mvc.Internal.MvcRouteHandler.RouteAsync(RouteContext context)
   at Microsoft.AspNetCore.OData.Routing.ODataRoute.<RouteAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Builder.RouterMiddleware.<Invoke>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.<Invoke>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.<Invoke>d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Sitecore.Commerce.Core.ClientCertificateValidationMiddleware.<Invoke>d__4.MoveNext()

Update 1

I took a look at the Postman AddCustomer action and tried recreating it in dotnet.

Below is my code


var data = new Dictionary<string, string>()
{
    { "FirstName", firstName },
    { "LastName", lastName },
    { "LoginName", username },
    { "Domain", "Storefront" },
    { "Email", email },
    { "AccountStatus", "ActiveAccount" },
    { "PhoneNumber", "" },
    { "Language", "en" },
};

var entityView = Proxy.GetEntityView(container, string.Empty, "Details", "AddCustomer", string.Empty);
foreach (var field in data)
{
    entityView.Properties.First(x => x.Name.Equals(field.Key,StringComparison.OrdinalIgnoreCase)).Value = field.Value;
}

var doUxActionCommand = container.DoUxAction(entityView);

var response = Proxy.DoCommand(doUxActionCommand);

The strange thing is that it is throwing errors saying that the "Action" is null, when I have already set it in my entityView. Anyone has an idea on this?

{
          "Id": "b97ed6f2a3394f69896b1572a7397075",
          "ResponseCode": "Error",
          "Messages": [
            {
              "MessageDate": "2019-06-21T09:47:55.757604Z",
              "Code": "Error",
              "Text": "Action for entity view '' cannot be null or empty.",
              "CommerceTermKey": "InvalidEntityViewAction"
            }
          ],
          "Models": [
            {
              "EntityId": "",
              "EntityVersion": 1,
              "Name": "",
              "Policies": []
            }
          ],
          "ActionUrl": "",
          "TaskId": 0,
          "Status": "",
          "IsFaulted": false,
          "IsCompleted": true,
          "IsCanceled": false,
          "StartTime": "2019-06-21T09:47:55.7566054Z",
          "ExecutionTimeMilliseconds": 1
        }
1

In order to use above code shared by @Andrei in your commerce storefront website, you need to do the following steps:

  1. You can create a new pipeline and a block to create new customer entity (where you can copy paste the above code) in your Commerce Engine Plugin project.
  2. Register the block to pipeline under ConfigureServices() method.
  3. Create your own Commerce API endpoint using Command (https://medium.com/@jackspektor/how-to-extend-sitecore-9-commerce-api-with-custom-commands-78909090f2c9) which can further execute/process your pipeline.

Also, Akshay Sura has written a very detailed blog on this: https://www.konabos.com/blog/create-a-new-customer-entity-in-sitecore-commerce-9-x

0

Sitecore has Sitecore.Commerce.Engine.Connect.Pipelines.Customers.CreateUser pipeline for Sitecore Commerce user creation:

public class CreateUser : PipelineProcessor
  {
    public CreateUser(IEntityFactory entityFactory)
    {
      this.EntityFactory = entityFactory;
    }

    public IEntityFactory EntityFactory { get; private set; }

    public override void Process(ServicePipelineArgs args)
    {
      CreateUserRequest request;
      CreateUserResult result;
      PipelineUtility.ValidateArguments<CreateUserRequest, CreateUserResult>(args, out request, out result);
      Assert.IsNotNull((object) request.UserName, "request.UserName");
      Assert.IsNotNull((object) request.Password, "request.Password");
      Container container = this.GetContainer(request.Shop.Name, string.Empty, "", "", args.Request.CurrencyCode, new DateTime?());
      CommerceUser commerceUser1 = result.CommerceUser;
      if (commerceUser1 != null && commerceUser1.UserName.Equals(request.UserName, StringComparison.OrdinalIgnoreCase))
      {
        string entityId = "Entity-Customer-" + request.UserName;
        ServiceProviderResult currentResult = new ServiceProviderResult();
        EntityView entityView = this.GetEntityView(container, entityId, string.Empty, "Details", string.Empty, currentResult);
        if (currentResult.Success && !string.IsNullOrEmpty(entityView.EntityId))
        {
          base.Process(args);
          return;
        }
      }
      EntityView entityView1 = this.GetEntityView(container, string.Empty, string.Empty, "Details", "AddCustomer", (ServiceProviderResult) result);
      if (!result.Success)
        return;
      entityView1.Properties.FirstOrDefault<ViewProperty>((Func<ViewProperty, bool>) (p => p.Name.Equals("Domain", StringComparison.OrdinalIgnoreCase))).Value = request.UserName.Split('\\')[0];
      entityView1.Properties.FirstOrDefault<ViewProperty>((Func<ViewProperty, bool>) (p => p.Name.Equals("LoginName", StringComparison.OrdinalIgnoreCase))).Value = request.UserName.Split('\\')[1];
      entityView1.Properties.FirstOrDefault<ViewProperty>((Func<ViewProperty, bool>) (p => p.Name.Equals("AccountStatus", StringComparison.OrdinalIgnoreCase))).Value = "ActiveAccount";
      if (!string.IsNullOrEmpty(request.Email))
        entityView1.Properties.FirstOrDefault<ViewProperty>((Func<ViewProperty, bool>) (p => p.Name.Equals("Email", StringComparison.OrdinalIgnoreCase))).Value = request.Email;
      CommerceCommand commerceCommand = this.DoAction(container, entityView1, (ServiceProviderResult) result);
      if (commerceCommand != null && commerceCommand.ResponseCode.Equals("ok", StringComparison.OrdinalIgnoreCase))
      {
        CommerceUser commerceUser2 = this.EntityFactory.Create<CommerceUser>("CommerceUser");
        commerceUser2.Email = request.Email;
        commerceUser2.UserName = request.UserName;
        commerceUser2.ExternalId = commerceCommand.Models.OfType<CustomerAdded>().FirstOrDefault<CustomerAdded>().CustomerId;
        result.CommerceUser = commerceUser2;
        request.Properties.Add(new PropertyItem()
        {
          Key = "UserId",
          Value = (object) result.CommerceUser.ExternalId
        });
      }
      base.Process(args);
    }
  }
  • Hmm.. how do I use this on my commerce storefront website codes? (note that I am not using SXA storefront) – Fredric Foo Jun 21 at 8:46
  • You can copy code for customer entity creation and implement your custom code for create sitecore user and link sitecore user with customer entity – Andrei Paliakou Jun 21 at 9:00
  • It doesnt sound right to copy over the pipeline codes to my storefront website. I would still want to be able retain the plugin functionality in Commerce Engine where I can add additional blocks to my CreateUser pipeline. – Fredric Foo Jun 21 at 10:07
0

I managed to solve this by using the following codes to create the Customer on my storefront website.

Dictionary<string, string> data = new Dictionary<string, string>()
{
    { "FirstName", firstName },
    { "LastName", lastName },
    { "LoginName", username },
    { "Domain", "Storefront" },
    { "Email", email },
    { "AccountStatus", "ActiveAccount" },
    { "PhoneNumber", "" },
    { "Language", "en" },
};

ObservableCollection<ViewProperty> properties = ObservableCollection<ViewProperty>();

foreach (var field in data)
{
    properties.Add(new ViewProperty()
    {   Name = field.Key,
        Value = field.Value
    });
}

return container.DoAction(new EntityView() { Action = "AddCustomer", Properties = properties }).GetValue();

This basically creates a request similar to "Add Customer" in the Postman samples from the Sitecore Commerce SDK. enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.