Get Business Process ID and Stage ID using name

Quick Post:

It is an normal request to get to auto set the Business Process Flow stage based on some condition. It is stright forward and you will also find many blogs to update the stage. But when i was working on it, it found it difficult to search for a post that explains how to get Process ID and Stage ID based on name. This is important to avoid hardcoding of process and stage id in the code.

Below sample will help to get both Process ID and Stage ID based on Process and Stage name and also update the business process stage.

Guid processId = Guid.Empty;
Guid stageId = Guid.Empty;

        //Get Process ID for BPF "Case Resolution"
        EntityCollection workflowRecords = new EntityCollection();
        QueryExpression query = new QueryExpression("workflow");
        query.NoLock = true;
        query.ColumnSet = new ColumnSet("workflowid");
        query.Criteria.AddCondition("name", ConditionOperator.Equal, "Case Resolution");
        workflowRecords = service.RetrieveMultiple(query);
        if (workflowRecords.Entities.Count > 0)
        {
            processId = workflowRecords.Entities.FirstOrDefault().GetAttributeValue<Guid>("workflowid");
        }

        //Get Stage ID for Stage "Resolution"
        EntityCollection stageRecords = new EntityCollection();
        QueryExpression query2 = new QueryExpression("processstage");
        query2.NoLock = true;
        query2.ColumnSet = new ColumnSet("processstageid");
        query2.Criteria.AddCondition("stagename", ConditionOperator.Equal, "Resolutions");
        query2.Criteria.AddCondition("processid", ConditionOperator.Equal, processId);
        stageRecords = service.RetrieveMultiple(query2);
        if (stageRecords.Entities.Count > 0)
        {
            stageId = stageRecords.Entities.FirstOrDefault().GetAttributeValue<Guid>("processstageid");
        }


        //Change the stage
        Entity updatedStage = new Entity("incident"); //Parent entity on which the BPF is
        updatedStage.Id = new Guid("ED973BF5-D382-493B-8E31-55892B2F0C52"); //Record id for which you which to update stage.
        updatedStage["stageid"] = stageId;
            updatedStage["processid"] = processId;
            service.Update(updatedStage);

Hope this helps!

Get selected options from Multiselect Option set

{QuickPost}

Recently had a requirement to get all selected option texts from a multi select option set. Getting selected values is straight forward but I wanted to get the Label of selected values. For normal option set we can get it easily by using FormattedValues but that does not work with Multiselect Option set.

For C#:

Then found an handy code by Ravi Kashyap that was posted on community here. I have just restructured the function to make it generic.

Now just call the getSelectedOptionSetText() function and pass Organization Service, entity name and MultiSelect optionset field name.

The function first retrieves all the labels from the Option Set and then filters it with selected option set values.

public string getSelectedOptionSetText(IOrganizationService service, Entity enRecord, string fieldName)
{
string selectedText = string.Empty;
// Get the Formatted Values of MultiSelect OptionSet
List multiSelectTextCollection = GetOptionSetTextCollection(service, enRecord.LogicalName, fieldName);

if (enRecord.Contains(fieldName))
{
OptionSetValueCollection multiSelectValueCollection = (OptionSetValueCollection)enRecord[fieldName];

foreach (var relationshipType in multiSelectValueCollection)
{
if (selectedText.Equals(string.Empty))
{
selectedText = multiSelectTextCollection.Where(x => x.Value == relationshipType.Value).FirstOrDefault().Label.UserLocalizedLabel.Label;
}
else
{
selectedText = selectedText + ", " + multiSelectTextCollection.Where(x => x.Value == relationshipType.Value).FirstOrDefault().Label.UserLocalizedLabel.Label;
}
}
}
return selectedText;
}


public static List GetOptionSetTextCollection(IOrganizationService service, string entityName, string fieldName)
{
var retrieveAttributeRequest = new RetrieveAttributeRequest()
{
EntityLogicalName = entityName,
LogicalName = fieldName,
RetrieveAsIfPublished = true
};

var retrieveAttributeResponse =(RetrieveAttributeResponse)service.Execute(retrieveAttributeRequest);

var attMetadata = (EnumAttributeMetadata)retrieveAttributeResponse.AttributeMetadata;

return attMetadata.OptionSet.Options.ToList();
}

For JavaScript:

To achieve the same using JavaScript is comparatively easy.

To get all selected option set values:

//Returns Array of Selected OptionSet Values: [798090000,798090001,798090002]
var selectedValues = formContext.getAttribute("new_multiselect").getValue();

To get all selected option set texts:

//Returns Array of Selected OptionSet Text: ["One", "Two","Three"]
var selectedTexts = formContext.getAttribute("new_multiselect").getText();

To set value (Overwrite existing selection with passed comma separated values)
formContext.getAttribute("net_multiselect").setValue([798090000,798090001,798090002]);

{Observation} Execution context of Real-time workflow

We all know that if we call a child workflow from a parent real-time workflow, it runs in context of that parent workflow but what happens if the trigger for the parent real-time workflow is updated using a custom application outside of CRM?

I had a custom .Net application which used to get values from CRM, send them to SAP and update the return values from SAP back into CRM. The custom application used to trigger as an batch application. There was a real-time workflow configured on update of the fields populated by the .Net application in CRM which internally used to call a child workflow process.

One day issue was reported by the .Net application as “[Microsoft.Xrm.Sdk.OrganizationServiceFault]: This workflow cannot run because one or more child workflows it uses have not been published or have been deleted. Please check the child workflows and try running this workflow again. (Fault Detail is equal to Microsoft.Xrm.Sdk.OrganizationServiceFault).”.

As the .Net application used to update the field which then used to trigger the real-time workflow (configured to run “After”) it was not quite obvious behavior for me. I did know that the child workflow was deactivated but my assumption was that the issue will be logged in system jobs and not in .Net application. I was expecting that .Net application will update the field in CRM, after which the parent real-time workflow will trigger which will try to call a deactivated child workflow and fail.

But after digging a bit it was observed that as the parent workflow was configured as real-time it keeps the context with calling .Net application and returns the error back to .Net application. Due to which the .Net application throw an exception. I’m still not sure why, but this is how it works.

DID YOU KNOW? Workflow process throws generic CRM error


Scenario

I have an OOB workflow process developed some time back and activated. Now when I try to open the process, I get generic CRM error (below image). Also the process status is changed to “Draft” automatically. There were no updates made to the process.

Cause

I have seen these issues frequently especially when working with Child Workflow Processes where I have a child workflow called from a Master workflow. If you have a Master workflow calling a child workflow and for some reason the child workflow is activated as “Process Template”, then user gets this error when they try to open the master workflow.

Conclusion

CRM throws generic exception and its status is changed to Draft if it linked/related to a “Process Template” instead of “Process”.

Note

Other common issue faced while working with workflow process is “Invalid Expression” error in “Condition Expression” step. This occurs if the field used in condition expression is either deleted from system or if Value is changed. Once you fix the field/value issue, all conditions in the process are automatically fixed.