Thursday, June 13, 2019

How to pass execution context as a parameter from a button click in Dynamic CRM


For this we can user Ribbon Workbench and add Crm Parameter as our first parameter and pass Primary Control as a value. 
Please refer below image for details.



In JavaScript we can access the field or other values as:

function openDymoPrintAssistance(context)
{
    var formContext = context.getFormContext();
    var clinicappointmentId = formContext.data.entity.getId();
    clinicappointmentId = clinicappointmentId.substring(1, clinicappointmentId.length - 1);


If you are looking for how to create a perfect button, refer here

https://vjcity.blogspot.com/2019/06/how-to-create-custom-button-in-dynamic.html

Thursday, June 6, 2019

Cleaning storage space in Dynamic CRM


Often Dynamic CRM Administrator receives an email from Microsoft stating following:

“You are approaching the storage limit for your tenant. Please review the storage usage across your instances and either purchase additional storage or free up storage.”



Is there any way to see storage capacity being consumed per organization (tenant – that is Sandbox/Production)

What specific data is growing or resulting in consumption of storage?


The answer to above questions is YES, with Power Platform Dashboard.

Navigate to Analytics -> Capacity, once you launched the power platform.
Here I would be demonstrating one of my client environments and the action that I took in order to free up some of the storage space.



File Usage: All file attachment added to emails and notes & attachment sections.

Log Usage is our Audit logs for User access and entities information.

I clearly identified that I do not need Audit and I need to delete all attachments in my Sandbox Environment. Further I can stop auditing in my sandbox.

Delete Audit Logs
Navigate to Settings->Audit Log Management

Note: You can delete only the oldest audit log, so start with the 1 serial number. 


It might be possible that the last audit might still be in use and CRM will restrict you to delete the log. In this case Navigate to Settings -> Audit Summary View


Important Note:
After you delete data, it may take 24 hours to reflect the deletion

Disable Auditing in Sandbox

Navigate to Settings->Global Audit Settings.




Deleting all Attachments.

You can check the actual count of Notes and attachment from Advanced Find Tool. Add the columns File Size if you want to check more in details.



You can also use Bulk Deletion Tool from XrmToolBox or CRM Bulk Record Deletion.




Some of the Common Entities where data can be deleted (Even for Production)
  • Data Import History
  • Bulk Record Deletion History
  • All System Job’s whose Status is Succeeded.
  • System Jobs where workflows are in waiting state.







Please do let me know what you did to release more free space in your Dynamic CRM Environment.

Regards,
Vipin Jaiswal

Wednesday, June 5, 2019

Dissecting Rollup Fields in Dynamic 365


If you are looking to Modify Recurrence for a Roll-up field, you can refer my earlier blog post here - https://vjcity.blogspot.com/2019/06/how-to-change-frequency-for-rollup.html


If you are looking to refresh Rollup field dynamically on the form 
at run-time refer here - Refreshing Rollup field using JavaScript


There were few freaking business scenarios that I came across which were blowing my mind up. I cannot operate on them as it was client productions so, I ended up doing my separate Rnd to understand and have a closer look at Rollup fields in Dynamic CRM.

1) Last Updated On: All Rollup Fields are provided with supported datetime field (always Read-Only) which state when was the last time the respective rollup field got update. Below is a self-explanatory image.




2) Manual Hard Refresh: A business user can manually refresh the values of the respective roll-up field on the given form.




3) Rollup field gets refreshed even when the Parent Record is De-Activated.

An Example:
  • Deactivated a contact record and try adding an opportunity.
  • Rollup field gets refreshed by system job automatically and job does not consider state of parent entity.
  • Usually we interpret that form is locked once it is de-activated, but this rules does not apply on Rollup field.


I added a single opportunity and checked its Rollup values and then deactivated the contact record.

Even on the deactivated contact record, I added few opportunities.





