Found my First BUG on MSCRM 2013 !!! ExecutionObj.getEventArgs().preventDefault() working variantly on Lead Qualification in CRM-2013

Tags

, , , , , , ,

Hello Everyone,

Few weeks back, I got stuck with this Issue in CRM 2013. Actually I wanted to check for some validation before Qualifying a lead. So, during an On-Save Event I used the following JScript code for Validation before Save-Event for Lead Qualification(16). But apparently this doesn’t seems to be working correctly at times.

function FrmOnSave(prmContext)
  {  
     // Local variable to store value indicating how the save event was initiated by the user.
     var wod_SaveMode, wod_SaveEventVal;
     
     // Change the Save Event Value as per required Save Event
     wod_SaveEventVal = 16;

    if (prmContext != null && prmContext.getEventArgs() != null)
  {
 
         wod_SaveMode = prmContext.getEventArgs().getSaveMode();
 
         if (wod_SaveMode == wod_SaveEventVal)
  {
          // Write your validation code here
          if(Xrm.Page.getAttribute("industrycode").getValue() == 1)
 {
             // Use the code line below only if validation is failed then abort function save event

           alert("Choose Appropriate Industry Code");

           prmContext.getEventArgs().preventDefault();
  
             //var x = prmContext.getEventArgs().isDefaultPrevented();
             //alert(x);
 }
 
         }
     }
 }

This is working fine on CRM 2011, But on CRM 2013 it’s showing variant results. Like:

1> If I open a lead record and directly click on Qualify button then it Qualifies the Lead without any validation (preventDefault() doesn’t seems to work).

2> If I open a Lead form, change any random field value and then click on the Qualify button, it works fine again.

Failing to know the exact cause of this behavior I posted this question to the Microsoft Dynamics CRM Social Forum. Many Thanks to MVP Mahender Pal for his quick response as he too found this to be unusual and logged this Issue as a BUG on Microsoft Connect here

If you too have been facing similar Issue or have came across such related to PreventDefault(), request you all to kindly vote for the Microsoft Connect link provided below:-

https://connect.microsoft.com/dynamicssuggestions/feedback/details/844463/mvp-bug-preventdefault-is-not-working-on-lead-qualification

Note:- Recently Microsoft also updated this Issue as a BUG !!!  Notification of Same can be found on the same link above.

While Microsoft will come back to this in detailed description as a Resolution, I had to complete this task anyhow under my deliverable timeline. So as a workaround I used the new feature of Synchronous Workflow to achieve the above desired Validation as discussed in my Previous Post.

Hope this will be helpful.

Thanks !!!

😀

 

Advertisements

Convert HTML to Plain Text for copying Email Message in CRM 2011 / 2013

Tags

, , , ,

Hello Everyone,

Recently I had a requirement from one of my client where at the Opportunity Stage, any conversation with Email was suppose to be recorded and updated under its Description field.  This was for the sake of ease to be familiar with the Current Ongoing Scenario, without opening up the related E-Mail.

OK, now in CRM 2013 there is already a Notes Section under Opportunity where the related Emails can be viewed very easily but the Client was not interested for that Activities Section showing up on the main form(they actually liked the Old form style of CRM-2011). So to fulfill this I created a workflow on the Incoming Email where Description Field of the related Opportunity was set with the Email Messages.

 

After saving and Activating my workflow when I tested the functionality with a Email Scenario, results were quite horrifying !!! 😮

Actually the Email messages were getting saved in the Description field as HTML. Definitely, I needed to figure out a way to convert this message from HTML to Plain Text and this could be easily done with the help of Plugin. I found a very helpful code from CodeProject which exactly suits my requirement.

I wrote the following plugin on Update message for Another field which was also getting updated via Workflow.(i.e. apart from the Description field itself, Reason: This would create an Infinite loop and plugin Execution would stop with an error)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Discovery;
using Microsoft.Crm.Sdk.Messages;
using System.ServiceModel;

