Introduction
During some scenarios we must automatically move BPF Stages based on server-side code. This can be achieved by CRM Plugin. As an example, on update of a title field for a selected contact record BPF will automatically moves to next stage.
Step 1
Login to the required environment and go to flows and select Business process flows – Vaccination and observe whether BPF is active or not, if not then activate it as shown in the below figure.
Step 2
After Step 1, open Business process flow and note down the logical name of the Business process flow by opening the above selected BPF which we will be going to use in next steps as shown in the below figure.
Step 3
After Step 2, we have to go to our plugin code [C# Class Library] under execute method on update message of contact for jobtitle field change and write the logic inside the code block as
public void Execute(IServiceProvider serviceProvider) {
IPluginExecutionContext context = (IPluginExecutionContext) serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory) serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService crmService = serviceFactory.CreateOrganizationService(null);
try {
string logicalNameOfBPF = "cr5bc_vaccination";
if (context.MessageName.ToLower().Equals("update") && context.PrimaryEntityName.ToLower().Equals("contact")) {
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity) {
Entity entity = (Entity) context.InputParameters["Target"];
if (entity.Contains("jobtitle") && entity["jobtitle"] != null) {
// logic here
}
}
}
} catch (InvalidPluginExecutionException ex) {
throw ex;
}
}
Which is shown in the below figure.
Step 4
After Step 3, we need to get active BPF details that is present on the selected contact record which was open, to get this we write a separate method and call this method to retrieve details passing contact entity which was obtained in Step 2 and by using RetrieveProcessInstancesRequest present in CRM SDK messages we can form the request and pass this to RetrieveProcessInstancesResponse to get the list of BPF process instances and select the first instance using below code
public Entity GetActiveBPFDetails(Entity entity, IOrganizationService crmService) {
Entity activeProcessInstance = null;
RetrieveProcessInstancesRequest entityBPFsRequest = new RetrieveProcessInstancesRequest {
EntityId = entity.Id,
EntityLogicalName = entity.LogicalName
};
RetrieveProcessInstancesResponse entityBPFsResponse = (RetrieveProcessInstancesResponse) crmService.Execute(entityBPFsRequest);
if (entityBPFsResponse.Processes != null && entityBPFsResponse.Processes.Entities != null) {
activeProcessInstance = entityBPFsResponse.Processes.Entities[0];
}
return activeProcessInstance;
}
As shown in the below figure
Step 5
After Step 4, we need to write another method and call it inside execute method to get all the stages details of the selected BPF on contact record so that we can extract next stage ID details by using RetrieveActivePathResponse present in CRM SDK messages and we have to pass parameters like Active Stage Id, Active BPF Id, IOrganization service object as input and an output parameter with int type to extract current Stage Position also retrieve active path request using the below code
public RetrieveActivePathResponse GetAllStagesOfSelectedBPF(Guid activeBPFId, Guid activeStageId, ref int currentStagePosition, IOrganizationService crmService) {
// Retrieve the process stages in the active path of the current process instance
RetrieveActivePathRequest pathReq = new RetrieveActivePathRequest {
ProcessInstanceId = activeBPFId
};
RetrieveActivePathResponse pathResp = (RetrieveActivePathResponse) crmService.Execute(pathReq);
for (int i = 0; i < pathResp.ProcessStages.Entities.Count; i++) {
// Retrieve the active stage name and active stage position based on the activeStageId for the process instance
if (pathResp.ProcessStages.Entities[i].Attributes["processstageid"].ToString() == activeStageId.ToString()) {
currentStagePosition = i;
}
}
return pathResp;
}
As shown in the below figure
Step 6
After Step 5, we need to call the methods obtained from Step 4 and Step 5 from the main method and then based on active path response and current Stage Position and looping through the stages and get the next stage id and set the next stage as active by forming an object of current BPF with selected BPF Instance Id and passing next stage id as activestageid of BPF with IOrganization service’s update method BPF Active stage will gets changed to next stage with the below code
Entity activeProcessInstance = GetActiveBPFDetails(entity, crmService);
if (activeProcessInstance != null) {
Guid activeBPFId = activeProcessInstance.Id; // Id of the active process instance, which will be used
// Retrieve the active stage ID of in the active process instance
Guid activeStageId = new Guid(activeProcessInstance.Attributes["processstageid"].ToString());
int currentStagePosition = -1;
RetrieveActivePathResponse pathResp = GetAllStagesOfSelectedBPF(activeBPFId, activeStageId, ref currentStagePosition, crmService);
if (currentStagePosition > -1 && pathResp.ProcessStages != null && pathResp.ProcessStages.Entities != null && currentStagePosition + 1 < pathResp.ProcessStages.Entities.Count) {
// Retrieve the stage ID of the next stage that you want to set as active
Guid nextStageId = (Guid) pathResp.ProcessStages.Entities[currentStagePosition + 1].Attributes["processstageid"];
// Set the next stage as the active stage
Entity entBPF = new Entity(logicalNameOfBPF) {
Id = activeBPFId
};
entBPF["activestageid"] = new EntityReference("processstage", nextStageId);
crmService.Update(entBPF);
}
}
As shown in the below figure.
Step 7
After Step 6, the final code looks like this
public void Execute(IServiceProvider serviceProvider) {
IPluginExecutionContext context = (IPluginExecutionContext) serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory) serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService crmService = serviceFactory.CreateOrganizationService(null);
try {
string logicalNameOfBPF = "cr5bc_vaccination";
if (context.MessageName.ToLower().Equals("update") && context.PrimaryEntityName.ToLower().Equals("contact")) {
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity) {
Entity entity = (Entity) context.InputParameters["Target"];
if (entity.Contains("jobtitle") && entity["jobtitle"] != null) {
Entity activeProcessInstance = GetActiveBPFDetails(entity, crmService);
if (activeProcessInstance != null) {
Guid activeBPFId = activeProcessInstance.Id; // Id of the active process instance, which will be used
// Retrieve the active stage ID of in the active process instance
Guid activeStageId = new Guid(activeProcessInstance.Attributes["processstageid"].ToString());
int currentStagePosition = -1;
RetrieveActivePathResponse pathResp = GetAllStagesOfSelectedBPF(activeBPFId, activeStageId, ref currentStagePosition, crmService);
if (currentStagePosition > -1 && pathResp.ProcessStages != null && pathResp.ProcessStages.Entities != null && currentStagePosition + 1 < pathResp.ProcessStages.Entities.Count) {
// Retrieve the stage ID of the next stage that you want to set as active
Guid nextStageId = (Guid) pathResp.ProcessStages.Entities[currentStagePosition + 1].Attributes["processstageid"];
// Set the next stage as the active stage
Entity entBPF = new Entity(logicalNameOfBPF) {
Id = activeBPFId
};
entBPF["activestageid"] = new EntityReference("processstage", nextStageId);
crmService.Update(entBPF);
}
}
}
}
}
} catch (InvalidPluginExecutionException ex) {
throw ex;
}
}
public Entity GetActiveBPFDetails(Entity entity, IOrganizationService crmService) {
Entity activeProcessInstance = null;
RetrieveProcessInstancesRequest entityBPFsRequest = new RetrieveProcessInstancesRequest {
EntityId = entity.Id,
EntityLogicalName = entity.LogicalName
};
RetrieveProcessInstancesResponse entityBPFsResponse = (RetrieveProcessInstancesResponse) crmService.Execute(entityBPFsRequest);
if (entityBPFsResponse.Processes != null && entityBPFsResponse.Processes.Entities != null) {
activeProcessInstance = entityBPFsResponse.Processes.Entities[0];
}
return activeProcessInstance;
}
public RetrieveActivePathResponse GetAllStagesOfSelectedBPF(Guid activeBPFId, Guid activeStageId, ref int currentStagePosition, IOrganizationService crmService) {
// Retrieve the process stages in the active path of the current process instance
RetrieveActivePathRequest pathReq = new RetrieveActivePathRequest {
ProcessInstanceId = activeBPFId
};
RetrieveActivePathResponse pathResp = (RetrieveActivePathResponse) crmService.Execute(pathReq);
for (int i = 0; i < pathResp.ProcessStages.Entities.Count; i++) {
// Retrieve the active stage name and active stage position based on the activeStageId for the process instance
if (pathResp.ProcessStages.Entities[i].Attributes["processstageid"].ToString() == activeStageId.ToString()) {
currentStagePosition = i;
}
}
return pathResp;
}
As shown in the below figure.
Step 8
After Step 7, build and sign the assembly and then using Plugin registration tool register this assembly and then create an update step on contact entity with update message, filtering attributes on jobtitle on preoperation with mode as Synchronous and click on create/update step as shown in the below figure
Step 9
After Step 8, now go to the contact record and observe the current stage as Contact Details before changing title as shown in the below figure
Step 10
After Step 9, update job title to Sri and save the record you should see automatically stage moves to Vaccination Status as shown in the below figure
Note
- I have concentrated more on the logic part that’s why only execute and other related methods were shown.
- Other custom logic like restriction based on roles and to perform other operations and transactions can also be written inside the execute method.
- In exception block we can write generic as well as specific exceptions like invalid exception.
- I have included screenshot outline of the methods in Step 7 because of constraint of taking screenshot with total code in a single image.
Conclusion
In this way, one can write CRM Plugin code to move automatically BPF Stages based on the given business requirement.