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:
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
Nice post
Good Explanation Deepesh
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();
}
}
Hi Ganesh,
I do not see service object being initialized in your code
Regards,
Deepesh
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();
}
}
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.
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);
}
}
You do not need to put setsubmitmode? what is the error that you are getting?
If you are not changing business process no need to update processid
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
Hi Ganesh,
Did you add javascript function onchange event of your field and call this script?
I think that is the issue here. Hope it helps!
Regards,
Deepesh
Yes, I added this script in my field’s onchange event and I still get Object Expected error.
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);
}
}
if (selectedProcess = true)
I think you should be using == here.
Can you remove this Xrm.Page.getAttribute(“CRMFieldSchemaName”).fireOnChange() and see if it is working?
Oh. sorry, that was a typo error while posting.. I have used == only. but still i get the error.
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);
}
}
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.
Try this(Curly braces issue):
var selectedProcess = Xrm.Page.data.entity.attributes.get(“dm_courseapproved”).getValue();
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.
Did you reload the page?
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?
Until you will reload the page the flags and the buttons would not be as per what you want. About save anyhow there is a save in script so your values will be saved.
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.”
Hi Dileep,
You must have some dirty fields on the form
Can you set them to non dirty and try it.
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?
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.
Hi Ganesh,
In that case, please use: https://dynamicsofdynamicscrm.wordpress.com/2014/04/03/refresh-crm-2013-form-using-script/
Regards,
Deepesh
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);
Can you paste your code here?
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);
}
}
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.
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
Thanks Ganesh, happy new year to you as well.
I have mailed you in person.
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
Hi Ganesh,
How is this error linked to the blog?
If its not linked, i will suggest you to mail me on my id. I will reply as soon as I can.
Regards,
Deepesh
this solution works only with existing records?
Yes, because before there are no processid and stageid assigned
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
Your query was not clear to me. can you explain your scenario?
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.
in that case do you keep having constant refresh, due to infinite loop?
no, it’s just one alert from the explorer and then the refresh.
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
If you do not use save your changes to process and stage will not get saved.
[…] Change Business Process Flow using JavaScript […]
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?
Hi Amit,
My suggestion is to introduce a Closed stage and move your records to this stage when process ends.
Regards,
Deepesh
I want to change Business Process flow step is not required from required using javascript, is it possible ?
Hi Rajesh,
You can trigger a business rule:
https://dynamicsofdynamicscrm.com/2016/01/08/crm-2016-tips-enhancements-to-business-rules-in-crm-2016/
Thanks and Regards,
Deepesh
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?
Hi Vijay, which version of CRM is it?
Dynamics 365 On-premise (version 8.2)