namespace UpdateOppDesc
{
    public class Class1:IPlugin
    {
        IOrganizationService service;
        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
            {
                Entity entity = (Entity)context.InputParameters["Target"];

                try
                {
                    if (entity.LogicalName == "opportunity")
                    {
                        ColumnSet attributes = new ColumnSet(new string[] { "description" });
                        Entity entty = service.Retrieve(entity.LogicalName, entity.Id, attributes);
                        if (entty.Contains("description") && entty.Attributes["description"] != null)
                        {
                            String desc = entty.Attributes["description"].ToString();
                            String desc2 = StripHTML(desc);
                            entty.Attributes["description"] = desc2;
                            service.Update(entty);
                        }
                        else { }
                          
                    }

                }

                catch (Exception ex)
                {
                    throw new InvalidPluginExecutionException("An error occured in the Plugin:-", ex);
                }

            }

        }

        private string StripHTML(string source)
        {
            try
            {
                string result;

                // Remove HTML Development formatting
                // Replace line breaks with space
                // because browsers inserts space
                result = source.Replace("\r", " ");
                // Replace line breaks with space
                // because browsers inserts space
                result = result.Replace("\n", " ");
                // Remove step-formatting
                result = result.Replace("\t", string.Empty);
                // Remove repeating spaces because browsers ignore them
                result = System.Text.RegularExpressions.Regex.Replace(result,
                                                                      @"( )+", " ");

                // Remove the header (prepare first by clearing attributes)
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*head([^>])*>", "<head>",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"(<( )*(/)( )*head( )*>)", "</head>",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         "(<head>).*(</head>)", string.Empty,
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);

                // remove all scripts (prepare first by clearing attributes)
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*script([^>])*>", "<script>",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"(<( )*(/)( )*script( )*>)", "</script>",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                //result = System.Text.RegularExpressions.Regex.Replace(result,
                //         @"(<script>)([^(<script>\.</script>)])*(</script>)",
                //         string.Empty,
                //         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"(<script>).*(</script>)", string.Empty,
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);

                // remove all styles (prepare first by clearing attributes)
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*style([^>])*>", "<style>",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"(<( )*(/)( )*style( )*>)", "</style>",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         "(<style>).*(</style>)", string.Empty,
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);

                // insert tabs in spaces of <td> tags
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*td([^>])*>", "\t",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);

                // insert line breaks in places of <BR> and <LI> tags
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*br( )*>", "\r",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*li( )*>", "\r",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);

                // insert line paragraphs (double line breaks) in place
                // if <P>, <DIV> and <TR> tags
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*div([^>])*>", "\r\r",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*tr([^>])*>", "\r\r",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*p([^>])*>", "\r\r",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);

                // Remove remaining tags like <a>, links, images,
                // comments etc - anything that's enclosed inside < >
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<[^>]*>", string.Empty,
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);

                // replace special characters:
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @" ", " ",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);

                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&bull;", " * ",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&lsaquo;", "<",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&rsaquo;", ">",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&trade;", "(tm)",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&frasl;", "/",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&lt;", "<",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&gt;", ">",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&copy;", "(c)",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&reg;", "(r)",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                // Remove all others. More can be added, see
                // http://hotwired.lycos.com/webmonkey/reference/special_characters/
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&(.{2,6});", string.Empty,
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);

                // for testing
                //System.Text.RegularExpressions.Regex.Replace(result,
                //       this.txtRegex.Text,string.Empty,
                //       System.Text.RegularExpressions.RegexOptions.IgnoreCase);

                // make line breaking consistent
                result = result.Replace("\n", "\r");

                // Remove extra line breaks and tabs:
                // replace over 2 breaks with 2 and over 4 tabs with 4.
                // Prepare first to remove any whitespaces in between
                // the escaped characters and remove redundant tabs in between line breaks
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         "(\r)( )+(\r)", "\r\r",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         "(\t)( )+(\t)", "\t\t",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         "(\t)( )+(\r)", "\t\r",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         "(\r)( )+(\t)", "\r\t",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                // Remove redundant tabs
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         "(\r)(\t)+(\r)", "\r\r",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                // Remove multiple tabs following a line break with just one tab
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         "(\r)(\t)+", "\r\t",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                // Initial replacement target string for line breaks
                string breaks = "\r\r\r";
                // Initial replacement target string for tabs
                string tabs = "\t\t\t\t\t";
                for (int index = 0; index < result.Length; index++)
                {
                    result = result.Replace(breaks, "\r\r");
                    result = result.Replace(tabs, "\t\t\t\t");
                    breaks = breaks + "\r";
                    tabs = tabs + "\t";
                }

                // That's it.
                return result;
            }
            catch
            {
                //throw new Exception("An error occured in the Plugin.");
                return source;
            }
        }

    }
}

 

