Posts

Showing posts from October, 2021

Ribbon debug/Command checker tool

Image
Background: Usually when something goes wrong in the button visibility or to find out the rules, actions(js) being used in the ribbon button, we go and find either in the ribbondiffxml or through ribbon workbench. This is a time taking process.  Solution: Now there is a built-in tool called 'Command Checker' available. Using this tool, we can easily troubleshoot the button visibility related issues in home page grid, entity form, sub grid and global command bar as well.  To use/enable this tool, pass "ribbondebug=true" parameter in the url. Example, if you want to troubleshoot some button in Account form, below is the url,  https://trial.crm5.dynamics.com/main.aspx?appid=4db5bcb8-675b-ec11-8f8f-002248599a15&pagetype=entityrecord&etn=account&id=83883308-7ad5-ea11-a813-000d3a33f3b4 &ribbondebug=true This will enable the command bar in 3 places. 1. In the header's right side 2. In the Form command bar 3. If the form has any sub grids. Co...

Run Power automate under modifying user context

Image
In Power automate, we can run the flow under the below mentioned user's context. 1. Modifying user 2. Flow owner 3. Record owner We had a requirement to run the flow under Modifying user. So, in the trigger we set 'Run as' Modifying user. But still we noticed it was running under the service account which we had selected in the update action and modified by of the record was always Service account. Finally, we found out that though we had selected Run as 'Modifying user' in the trigger, in every Dataverse action, we need to select 'use invoker's connection' as shown below to call CRM in the context of the modifying user.

D365 JS Best Practices

Based on the experiences in various CRM projects, I would like to share some of the best practices which will be helpful to make JS files easy to maintain, reusable, reliable and with industry code standards. 1. Create JS files with namespace and add all functions, objects and properties to the namespace. Example:   // Namespace in project level var SCH = SCH || {}; // Namespace in js file level SCH.RescheduleOrder = SCH.RescheduleOrder || {}; // Object declaration SCH.RescheduleOrder.StateCodeValues = { Active: 0, Inactive: 1 }; // Function declaration SCH.RescheduleOrder.onLoad = function (executionContext) { "use strict"; var formContext = executionContext.getFormContext(); //Usage of object inside the function if (formContext.getAttribute("statecode").getValue() === SCH.RescheduleOrder.StateCodeValues.Active) { // To do; } }; 2. "use strict" directive inside every functio...

Convert datetime to user local in D365 Plugins

Background: There could be times where we need to compare the datetime field with today's date in the plugin for some validations. Example: We had a requirement to compare the expiry date field with today's date.  During comparison it was not giving the expected result. In our scenario, User's timezone is in IST. In plugin, the expiry date was returned in UTC. So, if user sets the date set as 21-Oct-2021 12:00:00 PM IST, in plugin code it was returning  19-Oct-2021 12:00:00 IST and comparing with the today's date,  21-Oct-2021 12:00:00. This resulted in incorrect comparison. So, we had to convert both expiry date and today's date to user local before comparison. Following are the steps to achieve the same. 1. Get the initiating user id in plugin  2. Retrieve timezonecode from user settings entity based on  initiating user id. 3. Initiate  LocalTimeFromUtcTimeRequest by passing expiry date in UTC along with user's timezonecode to get the User's local time. B...

Run async calls sequentially in D365 JS

