1
votes

Below is a business process flow (BPF) on the Opportunity entity. I created a plugin that auto-advances this BPF to the last stage on Update of a field on the Opportunity.

bpf

When sending a RetrieveActivePathRequest message for an Opportunity that meets the condition pictured above (and thus utilizes all stages of the BPF), I receive a RetrieveActivePathResponse and my plugin works as expected:

        // Retrieve the process stages in the active path of the current process instance
        RetrieveActivePathRequest activePathRequest = new RetrieveActivePathRequest
        {
            ProcessInstanceId = activeProcessId
        };

        RetrieveActivePathResponse activePathResponse = (RetrieveActivePathResponse)service.Execute(activePathRequest);

However, sending a RetrieveActivePathRequest message for an Opportunity that does not meet the condition (and thus utilizes only the first three stages of the BPF) results in a System.ServiceModel.FaultException.

I suspect this may be due to the "else" condition in the BPF not branching to a stage. I added a shared stage and connected it with the "else" condition and the last stage pictured above and no longer experienced the exception. My plugin worked as expected and auto-advanced the BPF.

Two questions:

  1. Why is this the case? I can manually advance through each stage and finish the BPF in either scenario, so why can't I programmatically do the same?

  2. Is there an alternative way to handle this scenario so I can auto-advance the BPF (as is) with my plugin? I would prefer not creating a shared stage or two separate BPFs if at all possible.

Edit:

If anyone is curious, this is the entire function I use to auto-advance the BPF.

    private static void AdvanceBusinessProcessFlow(IOrganizationService service, ExtendedPluginContext context, Guid opportunityId)
    {
        // Retrieve all process instances
        RetrieveProcessInstancesRequest instanceRequest = new RetrieveProcessInstancesRequest
        {
            EntityId = opportunityId,
            EntityLogicalName = XrmOpportunity.EntityLogicalName
        };
        RetrieveProcessInstancesResponse instanceResponse = (RetrieveProcessInstancesResponse)service.Execute(instanceRequest);

        // First record is the active process instance
        Entity activeProcessInstance = instanceResponse.Processes.Entities[0];
        var activeProcessId = activeProcessInstance.Id;
        var activeStageId = new Guid(activeProcessInstance.Attributes["processstageid"].ToString());

        // Retrieve the process stages in the active path of the current process instance
        RetrieveActivePathRequest activePathRequest = new RetrieveActivePathRequest
        {
            ProcessInstanceId = activeProcessId
        };

        // System.ServiceModel.FaultException exception occurs here
        RetrieveActivePathResponse activePathResponse = (RetrieveActivePathResponse)service.Execute(activePathRequest);

        string activeStageName;
        int? activeStagePosition = null;
        int stageCount = activePathResponse.ProcessStages.Entities.Count;

        // Iterate through all process stages and identify active stage
        for (int i = 0; i < stageCount; i++)
        {
            if (activePathResponse.ProcessStages.Entities[i].Attributes["processstageid"].ToString() == activeStageId.ToString())
            {
                activeStageName = activePathResponse.ProcessStages.Entities[i].Attributes["stagename"].ToString();
                activeStagePosition = i;
            }
        }

        // If an active stage position is not identified, do nothing
        if (activeStagePosition == null)
        {
            throw new InvalidPluginExecutionException("No active stage of business process flow was identified!");
        }

        // Auto-advance active stages of BPF to last stage so that BPF can be auto-finished
        while (activeStagePosition < stageCount - 1)
        {
            // Retrieve the stage ID of the next stage to be set as the active stage
            var newActiveStageId = (Guid) activePathResponse.ProcessStages.Entities[(int) ++activeStagePosition].Attributes["processstageid"];

            // Retrieve the process instance record to update its active stage
            ColumnSet columnSet = new ColumnSet();
            columnSet.AddColumn("activestageid");
            Entity retrievedProcessInstance = service.Retrieve(dfnd_opportunitydispoprocess.EntityLogicalName, activeProcessId, columnSet);

            // Update active process stage
            retrievedProcessInstance["activestageid"] = new EntityReference(ProcessStage.EntityLogicalName, newActiveStageId);
            service.Update(retrievedProcessInstance);
        }
    }
1
What does the FaultException tell you? It has a Details property, which has Message property. This should give your more information on the exception. catch (FaultException<OrganizationServiceFault> ex) { // ex.Details.Message; } - Alessi
You cannot progress to an invalid stage - jasonscript
@Alessi "An unexpected error occurred." - shoogazer
@jasonscript That makes sense, but I do not attempt to progress the BPF until I receive the RetrieveActivePathResponse object which contains data about the stages in the BPF (via its ProcessStages property). Perhaps there is some documentation explaining why this object is not returned in my scenario? - shoogazer

1 Answers

0
votes

Directing to a stage when the BPF condition is not met prevented the error from being thrown:

bpf2

I still don't know why a RetrieveActivePathResponse object would not be returned for an Opportunity that does not meet the BPF condition.