Workflows Archives - Aric Levin's Digital Transformation Blog http://aric.isite.dev/tag/workflows/ Microsoft Dynamics 365, Power Platform and Azure Thu, 12 May 2022 04:57:39 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.3 Action Parameters referencing deleted entities http://aric.isite.dev/dynamics/post/action-parameters-referencing-deleted-entities/ Fri, 02 Feb 2018 06:35:00 +0000 https://aric.isite.dev/index.php/2018/02/02/action-parameters-referencing-deleted-entities/ While working on a project, I recently encountered a strange behavior in Dynamics 365. I am not sure if this is a bug (or feature), but something that we should be really careful about. One of the entities that we were working on, was created without following the organizational guidelines and had to be recreated. In this particular scenario it is not really a big deal because the project is still in early stages of implementation so changes and cleanup occur on a regular basis.

The post Action Parameters referencing deleted entities appeared first on Aric Levin's Digital Transformation Blog.

]]>
While working on a project, I recently encountered a strange behavior in Dynamics 365. I am not sure if this is a bug (or feature), but something that we should be really careful about. One of the entities that we were working on, was created without following the organizational guidelines and had to be recreated. In this particular scenario it is not really a big deal because the project is still in early stages of implementation so changes and cleanup occur on a regular basis.

We removed all of the references to the entity, and the entity got deleted. So far no issues. One of the developers on the project created an action which one of the Input Parameters was an Entity Reference to the deleted entity. After the entity was deleted, when the developer tried to open the Action, he got a Query error, and could not open the action. In the Url of the designer we could see that there was an error that said “Entity xyz_customentity does not exist in the metadata cache”.

After a little brainstorming, we exported the action (as a solution), and found the Xml of the input parameter that was pointing to the entity. We could remove the Input Parameter Xml and reimport the solution and hope that everything would work, or the probably easier solution was to just recreate the entity (no extra customization needed) and keep the same schema name.

We recreated the entity, and immediately the action opened up. We then went on and removed the Input Parameter that was causing the issue, activated the action and deleted the entity. Quick Fix to an annoying problem.
The bottom line is this this should probably not happen.

I submitted this to ideas.dynamics.com. If anyone is interested in voting up this idea, you can click on the following link:
https://ideas.dynamics.com/ideas/dynamics-crm/ID0003925

The post Action Parameters referencing deleted entities appeared first on Aric Levin's Digital Transformation Blog.

]]>
Process Deactivate Action on Multiple Records using Custom Workflow http://aric.isite.dev/dynamics/post/deactivate-multiple-records-custom-workflow/ Wed, 31 Jan 2018 04:16:00 +0000 https://aric.isite.dev/index.php/2018/01/31/process-deactivate-action-on-multiple-records-using-custom-workflow/ In a few of my recent implementation I had the requirement of making modifications to many child records. Some of these processes required the use of an external SSIS package that executed a console application and ran on a schedule, but more recently I had a requirement to simply deactivate multiple child records. This of course can also be done using cascading rules, but it's a little more complicated once you involve custom filtering.

The post Process Deactivate Action on Multiple Records using Custom Workflow appeared first on Aric Levin's Digital Transformation Blog.

]]>
In a few of my recent implementation I had the requirement of making modifications to many child records. Some of these processes required the use of an external SSIS package that executed a console application and ran on a schedule, but more recently I had a requirement to simply deactivate multiple child records. This of course can also be done using cascading rules, but it’s a little more complicated once you involve custom filtering.

In the post I will go through the steps of creating the custom workflow activity to handle this type of request. The first thing is that we create a new class library project (.NET Framework 4.5.2), and add references to the following assemblies:
Microsoft.Crm.Sdk.Proxy, Microsoft.Xrm.Sdk, Microsoft.Xrm.Sdk.Workflow, Newtonsoft.Json and System.Activities.

We will rename our class SetStateChildRecords, and set it to inherit from the CodeActivity abstract class. The namespace declarations should include the following:

using System;
using System.Activities;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Metadata;
using Microsoft.Xrm.Sdk.Metadata.Query;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Workflow;
using Microsoft.Crm.Sdk.Messages;
using Newtonsoft.Json;

We will need the following Input Parameters added to the class in order to implement the solution.

#region Input/Output Parameters

/// Url of the Parent Record that we want to disable the child records for
[Input("Record Url")]
[RequiredArgument]
public InArgument<string> RecordUrl { get; set; }

/// The name of the 1:N relationship between the parent and child record
[Input("Relationship Name")]
[RequiredArgument]
public InArgument<string> RelationshipName { get; set; }

