Sunday, February 9, 2014

What's New in CRM 2013?


 I would start with saying CRM 2013 has come with many UI features to make it much more user friendly than the Previous versions.
To mention a few,
·         Bing Maps embedded control
·         The Flat User Interface
·         The Quick Create feature, which makes creation of Task, Phone call, Email, Account, Contact etc.  easy , While you are in the middle of editing a Case, If you need to create a Phone call Activity, You can quickly do that!






·         The Quick View Feature- Users can see the Parent entity details in the Child record

·         Inline Notifications -On the Form, the errors/warnings/information can be seen in line with the field

·         Simplified Navigation, improving the Form Body view
·         The Auto-Save feature, where-in, a Record is automatically saved by CRM every 30 Seconds(The form viewed by the User), also on exit  from the form
·         Business Process Flow, which can be a visual indicative of a Flow

·         Social Pane,Yammer and Lync Integration,  One can follow a User or a record in the same organization
There are other exciting features for the developers like:
·         Portable Business Logic\Business Rules Basic Validations/ other Jscript functionalities like Field Enabling Disabling, Hiding/Showing , Populating can be done without necessity to write any code, this makes it easy for CRM functional analysts to work on it without any Developer’s help
·         Real Time Workflows- These are Synchronous Workflows, Using this, developers configure a Workflow instead of writing Custom Code Plugin wherever System Out Of the Box feature suffices
·         Actions Using these, Custom Messages can be created, hence frequently used operations can be packed into actions and can be called from Plugins/Custom Workflows.
These can be defined for a Particular entity/ As Global


Thursday, January 23, 2014

Programatically change the Business Process Flow/Stage - CRM 2013


The newly added feature- Business Process Flow in MS Dynamics CRM 2013 is a wonderful feature that lets an end- user visualize the exteent of completion of the flow, but wondering why the Active Stage of the Business Process Flow doesn't automatically change after completion of one??
Same here!
So the solution was to set the Stage Programatically..

The Plugin and the Custom Workflows to set the Process and the Stage are provided below..
The difference would be that if we write a Custom Workflow, we can just use it in any Real Time System Workflow by passing the Parameters for the Process Name, Stage name and the Entity name..

Here's the Custom Workflow code, It takes the Business Process Flow Name , The Stage Name and the Entity Name for which the Process / Stage have to be set are to be passed from the System Workflow..

namespace CustomWorkflows.SetBPFStage
{
    using System;
    using System.Activities;
    using System.ServiceModel;
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Workflow;
    using Microsoft.Xrm.Sdk.Query;
    using System.Runtime.Serialization;

    public sealed class  SetBPFStage: CodeActivity
    {

        [Input("ProcessName")]
        public InArgument<string> ProcessName { get; set; }


        [Input("StageName")]
        public InArgument<string> StageName { get; set; }

        [Input("EntityName")]
        public InArgument<string> EntityName { get; set; }

        protected override void Execute(CodeActivityContext executionContext)
        {
            // Create the tracing service
            ITracingService tracingService = executionContext.GetExtension<ITracingService>();
            if (tracingService == null)
            {
                throw new InvalidPluginExecutionException("Failed to retrieve tracing service.");
            }

            tracingService.Trace("Entered setProcessAndStage.Execute(), Activity Instance Id: {0}, Workflow Instance Id: {1}",
                executionContext.ActivityInstanceId,
                executionContext.WorkflowInstanceId);

            // Create the context
            IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();

            if (context == null)
            {
                throw new InvalidPluginExecutionException("Failed to retrieve workflow context.");
            }

            tracingService.Trace("setProcessAndStage.Execute(), Correlation Id: {0}, Initiating User: {1}",
                context.CorrelationId,
                context.InitiatingUserId);

            IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            try
            {
                // TODO: Implement your custom Workflow business logic.
                Entity targetEntity = (Entity)context.InputParameters["Target"];
                tracingService.Trace("Entering");


                string processName = ProcessName.Get<string>(executionContext);
                string stageName = StageName.Get<string>(executionContext);
                string entityName = EntityName.Get<string>(executionContext);


                // Get the Process Id based on the process name
                tracingService.Trace("building query to retrieve the Workflow id");
                QueryExpression query = new QueryExpression { EntityName = "workflow" };
                ColumnSet cols = new ColumnSet("name", "activeworkflowid");
                query.ColumnSet = cols;
                query.Criteria.AddCondition("name", ConditionOperator.Equal, processName);
                EntityCollection processes = service.RetrieveMultiple(query);
                if (processes != null && processes.Entities.Count != 0)
                {
                    tracingService.Trace("Count of the Processes with this name, {0}, {1}", processName, processes.Entities.Count.ToString());

                    //if (results.Entities[0].Attributes.Contains("name") || results.Entities[0].Attributes.Contains("activeworkflowid"))
                    //    {
                    //        localContext.TracingService.Trace(results.Entities[0].GetAttributeValue<string>("name"));
                    //    }
                    //    else
                    //        localContext.TracingService.Trace("No Name!");

                    QueryExpression queryStage = new QueryExpression { EntityName = "processstage" };
                    queryStage.ColumnSet = new ColumnSet("stagename", "processid");
                    queryStage.Criteria.AddCondition("stagename", ConditionOperator.Equal, stageName);
                    queryStage.Criteria.AddCondition("processid", ConditionOperator.Equal, processes.Entities[0].Id);
                    EntityCollection stages = service.RetrieveMultiple(queryStage);
                    if (stages != null && stages.Entities.Count != 0)
                    {

                        tracingService.Trace(stages.Entities.Count.ToString());
                        tracingService.Trace(stages.Entities[0].Id.ToString());

                        Entity updateEntity = service.Retrieve(entityName, targetEntity.Id, new ColumnSet("processid", "stageid"));

                        updateEntity.Attributes["stageid"] = stages.Entities[0].Id;
                        updateEntity.Attributes["processid"] = processes.Entities[0].Id;
                        service.Update(updateEntity);


                        //throw new InvalidPluginExecutionException("Adding Log");
                    }

                }
            }
            catch (FaultException<OrganizationServiceFault> e)
            {
                tracingService.Trace("Exception: {0}", e.ToString());

                // Handle the exception.
                throw;
            }

            tracingService.Trace("Exiting setProcessAndStage.Execute(), Correlation Id: {0}", context.CorrelationId);
        }
    }
}
------------------------------------------------------------------------------------------------------------
Here's the Plugin Code to set the Business Process Flow and the Stage