After Registering the above Plugin, this time when I tested the functionality again the results were quite satisfying. After the Workflow updated the description field of Opportunity with the Email Messages in HTML format, instantly my Plugin fired and updated the Field; converting Email Messages to Plain Text.

Hope this will be Helpful to someone.

Thanks !!!

😀

Show Dynamic Reporting using Multiple-Valued Parameter in SSRS

Tags

, , ,

Hello Everyone,

Herein I will discuss about a Dynamics Report using Multiple Valued Parameter in SSRS. As recently, I was creating a SSRS Report wherein I was suppose to show the Performance of Engineers to my boss on Monthly Basis.  So, I created an Year and Engineer Name as Query Parameters and a Specific value Parameter with Month Names.

Query Parameter:-

Specific Values Parameter:-

Now my simple report was working perfectly on Monthly basis, on the selection of any single Month from parameter.The Report used to show the total no of Development Task and Support Task done in any current month and also the productivity/efficiency of each engineer which was calculated by :-   Total no of task done(Dev. + Supp) / 22 days

I took out reports for the Month of Jan, Feb and March respectively which are as follows:-

 

But as any general requirement, everyone of us wanted the report to be flexible so that It can show the Report for any N no. of selected months and calculate the Efficiency accordingly on count of those Months.

i.e. new formula would be :-

Total no of task done in N Months(Dev. + Supp) / (22 days * N Months)

Now to achieve this behavior we have a functionality within SSRS Report Builder, where we can change the parameter values from Single Select to Multiple Select.

1> After selecting the check box for “Allow multiple Value”, we can see the reflected change in the report too. In the below figure we can select multiple values for Month at a time.

2> Now we would also need to make some changes in the Query accordingly like: Use IN for Multiple Values

Where  (DATENAME(mm, CreatedOn) IN (@Month))

3> Also the Expression under Productivity needs to be changed with :-

=Fields!productivity.Value / (22 * Parameters!Month.Count)

With all the above changes, Our report is finally ready to show results for N no. of Months and with correct Productivity/Efficiency value. If you compare the above individual reports for the 1st Quarter and this single report combined, then the result will be same.

Hope this will be helpful.

Thanks !!!

😀

 

Set Money Field values in CRM jScript while using oData(REST)

Tags

, , , , , , , ,

Hello Everyone,

Just wanted to point out a small issue with Money field while using oData(REST). When you use oData to retrieve values for a Entity1 record and set it to another Entity2 record, then some field values need data conversion(like Money Field).

In case while you use your oData Query link to fetch values of a record in I.E. and you end up with the screen below:

The you would probably need some Setting change in your I.E.

  • Open Internet Settings
  • Move to Content Tab 
  • Click Settings under Feeds and Web Slices
  • Remove Check from Advanced option Turn on Feed reading view

After you are done with the above settings then probably after an I.E. restart you will see the following screen as a result of your oData Query.

Now coming back to the issue detail, in one of my JScripts I was fetching the Product Value from Product Entity using oData(REST) and Setting up the same value to one of my Custom Entity field. Even though the values being fetched were correct but still while Setting up the Money Field it threw following Error on page.

This control only accepts number or null as input.

I then came across a blog which explains about this in details here. It says that some values needs conversion (like Money Field). Hence I changed my jScript from

Xrm.Page.getAttribute(price).setValue(value);
to
Xrm.Page.getAttribute(price).setValue(parseFloat(eval(value)));

and it worked perfectly onwards without any errors.

My oData Query to fetch values from Product Entity and Set to field in Custom Entity is as follows:

function SetPrice(results,price) {

//here is where you’d add the code to handle the data returned
debugger;
if (results[0].StandardCost.Value != null)
                      {
                        var value = results[0].StandardCost.Value;
                        Xrm.Page.getAttribute(price).setValue(parseFloat(eval(value)));
                        //Xrm.Page.getAttribute(price).setValue(value);
                      }
                     else 
                      {Xrm.Page.getAttribute(price).setValue(null);}
}


