Friday, April 16, 2021

How to design a custom grid using HTML Web Resource in Dynamic CRM

I came across a requirement where I was required to fetch data from outside dynamic crm using third party API and display related data in a sub-grid.

The sub-grid should have a look and feel close to dynamic crm sub-grid. So here it is what we came up with.


Will try to explain our implementation in 3 steps.

  1. HTML – to prepare the table skeleton.
  2. CSS – to apply formatting.
  3. JavaScript – to dynamically add row to the table based on data fetched.

1)   HTML – to prepare the table skeleton.

Some of the elements are highlighted and are self-explanatory.

<body onload="heloptyact.onload()">

<div style="position:absolute; top:0; left:0; right:0">

    <table id="tblDataList">

        <tbody>

            <tr>

                <th style="width:5%;text-align:center">Sr.</th>

                <th style="width:30%">Activity Name</th>

                <th style="width:42%">

                    Deliverables

                </th>

                <th style="width:8%;text-align:center">

                    Milestone

                </th>

                <th style="width:8%;text-align:center">

                    Value

                </th>

                <th style="width:7%">

                    <input class="helBtn" type="button" id="add" value="Add Activity" onclick="addNewActivity()">

                </th>

            </tr>

        </tbody>

        <tbody id="tblDataListbody"></tbody>

    </table>

</div>

</body>


2) CSS – to apply formatting.


<style>

    #tblDataList {

        font-family: 'Segoe UI', Helvetica, sans-serif;

        border-collapse: collapse;

        width: 100%;

        border: 1px solid #d6d6d6;

    }

 

    #tblDataListbody td {

        border-top: 1px solid #ddd;

        border-bottom: 1px solid #ddd;

        padding: 14px;

        font-size: 13px;

    }

 

    #tblDataList th {

        text-align: left;

        background-color: #F3F3F3;

        color: #505050;

        border-top: 1px solid #d6d6d6;

        border-bottom: 1px solid #d6d6d6;

        padding: 4px 8px 4px 8px;

        font-size: 12px;

        font-family: 'Segoe UI Semibold', Helvetica, sans-serif;

    }

 

    #tblDataList tr:nth-child(even) {

        background-color: white;

    }

 

    #tblDataList tr:hover {

        background-color: #F8FAFC;

    }

 

 

   .helBtn {

        padding: 6px 18px;

        font-size: 14px;

        font-weight: 600;

        font-family: 'Segoe UI';

        text-align: center;

        cursor: pointer;

        outline: none;

        color: white;

        background-color: #4dc6ff;

        border: none;

        border-radius: 8px;

        box-shadow: 0 2px #999;

    }

 

   .helBtn:active {

        background-color: #007ab3;

        box-shadow: 0 1px #666;

        transform: translateY(1px);

    }

 

   .helBtn:hover {

        background-color: #009de6;

    }

 

   .lbl {

        ​​​​​​​font-family: 'Segoe UI', Helvetica, sans-serif;

        font-size: 14px;

        width: 100%;

        text-align: center;

        color: red;

    }


        .helBtnGrey {

        padding: 6px 18px;

        font-size: 12px;

        font-weight: 500;

        font-family: 'Segoe UI';

        text-align: center;

        cursor: pointer;

        outline: none;

        color: black;

        background-color: #F0F0F0    ;

        border: none;

        border-radius: 8px;

        box-shadow: 0 2px #999;

        }


        .helBtnGrey:active {

            background-color: #D3D3D3;

            box-shadow: 0 1px #666;

            transform: translateY(1px);

        }


        .helBtnGrey:hover {

            background-color: #DCDCDC;

        }



</style>

3)   JavaScript – to dynamically add row to the table based on data fetched.

To simplify the demonstration, I fetched the data from within dynamic Crm using Xrm.WebApi and creating a table dynamically.

Here is a code which generate table row dynamically. Refer to the comments for more details.


bindOppActivity: function () 

