I am following this article to change my Business Process Flow stage within a c# plugin. I am able to move the stage forward to the next stage, but I am receiving an error when I try to move back to a previous stage. The error below is what I receive in the UI from Dynamics. When I debug the plugin, I receive a FaultException<OrganizationServiceFault>
exception that doesn't contain any information. Why am I receiving an error and how can I modify my code to successfully go back to a previous stage in my Business Process Flow?
Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: An unexpected error occurred.
Detail: <OrganizationServiceFault xmlns="http://schemas.microsoft.com/xrm/2011/Contracts" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ActivityId>5df51362-b7c1-4817-a8d0-de2d63b15c17</ActivityId>
<ErrorCode>-2147220970</ErrorCode>
<ErrorDetails xmlns:a="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
<Message>An unexpected error occurred.</Message>
<Timestamp>2018-07-19T18:55:42.6625925Z</Timestamp>
<ExceptionSource i:nil="true" />
<InnerFault>
<ActivityId>5df51362-b7c1-4817-a8d0-de2d63b15c17</ActivityId>
<ErrorCode>-2147220970</ErrorCode>
<ErrorDetails xmlns:a="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
<Message>System.NullReferenceException: Microsoft Dynamics CRM has experienced an error. Reference number for administrators or support: #0D309052</Message>
<Timestamp>2018-07-19T18:55:42.6625925Z</Timestamp>
<ExceptionSource i:nil="true" />
<InnerFault i:nil="true" />
<OriginalException i:nil="true" />
<TraceText i:nil="true" />
</InnerFault>
<OriginalException i:nil="true" />
<TraceText i:nil="true" />
</OrganizationServiceFault>
if (localContext == null)
{
throw new ArgumentNullException("localContext");
}
IPluginExecutionContext context = localContext.PluginExecutionContext;
IOrganizationService service = localContext.OrganizationService;
Client client = (Client)service.Retrieve(
Client.LogicalName,
new Guid("75FE165F-848B-E811-80F3-005056B33317"),
new ColumnSet(new String[]{
Client.Properties.ClientId
})
);
client.ChangeStage(service);
public void ChangeStage(IOrganizationService service)
{
// Get Process Instances
RetrieveProcessInstancesRequest processInstanceRequest = new RetrieveProcessInstancesRequest
{
EntityId = this.Id,
EntityLogicalName = this.LogicalName
};
RetrieveProcessInstancesResponse processInstanceResponse = (RetrieveProcessInstancesResponse)service.Execute(processInstanceRequest);
// Declare variables to store values returned in response
int processCount = processInstanceResponse.Processes.Entities.Count;
Entity activeProcessInstance = processInstanceResponse.Processes.Entities[0]; // First record is the active process instance
Guid activeProcessInstanceID = activeProcessInstance.Id; // Id of the active process instance, which will be used later to retrieve the active path of the process instance
// Retrieve the active stage ID of in the active process instance
Guid activeStageID = new Guid(activeProcessInstance.Attributes["processstageid"].ToString());
// Retrieve the process stages in the active path of the current process instance
RetrieveActivePathRequest pathReq = new RetrieveActivePathRequest
{
ProcessInstanceId = activeProcessInstanceID
};
RetrieveActivePathResponse pathResp = (RetrieveActivePathResponse)service.Execute(pathReq);
string activeStageName = "";
int activeStagePosition = -1;
Console.WriteLine("\nRetrieved stages in the active path of the process instance:");
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())
{
activeStageName = pathResp.ProcessStages.Entities[i].Attributes["stagename"].ToString();
activeStagePosition = i;
}
}
// Retrieve the stage ID of the next stage that you want to set as active
activeStageID = (Guid)pathResp.ProcessStages.Entities[activeStagePosition - 1].Attributes["processstageid"];
// Retrieve the process instance record to update its active stage
ColumnSet cols1 = new ColumnSet();
cols1.AddColumn("activestageid");
Entity retrievedProcessInstance = service.Retrieve("ccseq_bpf_clientsetup", activeProcessInstanceID, cols1);
// Set the next stage as the active stage
retrievedProcessInstance["activestageid"] = new EntityReference(ProcessStage.LogicalName, activeStageID);
service.Update(retrievedProcessInstance);
}
I found this article that explains how to update the Stage using the Web API. When I try this method, I get the error:
An undeclared property 'activestageid' which only has property annotations in the payload but no property value was found in the payload. In OData, only declared navigation properties and declared named streams can be represented as properties without values.
I've tried a few varieties of 'activestageid' without success (ActiveStageId, _activestageid_value).
Based on Arun's feedback, I tried the below Web API calls without success. The ID inside the brackets in the url (ccseq_bpf_clientsetups(###)) I pulled from the BusinessProcessFlowInstanceId on the ccseq_bpf_clientsetups table. The processstages ID I pulled from the ProcessStageId in the ProcessStageBase table
// Attempt 1
PATCH /COHEN/api/data/v8.2/ccseq_bpf_clientsetups(bc892aec-2594-e811-80f4-005056b33317) HTTP/1.1
{ "[email protected]": "/processstages(70018854-db7c-4612-915b-2ad7870a8574)"}
// Attempt 2
PATCH /COHEN/api/data/v8.2/ccseq_bpf_clientsetups(bc892aec-2594-e811-80f4-005056b33317) HTTP/1.1
{ "[email protected]": "/processstages(70018854-db7c-4612-915b-2ad7870a8574)"}
// Attempt 3
PATCH /COHEN/api/data/v8.2/ccseq_bpf_clientsetups(bc892aec-2594-e811-80f4-005056b33317) HTTP/1.1
{ "[email protected]": "/processstages(70018854-db7c-4612-915b-2ad7870a8574)"}
I downloaded jLattimer's CRM Rest Builder and tried running the JavaScript his tool generated. The code was identical to what I had written previously and unfortunately did not work. At this point I'm fairly confident that changing stages is not supported in v8.2 of the Web API.
I've got some code that attempts to move the Business Process Flow stage forward, as a custom workflow step (rather than a plugin). I've posted it below.
The difference that I see are:
active path
, I'm just getting all available stages for the processTraversedPath
propertyCode:
var activeInstancesRequest = new RetrieveProcessInstancesRequest
{
EntityId = TargetEntity.Id,
EntityLogicalName = TargetEntity.LogicalName
};
var activeInstancesResponse = (RetrieveProcessInstancesResponse)base.OrgService.Execute(activeInstancesRequest);
var process = activeInstancesResponse.Processes.Entities.Select(x => x.ToEntity<BusinessProcessFlowInstance>()).ToList();
var stages = base.XrmContext.ProcessStageSet
.Where(s => s.ProcessId.Id == process.FirstOrDefault().ProcessId.Id)
.Select(s => new ProcessStage
{
ProcessStageId = s.ProcessStageId,
StageName = s.StageName
})
.ToList();
var targetStage = stages.Where(stage => stage.StageName == targetStageName).FirstOrDefault();
if (targetStage != null)
{
crmWorkflowContext.Trace($"BPF contains target stage (\"{targetStageName}\"). Attempting to update BPF");
// Setting the Traversed Path is necessary for the Business Process Flow to show the active Stage
// If this is not updated then although the new Stage is set as current, the previous Stage remains actively selected
var traversedPath = $"{bpf.TraversedPath},{targetStage.ProcessStageId.Value}";
var update = new BusinessProcessFlowInstance()
{
BusinessProcessFlowInstanceId = bpf.BusinessProcessFlowInstanceId,
ProcessStageId = targetStage.ProcessStageId,
TraversedPath = traversedPath
};
xrmContext.Attach(update);
xrmContext.UpdateObject(update);
}