/// The name of the child entity lookup field pointing to the parent entity
[Input("Child Entity Related Field")]
[RequiredArgument]
public InArgument<string> ChildEntityRelatedFieldName { get; set; }

/// The integer value of the State Code field for deactivation (usually will be 1)
[Input("State Code")]
[RequiredArgument]
public InArgument<int> StateCode { get; set; }

/// The integer value of the Status Code field for deactivation (usually will be 2)
[Input("Status Code")]
[RequiredArgument]
public InArgument<int> StatusCode { get; set; }

#endregion

The ChildEntityRelatedFieldName is not required as this can be retrieved from the relationship, but was added here for ease of use. The StateCode and StatusCode field values allow using the same code for both Deactivation and Reactivation of child records.
An addition Input Argument called Condition can be added to add a Condition Expression to this logic, so that it will filter only a subset of the disabled records.

The next step will be to create the Execute method of the Workflow. The method will retrieve the relationship information, the child records and call an update method to update the Status of the Child Entity records.

protected override void Execute(CodeActivityContext executionContext)
{
    try
    {
        //Create the context
        IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
        IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
        IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

        EntityReference primaryEntity = ConvertToEntityReference(service, RecordUrl.Get<string>(executionContext));
        string relationshipName = RelationshipName.Get<string>(executionContext);

        Relationship relationship = new Relationship(relationshipName);
        RetrieveEntityRequest request = new RetrieveEntityRequest()
        {
            LogicalName = primaryEntity.LogicalName,
            EntityFilters = EntityFilters.Relationships,
            RetrieveAsIfPublished = true
        };
        RetrieveEntityResponse response = (RetrieveEntityResponse)service.Execute(request);

        string childEntityName = null;
        OneToManyRelationshipMetadata oneToNRelationship = response.EntityMetadata.OneToManyRelationships.FirstOrDefault(r => r.SchemaName == relationshipName);
        if (oneToNRelationship != null)
        {
            childEntityName = oneToNRelationship.ReferencingEntity;
        }

        if (childEntityName != null)
        {
            string parentAttributeName = ChildEntityRelatedFieldName.Get<string>(executionContext);

            EntityCollection rc = RetrieveChildEntityRecords(service, childEntityName, parentAttributeName, primaryEntity);
            if (rc.Entities.Count > 0)
            {
                int stateCode = StateCode.Get<int>(executionContext);
                int statusCode = StatusCode.Get<int>(executionContext);

                foreach (Entity entity in rc.Entities)
                {
                    UpdateEntityStatus(service, childEntityName, entity.Id, stateCode, statusCode);
                }
            }
        }
    }
    catch (FaultException<OrganizationServiceFault> ex)
    {
        throw new Exception("WorkflowUtilities.SetStateChildRecords: " + ex.Message);
    }
}

We will show ConvertToEntityReference function for the Record Url at the end. This method retrieve the name of the child entity of the relationship using the RetrieveEntityRequest and OneToManyRelationshipMetadata messages. Once all the information is contained we call the RetrieveChildRecords passing the entity name and the condition. The RetrieveChildEntityRecords is a simple query expression that has a condition where the lookup is equal to the parent record unique identifier and looks like this:

private EntityCollection RetrieveChildEntityRecords(IOrganizationService service, string entityName, string attributeName, EntityReference primaryEntity)
{
    QueryExpression query = new QueryExpression(entityName)
    {
        ColumnSet = new ColumnSet(entityName + "id"),
        Criteria = new FilterExpression(LogicalOperator.And)
        {
            Conditions =
            {
                new ConditionExpression(attributeName, ConditionOperator.Equal, primaryEntity.Id)
            }
        }
    };

    try
    {
        EntityCollection results = service.RetrieveMultiple(query);
        return results;
    }
    catch (FaultException<OrganizationServiceFault> ex)
    {
        throw new Exception("WorkflowUtilities.SetStateChildRecords.RetrieveChildEntityRecords: " + ex.Message);
    }
}

 Finally we look through the list of records, and call the UpdateEntityStatus method passing the StateCode and StatusCode values in order to call the SetStateRequest message:

private void UpdateEntityStatus(IOrganizationService service, string entityName, Guid entityId, int stateCode, int statusCode)
{
    EntityReference moniker = new EntityReference(entityName, entityId);

    SetStateRequest request = new SetStateRequest()
    {
        EntityMoniker = moniker,
        State = new OptionSetValue(stateCode),
        Status = new OptionSetValue(statusCode)
    };

    try
    {
        SetStateResponse response = (SetStateResponse)service.Execute(request);
    }
    catch (FaultException<OrganizationServiceFault> ex)
    {
        throw new Exception("WorkflowUtilities.SetStateChildRecords.RetrieveChildEntityRecords: " + ex.Message);
    }
}

 Finally, we have the ConvertToEntityReference function. The function retrieves the entity type code and record id from the Record Url, and call the RetrieveMetadataChangesRequest to get the Entity Reference for the record Url. The code is shown below.

