Saturday, April 25, 2015

Autonumbering in Dynamics CRM 2011

Out of the box, Dynamics CRM 2011 doesn't provide the functionality to create an autonumber on custom entities; this article describes my proposed solution to this.
For this demo code, we will create an autonumber required application called TracknTrace (new_trackntrace).

Prerequisites

Dynamics CRM 2011 SDK.
Create a provider for the solution, mine was named XRM with a prefix xrm. If you choose the default provider, you will have it prefixed as new, and all entities will be new_ instead of xrm_ as I have used here.

Creating the entities

The AutoNumber entity

First, create the AutoNumbering Entity as shown below:
counterEntity.png
Then create the field for holding the Counter value:
counterFields.png
The last thing to do for this entity is to add the Counter field to the form:
counterForm.png
Create a new entity in the AutoNumbering entity with the name CaseCounter and 0 in counter; this will hold the value for the incremental part of the plug-in.
counterEntity.png

The TrackNTrace Entity

Create the TrackNTrace entity:
ttEntity.png
Create the two fields; a thing to note here is xrm_SubTTNumber is a lookup on the TrackNTrace entity for looking up the parent TrackNTrace number to create a sub TT on.
ttFields.png
Last, add the two fields to the form of the TrackNTrace entity. I have added the TrackNTrace Number to the Header of the form, so nobody is able to change it, since we want all TrackNTrace numbers to be system generated.
ttForm.png
Now we have the entities ready, and we're ready to start coding.

The code

The TrackNTrace entity class

Open a command line and browse to your crm2011sdk\bin folder.
command.png
This will create the TTEntities.cs class which will hold the entities from your XRM solution, including the newly added xrm_AutoNumbering and xrm_TrackNTrace.

The creation of the plug-in

Open Visual Studio and create a new Class Project.
Add Microsoft.crm.sdk.proxy.dll and microsoft.xrm.sdk.dkk from the SDKFolder\bin folder. AddSystem.Runtime.Serialization from the .NET list in "Add Reference". It should look like this:
ClassProject.png
Add the newly generated TTEntities.cs to the project. By default, this will be located in the SDKFolder\bin folder.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;

namespace AutoNumber
{
  public class AutoNumber : IPlugin
  {
    public void Execute(IServiceProvider serviceProvider)
    {
      var context = (IPluginExecutionContext)serviceProvider.GetService(
                        typeof(IPluginExecutionContext));
      IOrganizationServiceFactory factory = 
        (IOrganizationServiceFactory)serviceProvider.GetService(
         typeof(IOrganizationServiceFactory));
      ITracingService tracingService = 
        (ITracingService)serviceProvider.GetService(typeof(ITracingService));

      if (context.InputParameters.Contains("Target") && 
          context.InputParameters["Target"] is Entity)
      {
            // Obtain the target entity from the input parmameters.
            Entity entity = (Entity)context.InputParameters["Target"];
            //Verify that the target entity represents an account.
            // If not, this plug-in was not registered correctly.
            if (entity.LogicalName == "xrm_trackntrace")
            {
                // An accountnumber attribute should not already exist because
                // it is system generated.

                if (entity.Attributes.Contains("xrm_ttnumber") == false)
                {
                    IOrganizationService service = 
                      factory.CreateOrganizationService(context.UserId);
                    OrganizationServiceContext orgContext = 
                      new OrganizationServiceContext(service);
                    if (entity.Attributes.Contains("xrm_subttnumber") == true)
                    {
                        var ttGuid = ((EntityReference)entity["xrm_subttnumber"]).Id;
                        //Grab case to create subcase on
                        var subTT = (from CN in orgContext.CreateQuery<xrm_trackntrace>()
                            where CN.Id == ttGuid
                            select CN).First();

                            //Check for existing sub TT numbers
                            var checkForCounter = 
                              from entry in orgContext.CreateQuery<xrm_autonumbering>()
                              where entry.xrm_name == subTT.xrm_TTNumber
                              select entry;
                            int i = 0;
                            foreach (var t in checkForCounter)
                            {
                                i++;
                            }
                            //if sub tt doesnt exist, create counter for sub tt
                            if (i == 0)
                            {
                                var counter = new xrm_autonumbering()
                                {
                                    xrm_name = subTT.xrm_TTNumber,
                                    xrm_Counter = 0
                                };
                                orgContext.AddObject(counter);
                                orgContext.SaveChanges();
                            }
                            // read from sub tt and tt number and set
                            // this number as TTNUBMER - SUBTTNUMBER on the form. Save changes
                            var subCounter = checkForCounter.First();
                            var sub = subCounter.xrm_Counter + 1;
                            var main = subTT.xrm_TTNumber;
                            entity.Attributes.Add("xrm_ttnumber", main + "-" + sub.ToString());

                            subCounter.xrm_Counter = sub;
                            orgContext.UpdateObject(subCounter);
                            orgContext.SaveChanges();
                        }
                        else
                        {
                            //if no sub tt has been selected create new, grab counter
                            //from CaseCounter entry and add 1, save after this
                            var query = from CN in orgContext.CreateQuery<xrm_autonumbering>()
                                where CN.xrm_name == "CaseCounter"
                                select CN;
                            foreach (var entry in query)
                            {
                                var t = entry.xrm_Counter + 1;
                                entity.Attributes.Add("xrm_ttnumber", t.ToString());
                                entry.xrm_Counter = t;
                                orgContext.UpdateObject(entry);
                                orgContext.SaveChanges();
                            }
                        }
                    }
                }
                else
                {
                    throw new InvalidPluginExecutionException(
                       "TrackNTrace Number can only be set by the system.");
                }
            }
        }
    }
}
The source code and the CRM 2011 solution are in the Zip file.

No comments:

Post a Comment

How to Deploy your API as a web app or API app

  Before you can call your custom API from a logic app workflow, deploy your API as a web app or API app to Azure App Service. To make your ...