4) Modifying Recurrence only affect with next Cycle.
I did some testing and come up with this conclusion. I was having few Rollup field on contact entity and by default its recurrence time was set to 12 Hours. Even after changing recurrence to 1 Hour, the rollup fields were not recalculating the values. I checked for few hours to see if they recalculate the values and even more surprisingly  when looking at system jobs I found that they were running and were Succeeded but they do not show any result. The values got changed in the next morning and then it accepted the change in recurrence for recalculating the Rollup field with every 1 hour.


I believe there is a super process running in Microsoft Background which keeps monitoring these value changes not immediately but some scheduled time.

So it is very important to think at least twice when we are choosing to have Roll-up field.

Regards,

vipinjaiswal12@gmail.com

Tuesday, June 4, 2019

How to change the frequency for rollup field in Dynamic 365


Refer here - Refreshing Rollup field using JavaScript if you are looking to refresh Rollup field dynamically on the form at run-time.



In Dynamic CRM for each entity that contains at least one rollup field, a recurring system job is automatically created to asynchronously calculate the rollup values.
To view such rollup system jobs:
  • Navigate to Settings > System Jobs.
  • Change the view to Recurring System Jobs.

Or you can use Advanced Find Tool as in below image.


Dynamic CRM follows specific naming convention for Rollup Jobs. 
EntityName.FieldName


Changing the recurrence is supported only at entity level and not on single field. So, opening the Calculate Rollup Field Job will give me this




When we open Rollup Job for a specific field, we can only Resume, postpone or pause it and it gets it Recurrence configuration value from the entity rollup job. A good example of Inheritance.



Some Notes
  • By default, CRM will automatically schedule a job to run approximately 12 hours from the time that the rollup field is saved.
  • Rollup field data type should be of the field which we might use for an aggregation. 
ü  For Example: Sum of Estimated Revenue for all opportunities – Currency – as because Est. Revenue field on opportunity is of currency type

  • Rollup Job will be listed or created only when we complete the formula definition. It means just creating a rollup field and not editing and defining the formula will not generate any job.


There is more about Rollup fields, which I have written on separate blog.
https://vjcity.blogspot.com/2019/06/dissecting-rollup-fields-in-dynamic-365.html


Regards,
Vipin Jaiswal
vipinjaiswal12@gmail.com

Friday, May 31, 2019

How to pass and retrieve custom data to a WebResource in Dynamic 365 CRM


Below blog uses deprecated method so I created another one to launch a webresource and pass parameter to it refer here... it uses navigateTo method

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

We all know the concept of querystring parameters in Url. The easiest and visible way to pass the small chunks of data to a webpage.

Here is sample querystring:
https://vjcity.crm4.dynamics.com//WebResources/cm_/html/Dymolabelprinter/vjLabDymo.html?accountId=EEA310A9-8483-E911-A85F-000D3AB0DC71&entitytypename=account


window.open(url) : Not working !!!
Reason: 
  • When we create any new webresource, CRM generates the Url automatically for us without any parameters and CRM know only that registered webresource.
  • When we try to browse the Url using window.open with querystring parameter, it searches the whole string against the registered webresource and throws an 500 error as say webresource not found.

Therefore Dynamic CRM has come up with - Xrm.Utility.openWebResource

Here is an example to understand how to pass custom parameters

function passParameters(accountId)
{
    var webResource = "cm_/html/Dymolabelprinter/NeoLabDymo.html"; // webresource schema name
    var customParameters = encodeURIComponent("entityId=" + accountId + "&entitytypename=account");
    Xrm.Utility.openWebResource(webResource, customParameters);
}


How to retrieve them within webpage


var entityId = getUrlVars()["entityId"];
var entityName = getUrlVars()["entitytypename"];



// Read a page's GET URL variables and return them as an associative array.
function getUrlVars()
{
    var vars = [], hash;
    var hashes = unescape(window.location.search.replace('?', ''));
    hashes = hashes.replace('Data=', '').replace('data=', '').split(',');
    for (var i = 0; i < hashes.length; i++)
    {
        hash = hashes[i].split('=');
        vars.push(hash[0]);
        vars[hash[0]] = hash[1];
    }
    return vars;
}