function getPrice(product,price) 
{
debugger;
    var pdt= Xrm.Page.data.entity.attributes.get(product);

    if (pdt.getValue() == null)
     {
         return;
     }
      
pdtId = pdt.getValue()[0].id;
var pagecontext = Xrm.Page.context;
var serverUrl = pagecontext.getServerUrl();
var oDataPath = serverUrl + "/XRMServices/2011/OrganizationData.svc";
var oDataSelect = oDataPath + "/ProductSet?$select=StandardCost&$filter=ProductId" + " eq guid'" + pdtId + "'";

$.ajax({
type: "GET",
contentType: "application/json; charset=utf-8",
datatype: "json",
url: oDataSelect,

beforeSend: function (XMLHttpRequest) { XMLHttpRequest.setRequestHeader("Accept", "application/json"); },

success: function (data, textStatus, XmlHttpRequest) {
SetPrice(data.d.results,price);
},

error: function (XmlHttpRequest, textStatus, errorThrown) { alert('OData Select Failed: ' + oDataSelect ); }
});

}

Hope this was helpful.

Thanks !!!

😀

Modify Lead Qualification Process in Microsoft Dynamics CRM 2013

Tags

, , , , , ,

Hello Everyone,

Lead Qualification process changed a bit in CRM 2013. Earlier in CRM 2011 we used to have a dialog box with options to choose from Account, Contact and Opportunity as in what all we would like to create on Lead Qualification.

But we don’t have that option available anymore in CRM 2013. Instead, inside the form there exists two fields as “Existing Contact” and “Existing Account” which are available to add Related Contact or Account already available. Based on the combination matrix mentioned here it creates New Account, Contact and Opportunity.

Even if you have not filled up those fields, still those two fields will pop up during the Lead Qualification to check for Duplicates depending on the Duplicate Detection Rule applied for Account and Contact.

Now this whole thing has been designed quite meaningfully by Microsoft but it doesn’t apply in every Industry. Like in my current Client Industry they do not require to create new Opportunity (This can even be handled through proper Rights/Roles on Opportunity). There was a need to replicate this functionality with the same set of Account and Contact in Lead. Moreover, they also wanted to suppress the Duplicate Detection check as it doesn’t fit their requirement somehow. Hence, keeping all these points in mind I needed to write a Business Logic that fulfills their requirement. I found a very nice blog here which pointed in the right direction to the solution.

I wrote the plugin code mentioned below for my scenario:-

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Discovery;
using Microsoft.Crm.Sdk.Messages;
using System.ServiceModel;
using System.Net;
using System.ServiceModel.Description;
//using System.Web.Services.Protocols;
using System.Xml;

namespace LeadQualification
{
    public class Class1:IPlugin
    {
        IOrganizationService service;

        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            service = serviceFactory.CreateOrganizationService(context.UserId);

            // Get the qualified lead
            EntityReference leadid = (EntityReference)context.InputParameters["LeadId"];
            ColumnSet attributes2 = new ColumnSet(new string[] { "new_contact", "new_existingcustomer" });
            Entity lead = service.Retrieve(leadid.LogicalName, leadid.Id, attributes2);
            try
            {
                
                    if (lead.Contains("new_contact") && lead.Contains("new_existingcustomer") && lead.Attributes["new_contact"] != null && lead.Attributes["new_existingcustomer"] != null)
                    {
                        context.InputParameters["CreateOpportunity"] = false; 
                        context.InputParameters["CreateAccount"] = false; 
                        context.InputParameters["CreateContact"] = false; 
                        context.InputParameters["SuppressDuplicateDetection"] = true; 
                    }

                    else if ((!lead.Contains("new_contact")) && lead.Contains("new_existingcustomer") && lead.Attributes["new_existingcustomer"] != null)
                    {
                        context.InputParameters["CreateOpportunity"] = false; 
                        context.InputParameters["CreateAccount"] = false; 
                        context.InputParameters["CreateContact"] = true; 
                        context.InputParameters["SuppressDuplicateDetection"] = true;
                    }

                    else
                    {
                        context.InputParameters["SuppressDuplicateDetection"] = true; 
                        context.InputParameters["CreateOpportunity"] = false; 
                    }
               
            }

