Looking for PowerObjects? Don’t worry, you’re in the right place! We’ve been part of HCL for several years, and we’ve now taken the final step in our acquisition journey: moving our website to the HCL domain. Nothing else is changing – we are still fanatically focused on Microsoft Business Applications!

PowerObjects Blog 

for Microsoft Business Applications


Getting Pre-Chat Survey Results with JavaScript on the Omnichannel Conversation Form

Post Author: Byron Matus |

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:

  • Getting the Pre-Chat Survey question answers in JavaScript
  • Getting the Conversation record ID with JavaScript
  • Updating the customer value and saving the Conversation form with JavaScript

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.

How to access the Pre-Conversation Survey question answers in JavaScript

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:

Graphical user interface, text, application, email

Description automatically generated

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:

Graphical user interface, text, application, email

Description automatically generated

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.

How to get the Conversation record ID with JavaScript?

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!

How to update the customer value and save the Conversation form with JavaScript

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.

Solution Summary with Example Code

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.

  1. Get the conversation ID with Microsoft.OmniChannel.getConversationId method
    const conversationRecordId = await Microsoft.Omnichannel.getConversationId()
  2. Retrieve the Omnichannel Survey answers from the msdyn_ocliveworkitemcontextitem entity using the WebApi.
    formContext.WebApi.retrieveMultipleRecords('msdyn_ocliveworkitemcontextitem', queryFilter)
  3. Use the Omnichannel Survey answers to get customer data from the third-party API.
  4. Update the customer lookup on the form using the Microsoft.Omnichannel.linkToConversation method.
    Microsoft.Omnichannel.linkToConversation('contact', newCustomerValue.id)

Full Example Code

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))
}
Joe CRM
By Joe D365
Joe D365 is a Microsoft Dynamics 365 superhero who runs on pure Dynamics adrenaline. As the face of PowerObjects, Joe D365’s mission is to reveal innovative ways to use Dynamics 365 and bring the application to more businesses and organizations around the world.

Leave a Reply

Your email address will not be published.

PowerObjects Recommends