Custom CRM Search Application in MVC


Today, I will explain how to create advanced search using MVC application same as CRM Advanced Search.
For that first you have to get field list of any entity from CRM using “RetrieveEntityResponse” method of CRM SDK. You can easily get code for this from internet.

Now create a view named “AccountFilter” and copy and paste following code to that page. It is full javascript code which is useful to control behavior like hide-show on specific option, change control on field type like optionset, text, Boolean etc.


@using SearchCRM.Lib;
@using SearchCRM.Lib.Entities;
@model SearchCRM.Lib.Entities.CRMSearch
@{
   ViewBag.Title = "Filter";
   Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Filter</h2>
<h2>Account columns</h2>

<table>
   <tr class="col-lg-12">
       <td class="col-lg-1">
           @if(Model.Fields.Count > 0)
           {
               <select id="ddlOperator" class="form-control">
                   <option value="-1"></option>
                   <option value="AND">AND</option>
                   <option value="OR">OR</option>
               </select>
           }
       </td>
       <td class="col-lg-3">
           <input type="hidden" id="hdType" name="hdType">
           @Html.DropDownList("ddlfield", new SelectList(Model.FilterModel.Fields, "LogicalName", "Name"), "--Select--", new { @class = "form-control", @onchange = "manageControl(this);" })
       </td>
       <td class="col-lg-3">
           @Html.DropDownList("ddlFilterTypes", new SelectList(ViewBag.FilterTypeList, "Value", "Text"), "--Select--", new { @class = "form-control", @onchange = "manageHideShow(this);" })
       </td>
       <td class="col-lg-3"><div id="divControl" hidden="hidden"></div></td>
       <td class="col-lg-1">
           <input type="button" class="btn" value="Add filter" id="addfilter" onclick="AddFilter()" />
       </td>
   </tr>
</table>
   <br />
   <br />

@using (Html.BeginForm("SearchAccount", "Search", FormMethod.Post))
{
       <table style="width:100%;">
       @if (Model.Fields != null && Model.Fields.Count > 0)
       {
           @Html.HiddenFor(m => m.Fields)
           foreach (var item in Model.Fields)
           {
               <tr class="col-lg-12">
                   <td class="col-lg-1">
                       @if (item.LogicalOperator != "undefined" && item != Model.Fields.First())
                       {
                           @Html.DisplayFor(m => item.LogicalOperator)
                       }
                   </td>
                   <td class="col-lg-3">
                       @Html.HiddenFor(m => item.Id)
                       @Html.DisplayFor(m => item.Name)
                   </td>
                   <td class="col-lg-3">
                       @item.FilterType.GetDescription()
                       @Html.HiddenFor(m => item.FilterType)
                   </td>
                   <td class="col-lg-3">
                       
                       @if (item.Type == "picklist")
                       {
                           if(item.FilterType == FilterType.EqualsTo || item.FilterType == FilterType.Contains || item.FilterType == FilterType.Starts_With)
                           {
                               @item.Options.Find(x => x.Value == int.Parse(item.Value)).Text
                           }
                       }
                       else if (item.Type == "lookup")
                       {
                           @Html.DisplayFor(m => item.LookupText)
                       }
                       else
                       {
                           @Html.DisplayFor(m => item.Value)
                       }
                   </td>
                   <td class="col-lg-1">
                       <a class="dltImage">
                           @Html.HiddenFor(m => item.RowIndex)
                       </a>
                   </td>
               </tr>
           }
       }
       </table>
   <br />
   <br />

   <input type="submit" value="Submit" class="btn" />
   <input type="button" value="Refresh Filter" class="btn" style="margin-left: 20px;" onclick="refreshFilter()"/>
}

<script type="text/javascript">

   function AddFilter()
   {
       var field = $('#ddlfield').val();
       var fieldText = $('#ddlfield option:selected').text();
       var fieldType = $('#hdType').val();
       var filterType = $('#ddlFilterTypes').val();
       var filterValue = "";
       var lookupText = "";
       if ($('#divControl').is(':visible'))
       {
           if ($('#divControl')[0].innerHTML.indexOf('txtFilterValue') !== -1) {
               filterValue = $('#divControl')[0].firstChild.value;
               fieldType = "string";
           }
           else if ($('#divControl')[0].innerHTML.indexOf('ddlLookup') !== -1) {
               filterValue = $('#divControl')[0].firstChild.value;
               lookupText = $('#divControl')[0].firstChild.selectedOptions[0].innerHTML;
               fieldType = "lookup";
           }
           else if ($('#divControl')[0].innerHTML.indexOf('ddlBoolean') !== -1) {
               filterValue = $('#divControl')[0].firstChild.value;
               fieldType = "boolean";
           }
           else if ($('#divControl')[0].innerHTML.indexOf('ddlOptionSet') !== -1) {
               filterValue = $('#divControl')[0].firstChild.value;
               fieldType = "picklist";
           }
       }
       var operator = $('#ddlOperator').val();
       if (operator == -1)
           return false;

       if (filterType == 'EqualsTo' && fieldType == 'picklist' && $('#ddlOptionSet').val() == '-1')
           return false;
       var str = "";
       str += '@Url.Action("AccountFilter")/?fieldname=' + field + '&fieldtext=' + fieldText;
       str += '&fieldtype=' + fieldType + '&filtertype=' + filterType + '&filtervalue=' + filterValue;
       str += '&lookuptext=' + lookupText + '&loperator=' + operator;
       window.location.href = str;
   }

   function manageControl(arg)
   {
       $("#ajax-loader").show();
       
       var field = arg.value;
       $.ajax({
           url: "@Url.Action("GetControlType", "Search")",
           data: { 'name': field },
           type: "GET",
           success: function (data)
           {
               if (data == "Lookup")
               {
                   $('#hdType').val('lookup');
                   fillDropDown();
                   GetLookup(field);
               }
               else if (data == "String")
               {
                   $('#hdType').val('string');
                   $('#divControl').show();
                   fillDropDown();
                   $('#divControl').empty().append('');
                   $("#ajax-loader").hide();
               }
               else if(data == "Integer")
               {
                   $('#hdType').val('integer');
                   $('#divControl').show();
                   fillDropDown();
                   $('#divControl').empty().append('');
                   $("#ajax-loader").hide();
               }
               else if (data == "Picklist")
               {
                   $('#hdType').val('picklist');
                   var picklist = $('#ddlFilterTypes');
                   if (picklist[0].length > 4) {
                       picklist[0].remove(2);
                       picklist[0].remove(2);
                   }
                   GetOptions(field);
                   $("#ajax-loader").hide();
               }
               else if (data == "Boolean")
               {
                   $('#hdType').val('boolean');
                   $('#divControl').show();
                   fillDropDown();

                   var picklist = $('#ddlFilterTypes');
                   picklist[0].remove(2);
                   picklist[0].remove(2);

                   var select = $('');
                       var option = $('');
                       option.attr('value', -1);
                       option.text('--Select--');
                       select.append(option);

                       var option1 = $('');
                       option1.attr('value', 'False');
                       option1.text('No');
                       select.append(option1);

                       var option2 = $('');
                       option2.attr('value', 'True');
                       option2.text('Yes');
                       select.append(option2);

                       $('#divControl').empty().append(select);
                       $("#ajax-loader").hide();
               }
           },
           error: function (xmlHttpRequest, errorText, thrownError) {
               alert(xmlHttpRequest + "|" + errorText + "|" + thrownError);
           }
       });
   }

   function GetLookup(str)
   {
       var div = $(this).nextUntil("div.divControl", "div");
       $.ajax({
           url: "@Url.Action("GetLookup", "Search")",
           data: { 'logicalName': str },
           type: "GET",
           success: function (data) {
               $('#divControl').show();
               var select = $('');

               var opt = $('');
               opt.attr('value', -1);
               opt.text('--Select--');
               select.append(opt);

               for (var i = 0; i < data.length; i++) {
                   var option = $('');
                   option.attr('value', data[i].ID);
                   option.text(data[i].Name);
                   select.append(option);
               }
               $('#divControl').empty().append(select);
           },
           complete: function (d) {
               $("#ajax-loader").hide();
           },
           error: function (xmlHttpRequest, errorText, thrownError) {
               alert(xmlHttpRequest + "|" + errorText + "|" + thrownError);
           }
       });
   }

   function GetOptions(field)
   {
       var div = $(this).nextUntil("div.divControl", "div");
       $.ajax({
           url: "@Url.Action("GetOptionList", "Search")",
           data: { 'logicalName': field },
           type: "GET",
           success: function (data)
           {
               $('#divControl').show();
               var select = $('');

               var opt = $('');
               opt.attr('value', -1);
               opt.text('--Select--');
               select.append(opt);

               for (var i = 0; i < data.length; i++)
               {
                   var option = $('');
                   option.attr('value', data[i].Value);
                   option.text(data[i].Text);
                   select.append(option);
               }
               $('#divControl').empty().append(select);
           },
           complete: function (d) {
               $("#ajax-loader").hide();
           },
           error: function (xmlHttpRequest, errorText, thrownError) {
               alert(xmlHttpRequest + "|" + errorText + "|" + thrownError);
           }
       });
   }

   function manageHideShow(arg)
   {
       var field = arg.value;
       if (field == "Contains_data" || field == "No_data")
       {
           $('#divControl').hide();
       }
       else
       {
           $('#divControl').show();
       }
   }

   function fillDropDown()
   {
       var select = $('#ddlFilterTypes');
       select[0].length = 0;
       var option = $('');
       option.attr('value', -1);
       option.text('--Select--');
       select.append(option);

       var option1 = $('');
       option1.attr('value', 'EqualsTo');
       option1.text('Equals');
       select.append(option1);

       var option2 = $('');
       option2.attr('value', 'Contains');
       option2.text('Contains');
       select.append(option2);

       var option3 = $('');
       option3.attr('value', 'Starts_With');
       option3.text('Starts With');
       select.append(option3);

       var option4 = $('');
       option4.attr('value', 'Contains_data');
       option4.text('Contains Data');
       select.append(option4);

       var option5 = $('');
       option5.attr('value', 'No_data');
       option5.text('No Data');
       select.append(option5);
   }

   function refreshFilter()
   {
       $("#ajax-loader").show();

       $.ajax({
           url: "@Url.Action("RefreshFilter", "Search")",
           data: '',
           type: "GET",
           success: function (data)
           {
               window.location.href= '@Url.Action("AccountFilter")';
           },
           complete: function (d) {
               $("#ajax-loader").hide();
           },
           error: function (xmlHttpRequest, errorText, thrownError) {
               alert(xmlHttpRequest + "|" + errorText + "|" + thrownError);
           }
       });
   }

   $(document).on('click', '.dltImage', function (e, ui) {
       var id = $(this).parent().context.children[0].value;
       
       $("#ajax-loader").show();

       $.ajax({
           url: "@Url.Action("RemoveField", "Search")",
           data: { 'id': id },
           type: "GET",
           success: function (data)
           {
               $('body').html(data);
           },
           complete: function (d) {
               $("#ajax-loader").hide();
           },
           error: function (xmlHttpRequest, errorText, thrownError) {
               alert(xmlHttpRequest + "|" + errorText + "|" + thrownError);
           }
       });
   });
</script>

When you run the application, it will show the page like below with the dropdown of field.
D:\Amit\Images\Blog Images\CRM Search-1.png

When you select text field from field list, the java script function “manageControl” automatically display a text box on right side as below image.

D:\Amit\Images\Blog Images\CRM Search-2.png

When you select OptionSet field from field list, the java script function “manageControl” calls the method “GetOptionList” on Search Controller and get the list of that OptionSet and set to right dropdown as below.

D:\Amit\Images\Blog Images\CRM Search-3.png


When you select lookup field from field list, the function “manageControl” call the method “GetLookup” on Search Controller and which get the list of contacts and set to right dropdown same as OptionSet.

The GetOptionList and GetLookup ActionResult methods are as below.

[HttpGet]
      public ActionResult GetLookup(string logicalName)
      {
          List<LookupItems> items = new List<LookupItems>();
          if (!string.IsNullOrEmpty(logicalName))
          {
              items = crmAPI.GetLookupItems_of_Account(logicalName);
          }
          return Json(items, JsonRequestBehavior.AllowGet);
      }

      [HttpGet]
      public ActionResult GetOptionList(string logicalName)
      {
          CRMField str = new CRMField();
          List<CRMField> field = ((List<CRMField>)Session["filterModel"]).ToList();
          str.Options = field.Where(x => x.LogicalName == logicalName).Select(x => x.Options).FirstOrDefault();
          return Json(str.Options, JsonRequestBehavior.AllowGet);
      }

You can see that the GetLookup calls the methods “GetLookipItems_of_Account” which get the list of Active Contact with GuId and Name. Code for this method is as below.


          ServerConnection.Configuration serverConfig = CRMHelper.ConnectToServer();
           OrganizationServiceProxy service = ServerConnection.GetOrganizationProxy(serverConfig);
           OrganizationServiceContext orgSvcContext = new OrganizationServiceContext(service);

           RetrieveAttributeRequest attributeRequest = new RetrieveAttributeRequest
           {
               EntityLogicalName = "account",
               LogicalName = fieldLogicalName,
               RetrieveAsIfPublished = true
           };

           // Execute the request
           RetrieveAttributeResponse attributeResponse =
               (RetrieveAttributeResponse)service.Execute(attributeRequest);

           AttributeMetadata retrievedAttributeMetadata = attributeResponse.AttributeMetadata;
           if (retrievedAttributeMetadata.AttributeType.Value == AttributeTypeCode.Lookup)
           {
               LookupAttributeMetadata lookups = (LookupAttributeMetadata)retrievedAttributeMetadata;
               string entityName = (lookups.Targets != null && lookups.Targets.Count() > 0) ? lookups.Targets[0] : string.Empty;
               if (!string.IsNullOrEmpty(entityName))
               {
                   if (entityName != "contact")
                   {
                       items = (from a in orgSvcContext.CreateQuery(entityName)
                                where a["statuscode"] == new OptionSetValue((int)StatusCode.Active)
                                select new LookupItems
                                {
                                    ID = (Guid)a[entityName + "id"],
                                    Name = (string)a["name"]
                                }).ToList();
                   }
               }
           }

           return items;
When you click on Add filter button after selecting field and their filter value the page shows like this.
D:\Amit\Images\Blog Images\CRM Search-4.png


In above picture, you can see a dropdown with “AND” and “OR” options, which is useful to add logical operator to filter query. This dropdown only shows after adding first filter to query. You can see that first time when you load application it will not display but it shows after added first filter.

Following image elaborate multiple filter options.

D:\Amit\Images\Blog Images\CRM Search-5.png


The above condition creates query like

(AccountName startwith A.) and (City = London or City = Liverpool).

The middle dropdown contains option like “Equals”, “Contains” etc. there is a provision with selection of these options the visibility of last div depends. When you select “Equals” or “Contains” or “Start With” the last div for selecting value to filter will display but when you select option like “Contains Data” of “No Data” the last div will not display. The behavior of this is same like CRM Advanced search window.

Finally, create a method which filters account records based on condition using QueryExpression as something like below code. The “Submit” button on above picture will call the following method. Note : the below code is only filter records with AND logical operator, you have to manage and update below method to filter records with “AND” and “OR”. When you use OR logical operator at that time you have to manage code to get the value of previous line and current line and then add with logical OR operator to query expression. I remain this part for you in below code.


  public List<Account> SearchAccount(List<CRMField> filterFields, out int totalRecords)
  {
     List<Account> accounts = new List<Account>();
           
     ServerConnection.Configuration serverConfig = CRMHelper.ConnectToServer();
     OrganizationServiceProxy service = ServerConnection.GetOrganizationProxy(serverConfig);
     OrganizationServiceContext orgSvcContext = new OrganizationServiceContext(service);

     QueryExpression qe = new QueryExpression();
     qe.EntityName = "account";
     qe.ColumnSet = new ColumnSet(true);

     FilterExpression mainfilter = new FilterExpression(LogicalOperator.And);
     for (int i = 0; i < filterFields.Count; i++)
     {
         if (filterFields[i].LogicalOperator == "AND" || filterFields[i].LogicalOperator == "undefined")
         {
             FilterExpression filter = new FilterExpression(LogicalOperator.And);
             if (filterFields[i].Type.Equals("string", StringComparison.InvariantCultureIgnoreCase))
             {
                 filter.AddCondition(this.CreateCondition(filterFields[i].LogicalName, filterFields[i].FilterType, filterFields[i].Value));
             }
             else if (filterFields[i].Type.Equals("lookup", StringComparison.InvariantCultureIgnoreCase))
             {
                 filter.AddCondition(this.CreateCondition(filterFields[i].LogicalName, filterFields[i].FilterType, filterFields[i].Value));
             }
             else if (filterFields[i].Type.Equals("picklist", StringComparison.InvariantCultureIgnoreCase) ||
                   filterFields[i].Type.Equals("integer", StringComparison.InvariantCultureIgnoreCase) ||
                   filterFields[i].Type.Equals("status", StringComparison.InvariantCultureIgnoreCase))
             {
                  filter.AddCondition(this.CreateCondition(filterFields[i].LogicalName, filterFields[i].FilterType, filterFields[i].Value));
             }
             else if (filterFields[i].Type.Equals("decimal", StringComparison.InvariantCultureIgnoreCase) ||
                    filterFields[i].Type.Equals("money", StringComparison.InvariantCultureIgnoreCase))
             {
                  filter.AddCondition(this.CreateCondition(filterFields[i].LogicalName, filterFields[i].FilterType, filterFields[i].Value));
             }
             else if (filterFields[i].Type.Equals("double", StringComparison.InvariantCultureIgnoreCase))
             {
                  filter.AddCondition(this.CreateCondition(filterFields[i].LogicalName, filterFields[i].FilterType, filterFields[i].Value));
             }
             else if (filterFields[i].Type.Equals("boolean", StringComparison.InvariantCultureIgnoreCase))
             {
                  filter.AddCondition(this.CreateCondition(filterFields[i].LogicalName, filterFields[i].FilterType, filterFields[i].Value));
             }

                   mainfilter.AddFilter(filter);
               }
           }
}

       qe.Criteria = mainfilter;
       qe.AddOrder("name", OrderType.Ascending);
       qe.PageInfo = new PagingInfo();
       qe.PageInfo.PagingCookie = null;
       qe.PageInfo.ReturnTotalRecordCount = true;

       EntityCollection ec = service.RetrieveMultiple(qe);
       
       totalRecords = ec.TotalRecordCount;

       accounts = (from c in ec.Entities
                   select new Account
                   {
                       AccountID = (Guid)c["accountid"],
                       Name = c.Contains("name") ? (string)c["name"] : string.Empty,
                       Primary_Contact = new Contact
                       {
                           ContactID = c.Contains("primarycontactid") ? ((EntityReference)c["primarycontactid"]).Id : Guid.Empty,
                           FullName = c.Contains("primarycontactid") ? ((EntityReference)c["primarycontactid"]).Name : string.Empty
                       },
                       Email = c.Contains("emailaddress1") ? (string)c["emailaddress1"] : string.Empty,
                       Main_Phone = c.Contains("telephone1") ? (string)c["telephone1"] : string.Empty,
                       Other_Phone = c.Contains("telephone2") ? (string)c["telephone2"] : string.Empty,
                       Fax = c.Contains("fax") ? (string)c["fax"] : string.Empty,
                       WebSite = c.Contains("websiteurl") ? (string)c["websiteurl"] : string.Empty,
                       Street_1 = c.Contains("address1_line1") ? (string)c["address1_line1"] : string.Empty,
                       Street_2 = c.Contains("address1_line2") ? (string)c["address1_line2"] : string.Empty,
                       City = c.Contains("address1_city") ? (string)c["address1_city"] : string.Empty,
                       StateProvince = c.Contains("address1_stateorprovince") ? (string)c["address1_stateorprovince"] : string.Empty,
                   }).ToList();
           return accounts;
       }
This is not useful for beginners in CRM and MVC with java script, because there are some other codes related to CRM connection, retrieve records, retrieve Optionset list which have to add to get proper work around same as CRM Advanced search. This is only java script part to manage control behavior as per Selected Field
This article has been shared by Ethan Millar working with Aegis SoftTech as senior developer from last five years. He has extensive experience in .Net development, different Java technologies and Frameworks. The objective of writing this post is to discuss about the custom CRM search application in MVC. The conclusion has been drawn after practical research and implementation by Asp.Net MVC development team at Aegis SoftTech.

Related

Programming 7423281479012339237

Post a Comment Default Comments

...

item