            catch (Exception ex)
            {
                throw new InvalidPluginExecutionException("An error occurred in the Plugin." + ex, ex); 
            }

        }

        private static string GetErrorCode(XmlNode errorInfo)
        {
            XmlNode code = errorInfo.SelectSingleNode("//code");

            if (code != null)
                return code.InnerText;
            else
                return "";
        }

    }
}

I then registered my plugin with ‘QualifyLead’ Message on Execution Pipeline as ‘Pre-Operation’.  And the functionality worked fine as expected.

The only one challenge that I faced using this way was, after I hit the Qualify Lead Button, operation used to work fine in the Background but the Lead page gets Freezed up. So I needed a way to Refresh the Lead Page and also the Parent Form i.e. the Grid from where the lead form was opened.

For the solution I wrote a webresource of type JScript with the following code.

function OnSave(pContext)
 {  
    var SaveMode, SaveEventVal;
    // debugger;
    // Change the Save Event Value as per required Save Event
    SaveEventVal = 16;

    if (pContext != null && pContext.getEventArgs() != null)
    {

        SaveMode = pContext.getEventArgs().getSaveMode();

        // 16 will pass on Lead Qualify button click
        if (SaveMode == SaveEventVal)
       {
         setTimeout(function()
                  {   
                      window.location.reload(true);
                      window.top.parent.document.location.reload(true);
                      
                   }, 2000);
          
        }
    }
}

 

I called the function during OnSave Event where this Script actually calls in for a reload of both the forms and parent Grid after a wait of 2 Seconds when Qualify Lead button is hit. This will solve the above mentioned issue and all-together they worked quite well.

Hope this will be helpful.

Thanks !!!

😀

 

Lead Qualification Validation using Real-Time / Synchronous Workflows in CRM 2013

Tags

, , , , , , ,

Hello Everyone,

In continuation with my Last Post I was also supposed to put validation on Lead Qualification. The condition was very simple that if a Lead is going to get Qualified that it must pass through all the Business Process Stages defined under the Organization. In my Earlier Post I showed how to capture the current Stage for any record.

I guess with the introduction of Business-Process, this must be a common requirement that all the Steps defined under various Stages in a Business Rule must be covered. This will not only be helpful in smooth business flow but also to get the complete Analysis done.

I designed a similar Business process as followed by my client in his organization. It has a last stage defined as Close stage. Under Close Stage there was a Step named as Remarks wherein the concerned salesperson need to fill in the details about the customer experiences and expectation with the product. This was a mandatory field as on basis of this field value analysis, further Quotation were getting generated.

Now the problem with the current system is, even you mark the field in the last stage as mandatory; you can still Qualify the Lead. Actually the mandatory field is set only for the purpose of smooth Business Process flow so that the Stages couldn’t be jumped without completing the important steps under current Stage.

As we are all aware that One way to stop this was through Plugins wherein we can stop the Qualification process if conditions are not met. But now the same can be achieved through Synchronous Workflows wherein we can stop the Qualification Process if the conditions are not met by Cancelling the Workflow process itself. Since Synchronous Workflow works with real time transaction hence it can be rolled back. With this new introduced feature we can even write the custom message as reason for the Error.

Step 1:

I created a Synchronous Workflow on Lead After the Status gets changed.

Setp 2:

Now I’ll mention all the conditions that needs to be fulfilled for the Lead Qualification to be successful, i.e. status changed to Qualified.

 Step 3:

The above mentioned conditions ensures that the Lead stage should be in the Last Stage i.e. Close and the Mandatory fields should be completed too along with the Lead status as Qualified. Failing which throws the following error message and cancels the Operation.

Once Completed, we can save and publish our Workflow and check with the conditions now. Failing to all the 3 conditions  populated the Error message with our Custom Message, which in turn cancels the Qualification operation.

 

This is one of the examples wherein we can use the Real-Time / Synchronous Workflow to achieve some functionality without writing any Plugin code.

Hope this will be Helpful.

Thanks !!!

😀

Lock or Set Visibility of field on Business Process Stage change in CRM 2013

Tags

, , , , , , , ,

Hello Everyone,

