Friday, May 13, 2016

Inject your code before CRM button event - Conditional deactivation of CRM record

I came across business requirement like, if there are any active child records, then show error message to users when deactivating parent record.


I can use plugin or real time workflow for this, but when error message thrown, CRM throws error like Business process error.


But customer doesn’t want to see this business process error, customer wants Alert message, and do not want to deactivate record.
To full fill this requirement I customized CRM button command, and added my own script on deactivate button.
Here are steps I did

I am using Ribbon workbench to customize entity ribbon.

1.       Customize Deactivate command

2.       Once command is customized you will see JavaScript library and function used by MS CRM.



3.  Change function Name to NaN and remove parameters for CRM default deactivates action.

This we need to do so when condition is satisfied, record should be deactivated and CRM JavaScript library should be loaded. 


4.       Now add your own JavaScript action in deactivate command, and call your own JavaScript function
When calling own JavaScript function make sure you are using same Parameters used in CRM default deactivates function, as we are going to call this function in our own JavaScript.







5.       In your JavaScript code, you can check, is there any active child records associated with this record? If not then deactivate record, otherwise show message. 

JavaScript code is

CheckOnDeactivation: function (CompanyId, entityName) {
        var req = new XMLHttpRequest();
        req.open("GET", Xrm.Page.context.getClientUrl() + "/XRMServices/2011/OrganizationData.svc/contact?$select=contactId&$filter=parentcustomerid/Id eq (guid'" + CompanyId + "') and statecode/Value eq 0", false);
        req.setRequestHeader("Accept", "application/json");
        req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
        req.onreadystatechange = function () {
            if (this.readyState === 4) {
                this.onreadystatechange = null;
                if (this.status === 200) {
                    var returned = JSON.parse(this.responseText).d;
                    var results = returned.results;
                    if (results.length >= 1) {
                        alert('You cannot deactivate this record because it has active child records.');                        
                        return false;
                    }
                    else {
                        Mscrm.CommandBarActions.deactivate(CompanyId, entityName);
                        return true;
                    }
                }
                else {
                    alert(this.statusText);
                }
            }
        };
        req.send();
    }

6. Save and Publish customization
Now when trying to deactivate parent if it has any active child then error will be thrown and record will not be deactivated. 

You can use same technique for any other button command.  

Friday, May 6, 2016

Calculate Rollup fields on button click

Rollup fields in CRM are updated asynchronously ever 1 hr.  you can manually refresh individual rollup field on form by clicking refresh button, but some time we need to refresh all rollup fields on form in single click.
To do this

1. Create custom workflow activity which will calculate all roll up fields and update values.
To calculate rollup fields you can use CalculateRollupFieldRequest message, and pass it to Execute.

Custom workflow activity code

using System;
    using System.Activities;
    using System.ServiceModel;
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Workflow;   
    using System.Linq;
    using System.Collections.Generic;   
    using Microsoft.Crm.Sdk.Messages;

    public sealed class CalculateRollupStatistics : CodeActivity
    {
        ///
        /// Executes the workflow activity.
        ///

        /// The execution context.

        [RequiredArgument]
        [Input("Company")]
        [ReferenceTarget(new_company.EntityLogicalName)]
        public InArgument<EntityReference> iCompany { get; set; }

        protected override void Execute(CodeActivityContext executionContext)
        {
            // Create the context
            IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();

            if (context == null)
            {
                throw new InvalidPluginExecutionException("Failed to retrieve workflow context.");
            }

            IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            try
            {
                EntityReference companyReference = iCompany.Get(executionContext);
                List<string> rollupAttributes = new List<string>() {"new_totalnoOfvehicals","new_totalcars","new_totalTrucks","new_totalbickes","new_totalbuses" };

                foreach (string attribute in rollupAttributes)
                {
                    CalculateRollupFieldRequest reqUpdateRollup = new CalculateRollupFieldRequest
                    {
                        FieldName = attribute,
                        Target = companyReference
                    };
                    service.Execute(reqUpdateRollup);
                }
            }
            catch (FaultException<OrganizationServiceFault> e)
            {

                throw;
            }
        }

    }

2. Now create one real time workflow for entity where wants to update rollup fields and call created custom workflow activity.

3. Set workflow, Available to Run = 

 No need to set any option for Automatic processes.

4. Now Create HTML web resource

<html>
<head>
    <meta charset="utf-8">
    <title></title>
    <script src="../jquery_1.10.2.js" type="text/javascript"></script>
    <script>
        function RefreshCount() {
            var url = parent.Xrm.Page.context.getClientUrl();
            var recordId = parent.Xrm.Page.data.entity.getId();
            var workflowId = "aa63b391-0001-4f49-9ef3-700957cf8d0d";
            var request = "< s:Envelope xmlns:s='http://schemas.xmlsoap.org/soap/envelope/'>" +
                  "< s:Body>" +
                    "< Execute xmlns='http://schemas.microsoft.com/xrm/2011/Contracts/Services' xmlns:i='http://www.w3.org/2001/XMLSchema-instance'>" +
                      "< request i:type='b:ExecuteWorkflowRequest' xmlns:a='http://schemas.microsoft.com/xrm/2011/Contracts' xmlns:b='http://schemas.microsoft.com/crm/2011/Contracts'>" +
                        "< a:Parameters xmlns:c='http://schemas.datacontract.org/2004/07/System.Collections.Generic'>" +
                          "< a:KeyValuePairOfstringanyType>" +
                            "< c:key>EntityId
" +
                            "< c:value i:type='d:guid' xmlns:d='http://schemas.microsoft.com/2003/10/Serialization/'>" + recordId + "
" +
                          "< /a:KeyValuePairOfstringanyType>" +
                          "< a:KeyValuePairOfstringanyType>" +
                            "< c:key>WorkflowId
" +
                            "< c:value i:type='d:guid' xmlns:d='http://schemas.microsoft.com/2003/10/Serialization/'>" + workflowId + "
" +
                          "< /a:KeyValuePairOfstringanyType>" +
                        "< /a:Parameters>" +
                        "< a:RequestId i:nil='true' />" +
                        "< a:RequestName>ExecuteWorkflow
" +
                      "< /request>" +
                    "< /Execute>" +
                  "< /s:Body>" +
                "< /s:Envelope>";

            var req = new XMLHttpRequest();
            req.open("POST", url + "/XRMServices/2011/Organization.svc/web"true);

            req.setRequestHeader("Accept""application/xml, text/xml, */*");
            req.setRequestHeader("Content-Type""text/xml; charset=utf-8");
            req.setRequestHeader("SOAPAction""http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
            req.onreadystatechange = function () {
                if (req.readyState == 4) {
                    if (req.status == 200) {                      
                       parent.Xrm.Utility.openEntityForm("Entity Logical Name", recordId);
                    }
                    else {
                        alert('Unable to calculate');
                    }
                }
            };

            req.send(request);

        }
    </script>
</head>
<body>
    <div>
        <table style="width:50%">
            <tr>
                <td style="text-alignright">  <input id="btnRecalculate" type="button" value="Recalculate" onclick="RefreshCount()" /></td>
                <td></td>
            </tr>
        </table>
     
    </div>
</body>
</html>


In above code replace Workflow Id with your real time workflow Id and Entity name with your entity name.


5.  Add this web resource on Form where you want to show button.



6. Save and publish Web resource, form and activate workflow.

Now when you click on Recalculate button, roll up attributes count will be recalculated and entity form refreshed to show latest result.