public EntityReference ConvertToEntityReference(IOrganizationService service, string recordReference)
{
    try
    {
        var jsonEntityReference = JsonConvert.DeserializeObject<JsonEntityReference>(recordReference);
        return new EntityReference(jsonEntityReference.LogicalName, jsonEntityReference.Id);
    }
    catch (Exception e)
    {
        throw new Exception("Error converting string to EntityReference", e);
    }
}

The above method Deserializes the Json object and returns an Entity Reference for the Record Url. You can also perform the same function by Parsing the record url and calling the RetrieveMetadataChangesRequest passing and EntityQueryExpression with the object type code and if from the actual Url.

The JsonEntityReference above is simply a class with two get/set properties (containing Json Attributes):

public class JsonEntityReference
{
    [JsonProperty(PropertyName = "entityType")]
    public string LogicalName { get; set; }

    [JsonProperty(PropertyName = "id")]
    public Guid Id { get; set; }
}

This is basically it. You can perform a lot of different workflow activities that manipulate relationships and multiple parent/child records, and even pass condition expression or complete fetchXml queries. The screenshot below shows the Custom Workflow window.

Set State Child Records Custom Workflow Activity
I would like to thank Andrew Butenko for some guidance in this area, and for proving the community with Ultimate Workflow Toolkit.

The post Process Deactivate Action on Multiple Records using Custom Workflow appeared first on Aric Levin's Digital Transformation Blog.

]]>
Creating an Workflow to Update an Composite Address field http://aric.isite.dev/dynamics/post/create-workflow-to-update-composite-address/ Wed, 22 Nov 2017 19:30:00 +0000 https://aric.isite.dev/index.php/2017/11/22/creating-an-workflow-to-update-an-composite-address-field/ There might be certain projects where you have a custom entity with address fields, and you want have a composite field that will display the address, and be able to use that process across the board in multiple entities. The example below shows how to do that.

The post Creating an Workflow to Update an Composite Address field appeared first on Aric Levin's Digital Transformation Blog.

]]>
There might be certain projects where you have a custom entity with address fields, and you want have a composite field that will display the address, and be able to use that process across the board in multiple entities. The example below shows how to do that.

The first step that we need is to create the Custom Workflow Activity. You can follow the msdn article on basic instructions how to create a workflow activity, and see the sample code that is available as part of the SDK.

Inside the Custom Workflow Activity, we need to specify the input and output parameters. The input parameters will be the address fields (street 1, street2, etc…), and the output parameter will be the Composite Address.

        [RequiredArgument]
        [Input("Street 1")]
        public InArgument<string> Street1 { get; set; }

        [RequiredArgument]
        [Input("Street 2")]
        public InArgument<string> Street2 { get; set; }

        [Input("Street 3")]
        public InArgument<string> Street3 { get; set; }

        [RequiredArgument]
        [Input("City")]
        public InArgument<string> City { get; set; }

        [RequiredArgument]
        [Input("State")]
        public InArgument<string> State { get; set; }

        [RequiredArgument]
        [Input("Postal Code")]
        public InArgument<string> PostalCode { get; set; }

        [Output("Composite Address")]
        public OutArgument<string> Address { get; set; }

In the Execute method of the Workflow Activity, we will get the values of the values that will be entered in the workflow, call the function to create the composite address, and then set the composite address to the Output parameter, as shown below:

        protected override void Execute(CodeActivityContext executionContext)
        {
            //Create the context
            IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
            IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            string street1 = Street1.Get<string>(executionContext);
            string street2 = Street2.Get<string>(executionContext);
            string street3 = Street3.Get<string>(executionContext);
            string city = City.Get<string>(executionContext);
            string state = State.Get<string>(executionContext);
            string postalCode = PostalCode.Get<string>(executionContext);

            string compositeAddress = GenerateFullAddress(street1, street2, street3, city, state, postalCode);
            Address.Set(executionContext, compositeAddress);
        }

