Tuesday 8 January 2013

Override the Resolve Case System ribbon button functionality?



When you resolve a Case a pop up appears where you can provide some details regarding closing the Case.Here I will show how can  we overwrite the default functionality of the Resolve Case.


When you resolve a case it creates an case resolution activity.Which we need to take care while overwriting the default functionality.


Customization Aim:
To remove the "Resolve Case" dialog  box and change the name of the Resolve Case to 'Close Case'.

What to do ?:What things needs to be customized in order to achieve this.


1)We need to customize the ribbon definition of Case entity i.e  RibbonDiffXml.
2)We need to create one custom field on Case entity.
3)We need to write a JavaScript function.
4)We have to write a plugin which will take care of resolving the Case.

Note:
Case can be resolved from 'Form' level and 'HomePage grid'.We will take care of both the scenarios.


1.Customizing the Case ribbon button(HomePage grid and  Form):

1.1) Create a solution containing Case entity.Export it and save it.Extract the solution file.
        Open the "customizations.xml" search the <RibbonDiffXml> tag.
        Replace this with the following


<RibbonDiffXml>
        <CustomActions>
 
          <CustomAction Id="new.Mscrm.Form.incident.Resolve.CustomAction" Location="Mscrm.Form.incident.MainTab.Actions.Controls._children" Sequence="3">
            <CommandUIDefinition>
              <Button Alt="$Resources:Ribbon.Form.incident.MainTab.Actions.Resolve" Command="Mscrm.Form.incident.Resolve" Id="Mscrm.Form.incident.Resolve" Image32by32="/_imgs/ribbon/resolvecase32.png" Image16by16="/_imgs/ribbon/ResolveCase_16.png" LabelText="$LocLabels:Mscrm.Form.incident.Resolve.LabelText" Sequence="3" TemplateAlias="o1" ToolTipTitle="$Resources:Ribbon.Form.incident.MainTab.Actions.Resolve" ToolTipDescription="$Resources:Ribbon.Tooltip.ResolveCase" />
            </CommandUIDefinition>
          </CustomAction>
 
 
          <CustomAction Id="new.Mscrm.HomepageGrid.incident.Resolve.CustomAction" Location="Mscrm.HomepageGrid.incident.MainTab.Actions.Controls._children" Sequence="3">
            <CommandUIDefinition>
              <Button Alt="$Resources:Ribbon.Form.incident.MainTab.Actions.Resolve" Command="Mscrm.HomepageGrid.incident.Resolve" Id="Mscrm.HomepageGrid.incident.Resolve" Image32by32="/_imgs/ribbon/resolvecase32.png" Image16by16="/_imgs/ribbon/ResolveCase_16.png" LabelText="$LocLabels:Mscrm.HomepageGrid.incident.Resolve.LabelText" Sequence="3" TemplateAlias="o1" ToolTipTitle="$Resources:Ribbon.Form.incident.MainTab.Actions.Resolve" ToolTipDescription="$Resources:Ribbon.Tooltip.ResolveCase" />
            </CommandUIDefinition>
          </CustomAction>
 
        </CustomActions>
        <Templates>
          <RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
        </Templates>
        <CommandDefinitions>
          <CommandDefinition Id="Mscrm.Form.incident.Resolve">
            <EnableRules>
              <EnableRule Id="Mscrm.CanChangeIncidentForm" />
            </EnableRules>
            <DisplayRules>
              <DisplayRule Id="Mscrm.CanChangeIncidentForm" />
              <DisplayRule Id="Mscrm.IncidentIsActive" />
            </DisplayRules>
            <Actions>
              <JavaScriptFunction FunctionName="resolveCase" Library="$webresource:new_CustomRibbonJavascript" />
            </Actions>
          </CommandDefinition>
          <CommandDefinition Id="Mscrm.HomepageGrid.incident.Resolve">
            <EnableRules>
              <EnableRule Id="Mscrm.SelectionCountExactlyOne" />
              <EnableRule Id="Mscrm.VisualizationPaneNotMaximized" />
            </EnableRules>
            <DisplayRules>
              <DisplayRule Id="Mscrm.CanChangeIncidentForm" />
            </DisplayRules>
            <Actions>
              <JavaScriptFunction FunctionName="resolveCaseFromGrid" Library="$webresource:new_CustomRibbonJavascript">
                <CrmParameter Value="SelectedControlSelectedItemIds" />
              </JavaScriptFunction>
            </Actions>
          </CommandDefinition>
 
        </CommandDefinitions>
        <RuleDefinitions>
          <TabDisplayRules />
          <DisplayRules>
 
            <DisplayRule Id="Mscrm.CanChangeIncidentForm">
              <EntityPrivilegeRule EntityName="incident" PrivilegeType="Write" PrivilegeDepth="Basic" />
              <EntityPrivilegeRule EntityName="incident" PrivilegeType="AppendTo" PrivilegeDepth="Basic" />
              <EntityPrivilegeRule EntityName="activitypointer" PrivilegeType="Create" PrivilegeDepth="Basic" />
              <EntityPrivilegeRule EntityName="activitypointer" PrivilegeType="Append" PrivilegeDepth="Basic" />
            </DisplayRule>
 
            <DisplayRule Id="Mscrm.IncidentIsActive">
              <FormStateRule State="Existing" />
            </DisplayRule>
 
          </DisplayRules>
          <EnableRules>
 
            <EnableRule Id="Mscrm.CanChangeIncidentForm">
              <FormStateRule State="Create" InvertResult="true" />
              <RecordPrivilegeRule PrivilegeType="Write" AppliesTo="PrimaryEntity" />
              <RecordPrivilegeRule PrivilegeType="AppendTo" AppliesTo="PrimaryEntity" />
            </EnableRule>
 
            <EnableRule Id="Mscrm.SelectionCountExactlyOne">
              <SelectionCountRule AppliesTo="SelectedEntity" Minimum="1" Maximum="1" />
            </EnableRule>
            <EnableRule Id="Mscrm.VisualizationPaneNotMaximized">
              <CustomRule FunctionName="Mscrm.RibbonActions.disableButtonsWhenChartMaximized" Library="/_static/_common/scripts/RibbonActions.js">
                <CrmParameter Value="SelectedControl" />
              </CustomRule>
            </EnableRule>
          </EnableRules>
        </RuleDefinitions>
        <LocLabels>
          <LocLabel Id="Mscrm.Form.incident.Resolve.LabelText">
            <Titles>
              <Title description="Close Case" languagecode="1033" />
            </Titles>
          </LocLabel>
          <LocLabel Id="Mscrm.HomepageGrid.incident.Resolve.LabelText">
            <Titles>
              <Title description="Close Case" languagecode="1033" />
            </Titles>
          </LocLabel>
 
        </LocLabels>
      </RibbonDiffXml>