namespace PostApprovalUpdate
{
    using System;
    using System.ServiceModel;
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Query;


    /// <summary>
    /// PostApprovalUpdate Plugin.
    /// Fires when the following attributes are updated:
    /// All Attributes
    /// </summary>
    public class PostApprovalUpdate: Plugin
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="PostApprovalUpdate"/> class.
        /// </summary>
        public PostApprovalUpdate()
            : base(typeof(PostApprovalUpdate))
        {
            base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(40, "Update", "new_approvalentity", new Action<LocalPluginContext>(ExecutePostApprovalUpdate)));

        }
        protected void ExecutePostApprovalUpdate(LocalPluginContext localContext)
        {
            if (localContext == null)
            {
                throw new ArgumentNullException("localContext");
            }
                        // TODO: Implement your custom Plug-in business logic.

            IPluginExecutionContext context = localContext.PluginExecutionContext;
            IOrganizationService service = localContext.OrganizationService;
            if(context.Depth==1){
                if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
                {
                    localContext.TracingService.Trace("Entering");
                    Entity CustomApprovalEntity = (Entity)context.InputParameters["Target"];

 
                    string businessProcessFlowName =<<Provide your business Process flow Name here>>;
               string stageName=<<Provide your stageName here>>;
             string entityName=<<Provide the EntityName for which the Stage has to be set>>;

                    // Get the Process Id based on the process name
                    localContext.TracingService.Trace("building query");
                    QueryExpression query = new QueryExpression { EntityName = "workflow" };
                    ColumnSet cols = new ColumnSet("name", "activeworkflowid");
                    query.ColumnSet = cols;

                    query.Criteria.AddCondition("name", ConditionOperator.Equal,businessProcessFlowName);
                    //localContext.TracingService.Trace(query.Criteria.Conditions.IndexOf(0).ToString());
                    EntityCollection processes = service.RetrieveMultiple(query);
                    localContext.TracingService.Trace(processes.Entities.Count.ToString());
                    if (processes.Entities[0].Attributes.Contains("name") || processes.Entities[0].Attributes.Contains("activeworkflowid"))
                    {
                        localContext.TracingService.Trace(processes.Entities[0].GetAttributeValue<string>("name"));
                 

                     QueryExpression queryStage = new QueryExpression { EntityName = "processstage" };
                     queryStage.ColumnSet = new ColumnSet("stagename", "processid");
                     queryStage.Criteria.AddCondition("stagename", ConditionOperator.Equal,stageName);
                     queryStage.Criteria.AddCondition("processid", ConditionOperator.Equal, processes.Entities[0].Id);
                     EntityCollection stages = service.RetrieveMultiple(queryStage);
                     localContext.TracingService.Trace(stages.Entities.Count.ToString());
                     localContext.TracingService.Trace(stages.Entities[0].Id.ToString());

                     localContext.TracingService.Trace("Setting the Stage ");
                     CustomApprovalEntity.Attributes["stageid"] = stages.Entities[0].Id;
   CustomApprovalEntity.Attributes["processid"] = processes.Entities[0].Id;
                     service.Update(CustomApprovalEntity);
      }
                }
            }
        }
    }
}