Finally, we will show the code to Generate the Composite address in the GenerateFullAddress function, as shown below:

        /// <summary>
        /// Generate Full Address. Fomrat Will be:
        /// Street 1
        /// Street 2
        /// City, State, Zip
        /// </summary>
        private string GenerateFullAddress(string line1, string line2, string line3, string city, string state, string postalcode)
        {
            StringBuilder sb = new StringBuilder();
            if (!String.IsNullOrEmpty(line1))
            {
                sb.Append(line1);
                sb.AppendLine();
            }

            if (!String.IsNullOrEmpty(line2))
                sb.AppendLine(line2);

            if (!String.IsNullOrEmpty(city))
            {
                sb.Append(city);
                if (!String.IsNullOrEmpty(state))
                {
                    sb.Append(", " + state);
                    if (!String.IsNullOrEmpty(postalcode))
                    {
                        sb.Append(" " + postalcode);
                    }
                }
                else
                {
                    if (!String.IsNullOrEmpty(postalcode))
                    {
                        sb.Append(" " + postalcode);
                    }
                }
            }
            else
            {
                if (!String.IsNullOrEmpty(state))
                {
                    sb.Append(", " + state);
                    if (!String.IsNullOrEmpty(postalcode))
                    {
                        sb.Append(" " + postalcode);
                    }
                }
                else
                {
                    if (!String.IsNullOrEmpty(postalcode))
                    {
                        sb.Append(" " + postalcode);
                    }
                }
            }
            return sb.ToString();
        }

Once we have create our custom activity workflow, we need to Publish it using the Plugin Registration Tool. After we publish the workflow, we will need to create a workflow within out CRM environment. The screenshot below shows the created workflow, and the steps to run it. The image below shows the create workflow and it’s steps:

Composite Address Workflow

Note that the Start options for the workflow are set to Record is created and Record fields change, so that it executes on both Create and Update. In the record fields change enter all the address fields that you want to execute the workflow on (street1, street2, city…).

Next you will see that there are two steps to the workflow.

The Generate Full Address (shown below), calls the custom workflow activity passing the address fields and gets the returned Output Composite Address field data.

AddressOnCreateUpdate

Next we will need to update the Composite Address field, by calling an Update method and passing the Composite Address that was returned from the first step, as shown in the following image:

Update Record

That’s basically it. You will not be able to make changes to the address fields and have them generate the Composite Address.

The source code of this blog article is available here.

The post Creating an Workflow to Update an Composite Address field appeared first on Aric Levin's Digital Transformation Blog.

]]>
Designing a Sales Process in CRM http://aric.isite.dev/dynamics/post/designing_a_sales_process/ Wed, 07 Nov 2012 02:54:00 +0000 https://aric.isite.dev/index.php/2012/11/07/designing-a-sales-process-in-crm/ Out of the box, Microsoft Dynamics CRM does not implement any particular sales process. It is a customizable platform, which means that you can built your own sales process, but it will not be available by default.

The post Designing a Sales Process in CRM appeared first on Aric Levin's Digital Transformation Blog.

]]>
Out of the box, Microsoft Dynamics CRM does not implement any particular sales process. It is a customizable platform, which means that you can built your own sales process, but it will not be available by default.

In this article we will show how to use the probability field in the opportunity entity to automatically design a sales process. This particular case is only a small example of what can actually be implemented. We can also use the Pipeline Phase or Process code fields to retrieve that information.

We will start by creating a new workflow process, and call the process Opportunity Probability Change. The Entity will be Opportunity and the Category Workflow.

Out of the box, Microsoft Dynamics CRM does not implement any particular sales process. It is a customizable platform, which means that you can built your own sales process, but it will not be available by default.

In this article we will show how to use the probability field in the opportunity entity to automatically design a sales process. This particular case is only a small example of what can actually be implemented. We can also use the Pipeline Phase or Process code fields to retrieve that information.

 We will start by creating a new workflow process, and call the process Opportunity Probability Change. The Entity will be Opportunity and the Category Workflow.

We will start by creating a Check Condition Step. The step will check if the Probability is Greater than or equal to 75. We will then add three more Conditional Branch Steps to check the other probabilities as shown in the table below:  

Step Entity Condition 1 Condition 2
Check Condition Opportunity Probability >= 75
Conditional Branch Opportunity Probability >= 50 Probability < 75
Conditional Branch Opportunity Probability >= 25 Probability < 50
Conditional Branch Opportunity Probability < 25

 After we have added the Check Conditions and Conditional Branches, we will go ahead and add the following actions to each step, as shown in the image below:

We can specify the due dates for each of the above tasks or email messages by setting number of days after the modified date using Dynamic Values.
Once we have finished setting up all the steps of the Workflow, we can go ahead and activate it.

We will not go ahead and open and opportunity and set the probability field to 10%, 35%, 60% and 80%.
I will save the record each time after I modify the probability field.

We can see that the activity records were automatically created for this opportunity in the image below:

As we mentioned in the beginning of the article, Microsoft Dynamics CRM does not support out-of-the-box sales process functionality, but can be easily used to create a customized sales process for your sales and marketing teams. 

The post Designing a Sales Process in CRM appeared first on Aric Levin's Digital Transformation Blog.

]]>
Recurring CRM Plugin Execution using Workflows http://aric.isite.dev/dynamics/post/recurring_crm_plugin_execution_using_workflows/ Sun, 02 Sep 2012 21:35:00 +0000 https://aric.isite.dev/index.php/2012/09/02/recurring-crm-plugin-execution-using-workflows/ Many times we are required to have a process or task that will automatically execute at a predefined interval (such as every first of the month), but Microsoft Dynamics CRM does not have a built-in engine to have such a process. An example of this would be updating the Month-To-Date totals of a particular entity.

The post Recurring CRM Plugin Execution using Workflows appeared first on Aric Levin's Digital Transformation Blog.

]]>
Many times we are required to have a process or task that will automatically execute at a predefined interval (such as every first of the month), but Microsoft Dynamics CRM does not have a built-in engine to have such a process. An example of this would be updating the Month-To-Date totals of a particular entity.

The example below will show how to use a Workflow Process with the combination of a Plugin to execute on a monthly basis.

Many times we are required to have a process or task that will automatically execute at a predefined interval (such as every first of the month), but Microsoft Dynamics CRM does not have a built-in engine to have such a process. An example of this would be updating the Month-To-Date totals of a particular entity.

The example below will show how to use a Workflow Process with the combination of a Plugin to execute on a monthly basis.

In order to implement the example, we will start by creating a custom entity that will store the execution log of the process.
For this example we created an entity called Process Log which the required field called title.

The next part will be created a Workflow that will run on a monthly basis to create a new record in this Process Log Entity that we created.

Notice that the workflow is Available to Run manually (As an on-demand process) and “As a child process“, for the subsequent runs which will be automatic.

Next, we will require to add the steps of the Workflow. These steps will include

  • Create a Process Log Record
  • Set Timeout of Process for One Month
  • Run the Process Again as a Child Workflow Process

Create a Process Log Record

We will create a New Process Log Record with a Proper Title to distinguish between the different process Logs.
We will later create a Process Log Create Plugin Message which will execute when the Workflow creates this record.

Add a Timeout for the Process

We will need to add a timeout to the workflow engine, so that the process will repeat every month.
In order to do so we will insert a Wait Step that will tell the Workflow Engine to wait for 1 month.

After we add the Timeout for the Process we will have to call the Workflow again, so that the same process will re-execute.
We do this recurring pattern by adding a Child Workflow step that will execute wait the Timeout has been completed.

Start Child Workflow

We will use the Add Step dropdown and choose the Start Child Workflow option.
We will choose Process Log as the Entity for the Workflow and Monthly System Update as the actual Workflow.

Before we execute the initial workflow for the first time, we have to make sure that the plug-in is in place, so we will develop a Create Message Plugin.

Build Create Message Plugin

public class ProcessLogOnCreate : IPlugin
{
IOrganizationService service;

public void Execute(IServiceProvider serviceProvider)
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
Entity entity;

if (context.InputParameters.Contains(“Target”) && context.InputParameters[“Target”] is Entity)
{
entity = (Entity)context.InputParameters[“Target”];
if (entity.LogicalName != “xrm_processlog”)
{
return;
}
}
else
{
return;
}

try
{
if (context.Depth > 1)
return;
else
{
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
service = serviceFactory.GetOrganizationService(context.UserId);

Guid id = (Guid)context.PrimaryEntityId;
ExecuteTasks(id);
}
}
catch (FaultException<OrganizationServiceFault> ex)
{
throw new InvalidPluginExecutionException(“An error occured in the plug-in”, ex);
}
}
}

Build, Deploy and Test the Plug-in in your CRM environment. Once we are satisfied that the Plugin works we initialize the Workflow.

Run the Workflow for the First Time

The Workflow must be instantiated manually for the first time. After the first time it is executed, it will automatically schedule itself to run monthly from the time of the original execution.
You can do this by going to the Primary Log Entity, for which the workflow was created, and Run the Workflow Manually.

After the initial run of the Workflow, you can check the Progress of the Workflow in the System Jobs, which will show it is waiting for execution to resume in one month.

Note: You should consider resources consumption when creating recurring workflows. Recurring workflows tend to use a large amount of resources in the CRM Asynchronous Processing Service. 

The post Recurring CRM Plugin Execution using Workflows appeared first on Aric Levin's Digital Transformation Blog.

]]>