With the introduction of "Xrm.WebApi.online", calls to CRM api becomes asynchronous always. Due to this, most of the times, we need to use ".then()" function and write the logic which needs to be executed next, or we are going with "XMLHttpRequest" for making synchronous calls.  Now, using  'async and await' keywords, we can make the code to run in a synchronous way with  "Xrm.WebApi.online" as well. Example: Consider the following requirement. In Contact home page, show or hide the 'Summary' ribbon button based on the view selected. the view name is stored in the custom entity 'Configuration Settings' which has two fields: Name and Value. There is already a method implemented to retrieve the  Configuration Settings Value based on the Name passed using  Xrm.WebApi.online. retrieveMultipleRecords . function GetConfigSettings(viewName) { try { var result = Xrm.WebApi.online.retrieveMultipleRecords("new_conf...

Show/Hide ribbon button based on the selected view in the D365 subgrid

Image
Requirement: We had a requirement where we need to create custom button for 'Add Existing' functionality and show/hide this button based on the view selected in the Order subgrid. Solution: In the enable rule of the button, register a custom JS function and pass 'selected control' crm parameter to the function as shown below. Below is the code snippet to retrieve the view name from the subgrid view. selectedControl.getViewSelector().getCurrentView().name With this you can compare the view name which you want and return true/false based on the selected  view . Complete function: function ShowHideCustomAddExistingOrderSubgrid(selectedControl) { var enable = false; if (selectedControl !== null && selectedControl.getEntityName() === "salesorder" && selectedControl.getViewSelector().getCurrentView() !== null) { var viewName = "Orders in reschedule queue"; if (selectedControl.getViewSelector().getCu...

Using Resx file in D365 JS

Image
Most of the times, we have validation or error messages to be shown to user during client side validations in D365. We directly hardcode these message in the JS functions. Instead we can use Resources file(.resx) to store all the messages in common file and use it wherever it is required.  This helps in maintaining the code and also promotes reusability.  This file format is also supported in web resources. Creation of Resource file: In visual studio, resource file can be directly created as shown below. Add the validations message as shown below and save the file(eg:UserValidationMessages) . You can add as many messages you want. Open D365 web resource and upload this file by selecting Type as 'String(RESX)' as shown below. Below is the sample code to retrieve the messages from resource file. var emailValidationMessage = Xrm.Utility.getResourceString("new_UserValidationMessages.resx", "AccountEmailAddressValidationMessage"); The resource file can be upd...

Using Resx file in D365 Plugins/Workflows

Image
Usage of Resx file in D365 Plugins/Workflows: Most of the times, we use fetchxml queries in the plugins and workflows to retrieve the entity data and we directly hardcode these queries in the retrieve methods. Instead we can use Resources file(.resx) to store all the fetchxml in common file and use it wherever it is required. This helps in maintaining the code and also promotes reusability. Creation of Resx File: Below is the sample Plugin project I have created to explain the usage of Resx file. Once the project is created, add new resource file named as "FetchXML.resx" as shown below. After the file is added to the project, open the "FetchXML.resx" file from solution explorer. Add the Name and Fetchxml under the value as shown below. For filter conditions, you can use some placeholders(eg: {EmailAddress}) and replace after you retrieve the Fetchxml string from resource file. Once added save this file. In the plugin code, fetchxml  can be retrieved from resource fi...

Update lookup Views/filters dynamically using D365 JS

Requirement: We had a requirement recently where we need to update the Product field's Lookup View dynamically based on the other fields, lets say Brand and Type.  If Brand has a value then Product lookup should be set with new custom view and if Brand is cleared then "All products" view should be shown with the added filter condition for Type. Solution: Create the following two functions to add in the 'addpresearch' event of Product lookup. 1. If Brand has a value, create a new function named 'filterProductBasedonBrand' and set the custom view  for Product lookup. Example:  function filterProductBasedOnBrand(executionContext) { //Get Form context var formContext = executionContext.getFormContext(); //Get Brand. var brand = formContext.getAttribute("brand").getValue(); //If Brand has value, set the custom lookup view to product lookup. if (brand !== null) { //Get Brand Id. brand = productBrand[0]....

Using Shared libraries in D365 CE Plugin

Image
Requirement: Sometimes, we may need to create a common project to keep the business logics which should be referenced in multiple projects. Solution: In C#, we have a project type called, "Shared Projects". Below is the screenshot for reference. Project will be created as a blank project. Project structure can be created by adding folders and files manually in the way we want as shown in the above screenshot. This project can be referenced in others projects similar to the regular library project as shown below. Shared project cannot be compiled/build directly. During the build of the Project where it is referenced, it will be build and part of the Project's dll where it is referenced. Pros: Shared projects helps us separate the Business logics(helper classes) from the actual Plugin project and can be referenced in other project areas too like integrations instead of duplicating the logics.

Popular posts from this blog

Convert datetime to user local in D365 Plugins

Show/Hide ribbon button based on the selected view in the D365 subgrid

Ribbon debug/Command checker tool