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.









Thursday, May 5, 2016

Add / remove users to Access team dynamically

Sometime we need to add users into access teams when record is created or based on some business logic.

From MS CRM 2013 Microsoft introduced access teams.

For information about access team
 Use access teams and owner teams to collaborate and share information

You can enable access team for entity and create access team templates.

Here is more information on how to create access team template and add to form.
Create Access Teams

when you created access team template you can add it to form and add users manually to give access to record, but some time we need to it automatically.

To do automatically, you can use  AddUserToRecordTeamRequest messge.

AddUserToRecordTeamRequest addReq = new AddUserToRecordTeamRequest()
       {
         Record = Record Entity Reference,
         SystemUserId = UserId,
         TeamTemplateId = TemplateId

       };

service.Execute(addReq);

and use Execute to pass this request.

If want to do add user into multiple records access Team.

public void AddUserToAccessTeam(IOrganizationService service, List<EntityReference> lstentref, string templateName, Guid loggedInUser, Guid? TemplateId)
        {
         using (XrmServiceContext Xrmcontext = new XrmServiceContext(service))                                  {
                Guid teamTemplate = new Guid();

                if (TemplateId == null || TemplateId.Value == Guid.Empty)
                    /*Get Access Team Template from Template Name*/
                    teamTemplate = Xrmcontext.TeamTemplateSet.Where(ttm => ttm.TeamTemplateName == templateName).Select(ttm => ttm.TeamTemplateId.Value).FirstOrDefault();
                else
                    teamTemplate = TemplateId.Value;

                int count = 0;
                int TotalRecord = lstentref.Count();
                int recordRemaining = TotalRecord;

                /*Initialize multiple request*/
                ExecuteMultipleRequest requestWithResults = new ExecuteMultipleRequest()
                {
                    Settings = new ExecuteMultipleSettings()
                    {
                        ContinueOnError = true,
                        ReturnResponses = true
                    },
                    Requests = new OrganizationRequestCollection()
                };

                foreach (EntityReference entRef in lstentref)
                {
                    recordRemaining--;
                    /*Add user to Record Team request*/
                    AddUserToRecordTeamRequest addReq = new AddUserToRecordTeamRequest()
                    {
                        Record = entRef,
                        SystemUserId = loggedInUser,
                        TeamTemplateId = teamTemplate
                    };

                    requestWithResults.Requests.Add(addReq);
                    count++;
                    if (count == 999 || recordRemaining == 0)
                    {
                        ExecuteMultipleResponse responseWithResults = (ExecuteMultipleResponse)Xrmcontext.Execute(requestWithResults);
                        requestWithResults.Requests.Clear();
                        count = 1;
                    }
                }
            }
        }


To remove user from access team you can use RemoveUserFromRecordTeamRequest messge.

RemoveUserFromRecordTeamRequest addReq = new RemoveUserFromRecordTeamRequest()
    {
      Record = Record Entity Reference,,
      SystemUserId = User Id,
      TeamTemplateId = Access Team Template Id

    };

Use similar method as AddUserToAccessTeam, if you want to remove user from multiple records.