Improving CRM Data Quality with Automated Data Validation

Ensuring high-quality data in your CRM system is critical for making informed business decisions and improving overall efficiency. However, manually validating data can be time-consuming and prone to errors. Automated data validation can help streamline this process and ensure that your CRM data is accurate and up-to-date.

What is automated data validation?

Automated data validation is the process of using software tools to automatically check data for accuracy and completeness. This can include checking for data types, formatting, and completeness. Automated data validation can be performed in real-time as data is entered into the CRM system or in batch mode on existing data.

Why is automated data validation important?

Automated data validation is important for several reasons:

  • Improves data quality: Automated data validation ensures that your CRM data is accurate, complete, and consistent. This improves the quality of your data and ensures that your business decisions are based on reliable information.
  • Reduces errors: Manually validating data is prone to errors and can be time-consuming. Automated data validation helps reduce errors and ensures that data is validated consistently across your organization.
  • Streamlines data entry: Real-time data validation can help streamline the data entry process by providing immediate feedback to users. This ensures that data is entered correctly the first time, reducing the need for data cleanup later.

How to implement automated data validation in your CRM system

Here are some steps to follow when implementing automated data validation in your CRM system:

  1. Identify the data to be validated: Determine which fields and data types need to be validated.
  2. Create validation rules: Create validation rules that define the criteria for valid data. This can include rules for data types, formatting, and completeness.
  3. Implement validation rules: Implement the validation rules in your CRM system using workflows, plugins, or JavaScript.
  4. Test validation rules: Test the validation rules to ensure that they're working correctly and providing the desired results.
  5. Monitor data quality: Monitor data quality over time to ensure that the validation rules continue to be effective and that data quality is improving.

Conclusion

Automated data validation is a powerful tool for improving the quality of your CRM data. By implementing validation rules and monitoring data quality over time, you can ensure that your business decisions are based on reliable information and that your CRM system is providing maximum value to your organization.

Tips and Tricks for Customizing Dynamics CRM Forms

Dynamics CRM provides a wide range of options for customizing forms to meet your business needs. However, it can be overwhelming to navigate through all the available options and features. In this blog post, we'll share some tips and tricks for customizing Dynamics CRM forms that can help streamline your business processes.

1. Use form design best practices

The first step in customizing Dynamics CRM forms is to ensure that they're designed with best practices in mind. This means that your forms should be user-friendly, visually appealing, and consistent across your organization. Consider the following best practices:

  • Use a consistent layout across all your forms: This makes it easier for users to navigate through different forms and fields.
  • Limit the number of fields on a form: Too many fields can overwhelm users and reduce the effectiveness of the form.
  • Use sub-grids to display related records: Sub-grids allow you to display related records on a form, reducing the need for users to navigate to different areas of the CRM.
  • Use business rules to hide or show fields: Business rules can be used to show or hide fields based on specific conditions, making the form more relevant to users.

2. Customize fields to match your business needs

Dynamics CRM provides a range of field types that can be used to capture data. By customizing these fields to match your business needs, you can ensure that the data captured is accurate and relevant. Consider the following customizations:

  • Rename fields: Rename fields to match your business terminology.
  • Add custom fields: If you need to capture additional data that isn't included in the default fields, create custom fields.
  • Modify field properties: Modify field properties, such as the maximum length of a field or the format of a date field, to match your business needs.

3. Use form scripts to automate processes

Form scripts can be used to automate processes within Dynamics CRM, reducing the amount of manual work required by users. Consider the following use cases:

  • Automatically populate fields: Form scripts can be used to automatically populate fields based on specific criteria, such as the user's location or the type of record being created.
  • Validate data: Form scripts can be used to validate data entered by users, reducing errors and ensuring data accuracy.
  • Perform calculations: Form scripts can be used to perform calculations based on data entered by users, such as calculating the total value of an opportunity.

4. Leverage the power of sub-grids

Sub-grids allow you to display related records on a form, making it easier for users to access relevant data. Consider the following use cases:

  • Display related activities: Use a sub-grid to display related activities, such as emails or phone calls, on a contact form.
  • Display related opportunities: Use a sub-grid to display related opportunities on an account form.
  • Display related cases: Use a sub-grid to display related cases on a contact form.

By following these tips and tricks, you can customize Dynamics CRM forms to match your business needs, improve user productivity, and streamline business processes.

Remove irrelevant activities from timeline for a record, by removing the activity parties

