In this webinar, our experts showcase a variety of demo use cases of how different components of the...
If you’re having issues using custom JavaScript on the Conversation form in the Omnichannel for Customer Service app while using Live Chat, you are not alone.
Some challenges you might face:
If you’re impatient and want the solution without all the preamble (like I often do), feel free to jump ahead to the Solution Summary with Example Code.
After setting up Pre-Conversation survey questions and starting a chat, the customer’s answers to the survey automatically appear on the Conversation summary form:
Great, so the data must be available in fields on the form, right?
Not so fast.
If you look at that control in the form editor, you will see it is a special PCF control bound to a single field named msdyn_conversationsummaryfield:
Go ahead and retrieve the value of that field on the form and you will see it is null:
So where is the Pre-Conversation Survey data?
Digging through the Microsoft Docs, I discovered that the data is available in the msdyn_ocliveworkitemcontextitem entity.
When the system creates the conversation record, it also creates one msdyn_ocliveworkitemcontextitem record for each survey question the customer responded to, as well as a couple of other records that the system creates for its own behind the scenes uses.
The msdyn_ocliveworkitemcontextitem entity has a lookup attribute to the Conversation (msdyn_ocliveworkitem) entity so you can retrieve the pre-chat survey responses using the JavaScript WebApi method like so:
const queryFilter = `?$filter=_msdyn_ocliveworkitemid_value eq ${conversationRecordId}&$select=msdyn_name,msdyn_value` formContext.WebApi.retrieveMultipleRecords('msdyn_ocliveworkitemcontextitem', queryFilter) .then(result => { console.log(result.entities.map(r => { return { name: r.msdyn_name, value: r.msdyn_value } })) }) .catch(error => { Xrm.Navigation.openErrorDialog(error) })
The above will log something like:
[ { "name": "scp", "value": "de0f91df-cb94-4405-8454-9116f484edd6" }, { "name": "LastName", "value": "Dent" }, { "name": "Phone", "value": "1112223333" }, { "name": "msdyn_inbox_threadids", "value": "{\"t1ChatId\":\"19:chatbridge_a06ecd1fda3e400183cfb357ffbbfaee@thread.skype\",\"t2ChatId\":\"19:chatbridge_8ba45ec1b098481fb83fef61a98cc825@thread.skype\",\"platformtype\":\"Teams\"}" }, … etc … etc ]
Making progress! You can see your survey question results in there, but also some other records that you probably don’t need so you could further filter the results to get only the survey questions with something like this:
const MySurveyQuestionNames = [ 'Email', 'Phone', 'First Name', 'Last Name', ] const finalResult = result.entities.map(r => { return { name: r.msdyn_name, value: r.msdyn_value } }) .filter(r => MySurveyQuestionNames.includes(r.name))
There’s only one catch… in the above example, where did conversationRecordId come from? Well, that leads us to the next challenge.
Getting the record ID on a Dynamics Model Driven App form is usually as easy as:
formContext.data.entity.getId()
Try this on a Conversation Summary form in Omnichannel Customer Service Workspace, though, and it will return an empty string. Huh?
It seems this has something to do with the Conversation Summary form’s formType always being set to 1 (Create) when viewing it with an active chat pane in the Omnichannel for Customer Service to accommodate some background magic.
So, how do you get the current Conversation record ID?
There is a separate Omnichannel JavaScript API and it has a special method to help us with this problem, getConversationId that returns a promise with the Conversation ID in it 😊.
Microsoft.Omnichannel.getConversationId() .then(conversationId => { // Do something with the id })
One more problem solved!
Now you can get the conversation record ID and the survey question answers, but you might run into issues trying to programmatically update the customer field and save the Conversation Customer Summary form.
Normally, this is a simple operation on a model driven app form. You get the customer attribute, update the value, and save the form
formContext.getAttribute('msdyn_customer').setValue(customerValue) formContext.data.save()
but in Omnichannel with live chat, you will get this error in the JavaScript console:
{ "errorCode": 2200000005, "message": "This form can’t be saved due to a custom setting.", "code": 2200000005, "title": "Unable to save" }
Here is yet another way in which the Omnichannel Conversation Summary form does not behave like other MDA forms.
The solution was once again buried in the Microsoft Docs. I found another method in the Omnichannel JavaScript API, linkToConversation that can be used to set the Customer or Case lookup field on the current Conversation record. Beware, this method only updates the data and not the UI, so you are responsible for setting the UI appropriately.
Here is a simplified version of what I ended up with in order to retrieve Omnichannel Pre-Chat Survey results using the Conversation record ID, lookup customer data with a third-party API, and then update and save the Customer field on the Conversation form.
async function updateCustomer (executionContext) { const formContext = executionContext.getFormContext() // Get the Id of the current Conversation record const conversationRecordId = await Microsoft.Omnichannel.getConversationId() // Retrieve the answers provided by the customer when they filled out the pre-conversation survey const queryFilter = `?$filter=_msdyn_ocliveworkitemid_value eq ${conversationRecordId}&$select=msdyn_name,msdyn_value` const preConversationSurveyData = await formContext.WebApi.retrieveMultipleRecords('msdyn_ocliveworkitemcontextitem', queryFilter) .then(result => { return result.entities.map(r => { return { name: r.msdyn_name, value: r.msdyn_value, } }) .filter(r => MySurveyQuestionNames.includes(r.name)) }) .catch(error => Xrm.Navigation.openErrorDialog(error)) // Retrieve customer data from third-party API // (this is a much-simplified version of what I was doing with a PCF control and some intermediate fields) const newCustomerValue = await ThirdPartyApi.RetrieveCustomer(preConversationSurveyData) // Update the customer value on the backend with the Omnichannel API Microsoft.Omnichannel.linkToConversation('contact', newCustomerValue.id) .then(r => { // Update the customer value on the form so it displays correctly formContext.getAttribute('msdyn_customer').setValue([newCustomerValue]) }) .catch(error => Xrm.Navigation.openErrorDialog(error)) }