2.Case form Customization:

Customize the Case entity create one radio button with logical name "new_iscaseresolved" having default value false.Hide this field on the form.We will make use of this field for writing our logic.


3.Write the Javascipt function:

Create a webresource  called "new_CustomRibbonJavascriptand add the following JavaScript functions to it.


function resolveCase() {
    try {
        var answer = confirm("Do you want to Close the Case?")
        if (answer) {
 
            Xrm.Page.getAttribute("new_iscaseresolved").setValue(true);
            Xrm.Page.data.entity.save();
 
        }
    }
    catch (err) { }
}
 
function resolveCaseFromGrid(CaseIdList) {
 
    try {
        if (CaseIdList != null) {
            var caseid = CaseIdList[0]
            caseid = caseid.replace('{''').replace('}''');
            var answer = confirm("Do you want to Close the Case?")
            if (answer) {
                if (checkCaseOpenActivities(caseid)) {
                    var ChangesToUpdate = {
                        new_IsCaseResolved: true
                    };
 
                    updateRecord(caseid, ChangesToUpdate, "IncidentSet");
 
 
                }
 
                else {
 
                    alert('Please close open activities before closing the Case');
                }
            }
        }
 
    }
    catch (err) { }
}
 
 
function checkCaseOpenActivities(caseId) {
    var ODataSelect = "ActivityPointerSet?$select=ActivityId&$filter=RegardingObjectId/Id eq guid'" + caseId + "' and StateCode/Value eq 0";
    var CaseActivities = GetDataUsingODataServiceWithJQuery(ODataSelect);
    if (CaseActivities != null) {
        if (CaseActivities.results.length > 0) {
            return false;
        }
        else {
            return true;
        }
    }
    else {
        return true;
    }
 
 
}

function GetContext() {
    var _context = null;
    if (typeof GetGlobalContext != "undefined")
        _context = GetGlobalContext();
    else if (typeof Xrm != "undefined")
        _context = Xrm.Page.context;
    return _context
}
 
 
DisplayError = function (cntrl, func, err) {
    alert("Control : " + cntrl + "\nODataCommonFunctions.: " + func + "\nError : " + err);
}
 
// ========== OData Serivce - JQuery Request Functions Start ==========
 