As CRM append querystring data to the Url, it also appends data by default, we might want to get rid of that and just interested in the parameters that we have passed.


Another Limitation
Xrm.Utility.openWebResource always opens in new window in (IE11)
Xrm.Utility.openWebResource() performs different in Chrome as compared to IE11.

openInNewWindow = true/false in parameter window Options is not available for openWebResource()


An unsupported approach described by Andrii here - a33ik.blogspot.co.uk/.../step-by-step-creating-dialog-windows.html


Hope this helps.

Thanks
Vipin Jaiswal
vipinjaiswal12@gmail.com



Wednesday, May 22, 2019

Generic way to get OptionSet value or Text in Dynamic 365 CRM


Here are C# methods

public static string GetOptionSetTextFromValue(string entityName, string attributeName, int value)
{
    string optionsetText = string.Empty;
    var attributeRequest = new RetrieveAttributeRequest
    {
        EntityLogicalName = entityName,
        LogicalName = attributeName,
        RetrieveAsIfPublished = true
    };
    // Execute the request.
    var attributeResponse = (RetrieveAttributeResponse)service.Execute(attributeRequest);
    // Access the retrieved attribute.
    var attributeMetadata = (EnumAttributeMetadata)attributeResponse.AttributeMetadata;
    // Get the current options list for the retrieved attribute.
    var optionList = (from o in attributeMetadata.OptionSet.Options
                        select new { Value = o.Value, Text = o.Label.UserLocalizedLabel.Label }).ToList();

    optionsetText = optionList.Where(o => o.Value == value)
                                .Select(o => o.Text)
                                .FirstOrDefault();
    return optionsetText;
}


public static int GetOptionSetValueFromLabel(string entityName, string attributeName, string optionsetText)

{
    int optionSetValue = 0;
    var attributeRequest = new RetrieveAttributeRequest()
    {
        EntityLogicalName = entityName,
        LogicalName = attributeName,
        RetrieveAsIfPublished = true
    };
    // Execute the request.
    var attributeResponse = (RetrieveAttributeResponse)service.Execute(attributeRequest);
    // Access the retrieved attribute.
    var picklistAttributeMetadata = (PicklistAttributeMetadata)attributeResponse.AttributeMetadata;
    // Get the current options list for the retrieved attribute.
    OptionSetMetadata optionsetMetadata = picklistAttributeMetadata.OptionSet;
    var optionList = (from o in optionsetMetadata.Options
                        select new { Value = o.Value, Text = o.Label.UserLocalizedLabel.Label }).ToList();

    optionSetValue = (int)optionList.Where(o => o.Text.ToLower() == optionsetText.ToLower())
                                .Select(o => o.Value)
                                .FirstOrDefault();
    return optionSetValue;

}


Here is REST API Way

We need to include Formatted Values to get OptionSet Labels

var req = new XMLHttpRequest();
var fetchStatement = "/api/data/v9.1/contacts(EF5D6F09-399A-E811-A966-000D3AB0C08C)?$select=cm_language";
req.open("GET", Xrm.Page.context.getClientUrl() + fetchStatement, false);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
req.onreadystatechange = function () {
    if (this.readyState === 4) {
        req.onreadystatechange = null;
        if (this.status === 200) {
            var result = JSON.parse(this.response);
            //OptionSet Value
            var cm_language = result["cm_language"];
            //OptionSet Label (Text)
            var cm_language_formatted = result["cm_language@OData.Community.Display.V1.FormattedValue"];
        } else {
            Xrm.Utility.alertDialog(this.statusText);
        }
    }
};

req.send();


Here is a Form Script



Xrm.Page.getAttribute("cm_customerservicelanguage").getSelectedOption().value 
Xrm.Page.getAttribute("cm_customerservicelanguage").getSelectedOption().text