Recently, I had faced an issue. A Contact record ABC was updated with email address 2 field to the Customer Service Email (customerservice@company.com) on the 10th of May. Upon investigating, one of the users intentionally updated this contact record. This led to all of the Customer Service emails getting tracked to the timeline of ABC's Contact record. The customer service email address was removed from the Contact record on 20th May. While no new emails are getting tracked to the timeline, but any replies to the already tracked emails are still syncing to the Contact daily.


Potential Solution: A ticket was raised with MS for finding a solution to this issue. They proposed deleting and recreating the contact record, however, the contact contains several child records and the impact would be severe. So an alternate solution could be to remove the Contact (ABC) from the "To" field of the tracked emails, by running a script.


Following is the code that I have used to remove the ABC contact record from the collection of the "TO" parties, for the closed email messages.


    Guid contactGuid = new Guid("f6c42c30-150c-e511-80c8-00155d0a471a");

    string query = @"<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='true'>
                    <entity name='email'>
                    <attribute name='activityid' />
                    <attribute name='to' />
                    <attribute name='statuscode' />
                    <attribute name='statecode' />
                        <filter type='and'>
                            <condition attribute='createdon' operator='on-or-after' value='2022-05-10' />                           
                            <condition attribute='regardingobjectid' operator='ne' value='{F6C42C30-150C-E511-80C8-00155D0A471A}' />
                        </filter>
                    <link-entity name='activityparty' from='activityid' to='activityid' link-type='inner' alias='ao'>
                        <filter type='and'>
                            <condition attribute='partyid' operator='eq' value='{F6C42C30-150C-E511-80C8-00155D0A471A}' />
                            <condition attribute='participationtypemask' operator='eq' value='2' />
                        </filter>
                    </link-entity>
                    </entity>
                    </fetch>";

    List<Entity> emails = service.RetrieveMultiple(new FetchExpression(query)).Entities.ToList();//.Take(1).ToList();       
    emails.ForEach(email =>
    {
        EntityCollection to = email.GetAttributeValue<EntityCollection>("to");
        if (to != null)
        {
            to.Entities.ToList().ForEach(party =>
            {
                EntityReference partyId = party.GetAttributeValue<EntityReference>("partyid");
                string addressUsed = party.GetAttributeValue<string>("addressused");
                if (partyId?.Id == contactGuid && addressUsed == "customerservice@company.com")
                {
                    to.Entities.Remove(party);
                }
            });
        }

        //Set to Draft
        var stateRequest = new SetStateRequest
        {
            State = new OptionSetValue(0),
            Status = new OptionSetValue(1),
            EntityMoniker = email.ToEntityReference()
        };
        service.Execute(stateRequest);

        //Update Parties
        Entity emailToUpdate = new Entity("email");
        emailToUpdate.Id = email.Id;
        emailToUpdate["to"] = to;
        service.Update(emailToUpdate);

        //Revert Status
        var stateRequestAfter = new SetStateRequest
        {
            State = email.GetAttributeValue<OptionSetValue>("statecode"),
            Status = email.GetAttributeValue<OptionSetValue>("statuscode"),
            EntityMoniker = email.ToEntityReference()
        };
        service.Execute(stateRequestAfter);

    });
Hopefully, this post might help someone else who has simiar issues

How to call AddUserToRecordTeam action using Web API

Nothing special, but I'm posting here a simple JS function that calls the AddUserToRecordTeam action using web API. It took some time for me, especially in setting/fixing the parameters. Posting here reference.

    /*
    entityId: Guid of entity record
    entityLogicalName: Logical name of the entity, like lead/contact etc
    userId: Guid of the systemuser 
    templateId: team template id
    */
    function createAccessTeam(entityId,entityLogicalName,userId,templateId)
    {
        var parameters = {
            Record: { [entityLogicalName+"id"]: entityId, "@odata.type": "Microsoft.Dynamics.CRM."+entityLogicalName },
            TeamTemplate:{ "teamtemplateid": templateId, "@odata.type": "Microsoft.Dynamics.CRM.teamtemplate" }
        };
        var req = new XMLHttpRequest();
        req.open("POST", Xrm.Page.context.getClientUrl() + "/api/data/v9.1/systemusers("+userId+")/Microsoft.Dynamics.CRM.AddUserToRecordTeam", true);
        req.setRequestHeader("OData-MaxVersion", "4.0");
        req.setRequestHeader("OData-Version", "4.0");
        req.setRequestHeader("Accept", "application/json");
        req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
        req.onreadystatechange = function() {
            if (this.readyState === 4) {
                req.onreadystatechange = null;
                if (this.status === 200) {
                    var results = JSON.parse(this.response);
                } else {
                    Xrm.Utility.alertDialog(this.statusText);
                }
            }
        };
        req.send(JSON.stringify(parameters));
    }
