Blogs
-
D365 CRM: Hide Create New Record in Lookup Field
by Damian Sinay on 09/19/2024 – 0 comments
By default the lookup field in a form will have the “+ New Record“ when you try to search for an existing one.To hide this option you need to:
-
Create an Unmanaged solution in the environment.
-
Add the entity with the form that contains the lookup.
-
Export the solution.
-
Extract the zip file.
-
Open the customization.xml file to edit.
-
Locate in the XML the lookup control.
-
Inside the <control><pararemeters> node add the following:
-
<IsInlineNewEnabled>false</IsInlineNewEnabled>
-
-
Save the file.
-
Generate the zip file.
-
Import to the environment.
-
Publish the Customizations.
-
-
D365 CRM: Formatting the Phone Number in US format using JavaScript
by Damian Sinay on 06/05/2024 – 0 comments
If you need to format a phone number field in an Entity Form in CRM, use the following JavaScript code to accomplish this:
function formatFieldPhoneNumber(executionContext, fieldName) {
formContext = executionContext.getFormContext();
var value = formContext.getAttribute(fieldName).getValue();
var phoneFormat = formatPhoneNumber(value);
formContext.getAttribute(fieldName).setValue(phoneFormat);
}
let formatPhoneNumber = (str) => {
//Filter only numbers from the input
let cleaned = ('' + str).replace(/\D/g, '');
//Check if the input is of correct
let match = cleaned.match(/^(1|)?(\d{3})(\d{3})(\d{4})$/);
if (match) {
//Remove the matched extension code
//Change this to format for any country code.
let intlCode = (match[1] ? '+1 ' : '')
return [intlCode, '(', match[2], ') ', match[3], '-', match[4]].join('')
}
return null;
}
Simple add the function “formatFieldPhoneNumber” in the OnChange event of your phone attribute field in the Form passing the execution context as a first parameter and the field name (telephone1 for ex.) as a second parameter and try it.
Enjoy it!
-
How to create a Web Job in Azure
by Damian Sinay on 02/14/2024 – 0 comments
Hi, the purpose of this blog post is to show developers how to create a Web Job.
-Go to Azure Portal.
-Search for Resource group. Create a Resource group if you don’t have any:
-Search for APP service and create an app service
-Give any name you want to the instance:
-Select the publish code, or if you need any docker, select Runtime stack (either core or asp.net frame work or tomcat). Go to Monitoring tab if you want to enable Application insights. By default, it will be enabled.
-Click on Review + Create and click on Create.
-Go to Azure portal -> Resource groups -> Resource (which you have created) -> WebJob App Service. Click on “Get Publish profile” and download and import it into Visual studio to publish the web job.
-Go to Azure portal and search for Storage accounts and click on it.
-Click on Add.
-Click on Review +Create.
-Go to Storage account, click on the Storage account previously created. Click on Access keys and copy the connection string.
Now go to Visual Studio to create a Console APP
File->New->Project
Right click on solution -> click add -> New Item -> select JSON type and name it Settings.job
Open the “Settings.job” and add the following CRON expression:
{
"schedule": "0 0 */4 * * *"
}
Note: The basic format of the CRON expressions in Azure is:
{second} {minute} {hour} {day} {month} {day of the week}
Here more documentation about this:
https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-timer?tabs=in-process&pivots=programming-language-csharp#ncrontab-expressions
Go to App.config and add the below line in the connectionstring tag
<add name="AzureWebJobsDashboard" connectionString= ”<connection string copied from storageaccount> " />
<add name="AzureWebJobsStorage" connectionString = ”<connection string copied from storageaccount>" />
Click on "Microsoft Azure App service" or Import the profile which we downloaded in the earlier steps.
Below, the screen should appear. Go to the settings tab, set configuration to “Release,” and publish.
Wait for the message below to appear in the output window of Visual Studio to assure that the deployment was successful.
Go to the Azure Portal on the below screen and click on WebJob. You should be able to find your WebJob here.
Original Source:
https://www.c-sharpcorner.com/blogs/how-to-create-a-webjob-in-azure-using-visual-studio
Official Documentation:
https://docs.microsoft.com/en-us/azure/app-service/webjobs-dotnet-deploy-vs
Enjoy it!
-
D365 CRM: How to access in Canvas APP the form fields of an Entity record
by Damian Sinay on 09/14/2023 – 1 comments
By default, when you create a Canvas App and try to access the form fields of an entity record, these fields will not be present in the designer:
To do this you need to add first the Canvas APP in the form. Copy your Canvas APP Id in the Details section:
And paste it in the form editor:
The Entity name field will auto populate the entity logical name of the form, in this case Contact.
Then click the Customize button. This will be open a new window to edit the Canvas APP but now with a new component: ModelDrivenFormIntegration.
Then select the control you need to use to show the field value, for example fullname. In the Text function put “[@ModelDrivenFormIntegration].Item.'Full Name'”. But this will throw an error because we need to specify to ModelDrivenFormIntegration the DataSource. To do this add the Entity in the Data section:
And map that in the DataSource property of the ModelDrivenFormIntegration.
And now you will see the values correctly:
Save and publish the Canvas APP.
Save and publish the form.
Enjoy it!
-
Edit Form, our new Product
by Damian Sinay on 06/29/2023 – 0 comments
In the previous versions of D365/Dynamics CRM you could access in the Form Editor clicking this button in the ribbon, and Microsoft removes/hides this button.
Now with our product Edit Form you can recover this button in the new versions.
With the Edit form solution you can easily edit the form of the table you are working on, click on Edit Form and select the modern or classic option.
This solution helps you edit the form without having to go and find the solution where the form is installed.
NOTE: you need to be System Administrator or System Customizer to be able to see the Edit Form button.
Check our product section to download Edit Form FREE and install it in your D365 environment.
-
D365 CRM: Call Azure Function from Dynamics CRM using Plugin Webhook
by Damian Sinay on 04/27/2023 – 0 comments
D365 CRM has the feature to call an Azure Function app as a plugin step triggered in a target event of an entity record, for example in the create of an account record. To archive this you need to develop in Visual Studio a project of type Function App an create a Http Trigger Get function in this project with the following code to read the request of CRM:
This simple code will get the request body as a string that you can parse in a JSON object. You can use RemoteExecutionContext class to actually get all the contextual information into the Function app and then use it further.
Once ready your code, Publish in Azure.
Open Plugin Registration Tool to register the new Webhook:
Enter the Webhook details. Select Authentication type as WebhookKey:
To get the URL and the key, go to the Function App deployed in portal and look </> Get function URL to copy the function URL:
The key will be the value after code= in the URL. Paste it in the Webhook and Save.
Then create a New Step inside of the Webhook in a target event of the entity (for example in the Create of account) and test it.
You can check the log of the function to see the request body that CRM sent to the Function App:
And with this, you can call Azure Function using Plugins with D365 CRM.
Enjoy it!
-
D365 CRM: How to get the connection string of your CRM Organization stored in an Azure Key Vault
by Damian Sinay on 01/26/2023 – 0 comments
It is a good practice for security purpose to store in an Azure Key Vault your credentials and/or connections to the different system such as the D365 CRM connection string. Here I will share a portion of code to retrieve a Secret stored in an Azure Key Vault, for example a Connection String to use in the CRM API.
Important Note: You will need for this a Client ID and a Secret Key generated to authenticate to the Key Vault.
-First of all, create in your Azure Key Vault a Secret that contains your connection string of CRM.
-Take a note of your Client ID, Secret Key, Vault URI, and the Secret Name created in the previous step.
-Use the following code to get the Secret value of the Key Vault:
This will return the secret value stored in that Key Vault Secret, in this case the connection string to connect to the CRM API.
Enjoy it!
-
D365 CRM: Execute server-side bulk operations in Dynamics CRM
by Damian Sinay on 12/22/2022 – 0 comments
In Microsoft Dynamics 365 Customer Engagement server-side automation, ex. plugins and custom workflow activities, we can write code to create, retrieve, update and delete individual records. When we need to carry out these operations on multiple records, we can use a loop and query the database for each record. However, a much more efficient way to carry out bulk operations is to use the ExecuteMultipleRequest message (to Create, Update and Delete records in bulk). In this example I will show a code to execute a bulk update of thousands of accounts records that would make the process a bit slow if you do it by one by one.
1. EntityCollection accountActiveCollection = GetActiveAccounts(); // Your query to get the accounts
2. List<Entity> accountList = new List<Entity>();
3.
4. foreach (Entity account in accountActiveCollection.Entities)
5. {
6. Entity up = new Entity("account", account.Id);
7. up["remcod_legalname"] = account["name"].ToString();
8.
9. accountList.Add(up);
10.
11. if (accountList.Count > 500) // When the batch list is more than 500 then I will call the execute request to update
12. {
13. Console.WriteLine("Update");
14.
15. ExecuteMultipleRequest requestWithResults = new ExecuteMultipleRequest();
16. requestWithResults = new ExecuteMultipleRequest()
17. {
18. // Assign settings that define execution behavior: continue on error, return responses.
19. Settings = new ExecuteMultipleSettings()
20. {
21. ContinueOnError = true,
22. ReturnResponses = false
23. },
24. // Create an empty organization request collection.
25. Requests = new OrganizationRequestCollection()
26. };
27.
28. foreach (Entity entity2 in accountList)
29. {
30. UpdateRequest UpdateRequest = new UpdateRequest { Target = entity2 };
31. requestWithResults.Requests.Add(UpdateRequest);
32. }
33.
34. try
35. {
36. ExecuteMultipleResponse responseWithResults = (ExecuteMultipleResponse)Service.Execute(requestWithResults);
37. }
38. catch (Exception ex)
39. {
40. Console.WriteLine(ex.ToString());
41. }
42.
43. accountList.Clear(); // Clean the list to start again to fill it until 500
44. }
45. }
46.
47. if (accountList.Count > 0) // Execute the remaining records
48. {
49. Console.WriteLine("Update");
50.
51. ExecuteMultipleRequest requestWithResults = new ExecuteMultipleRequest();
52. requestWithResults = new ExecuteMultipleRequest()
53. {
54. // Assign settings that define execution behavior: continue on error, return responses.
55. Settings = new ExecuteMultipleSettings()
56. {
57. ContinueOnError = true,
58. ReturnResponses = false
59. },
60. // Create an empty organization request collection.
61. Requests = new OrganizationRequestCollection()
62. };
63.
64. foreach (Entity entity2 in accountList)
65. {
66. UpdateRequest UpdateRequest = new UpdateRequest { Target = entity2 };
67. requestWithResults.Requests.Add(UpdateRequest);
68. }
69.
70. try
71. {
72. ExecuteMultipleResponse responseWithResults = (ExecuteMultipleResponse)Service.Execute(requestWithResults);
73. }
74. catch (Exception ex)
75. {
76. Console.WriteLine(ex.ToString());
77. }
78.
79. accountList.Clear();
80. }
Let explain the code:
-First, I get by query all the active accounts.
-Then for each one I take the account name and set the remcod_legalname (custom field) with that value (here put your business operation).
-I add in a custom List collection, the record to update.
-When that list has more than 500 items, then I will generate ExecuteMutipleRequest with these records to update and clean the list. I recommend this max number of items to send to the request to not generate time out exceptions in CRM.
-When the for each end, I check if the list contains some remaining account record to execute them.
Original Source:
https://itsfascinating.com/d365/tag/updaterequest/
Official Documentation:
https://docs.microsoft.com/en-us/dotnet/api/microsoft.xrm.sdk.messages.updaterequest?view=dataverse-sdk-latest
Enjoy it!
-
D365 CRM: How to get de Audit Details of an Entity record using the SDK API
by Damian Sinay on 11/10/2022 – 0 comments
If you need to get the audit details of an entity record here is a simple code using the SDK API of CRM:
First, we need to get the audit collection of the entity record:
QueryExpression auditQuery = new QueryExpression();
auditQuery.EntityName = "audit";
auditQuery.PageInfo.PageNumber = 1;
auditQuery.PageInfo.PagingCookie = null;
auditQuery.ColumnSet = new ColumnSet(true);
FilterExpression filter = new FilterExpression();
filter.FilterOperator = LogicalOperator.And;
ConditionExpression condExp = new ConditionExpression();
condExp.AttributeName = "objectid";
condExp.Operator = ConditionOperator.Equal;
condExp.Values.Add(recordId); // The record Id to get the auditing
filter.AddCondition(condExp);
// You can add here another condition to get the audit of a specific date using the created-on attribute filter
OrderExpression order = new OrderExpression();
order.AttributeName = "createdon";
order.OrderType = Microsoft.Xrm.Sdk.Query.OrderType.Ascending;
auditQuery.Orders.Add(order);
auditQuery.Criteria = filter;
EntityCollection auditCollection = Service.RetrieveMultiple(auditQuery);
Then we need to get for each audit retrieved, the details to see the difference of the attribute value:
foreach (Entity audit in auditCollection.Entities)
{
string action = audit.FormattedValues["action"].ToString(); // The action of the change for ex. Create/Update/Delete
RetrieveAuditDetailsRequest auditDetailsRequest = new RetrieveAuditDetailsRequest
{
AuditId = audit.Id
};
RetrieveAuditDetailsResponse auditDetailsResponse = (RetrieveAuditDetailsResponse)Service.Execute(auditDetailsRequest);
if (((AttributeAuditDetail)auditDetailsResponse.AuditDetail).NewValue != null)
{
if (((AttributeAuditDetail)auditDetailsResponse.AuditDetail).NewValue.Attributes.Count > 0)
{
var oldDetail = ((AttributeAuditDetail)auditDetailsResponse.AuditDetail).OldValue; // The Old value
var newDetail = ((AttributeAuditDetail)auditDetailsResponse.AuditDetail).NewValue; // The New value
}
}
}
Enjoy it!
-
D365 CRM: Use Certificate Thumbprint to connect to D365 CRM API
by Damian Sinay on 10/20/2022 – 0 comments
We know that we can use User/Password or App ID/Secret key to connect a D365 Organization CRM API but if you need a high security level of connection, using of a certificate is available. For this you need to configure a valid Certificate in your Azure Tenant where your D365 CRM is installed.
You need to run the following Nugets in your project to access the different classes to archive this:
Install-Package System.Security.Cryptography.X509Certificates
Install-Package Microsoft.CrmSdk.XrmTooling.CoreAssembly
The code to connect to a CRM API using thumbprint is the following:
1. private CrmServiceClient InitializeOrganizationService()
2. {
3. // Force TLS 1.2 for Dynamics 365 v9.0 instances
4. System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
5. string tenantID = “your TENANT ID”;
6. string thumbprint = “the THUMBPRINT of your certificate”;
7. // Get the Access Token using the thumbprint certificate
8. AuthenticationContext authenticationContext = new AuthenticationContext(“https://login.microsoftonline.com/” + tenantID);
9. // Find the Certificate by thumbprint in Azure
10. X509Certificate2 userCert = FindCertificate(thumbprint, StoreName.My);
11.
12. var clientCertificate = new ClientAssertionCertificate(clientId, userCert);
13.
14. AuthenticationResult authenticationResult = authenticationContext.AcquireToken(organizationUrl, clientCertificate);
15. string diferencesDate = (authenticationResult.ExpiresOn.Offset.Hours * -1).ToString();
16.
17. var requestedToken = authenticationResult.AccessToken;
18.
19. OrganizationWebProxyClient orgWebProxService = new OrganizationWebProxyClient(GetServiceUrl(organizationUrl), false);
20.
21. orgWebProxService.HeaderToken = requestedToken;
22. CrmServiceClient service = new CrmServiceClient(orgWebProxService);
23. if (!service.IsReady)
24. {
25. throw new Exception(service.LastCrmError);
26. }
27. // Return the Service connected to the CRM Organization
28. return service;
29. }
And use this code to find the certificate by thumbprint in your tenant:
1. private static X509Certificate2 FindCertificate(string certificateThumbprint, StoreName storeName)
2. {
3. var storeLocationArray = new[]
4. {
5. StoreLocation.CurrentUser,
6. StoreLocation.LocalMachine
7. };
8.
9. try
10. {
11. X509Certificate2Collection certificates = null;
12. if (storeLocationArray.Any(storeLocation => TryFindCertificatesInStore(certificateThumbprint, storeLocation, storeName, out certificates)))
13. {
14. return certificates[0];
15. }
16. }
17. catch (Exception ex)
18. {
19. throw new Exception(string.Format("Failed to find certificate with thumbprint: {0}.", certificateThumbprint), ex);
20. }
21.
22. throw new Exception(string.Format("Failed to find certificate with thumbprint: {0}.", certificateThumbprint));
23. }
24.
25. private static bool TryFindCertificatesInStore(string thumbprint, StoreLocation location, StoreName storeName, out X509Certificate2Collection certificates)
26. {
27. X509Store store = new X509Store(storeName, location);
28. store.Open(OpenFlags.ReadOnly);
29. certificates = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
30. store.Close();
31.
32. return certificates.Count > 0;
33. }
Enjoy it!