Change Business Process Flow using JavaScript

Out of box Business Process Flows in CRM 2013 will only move in one direction , i.e. from first stage to second till last. But , real world scenarios are different. We want to create Business Process flows with conditional formatting. From one business process to another based on some condition . I can also move to some third process on some other condition. So, I am taking an example in JavaScript in which based on an option set We will be changing Process flow.First we need to get various stage Guids out from CRM . For which, the following code will list out all the Process Id and Stage Id for given business process:

var service = (IOrganizationService)proxy;
                  QueryExpression query = new QueryExpression
                {
                    EntityName = “workflow”,
                    ColumnSet = new ColumnSet(true)
                };
                 Console.WriteLine(“Please enter the Process Name: “);
                 var processName = Console.ReadLine();
                 query.Criteria.Conditions.Add(new ConditionExpression(“name”, ConditionOperator.Equal, processName));
                EntityCollection response = service.RetrieveMultiple(query);
                QueryExpression query1 = new QueryExpression
                {
                    EntityName = “processstage”,
                    ColumnSet = new ColumnSet(true)
                };

                query1.Criteria.Conditions.Add(new ConditionExpression(“processid”, ConditionOperator.Equal, response.Entities[0].Id));
                EntityCollection response1 = service.RetrieveMultiple(query1);
                Console.Clear();
                Console.WriteLine(“Process Name: ” + processName);
                Console.WriteLine(“Process GUID: ” + response.Entities[0].Id);
                foreach (Entity processStage in response1.Entities)
                {
                    Console.WriteLine(processStage.Attributes[“stagename”] + ” : ” + processStage.Attributes[“processstageid”].ToString());
                }

If you paste this code to a console application it is going to give you output like this:

Process Name: Opportunity Sales Process 1
Process GUID: a013aa17-3d9e-4bfa-b7f7-72a10817d1c2
close : 0e6bbc59-345c-4cce-8bd0-c63d42b79e34
qualify : 155f3ba4-c9c3-43e4-855d-df97c0bc12ef
propose : 5721bc3b-d2fc-42be-ae37-f332ab3f2eb8
develop : 22e8f4ea-9e9a-4ba3-8c2f-ff15026d7d6f

Now the thing to note here is you need to maintain processid, stageid combination , else CRM will not map the process correctly. In this case I am going to move to Propose stage in Opportunity Sales Process 1 . What you got to be careful is all validations (required fields of previous stages are filled in already by the user. Out of box, CRM would have done it for you but now as you are changing it on the fly, you need to be extra careful here)

On opportunity form I have added an Option set having text option set same as Process Name and values as 1,2 for Process Stage selection. Please refer image below:

image

Following onchange function in JavaScript for Opportunity Process Selection option set will take care of the rest:

function onchange() {

    var selectedProcess = Xrm.Page.getAttribute(“new_processselection”);
    var selectedProcessValue = selectedProcess.getValue();
    if (selectedProcessValue == 2) {
        //Set Guid for Opportunity Sales Prcoess 1

        Xrm.Page.getAttribute(“processid”).setSubmitMode(“always”);
        Xrm.Page.getAttribute(‘processid’).setValue(‘a013aa17-3d9e-4bfa-b7f7-72a10817d1c2’);

        //Set Guid for Opportunity Sales Prcoess 1 Stage Propose
        Xrm.Page.getAttribute(“stageid”).setSubmitMode(“always”);
        Xrm.Page.getAttribute(‘stageid’).setValue(‘5721bc3b-d2fc-42be-ae37-f332ab3f2eb8’);

        //Call Entity save
        Xrm.Page.data.entity.save();

        //Call window refresh
        window.location.reload(true);
    }
}

Hope it helps!


Check this out

Trying to Learn Dynamics CRM: Download!

Learn Dynamics CRM App on Google Play store

Advertisement