In my Previous Post I explained about capturing the Business Process Name and the Stage Name into which the current record exists. Once the Stage Name is captured then it can be used as trigger to perform many Actions like Lock a field or Set the Visibility of fields through Business Rules or Synchronous Workflows; based on the various Stage change scenarios.

In my current Ongoing Project I too had similar kind of requirement wherein I was suppose to hide the Field of other Stages until the Lead moves from the First Qualification Stage. Now Since I had the updated Field with Process Stage Name, I wrote a Business Rule using the same field as condition to hide the other fields from Other Stages.

For this purpose I had to write 3 Business Rules:

  • 1> First, Lock fields for the Lead when its not even created yet i.e. when Process Stage Name is Null.

 

  • 2> Second, Lock fields for the Lead when its newly created i.e. when Process Stage is Qualification.

 

  • 3> Third, Unlock fields For the Lead when it Moved to Next Stage from the First Qualification Stage.
  • Now, as the final result when I opened a New Lead or Lead in Qualification Stage then I have the Fields already set as Read-Only.

 

  • And When the Lead is moved to the Next Stage then instantly the Lock fields gets Unlocked.

 

Hope this will be Helpful for Customizing your Business Process accordingly.

Thanks !!!

😀

 

 

Capture Business Process and Stage Name for each record in CRM 2013

Tags

, , , , , , , ,

Hello Everyone,

I was working on a requirement wherein on the change of the Stage in Business Process flow,  I was suppose to do some Action like setting the visibility for few fields and making other fields Read Only. The actions can be completed easily with the Business Rules but the main criteria was to capture the current Stage name so that the Trigger for Synchronous Workflow or Business Rules could be set.

Well, we know that when the Business Process is selected under any Entity then two new fields are introduced:-

Procesid :- Type of Unique Identifier which Shows the ID of Process.

Stageid :- Type of Unique Identifier which Shows the ID of Stage.

I recently ran through a post from my one of fav MVP Jukka Niiranen, who has written some excellent post on Business Process and Synchronous Workflow. I’ll also explain same in my scenario as how using the combination of both new features; some functionality can be achieved without writing any plugin-code.

In my scenario to capture the Current Business Process Name and Stage Name I created two Text fields on Lead Entity. If you explore the Database a bit then you’ll find that the Stage Name is maintained in the ProcessStage Table, which we can use to capture the exact Stage Names from.

 I then created a Synchronous workflow to update Lead with current Process Name and Stage Name whenever Any Stage changes within, mentioned as “Stageid“.

Now the Lead can be updated with those two new Text fields that were created to capture the current Process Names and Stage Name from the related Entity Process Stage.

Once the process is Saved and Activated, the Stage Name Fields were getting populating both on Creation of new Lead and Stage change. The same can be achieved with any Entity running Business Process Flow.

This field can be set to be hidden and used as trigger for further Asynchronous Workflows and Business Rules. In my next posts I’ll discuss how to utilize this process to take further Actions on a record.

Hope this will be Helpful.

Thanks !!!

😀

 

 

 

Dependent Picklist / OptionSet in CRM 2011 / 2013

Tags

, , , , , , , , , ,

Hello Everyone,

In a recent scenario, I was asked to create a dependent Pick-list where the options of Second Pick-list are dependent on the Value of the First one.  By default there is no such option in Dynamics CRM but I came across a solution from MSDN which worked perfectly for me. For an example scenario A managed solution is present in the latest version of SDK at

SDK\SampleCode\JS\FormScripts

I’m going to share the steps in detail for the solution here :-

Step 1:-

Create a Webresource of type Jscript and upload the JScript file from

SDK -> SsmpleCode -> JS -> FormScripts -> SDK.DependentOptionSet.js

Step 2 :-

Create Another Webresource of type XML and upload the XML file from

SDK -> SsmpleCode -> JS -> FormScripts -> TicketDependentOptionSetConfig.xml

Step 3:-

Modify the XML based upon your options from Parent optionSet and Dependent optionSet

In my case the XML is like the following:-