function GetDataUsingODataServiceWithJQuery(ODataSelect) {
    var resultOfGet = null;
    var CRMContext = GetContext();
    if (CRMContext != null) {
        var serverUrl = CRMContext.getServerUrl();
        var ODataEndpoint = "/XRMServices/2011/OrganizationData.svc";
        var ODataURL = serverUrl + ODataEndpoint + "/" + ODataSelect;
        try {
            $.ajax({
                type: "GET",
                contentType: "application/json; charset=utf-8",
                datatype: "json",
                async: false,
                url: ODataURL,
                beforeSend: function (XMLHttpRequest) { XMLHttpRequest.setRequestHeader("Accept""application/json"); },
                success: function (data, textStatus, XmlHttpRequest) {
                    resultOfGet = data.d;
                },
                error: function (XMLHttpRequest, textStatus, errorThrown) {
                    DisplayError("ODataService""Fetch", textStatus + " - " + JSON.parse(XMLHttpRequest.responseText).error.message.value);
                    //resultOfGet = null;
                }
            });
        } catch (e) {
            DisplayError("ODataService""Fetch", e.description);
            //resultOfGet = null;
        }
    }
    return resultOfGet;
}
function updateRecord(id, entityObject, odataSetName) {     var jsonEntity = window.JSON.stringify(entityObject);     // Get Server URL     var serverUrl = Xrm.Page.context.getServerUrl();     //The OData end-point     var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc";     //Asynchronous AJAX function to Update a CRM record using OData     $.ajax({         type: "POST",         contentType: "application/json; charset=utf-8",         datatype: "json",         data: jsonEntity,         url: serverUrl + ODATA_ENDPOINT + "/" + odataSetName + "(guid'" + id + "')",         beforeSend: function (XMLHttpRequest) {             //Specifying this header ensures that the results will be returned as JSON.             XMLHttpRequest.setRequestHeader("Accept""application/json");             //Specify the HTTP method MERGE to update just the changes you are submitting.             XMLHttpRequest.setRequestHeader("X-HTTP-Method""MERGE");         },         success: function (data, textStatus, XmlHttpRequest) {             alert("Case resolved successfully");         },         error: function (XmlHttpRequest, textStatus, errorThrown) {             if (XmlHttpRequest && XmlHttpRequest.responseText) {                 alert("Error while updating " + odataSetName + " ; Error – " + XmlHttpRequest.responseText);             }         }     }); }



4.Writing the plugin:
Create a plugin as given below


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Crm.Sdk;
using Microsoft.Xrm.Sdk;
using System.ServiceModel;
using System.Diagnostics;
using System.Data.Services;
using System.Data.Services.Client;
using contoso;
using System.Xml;
using Microsoft.Crm.Sdk.Messages;
 
 
namespace Xrm.Case
{
   public class IncidentPostUpdate:IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            contoso.Incident IncRec;
 
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
 
 
            if (context.InputParameters.Contains("Target") && context.InputParameters["Target"is Entity)
            {
 
                //Verify that the entity represents an Case
                if (((Entity)context.InputParameters["Target"]).LogicalName != contoso.Incident.EntityLogicalName || context.MessageName != "Update")
                    return;
                else
                    IncRec = ((Entity)context.InputParameters["Target"]).ToEntity<contoso.Incident>();
            }
            else
            {
                return;
            }
 
            try
            {
                IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
                IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
 
 
                using (var serenaContext = new SerenaContext(service))
                {
 
                    
                        if (context.Depth == 1)
                        {
 
                            if (IncRec.new_IsCaseResolved == true)
                            {
 
                                contoso.Incident inc = new Incident();
 
                                inc.new_IsCaseResolved = false;
                                inc.Id = IncRec.Id;
                                service.Update(inc);
 
                      
                                 List<contoso.ActivityPointer> caseOpenActivities = serenaContext.ActivityPointerSet
                                .Where(a => a.StateCode == ActivityPointerState.Open && a.RegardingObjectId.Id ==IncRec.Id)
                                .Select(a => new contoso.ActivityPointer
                                {
                                    Id = a.Id
                                }).ToList<contoso.ActivityPointer>();
 
                                 if (caseOpenActivities != null && caseOpenActivities.Count > 0)
                                 {
                                     throw new InvalidPluginExecutionException("Close all the Open activities associated with Case before closing the Case");
                                 }
                                 else
                                 { 
                                    // Create the incident's resolution.
                                    Entity caseResolution = new Entity("incidentresolution");
                                    caseResolution.Attributes.Add("incidentid"new EntityReference("incident", IncRec.Id));
                                    caseResolution.Attributes.Add("subject""Closed");
 
 
                                    // Close the incident with the resolution.         
 
                                    CloseIncidentRequest req = new CloseIncidentRequest();
                                    req.IncidentResolution = caseResolution;
                                    req.RequestName = "CloseIncident";
                                    OptionSetValue o = new OptionSetValue();
                                    o.Value = 5;
                                    req.Status = o;
 
                                    CloseIncidentResponse resp = (CloseIncidentResponse)service.Execute(req);
                                 }
 
                             }
                        }
                                    
                    
                }
 
 
            }
            catch (InvalidPluginExecutionException)
            {
                throw;
            }
            catch (FaultException<OrganizationServiceFault> ex)
            {
                throw new InvalidPluginExecutionException("An error occurred in the plug-in.", ex);
            }
 
        }
    }
}


5.Register the plugin as per the below screen shots:




We are done with the customization to override the Resove case Please note some points.

1.In plugin I am using early bound approach you can go with late bound as you wish.
2.Please include "Json" and "jquery" file on Case form as they will we used by the "updateRecord" method.


Hope it helps you.


Regards,
Yusuf

No comments:

Post a Comment