53 thoughts on “Change Business Process Flow using JavaScript

  1. When i am trying to run the first code in console i get this error “Object reference not set to an instance of an object”

    Here is my code.

    class Process
    {
    public OrganizationServiceProxy proxy;

    public void GetProcessAndStageGuid()

    {
    var service = (IOrganizationService)proxy;
    QueryExpression query = new QueryExpression
    {
    EntityName = “workflow”,
    ColumnSet = new ColumnSet(true)
    };
    Console.WriteLine(“Please enter the Process Name: “);
    var processName = Console.ReadLine();
    query.Criteria.Conditions.Add(new ConditionExpression(“name”, ConditionOperator.Equal, processName));
    EntityCollection response = service.RetrieveMultiple(query);
    QueryExpression query1 = new QueryExpression
    {
    EntityName = “processstage”,
    ColumnSet = new ColumnSet(true)
    };

    query1.Criteria.Conditions.Add(new ConditionExpression(“processid”, ConditionOperator.Equal, response.Entities[0].Id));
    EntityCollection response1 = service.RetrieveMultiple(query1);
    Console.Clear();
    Console.WriteLine(“Process Name: ” + processName);
    Console.WriteLine(“Process GUID: ” + response.Entities[0].Id);
    foreach (Entity processStage in response1.Entities)
    {
    Console.WriteLine(processStage.Attributes[“stagename”] + ” : ” + processStage.Attributes[“processstageid”].ToString());
    }
    }

    }
    class Program
    {
    static void Main(string[] args)
    {
    Process pr=new Process();
    pr.GetProcessAndStageGuid();
    Console.ReadLine();
    }

    }

  2. Thanks for your prompt reply Deepesh. Here is my modified code where i have initialized service object. But now i get this error “Index was out of range. Must be non-negative and less than the size of the collection”

    class Process
    {
    public OrganizationServiceProxy serviceProxy;

    public void GetProcessAndStageGuid()
    {

    ClientCredentials clientCredentials = new ClientCredentials();
    clientCredentials.UserName.UserName = “administrator”;
    clientCredentials.UserName.Password = “pass@word1”;

    Uri OrganizationUri = new Uri(string.Format(“http://win-qctbmlihah0:5555/CRM-Telecom/XRMServices/2011/Organization.svc”));

    Uri HomeRealmUri = new Uri(“http://win-qctbmlihah0:5555/CRM-Telecom/XRMServices/2011/Organization.svc”);

    using (serviceProxy=new OrganizationServiceProxy(OrganizationUri,null,clientCredentials,null))
    {
    serviceProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior());
    var service = (IOrganizationService)serviceProxy;

    QueryExpression query = new QueryExpression
    {
    EntityName = “workflow”,
    ColumnSet = new ColumnSet(true)
    };
    Console.WriteLine(“Please enter the Process Name: “);
    var processName = Console.ReadLine();
    query.Criteria.Conditions.Add(new ConditionExpression(“name”, ConditionOperator.Equal, processName));
    EntityCollection response = new EntityCollection();
    response = service.RetrieveMultiple(query);
    QueryExpression query1 = new QueryExpression
    {
    EntityName = “processstage”,
    ColumnSet = new ColumnSet(true)
    };

    query1.Criteria.Conditions.Add(new ConditionExpression(“processid”, ConditionOperator.Equal, response.Entities[0].Id));//Here i get error “Index was out of range. Must be non-negative and less than the size of the collection”
    EntityCollection response1 = service.RetrieveMultiple(query1);
    Console.Clear();
    Console.WriteLine(“Process Name: ” + processName);
    Console.WriteLine(“Process GUID: ” + response.Entities[0].Id);
    foreach (Entity processStage in response1.Entities)
    {
    Console.WriteLine(processStage.Attributes[“stagename”] + ” : ” + processStage.Attributes[“processstageid”].ToString());
    }
    }

    }

    }
    class Program
    {
    static void Main(string[] args)
    {
    Process pr = new Process();
    pr.GetProcessAndStageGuid();
    Console.ReadLine();
    }
    }

  3. I replaced query1.Criteria.Conditions.Add(new ConditionExpression(“processid”, ConditionOperator.Equal, response.Entities[0].Id))

    with
    query1.Criteria.Conditions.Add(new ConditionExpression(“processid”, ConditionOperator.Equal, response1.Entities[0].Id))

    AND this Console.WriteLine(“Process GUID: ” + response.Entities[0].Id);
    with
    Console.WriteLine(“Process GUID: ” + response1.Entities[0].Id);

    it worked fine.

    Thanks in advance.

  4. I want to move the stage to Course Approved when I select Approved in my CRM form

    my stages are New Course Approval, Course Content Dev, Course Approval, Course Rejected, Course Approved.

    function onchange() {

    debugger;
    var selectedProcess = Xrm.Page.data.entity.attributes.get(“dm_courseapproved”).getValue();
    if (selectedProcess == true) {
    //Set Guid for Course Approval Process

    Xrm.Page.getAttribute(“processid”).setSubmitMode(“always”);//When i run this JS in the field events is throwing error
    Xrm.Page.getAttribute(‘processid’).setValue(‘650e06b4-789b-46c1-822b-0da76bedb1ed′);

    //Set Guid for Stage Course Approved
    Xrm.Page.getAttribute(“stageid”).setSubmitMode(“always”);
    Xrm.Page.getAttribute(‘stageid’).setValue(‘ee37ba10-e12b-dc47-70e0-dda2354b813a′);

    //Call Entity save
    Xrm.Page.data.entity.save();

    //Call window refresh
    window.location.reload(true);
    }
    }

  5. I get this error: There was an error in fields customized event.
    Field:dm_courseapproved // This CRM form has approved, not approved options
    Event:onchange/
    Error: Object Expected

    When I want to select “Approved” option in my CRM form, I want the stage to move to Course Approved.

    I have commented the process and process id…But still I get this error.

    When i run my console app i get this output:

    Process Name: Change Approval Process
    Process GUID: 650e06b4-789b-46c1-822b-0da76bedb1ed

    develop : 650e06b4-789b-46c1-822b-0da76bedb1ed//Stage Category
    Identify : 15322a8f-67b8-47fb-8763-13a28686c29d//Stage Category
    qualify : 6b9ce798-221a-4260-90b2-2a95ed51a5bc//Stage Category
    develop : bfc9108c-8389-406b-9166-2c3298a2e41f//Stage Category

    course approval : c516b822-c342-129b-cca5-358b16304d53//Stage
    propose : 3a275c22-fc45-4e89-97fc-41e5ec578743//Stage Category
    Resolve : 356ecd08-43c3-4585-ae94-6053984bc0a9//Stage Category
    course rejected : 0859debe-d71e-0884-de2d-652d23aec2b2//Stage
    close : bb7e830a-61bd-441b-b1fd-6bb104ffa027//Stage Category
    qualify : f99b4d48-7aad-456e-864a-8e7d543f7495//Stage Category
    course content development : ae5e4405-3820-4b15-5b87-a636b7aa7a36//Stage
    close : 7f5247fe-cfc3-42bc-aa77-b1d836d9b7c0//Stage Category
    course approved : ee37ba10-e12b-dc47-70e0-dda2354b813a//Stage
    Research : 92a6721b-d465-4d36-aef7-e8822d7a5a6a
    New course proposed : a8a4996d-5aba-4d0a-b5dc-f0d2a5f3673e//Stage
    propose : d3ca8878-8d7b-47b9-852d-fcd838790cfd//Stage Category

    Not sure why I am getting guid for Stage Category

    Please suggest

  6. I am using two option field(Approved, Not Approved) in my CRM form and trying to use this code. In your case its optionset field(1,2). Will that be an issue?

    function onchange() {

    debugger;
    var selectedProcess = Xrm.Page.data.entity.attributes.get(“dm_courseapproved”).getValue();
    if (selectedProcess = true) {

    //Set Guid for Course Approval Process

    // Xrm.Page.getAttribute(“CRMFieldSchemaName”).fireOnChange();

    //Set Guid for Stage Course Approved
    // Xrm.Page.getAttribute(“stageid”).setSubmitMode(“always”);
    Xrm.Page.data.entity.attributes.get(‘stageid’).setValue(‘ee37ba10-e12b-dc47-70e0-dda2354b813a′);

    //Call Entity save
    Xrm.Page.data.entity.save();

    //Call window refresh
    window.location.reload(true);
    }
    }

  7. Xrm.Page.getAttribute(“CRMFieldSchemaName”).fireOnChange() is anyways commented and am not using that now..just for later purpose if required. But i have removed that also. I don’t understand this error object expected.

    • It seems to be issue with curly braces and single braces:

      Try this

      function onchange() {

      debugger;
      var selectedProcess = Xrm.Page.data.entity.attributes.get(“dm_courseapproved”).getValue();
      if (selectedProcess == true) {

      //Set Guid for Course Approval Process

      // Xrm.Page.getAttribute(“CRMFieldSchemaName”).fireOnChange();

      //Set Guid for Stage Course Approved
      Xrm.Page.getAttribute(“stageid”).setSubmitMode(“always”);
      Xrm.Page.data.entity.attributes.get(‘stageid’).setValue(‘ee37ba10-e12b-dc47-70e0-dda2354b813a’);

      //Call Entity save
      Xrm.Page.data.entity.save();

      //Call window refresh
      window.location.reload(true);
      }
      }

  8. When I debug my script in F12 I see the error at this line near field
    var selectedProcess = Xrm.Page.data.entity.attributes.get(“dm_courseapproved”).getValue();
    But thats the correct schema name. And the error is same Object expected.

  9. In one of our requirements, on selection of a field value “yes”, then we have to move to last stage.
    I have used above code, it moved to the last stage. However colour of the stage remained same, only flag is moved.
    Also “next stage” remained same, it should change to “Back arrow”. Can you please help me on how to do these changes when a stage is changed.

  10. I commented the line “reload page code”. If i reload the page, all the values entered in the first stage are not getting saved.

    Xrm.Page.getAttribute(“stageid”).setSubmitMode(“always”);
    Xrm.Page.getAttribute(“stageid”).setValue(“BCE6EB95-6E4E-C3D8-EB8E-33934285D9BC”);
    Xrm.Page.ui.tabs.get(“tab_6”).setVisible(true);
    Xrm.Page.ui.tabs.get(“tab_7”).setVisible(false);
    Xrm.Page.ui.tabs.get(“tab_4”).setVisible(false);
    //Call Entity save
    Xrm.Page.data.entity.save();
    //Call window refresh
    // window.location.reload(true);

    My two value optionset attribute is present in the Business process flow. May be this is causing the issue?

  11. Even though i am saving the record using Xrm.Page.data.entity.save(); when i selected a value for the attribute, system is throwing an alert
    ” Are you sure you want to navigate away from this page? Please click “OK” to move further with out saving changes and “Cancel” to stay on the same page.”

  12. Hi Deepesh,

    I really appreciate for your help and loads of thanks:-)

    Now the script is working fine. But two things i need to set.
    First I want the CRM page to be on the same record instead going to new form.

    When I select the option Approve in my form, the page just reloads and new form is displayed. I go back and check that record, the stage is moved to what i expected. But then how can avoid displaying a new form.

    Second, in my last stage i have a step Approved, Rejected. By default that step is set to Rejected. How can I set this step to approved when the page reloads after saving?

  13. For the step to change , I have added Business rules. Now even that is working fine. But Need to get how to avoid getting into new CRM form.

  14. Now the Flag is moved the stage what I expected but the color highlights to the previous stage only. And the page does not refresh. Even If i keep Windows.location.reload(true);

  15. Hi Deepesh,

    Sorry for the late reply. Now my script works fine as I expected. Here is my code.

    JavaScript
    =========

    function StatusApprove(){
    debugger;

    var coursestatus = Xrm.Page.getAttribute(“dm_courseapproved”).getValue();

    if (coursestatus==1){

    Xrm.Page.getAttribute(“dm_courseapproved”).setValue(0);
    }
    else{
    Xrm.Page.getAttribute(“dm_courseapproved”).setValue(1);
    Xrm.Page.getAttribute(“stageid”).setSubmitMode(“always”);
    Xrm.Page.data.entity.attributes.get(‘stageid’).setValue(‘ee37ba10-e12b-dc47-70e0-dda2354b813a’);

    Call Entity save
    Xrm.Page.data.entity.save();
    Xrm.Page.data.refresh(save).then(successCallback, errorCallback);

    Call window refresh
    window.location.reload(true);

    }

    }

    I want to try changing the business process stage using workflow also. But I get error which i have commented.

    Workflow
    ========

    public sealed class SetBPFStage : CodeActivity
    {
    [Input(“ProcessName”)]
    public InArgument ProcessName { get; set; }

    [Input(“StageName”)]
    public InArgument StageName { get; set; }

    [Input(“EntityName”)]
    public InArgument EntityName { get; set; }

    ///
    /// Executes the workflow activity.
    ///
    /// The execution context.
    protected override void Execute(CodeActivityContext executionContext)
    {

    // Create the tracing service
    ITracingService tracingService = executionContext.GetExtension();

    if (tracingService == null)
    {
    throw new InvalidPluginExecutionException(“Failed to retrieve tracing service.”);
    }

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

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

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

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

    IOrganizationServiceFactory serviceFactory = executionContext.GetExtension();
    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(executionContext);
    string stageName = StageName.Get(executionContext);
    string entityName = EntityName.Get(executionContext);//I am not sure what to set in the workflow properties for entity name. I get error here. This should read the entity name(dm_courseapproved)

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

  16. I gave the correct entity (dm_course). There is no error in the code now. But the stage has not moved even after reloading the page.

  17. Hi Deepesh,

    First of all I wish you a very Happy New Year.

    I am new to MS Dynamics CRM and I am looking for a job in the same. i am trying to find some CRM project scenarios. Could you please help me in getting some common project scenarios where CRM can be implemented using Plugins, Workflows, javascripts.

    This will help me in facing the interviews.

    Regards,
    Ganesh A

  18. Hi Deepesh,

    When I deploy CRM package solution in VS2012. I get “Error registering plugins and/or workflows. Plug-in assembly does not contain the required types or assembly content cannot be updated.”

    I tried:
    1. Unregistered the plugins/workflows from plugin registration tool
    2. Delete the SDK messages for the process from CRM solution
    3. Clean and Build the solution.

    Still the error exists.

    Please suggest me how to resolve this issue.

    Regards,
    Ganesh A

      • Thank you depeche,
        two more question, When the process flow on opportunities is changed, this affect other records?
        Can I change from a default process flow to a new one? because I tried this but the explorer began to crush on every try. I got the fields “processid” and the “stageid” corrects (I’ve got those from a advanced search).
        Regards

      • function onchange() {
        // que funcione solo para el usuario administrador
        var UserName = Xrm.Page.context.getUserName();
        if (UserName == “Admin CRM”) {
        if(Xrm.Page.ui.getFormType() == 2){
        var selectedProcess = Xrm.Page.getAttribute(“cmtx_flujodeoportunidad”);
        var selectedProcessValue = selectedProcess.getValue();
        if (selectedProcessValue == “709460001”) {
        Xrm.Page.data.entity.attributes.get(“processid”).setSubmitMode(“always”);
        Xrm.Page.data.entity.attributes.get(“stageid”).setSubmitMode(“always”);
        Xrm.Page.data.entity.attributes.get(“processid”).setValue(“e61e41b3-621f-42aa-b963-d0a27d9634ab”);
        Xrm.Page.data.entity.attributes.get(“stageid”).setValue(“00ce3630-a542-472d-a836-e3e1624c30c8”);
        Xrm.Page.data.entity.save();
        window.location.reload(true);
        }else if(selectedProcessValue == “709460000” || selectedProcessValue == “709460002” || selectedProcessValue == “709460003”){
        Xrm.Page.data.entity.attributes.get(“processid”).setSubmitMode(“always”);
        Xrm.Page.data.entity.attributes.get(“stageid”).setSubmitMode(“always”);
        Xrm.Page.data.entity.attributes.get(“processid”).setValue(“3e8ebee6-a2bc-4451-9c5f-b146b085413a”);
        Xrm.Page.data.entity.attributes.get(“stageid”).setValue(“6b9ce798-221a-4260-90b2-2a95ed51a5bc”);
        Xrm.Page.data.entity.save();
        window.location.reload(true);
        }
        }
        }
        }
        This is my code, but like I said when I try to change the process flow my “I.E” crush and the CRM refresh the page.

      • I found out why my code doesn’t work, I think that this happen when I’m using the method Xrm.data.entity.save() on my oportunities
        Could you help me with this issue?
        regards

  19. Hi Deepesh,

    I want to change label name of stage. I have scenario that on last stage of Business process i have completed everything but Business process flow is showing last stage is active. Can i change it active to resolved or closed?

  20. Hi Deepesh
    I have a need to enforce using JScript when user clicks Next Stage on Solutionize (Opportunity) to Negotiate (Quote). Basically it is cross-entity BPF from Opportunity to Quote. When user clicks on Next Stage, it should create Quote behind and next stage it should land directly on Quote entity. How is this possible?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s