Dynamics Async Await XRM WebApi JavaScript Functions
One problem that Dynamics 365 CE (CRM) JavaScript Web Resource developers may have is they need to make a backend query to the Dataverse in order to perform some operations on the CE form. The technologies used to implement this objective are Dynamics 365 CE / Dataverse, JavaScript Web Resource, and Xrm.WebApi.
I will demonstrate how to utilize the Xrm.WebApi, using the retrieveMultipleRecords method as an example to create a generic reusable function that will maintain a desired execution order. On its own, this method, and others like it, leans toward the implementation of the results inside the method or with a callback function. Later, we will discover some limitations and drawbacks with these approaches.
Implementing inside the method would look something like this:

After successfully receiving the results, loop through the results and implement whatever logic as it seems fit. If this method was needed more than once, it would have to be implemented all over again, most likely using the same code, which is not efficient.
Additionally, you can use callback functions like this:
var SetValues = function () {
var options = "?$select=msdyn_projectteamid,_msdyn_project_value,_msdyn_bookableresourceid_value,_enc_feecategoryid_value,enc_discount";
options += "&$filter=_msdyn_project_value eq " + project_id;
options += " and _msdyn_bookableresourceid_value eq " + bookable_resource_id;
Xrm.WebApi.retrieveMultipleRecords("msdyn_projectteam", options).then(handleProjectTeams, handleError);
}
var handleProjectTeams = function (data) {
}
var handleError = function (data) {
}
This is a step in the right direction and would allow the callback function to be reused for different scenarios; it would avoid handling the results in the same way each time, thus creating duplication of code. However, it doesn’t solve the issue of needing to call the retrieveMultipleRecords more than once and having to implement the processing logic again which creates a duplication of code.
Business Logic
Before we review the code, it is worth a quick look at the overall business logic and participating tables. Specifically, I was working with the quick create Time Entry form for Dynamics Project Service. The Time Entry form had a lookup to Bookable Resource and Project. Bookable Resource had a custom column – Fee Category. Project had a custom column – Discount. Project Team Member – a join table between Bookable Resource and Project – had both these custom columns.
The business logic determined that if the Bookable Resource was associated to a Project Team Member record for the corresponding Project, the Fee Category and Discount data would be pulled from that record. If the Project Team Member record did not exist for the Bookable Resource and Project, Fee Category should be pulled from the Bookable Resource record, and Discount would be pulled from the Project record. Here is another way to consider the business rules.
// Bookable Resource is a hidden field
// On create, Bookable Resource is null so need to set
// On change, field is already populated
IF Bookable Resource does not contain data THEN
Retrieve Bookable Resource with user id
Set Bookable Resource
END IF
IF Time Entry Project contains data THEN
IF Time Entry Bookable Resource is part of a Project Team Member for the selected Project THEN
Retrieve Fee Category and Discount from Project Team Member
Set Time Entry Fee Category from Project Team Member
Set Time Entry Discount from Project Team Member
ELSE
Retrieve Fee Category from Bookable Resource
Set Time Entry Fee Category from Bookable Resource
Retrieve Discount from Project
Set Time Entry Discount from Project
END IF
ELSE
Clear Time Entry Fee Category
Clear Time Entry Discount
END IF
Tables and Columns
Here are the Dataverse tables and contributing columns:
Time Entry
- Bookable Resource
- Project
- Fee Category
- Discount
Bookable Resource
- Fee Category
Project
- Discount
Project Team Member
- Bookable Resource
- Project
- Fee Category
- Discount
Issue
I needed to create a handful of JavaScript functions to retrieve data from the backend and set a few fields based on various conditions. I ran into an issue where, depending on the criteria, I had to pull the backend data from a different source, and this almost led me down a path of duplicating code to cover all the dependent scenarios. To accommodate good coding practices, I created functions, that called the Xrm.WebApi.retrievMultipleRecords methods, that could be reused. However, initial results were disappointing.
WebApi Results
I observed that the JavaScript function logic would: 1) process lines, 2) call the asynchronous function, and 3) process the remainder of the function logic. However, it would not process according to my desired order.
Imagine we have code that looks something like this:
JS action 1
Asynchronous action 1
JS action 2
Using the developer tools, we would see the results in this order:
JS action 1
JS action 2
Asynchronous action 1
Why? Because the flow logic will call JS action 1, call Asynchronous action 1, but will not wait for it to finish, and then call JS action 2. As a result, JS action 2 would finish before Asynchronous action 1.
This created a problem in that the business rules in place stated that data should be pulled one way if a Project Team Member existed or another way if it didn’t. I discovered a null value for Bookable Resource each time I tested when I knew it existed before the lines of code executed after it.
I needed to find a way to keep the code as clean as possible, prevent a duplication of code (writing similar functionality based on different scenarios), and have it process in the right order.
Xrm.WebApi.retrieveMultipleRecords
Before moving on to the solution, here is Microsoft’s documentation statement regarding Xrm.WebApi:
Provides properties and methods to use Web API to create and manage records and execute Web API actions and functions in model-driven apps.
This means any call using the Xrm.WebApi happens in the background on its own, thus the term asynchronous which means “not existing or happening at the same time.” We can tell from our results above that normal JavaScript functions definitely did not “happen” at the same time as the asynchronous one. Thankfully, there is a simple solution.
Solution
Debajit Dutta came to the rescue with the below article. Thanks, Debajit.
https://debajmecrm.com/how-to-make-xrm-webapi-calls-synchronous-in-dynamics-365-cds/
The heart of the article surrounds these lines of code:

With a proper use of the keywords async and await, you can keep the asynchronous flow of the code in order that you desire. This was a great start to get me to my final destination.
I wanted to change things around and not process inside the function that contained the retrieveMultipleRecords call because I needed to universalize it and use it many times with different purposes. Before I go on, here is a list of all the functions in my final code:
- onLoad
- onProjectChange
- onBookableResourceChange
- setValues
- getBookableResource
- getProject
- getProjectTeamMember
- setDiscount
- setFeeCategory
- GetLookupId
- GetLookupName
- SetLookupValue
- StripGUID
1 through 3 are generic functions to call the setValues main function and are needed to communicate from the quick entry Time Entry form to the JavaScript code.
4 is the main function that determines what supporting functions will be called based on available form data (Bookable Resource and Project).
5 through 7 are the Xrm.WebApi functions that retrieve the Dataverse data in the background and pass the results to the calling line.
8 and 9 simply receive the Xrm.WebApi results and sets the appropriate fields on the form.
10 through 13 are black-box functions to write-once-use-many times in client scripting.
This blog will only cover functions 4 through 9.
setValues Function
Below is the basic logic of this function.
Get form context.
If form context exists, continue.
If Bookable Resource does not contain data (on create), call Xrm.WebApi to retrieve Bookable Resource, based on the user’s id and set the Time Entry Bookable Resource.
Time Entry Bookable Resource will now be populated.
If Project does not contain data, clear Fee Category and Discount.
If Project contains data, call Xrm.WebApi to retrieve Project Team Member data, using the Bookable Resource and Project.
If Project Team Member record does exist, set Fee Category and Discount.
If Project Team Member record does not exist, call Xrm.WebApi to retrieve Bookable Resource Fee Category and set Time Entry Fee Category. Call Xrm.WebApi to retrieve Project Discount and set Time Entry Discount.
getBookableResource Function
This function will cover the main point of this blog in that it is calling data asynchronously, but how it is called and used will keep the processing in the desired order.
var getBookableResource = async function (user_id) {
var options = "?$select=bookableresourceid,_userid_value,_enc_feecategoryid_value";
options += "&$filter=_userid_value eq " + user_id;
var data = await Xrm.WebApi.retrieveMultipleRecords("bookableresource", options);
return data;
} // getBookableResource
Notice the word async right before the word function. This states that the JavaScript function will be processed asynchronously. I prep the async call according to the Xrm.WebApi parameters, using the option variable. The next line takes the Xrm.WebApi results and stores them in the variable named data and returns the data back to the line that called it.
Here is one of the most important points: the word await is set right before the Xrm.WebApi call. The word dictates that the function will act as if it is a synchronous function.
According to Mozilla, “Await expressions make promise-returning functions behave as though they’re synchronous by suspending execution until the returned promise is fulfilled or rejected.”
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Let us review how we would call and use the function.
var bookable_resource_data;
var brd_cnt = 0;
var bookable_resource_id = GetLookupId(formExecutionContext, "msdyn_bookableresource");
if (bookable_resource_id == null) {
bookable_resource_data = await getBookableResource(user_id,null);
brd_cnt = bookable_resource_data.entities.length;
if (brd_cnt > 0) {
bookable_resource_id = bookable_resource_data.entities[0]["bookableresourceid"];
bookable_resource_name = bookable_resource_data.entities[0]["_userid_value@OData.Community.Display.V1.FormattedValue"];
SetLookupValue(formExecutionContext, "msdyn_bookableresource", bookable_resource_id, bookable_resource_name, "bookableresource");
}
}
First, we don’t want to call the function unless the Bookable Resource field is blank.
Retrieve the current Bookable Resource value from the lookup field:
var bookable_resource_id = GetLookupId(formExecutionContext, "msdyn_bookableresource");
if (bookable_resource_id == null) {
Call the function that holds the Xrm.WebApi call:
bookable_resource_data = await getBookableResource(user_id,null);
Proceed only if there are results:
if (brd_cnt > 0) {
Place retrieved value into a variable that can be used throughout the rest of the code if needed:
bookable_resource_id = bookable_resource_data.entities[0]["bookableresourceid"];
Set the Bookable Resource lookup field, using the SetLookupValue function:
SetLookupValue(formExecutionContext, "msdyn_bookableresource", bookable_resource_id, bookable_resource_name, "bookableresource");
getProjectTeamMember Function
After the Bookable Resource is set, we need to determine from which tables to pull our Fee Category and Discount values.
Here is the code for the getProjectTeamMember Xrm.WebAPI call:
var getProjectTeamMember = async function (project_id, bookable_resource_id) {
var options = "?$select=msdyn_projectteamid,_msdyn_project_value,_msdyn_bookableresourceid_value,_enc_feecategoryid_value,enc_discount";
options += "&$filter=_msdyn_project_value eq " + project_id;
options += " and _msdyn_bookableresourceid_value eq " + bookable_resource_id;
var data = await Xrm.WebApi.retrieveMultipleRecords("msdyn_projectteam", options);
return data;
} // getProjectTeamMember
Back in the setValues function, below is how we call and use the results:
if (project_id == null || bookable_resource_id == null) {
formContext.getAttribute("enc_feecategoryid").setValue(null);
formContext.getAttribute("enc_discount").setValue(null);
}
else if (project_id != null && bookable_resource_id != null) {
var project_team_member_data = await getProjectTeamMember(project_id, bookable_resource_id);
var ptm_cnt = project_team_member_data.entities.length;
if (ptm_cnt > 0) {
console.info("Project Team Member exists");
setFeeCategory(formExecutionContext, project_team_member_data);
setDiscount(formExecutionContext, project_team_member_data);
}
else {
console.info("Project Team Member does not exist");
setFeeCategory(formExecutionContext, bookable_resource_data);
var project_data = await getProject(project_id);
var project_cnt = project_data.entities.length;
if(project_cnt > 0) setDiscount(formExecutionContext, project_data);
}
}
If Project does not contain data, clear Fee Category and Discount:
if (project_id == null || bookable_resource_id == null) {
formContext.getAttribute("enc_feecategoryid").setValue(null);
formContext.getAttribute("enc_discount").setValue(null);
}
If Project and Bookable Resource contain data, continue.
else if (project_id != null && bookable_resource_id != null) {
Retrieve Project Team Member data:
var project_team_member_data = await getProjectTeamMember(project_id, bookable_resource_id);
var ptm_cnt = project_team_member_data.entities.length;
If there is a Project Team Member record, set Fee Category and Discount from the Project Team Member record:
if (ptm_cnt > 0) {
console.info("Project Team Member exists");
setFeeCategory(formExecutionContext, project_team_member_data);
setDiscount(formExecutionContext, project_team_member_data);
}
If there is no Project Team Member record, retrieve Fee Category from Bookable Resource and Discount from Project with yet another backend Xrm.WebApi call:
else {
console.info("Project Team Member does not exist");
setFeeCategory(formExecutionContext, bookable_resource_data);
var project_data = await getProject(project_id);
var project_cnt = project_data.entities.length;
if(project_cnt > 0) setDiscount(formExecutionContext, project_data);
}
Line 3 of the code above is a great example of reusing previous backend results. If you remember, we called the getBookableResource function if the Bookable Resource field is blank. We used the results to populate the Bookable Resource field, and here we reuse the results to get the Fee Category data instead of recalling, or, worse, rewriting the same call for a different purpose.
setDiscount Function
Regardless of where the data is being pulled from the Xrm.WebApi (Project Team Member or Bookable Resource), setDiscount will take the retrieved data and set the Discount field.
var setDiscount = function (formExecutionContext, data) {
formContext = formExecutionContext.getFormContext();
// ptm = project team member
var ptm_discount;
if (data.entities.length > 0) {
ptm_discount = data.entities[0]["enc_discount"];
console.info("ptm discount: " + ptm_discount);
if (ptm_discount != null)
{
formContext.getAttribute("enc_discount").setValue(ptm_discount);
}
}
} // setDiscount
setFeeCategory Function
Regardless of where the data is being pulled from the Xrm.WebApi (Project Team Member or Project), setFeeCategory will take the retrieved data and set the Discount field.
var setFeeCategory = function (formExecutionContext, data) {
formContext = formExecutionContext.getFormContext();
// ptm = project team member
var ptm_fee_category_id;
var ptm_fee_category_name;
if (data.entities.length > 0) {
ptm_fee_category_id = data.entities[0]["_enc_feecategoryid_value"];
console.info("ptm fee category id: " + ptm_fee_category_id);
ptm_fee_category_name = data.entities[0]["_enc_feecategoryid_value@OData.Community.Display.V1.FormattedValue"];
if (ptm_fee_category_id != null)
{
SetLookupValue(formExecutionContext, "enc_feecategoryid", ptm_fee_category_id, ptm_fee_category_name, "enc_feecategory");
}
}
} // setFeeCategory
Results
The code works great! Fee Category and Discount are cleared if Project or Bookable Resource are blank. If both are populated and a Project Team Member exists, it pulls the correct data from this record; if Project Team Member does not exist, it pulls Fee Category from the Bookable Resource record and Discount from the Project record.
When a Project is selected where the Bookable Resource is associated to a Project Team Member record:

Notice:
Fee Category = Executive Management
Discount = 50.00
When a Project is selected where the Bookable Resource is not associated to a Project Team Member record:

Notice:
Fee Category = Systems Development and Support
Discount = 10.00
Seeing the Difference
Hopefully, we will see the advantage of the second approach and how the functions can be reused.
Version 1 | Version 2 |
Call Xrm.WebAPI [Bookable Resource]( | Call Xrm.WebAPI [Bookable Resource] |
Process inside | Process outside |
Next call Xrm.WebAPI [Project Team Member]( | Call Xrm.WebAPI [Project Team Member] |
Process inside | Process outside |
Next call Xrm.WebAPI [Project]( | Call Xrm.WebAPI [Project] |
Process inside | Process outside |
) | |
) | |
) | |
// CALL PROJECT TEAM MEMBER FOR DIFFERENT SCENARIO | // CALL PROJECT TEAM MEMBER FOR DIFFERENT SCENARIO |
// THIS WOULD POTENTIALLY PROCESS OUT OF ORDER | // THIS WOULD PROCESS IN ORDER |
On the left, because it is asynchronous, all processing must occur inside the initial Bookable Resource results. If, for whatever reason, the Project Team Member function needed to be called again, it would have to be placed inside the Bookable Resource call; otherwise, it may process before the Bookable Resource call has completed, potentially causing problems. Each call is dependent on the first call.
On the right, all processing occurs in order and there are no processing dependencies.
Conclusion
This blog has demonstrated how to use the Xrm.WebApi along with async and await to create reusable backend-call JavaScript functions that will maintain a synchronous processing order.
Final Code
My Code Comments
You may notice a few personal coding implementations. I will attempt to briefly explain these differences.
Form Context
Form context is common for Dynamics client scripting, but I discovered that the context object will be different depending on how it was sent.
Microsoft suggests this method:
However, this will not work when calling a JavaScript function from a custom button which I frequently do.
PRIYESHWAGH777 demonstrates how to utilize the context coming from a custom button here:

The former method takes the incoming parameter and then calls the getFormContext method, whereas the latter method takes the incoming parameter as is.
So, in the code below, I have allowed for either method.
var setValues = async function (formExecutionContext, _caller) { console.info(_caller + " called this function"); var formContext = null; try { formContext = formExecutionContext.getFormContext(); // call from home or sub grid } catch (e) { console.warn("Not being called from Home or Sub Grid"); try { formContext = formExecutionContext; } catch (e) { console.warn("Not being called from Form"); } } }
Function Caller
This is an interesting one. At one point JS programmers could use the arguments.caller property, but that was deprecated. Then the Function.caller was used, but that has been deprecated as well.
Here are some sites that explain the issue:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/callee
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller
I couldn’t find a reasonable replacement in a reasonable amount of time, so I added the function name as one of the parameters as suggested above. Not the greatest, but it works and it’s easy to implement and change or upgrade when the time comes.
if (typeof (Encore) == 'undefined') { Encore = { __namespace: true } } if (typeof (Encore.TimEntryLibrary) == 'undefined') { Encore.TimEntryLibrary = {} } Encore.TimEntryLibrary = (function () { var onLoad = async function (formExecutionContext) { await setValues(formExecutionContext, "onLoad"); } // onLoad var onProjectChange = async function (formExecutionContext) { await setValues(formExecutionContext, "onProjectChange"); } // onProjectChange var onBookableResourceChange = async function (formExecutionContext) { await setValues(formExecutionContext, "onBookableResourceChange"); } // onBookableResourceChange var setValues = async function (formExecutionContext, _caller) { console.info(_caller + " called this function"); var formContext = null; try { formContext = formExecutionContext.getFormContext(); // call from home or sub grid } catch (e) { console.warn("Not being called from Home or Sub Grid"); try { formContext = formExecutionContext; } catch (e) { console.warn("Not being called from Form"); } } try { var formType = formContext.ui.getFormType(); var userSettings = Xrm.Utility.getGlobalContext().userSettings; var user_id = userSettings.userId; user_id = StripGUID(user_id); if (formContext != null) { var project_id = GetLookupId(formExecutionContext, "msdyn_project"); var project_name = GetLookupName(formExecutionContext, "msdyn_project"); // PROJECT TEAM MEMBER = BOOKABLE RESOURCE var bookable_resource_id = GetLookupId(formExecutionContext, "msdyn_bookableresource"); var bookable_resource_name = GetLookupName(formExecutionContext, "msdyn_bookableresource"); // GRAB BOOKABLE RESOURCE FOR CREATE AND UPDATE var bookable_resource_data; var brd_cnt = 0; // ALLOW USER TO CHANGE BOOKABLE RESOURCE // ONLY PULL BOOKABLE RESOURCE FROM USER ON LOAD if (bookable_resource_id == null && (_caller == "onLoad")) { bookable_resource_data = await getBookableResource(user_id,null); brd_cnt = bookable_resource_data.entities.length; if (brd_cnt > 0) { bookable_resource_id = bookable_resource_data.entities[0]["bookableresourceid"]; bookable_resource_name = bookable_resource_data.entities[0]["_userid_value@OData.Community.Display.V1.FormattedValue"]; SetLookupValue(formExecutionContext, "msdyn_bookableresource", bookable_resource_id, bookable_resource_name, "bookableresource"); } } // GET BOOKABLE RESOURCE DATA FROM ENTERED BOOKABLE RESOURCE else if (bookable_resource_id != null) { bookable_resource_data = await getBookableResource(null,bookable_resource_id); brd_cnt = bookable_resource_data.entities.length; if (brd_cnt > 0) { bookable_resource_id = bookable_resource_data.entities[0]["bookableresourceid"]; bookable_resource_name = bookable_resource_data.entities[0]["_userid_value@OData.Community.Display.V1.FormattedValue"]; SetLookupValue(formExecutionContext, "msdyn_bookableresource", bookable_resource_id, bookable_resource_name, "bookableresource"); } } // CLEAR FEE CATEGORY AND DISCOUNT if (project_id == null || bookable_resource_id == null) { formContext.getAttribute("enc_feecategoryid").setValue(null); formContext.getAttribute("enc_discount").setValue(null); } // SET FEE CATEGORY AND DISCOUNT // ON CHANGE else if (project_id != null && bookable_resource_id != null) { var project_team_member_data = await getProjectTeamMember(project_id, bookable_resource_id); var ptm_cnt = project_team_member_data.entities.length; if (ptm_cnt > 0) { console.info("Project Team Member exists"); setFeeCategory(formExecutionContext, project_team_member_data); setDiscount(formExecutionContext, project_team_member_data); } else { console.info("Project Team Member does not exist"); setFeeCategory(formExecutionContext, bookable_resource_data); var project_data = await getProject(project_id); var project_cnt = project_data.entities.length; if(project_cnt > 0) setDiscount(formExecutionContext, project_data); } } // SINCE BOOKABLE RESOURCE IS BEING SET ABOVE, THIS WILL NOT BE REACHED else if (project_id != null && bookable_resource_id == null) { } } // if formContext != null } catch (e) { console.error("ERROR [setValues]: " + e); } } // setValues var getBookableResource = async function (user_id, bookable_resource_id) { var options = "?$select=bookableresourceid,_userid_value,_enc_feecategoryid_value"; if (user_id != null) options += "&$filter=_userid_value eq " + user_id; if (bookable_resource_id != null) options += "&$filter=bookableresourceid eq " + bookable_resource_id; var data = await Xrm.WebApi.retrieveMultipleRecords("bookableresource", options); return data; } // getBookableResource var getProject = async function (project_id) { var options = "?$select=msdyn_projectid,enc_discount"; options += "&$filter=msdyn_projectid eq " + project_id; var data = await Xrm.WebApi.retrieveMultipleRecords("msdyn_project", options); return data; } // getProject var getProjectTeamMember = async function (project_id, bookable_resource_id) { var options = "?$select=msdyn_projectteamid,_msdyn_project_value,_msdyn_bookableresourceid_value,_enc_feecategoryid_value,enc_discount"; options += "&$filter=_msdyn_project_value eq " + project_id; options += " and _msdyn_bookableresourceid_value eq " + bookable_resource_id; var data = await Xrm.WebApi.retrieveMultipleRecords("msdyn_projectteam", options); return data; } // getProjectTeamMember var setDiscount = function (formExecutionContext, data) { formContext = formExecutionContext.getFormContext(); var ptm_discount; if (data.entities.length > 0) { ptm_discount = data.entities[0]["enc_discount"]; if (ptm_discount != null) // discount == null && { formContext.getAttribute("enc_discount").setValue(ptm_discount); } } } // setDiscount var setFeeCategory = function (formExecutionContext, data) { formContext = formExecutionContext.getFormContext(); var ptm_fee_category_id; var ptm_fee_category_name; if (data.entities.length > 0) { ptm_fee_category_id = data.entities[0]["_enc_feecategoryid_value"]; ptm_fee_category_name = data.entities[0]["_enc_feecategoryid_value@OData.Community.Display.V1.FormattedValue"]; // SET FEE CATEGORY if (ptm_fee_category_id != null)// fee_category_id == null && { SetLookupValue(formExecutionContext, "enc_feecategoryid", ptm_fee_category_id, ptm_fee_category_name, "enc_feecategory"); } } } // setFeeCategory function GetLookupId(executionContext, _lookup) { var rtn = null; var formContext = executionContext.getFormContext(); var lookup_field = formContext.getAttribute(_lookup); if (lookup_field != null) { if (lookup_field.getValue() != null) { rtn = lookup_field.getValue()[0].id; rtn = rtn.replace("{", "").replace("}", ""); } } // lookup != null return rtn; } // GetLookupId function GetLookupName(executionContext, _lookup) { var rtn = null; var formContext = executionContext.getFormContext(); var lookup_field = formContext.getAttribute(_lookup); if (lookup_field != null) { if (lookup_field.getValue() != null) { rtn = lookup_field.getValue()[0].name; } } // lookup != null return rtn; } // GetLookupName function SetLookupValue(executionContext, fieldName, id, name, entityType) { var formContext = executionContext.getFormContext(); if (fieldName != null) { var lookupValue = new Array(); lookupValue[0] = new Object(); lookupValue[0].id = id; lookupValue[0].name = name; lookupValue[0].entityType = entityType; if (lookupValue[0].id != null) { formContext.getAttribute(fieldName).setValue(lookupValue); } } } // SetLookupValue var StripGUID = function (_guid) { var rtn = ""; rtn = _guid.replace("{", "").replace("}", ""); return rtn; } // StripGUID return { onLoad: onLoad, setValues: setValues, onProjectChange: onProjectChange, onBookableResourceChange: onBookableResourceChange } })()
If you have any questions about this process, please get in touch with us.
41 pages of step-by-step instructions for 6 different key tasks in Dynamics 365 CRM apps. Includes interactions with Power Apps and Power Automate!