Below is an example to call the above function:
var entityId="db4633b7-56d1-e811-a979-000d3af3e0db";
var entityLogicalName="lead",
var userId ="db4633b7-56d1-e811-a979-000d3af3e0db"
var templateId="45CED4C4-A971-EB11-A812-000D3AF39D99"
createAccessTeam(entityId,entityLogicalName,userId,templateId);

Upload a file to SharePoint document library using Power automate Flow

I had a requirement to upload a document to the SharePoint document library (integrated with the Account entity in dynamics CE) from the power apps portal, the portal supports uploading and displaying SharePoint documents as documented here ( by placing the document location subgrid on a portal basic/advance form), but our requirement was to allow portal users to upload documents only to specific folders.

For achieving this specific requirement, I have used an Html file uploader and a power automate flow that receives the request and creates a file in the SharePoint document library specific folder.

The file uploader HTML code snippet, that I have used.

The JS code that I used for posting the form-data with "mimeType": "multipart/form-data", to my power automate flow.


Following is the power automate flow that, triggers When a HTTP request is received.
For convenience 😋 one can copy the expression below.

A successful run output.
After a successful run, the file is added to the specific document library folder.

Hide a ribbon button on SubGrid for specific primary entity

When need to hide a button on a subgrid, we can solve this by just hiding the button. But in case we need to hide the button only on specific primary entities then we need to write/construct an enable rule. 

Here I was a having problem to hide the "+ New Activity" button on activity subgrid only when the subgrid is on the Contact entity. Achieved it by following quick steps :)


Create a solution | add Activity entity in solution | Open the solution in Ribbon Workbench | Right Click "Add New {0}" in the subGrid ribbon | Click Customize Command | find the command | add an enable Rule | add a step [EntityRule] | define it as following.




Publish the solution by clicking the Publish button in the top.






Dynamically Populating Ribbon Flyout Menu in Unified Interface

My previous post Dynamically Populating Ribbon Flyout Menu describes the way to add flyout menu buttons dynamically, but that was limited to web client and was not working in unified Interface.

As per my R&D and support ticket with Microsoft, we are still not able to add dynamic Flyout menu buttons directly in Unified Interface (UCI).

The issue is due to the dynamic buttons don't detect the command that we were adding through our JS code, so we need to attach that command to a hidden button so that internally the command is visible.

Following are the two steps we need to achieve the dynamic flyout in both Web client and unified interface. These steps are actually a continuation of my previous post.

Step#1: Adding a button, where you need to call the command that we have used for dynamic buttons.
Add a flyout button and make it hidden.


Add a menu section and then add a button, and call the command here.

Step#2: We need to update the JS code, i.e to append "lead|NoRelationship|Form|" (this is for lead entity form ribbon) at the start of the actual command for UCI.
function populateEnrollmentFlyout(commandProperties) { 
    var programsRibbonXml = "";
    var programs = retrieveMultiple('msd_programs', "?$select=msd_programid,msd_name");
 
    var command="msd.lead.Command.ProgramClicked";

    //This code is used to build the command string for UCI
    if(commandProperties.SourceControlId!=null)
    {
        var source=commandProperties.SourceControlId.split('|');
        if(source.length>3)
        {
             //command="lead|NoRelationship|Form|msd.lead.Command.ProgramClicked"
             command=source[0]+"|"+source[1]+"|"+source[2]+"|"+command;
        }
    }
    programsRibbonXml +="<MenuSection Id='msd.Lead.Programs.MenuSection' Sequence='10'><Controls Id='msd.Lead.Programs.Control'>"
    if (programs != null) {      
        for (var i = 0; i < programs.length; i++) {           
                var Name = programs[i].msd_name;
                var Value = programs[i].msd_programid;                
                programsRibbonXml+="<Button Id='" + Value + "' Command='" + command + "'  Sequence='"+((i+1)*10)+"' LabelText='" +Name +"' />" 
        }
    }
    programsRibbonXml +="</Controls></MenuSection>";
    commandProperties["PopulationXML"] = '<Menu Id="msd.Lead.Programs.Menu">' + programsRibbonXml + "</Menu>";
}
function programClicked(commandProperties) {
 alert ("program with id "+commandProperties.SourceControlId +" selected.");
}

Finally, the dynamic menu buttons will be shown in both Web client & Unified Interface.


















Hope it will help!