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);
      }
                }
            }
        }
    }
}


2 comments:

  1. Hi ,
    thanks , I am able to populate the processid and stage id . The new business process flow is not appearing in the entity . It says " A New Business process flow was assigned " . Please help

    thanks

    ReplyDelete
  2. I am able to run the workflow code but my business process stage is not moved to what i expected. Rather is has gone to first stage after reloading the page.

    Here is my code.
    ============

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

    string processName = ProcessName.Get(executionContext);
    string stageName = StageName.Get(executionContext);
    string entityName = EntityName.Get(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("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 e)
    {
    tracingService.Trace("Exception: {0}", e.ToString());

    // Handle the exception.
    throw;
    }

    tracingService.Trace("Exiting SetBPFStage.Execute(), Correlation Id: {0}", context.CorrelationId);
    }
    }

    ReplyDelete