{

    document.getElementById('tblDataListbody').innerHTML = '';

    var selClause = "?$select=hel_srno,hel_name,hel_milestone,hel_deliverablesvalue,hel_helixorgactivityname,hel_details,hel_opportunityactivitiesid,_hel_opportunityid_value";

    var filClause = "&$filter=_hel_opportunityid_value eq " + heloptyact.optyId;

    var ordClause = "&$orderby=hel_srno asc,hel_name asc";

 

window.parent.Xrm.WebApi.online.retrieveMultipleRecords("hel_opportunityactivities", selClause + filClause + ordClause).then(

function success(results)

{

    for (var i = 0; i < results.entities.length; i++) {

 

        var hel_deliverablesvalue_formatted = results.entities[i]["hel_deliverablesvalue@OData.Community.Display.V1.FormattedValue"];

        var hel_srno = results.entities[i]["hel_srno"];

        var hel_helixorgactivityname = results.entities[i]["hel_helixorgactivityname"];

        var hel_milestone_formatted = results.entities[i]["hel_milestone@OData.Community.Display.V1.FormattedValue"];

        var hel_name = results.entities[i]["hel_name"];

        var hel_primarykeyid = results.entities[i]["hel_opportunityactivitiesid"];

 

 

        var table = document.getElementById("tblDataListbody");

        var rowCount = table.rows.length;

        var row = table.insertRow(rowCount);

 

        row.insertCell(0).innerHTML = hel_srno;

        row.cells[0].setAttribute("style", "text-align:center");

 

        row.insertCell(1).innerHTML = hel_helixorgactivityname;

 

        // Creating a hyperlink to open record

        row.insertCell(2).innerHTML = '<a  href="#" \

                                        onclick="heloptyact.openOptyActivityRecord(\'' + hel_primarykeyid + '\')">' + hel_name + '</a>'

 

        row.insertCell(3).innerHTML = (hel_milestone_formatted == null) ? "" : hel_milestone_formatted;

        row.cells[3].setAttribute("style", "text-align:center");

 

        row.insertCell(4).innerHTML = (hel_deliverablesvalue_formatted == null) ? "" : hel_deliverablesvalue_formatted;

        row.cells[4].setAttribute("style", "text-align:right");

 

        // For Deleting the record, we are using a Delete icon web-resource and calling a method on it click event

        row.insertCell(5).innerHTML = '<input width="16px" height="17px" type="image" \

                                        src = "/WebResources/hel_/image/icon/delete/recyclebin.png" \

                                        value = "Delete" \

                                        onClick = "heloptyact.deleteOpportunityActivity(this, \'' + hel_primarykeyid + '\')" > ';

        row.cells[5].setAttribute("style", "text-align:right");

    }

},

function (error) {

    window.parent.Xrm.Utility.alertDialog(error.message);

}

);

 


deleteOpportunityActivity: function (obj, id) {

    window.parent.Xrm.WebApi.online.deleteRecord("hel_opportunityactivities", id).then(

        function success(result) {

            bindOppActivity();

 

        },

        function (error) {

            window.parent.Xrm.Utility.alertDialog(error.message);

        }

    );

},

 

 

openOptyActivityRecord: function (recordId) {

 

    var entityFormOptions = {};

    entityFormOptions["entityName"] = "hel_opportunityactivities";

    entityFormOptions["entityId"] = recordId;

    entityFormOptions["openInNewWindow"] = true;

 

    // Open the form.

    window.parent.Xrm.Navigation.openForm(entityFormOptions).then(

        function (success) {

            console.log(success);

        },

        function (error) {

            console.log(error);

        });

}

Friday, April 2, 2021

Reopen closed activity in Dynamic 365 Crm

Once an activity is marked as completed in Dynamic 365 CRM, there is no out-of-box way to reopen it.

So here I am outlining a solution that could be used to reopen any close activity. 

It involves us to

  1. A Jscript WebResource
  2. Creating custom button on specific activity entity

1] Create a Jscript Web resource

function reopenActivity() 

{ 

    var activityName = Xrm.Page.data.entity.getEntityName();

    var activityId = Xrm.Page.data.entity.getId().replace('{', '').replace('}', '');

 

    var entity = {};

    entity.statuscode = 1;

    entity.statecode = 0;

 

    Xrm.WebApi.online.updateRecord(activityName, activityId, entity).then(

        function success(result) {

            var updatedEntityId = result.id;

            Xrm.Page.data.refresh();

        },

        function (error) {

            Xrm.Utility.alertDialog(error.message);

        }

    );

}