<!--<snippetTicketDependentOptionSetConfig.xml>-->
<DependentOptionSetConfig entity="account" >
 <ParentField id="new_category"
              label="Category">
  <DependentField id="new_subcategory"
                  label="Sub Category" />
  <Option value="1"
          label="Gold">
   <ShowOption value="1"
               label="90-100 (K)" />
   <ShowOption value="2"
               label="80-90 (K)" />
   <ShowOption value="3"
               label="70-80 (K)" />
  </Option>
  <Option value="2"
          label="Silver">
   <ShowOption value="4"
               label="60-70 (K)" />
   <ShowOption value="5"
               label="50-60 (K)" />
   <ShowOption value="6"
               label="40-50 (K)" />
  </Option>
  <Option value="3"
          label="Bronze">
   <ShowOption value="7"
               label="30-40 (K)" />
   <ShowOption value="8"
               label="20-30 (K)" />
   <ShowOption value="9"
               label="10-20 (K)" />
   <ShowOption value="10"
               label="00-10 (K)" />
  </Option>
 </ParentField>
 </DependentOptionSetConfig>
<!--</snippetTicketDependentOptionSetConfig.xml>-->

Step 4 :-

Attach the Liabrary and add the function on OnLoad event of the Form:

Function :-  SDK.DependentOptionSet.init

Parameter :- “new_TicketDependentOptionSetConfig.xml” (its basically the xml that we created before)

Step 5 :-

Add the function at onChage Event of the First OptionSet / Picklist

Function :- SDK.DependentOptionSet.filterDependentField

Parameter :- “new_category”,”new_subcategory”

 

Now You’re done. After Publishing all the changes, when you first open the record you’ll get the dependent picklist / OptionSet as ReadOnly.

After the selection of the Parent Picklist, the Dependent picklist will show the dependent values.

 

Hope this was helpful !!!

Thanks.

😀

 

Replace ActiveXObject by using oData endpoint to Fetch values from Lookup record in CRM 2013

Tags

, , , , ,

Hello Everyone,

I just wanted to share this piece of code actually with myself as this comes useful  quite very often now.

In my Previous Post I used City, State and Country as a Lookup in the Address field under Account.  With the selection of City the related records State and Country also gets populated automatically using JScript. Earlier the Jscript used included the use of ActiveXObject which is now deprecated from CRM 2013.

I read a very helpful post from PowerObjects related to this issue in this post. I used their code to build a small Jscript for myself which I use it most of the time directly.

The code is as follows:-

function SetStateCountry(results) {
//debugger;
if (results[0].new_State != null)
                     {
                        //alert(results[0].new_State.Name);
                        var name = results[0].new_State.Name;
                        var id = results[0].new_State.Id;
                        Xrm.Page.getAttribute("new_state").setValue([{ id: id, name: name, entityType: 'new_state'}]);
                      }
                     else 
                      {
                        //Xrm.Page.getAttribute("new_state").setValue("");
                      }

if (results[0].new_Country != null)
                     {
                        //alert(results[0].new_Country.Name);
                        var name = results[0].new_Country.Name;
                        var id = results[0].new_Country.Id;
                        Xrm.Page.getAttribute("new_country").setValue([{ id: id, name: name, entityType: 'new_country'}]);
                      }
                     else 
                      {
                        //Xrm.Page.getAttribute("new_country").setValue("");
                      }
}


function getCSC() 
{
//debugger;
    var cty= Xrm.Page.data.entity.attributes.get("new_city");

    if (cty.getValue() == null)
     {
         return;
     }
      
ctyId = cty.getValue()[0].id;
var pagecontext = Xrm.Page.context;
var serverUrl = pagecontext.getServerUrl();
var oDataPath = serverUrl + "/XRMServices/2011/OrganizationData.svc";
var oDataSelect = oDataPath + "/new_citySet?$select=new_State,new_Country&$filter=new_cityId" + " eq guid'" + ctyId + "'";

$.ajax({
type: "GET",
contentType: "application/json; charset=utf-8",
datatype: "json",
url: oDataSelect,

beforeSend: function (XMLHttpRequest) { XMLHttpRequest.setRequestHeader("Accept", "application/json"); },

success: function (data, textStatus, XmlHttpRequest) {
SetStateCountry(data.d.results);
},

error: function (XmlHttpRequest, textStatus, errorThrown) { alert('OData Select Failed: ' + oDataSelect ); }
});


}

Thanks !!!

🙂