3] Create a custom button using Ribbon Workbench

Create a button and define its various parameters.

ID : new.synlims_inspection.ReOpenActivity.Button

Tool Tip Description: Re-Open this Activity to be edited.<br/><br/>The activity moves to the open activity views.

Create a command for button and link the Jscript library created above.

Command ID : new.synlims_inspection.ReOpenActivity.Command

Create a FormStateRule

ID : new.synlims_inspection.ReOpenActivity.EnableRule


Here is a video link for detailed explanations




Tuesday, January 12, 2021

Dynamic CRM RetrieveMultiple fetching Condition expression

 

I observe that RetrieveMultiple build query different for Classic UI and UCI and to my surprise when we use default sub grid with displaying only related records, it comes with a difference in query getting generated. 

Here, I have used to code to fetch condition expression that I was interested in and use it further to my business logic


 

if (context.InputParameters.Contains("Query"))

{

if (context.InputParameters["Query"] is FetchExpression)

{  

    // It seems UCI build and send Fetch Expression

    FetchExpression fetchExpression = context.InputParameters["Query"] as FetchExpression;

    FetchXmlToQueryExpressionRequest fetchXmlToQueryExpressionRequest = new FetchXmlToQueryExpressionRequest()

    {

        FetchXml = fetchExpression.Query

    };

    FetchXmlToQueryExpressionResponse fetchXmlToQueryExpressionResponse = (service.Execute(fetchXmlToQueryExpressionRequest) as FetchXmlToQueryExpressionResponse);

    pluginQueryExp = fetchXmlToQueryExpressionResponse.Query;                                   

}

else

if (context.InputParameters["Query"] is QueryExpression)

{

    // Classic UI build and send Query Expression

    pluginQueryExp = ((QueryExpression)context.InputParameters["Query"]);                

}

 

if (pluginQueryExp.LinkEntities.Count > 0)

{

    if (pluginQueryExp.LinkEntities[0].LinkCriteria.Conditions.Count > 0)

    {

        accountId = new Guid(pluginQueryExp.LinkEntities[0].LinkCriteria.Conditions[0].Values[0].ToString());

    }

}

else if (pluginQueryExp.Criteria.Conditions[0].Values.Count > 0)

    accountId = new Guid(pluginQueryExp.Criteria.Conditions[0].Values[0].ToString());

}


Friday, October 30, 2020

Remove comma from number fields in Dynamic 365 CRM

 

 

  • Not a text field and copying value from the existing integer field and hiding the original one.
  • Not changed the personalization setting to change the number formatting for all integers.
  • Created a simple web-resource with similar look and feel.

Refer below for configuration and web-resource code









<html>

<head>

<script>

function setYearValue()

{

    debugger; 

    document.getElementById('yearvalue').innerText = window.parent.Xrm.Page.getAttribute('new_verificationexpireyear2').getValue();

    document.getElementById('lockSymbol').src = window.parent.Xrm.Page.context.getClientUrl() + "//WebResources/new_/image/locksymbol.png";

    window.parent.Xrm.Page.getControl('new_verificationexpireyear2').setVisible(false);

}

</script>

</head>


<body onload="setYearValue();"> 

<div style="border-bottom:1px solid rgb(216,216,216); width:100%; margin-left:-12px; ">

<table>

<tr>

    <td style="padding:0px 0px 10px 5px">

        <img id="lockSymbol" />

    </td>

    <td style="padding:0px 85px 10px 5px">

        <span class="ms-crm-InlineEditLabelText ms-crm-UnsetWhiteSpace"

                id="new_verificationexpireyear_cl"

                style="white-space: nowrap; margin: 0px; font-family: 'Segoe UI', Tahoma, Arial; font-size: 1rem; float: left; color: rgb(68,68,68);">

            Expiry Year

        </span>

    </td>

    <td style="padding:0px 80px 10px 5px">

        <span id="yearvalue" style="margin: 0px; font-family: SegoeUI, 'Segoe UI', Tahoma, Arial; font-size: 1rem; opacity:1; color:rgb(51,51,51);">

                       

        </span>

    </td>

</tr>

</table>

</div>

</body>

</html>