D365 CE- Incident management integration with SAP Qualtrics – Part 2

Recap to the previous blog:

  • Created transaction batch
  • Linked transaction batch to the customer along with the transaction data
  • Checked Import Status if its not completed

Step 4 : Generate distribution link for the transaction Batch

Post : https://fra1.qualtrics.com/API/v3/distributions

Body :{        
“transactionBatchId”: “{transactionID}”,  “surveyId”: “{surveyId}”,  “linkType”: “Individual”,  “description”: “test distribution List”, “action”: “CreateTransactionBatchDistribution”              
}

Capture distribution Link Id form the response, this will be used to get the actual link

Step 5 : Retrieve Generated distribution link, which will have actual survey link

GET: https://fra1.qualtrics.com/API/v3/distributions/{distributionID}/links?surveyId=sureveyID

Get the link and embed it into the email sent to the customer.

So far so good, we have validate all the API’s required to get survey link from qualtrics.

What should be the end to end design ?

Based on my experience and the amount of cases/incidents getting closed per day (i.e.600). I have decided to have a Qualtrics transaction record entity which will get created by an Asynchronous plugin registered on close message of incident. Which will have all the details required to perform 5 Api’s call as explained earlier. Once done a workflow will be triggered after the survey link update in the record which will send the email to the customer.

I have also used configuration entity in CRM to store directory id, survey Id , mailing list Id, APi’s URL structure

Plugin Details: This async plugins creates a related case record and gather all the necessary information needed to make API call. This plugin also fetches information from configuration record.

When you resolve a case , check the related record created

On Create of transaction record in CRM I have create a triggered Flow in Common Data Service which gets triggered and Call Quatrics API’s mentioned in step 1 to 3 on my previous blog. SO when you open the record you will have Qualtrics Transaction Batch ID, survey ID, and tracking URL for the import status

To achieve step 4 and 5 mentioned, I have a scheduled CDS connector which runs every 5 minute and fetch only those transaction records whose import status is in progress. Check if it is completed 100% and then make HTTP call remaining API’s. Get the survey link and update backs Qualtrics Survey link record. On update of that field I have a workflow registered which will send email to the customer along with the survey link and set the regarding as case entity.

Now Lets check if the email is generated with the link that we want to achieve

Next lets check how it looks like in Qulatrics after submitting the response

This is the end to end journey. Their is one thing that I haven’t explain in details about how I used CDS connectors to make HTTP call and update back CR record. I will be explaining those in my next blog

Go to my Previous Blog : https://rahultiwarydynamiccrmblogs.wordpress.com/2020/08/04/d365-ce-incident-management-integration-with-sap-qualtrics-part-1/

D365 CE- Incident management integration with SAP Qualtrics – Part 1

In my current project, I had a requirement to integrate Microsoft’s Customer Service module with SAP’s Qualtrics survey management system in real time.

Detailed Requirement: When a service agent resolves an incident in D365 CRM a survey link should be sent to the customer via email for CSAT (customer satisfaction) which will get captured in Qualtrics and reporting will be done accordingly.

Responsibilities of each System :
a) D365 CRM – Send customer information along with the case details to Qualtrics.
Capture survey link from the Qualtrics response and embeds in email and send it to the customer.
b) Qualtrics – Identifies the customer, if it’s new just create it else update and give us a survey link specific to that case/incident and customer. Once customer fills that information, capture the response and link it back to specific case number, customer and the agent who resolved this.

How we achieved this ?

Qualtrics :

Need a token to authenticate, mailing list, directories, survey and all the API’s that will help to achieve this
Login to qualtrics and go to account settings -> Qualtrics IDs

Get your Directory ID , Survey ID, Mailing List Id and Token to authenticate

Now we have all the prerequisite to start working with the Qualtrics API’s. https://api.qualtrics.com/api-reference/
Let’s open postman and start validating API’s that we require to achieve this functionality

Header will be common to all the API calls
Key : X-API-TOKEN and Value : Token
Key : Content-Type and Value : application/json

Body : {  “transactionIds”: []
}
Store the transaction ID that you will get as a response

  • Step 2 : Call Contact Import transaction API : This is basically to attach contact details with the transaction along with transaction data if any. Now in our case want to pass case number and agent name who resolved the case.

    POST : https://fra1.qualtrics.com/API/v3/directories/{directoryId}/mailinglists/{mailingListId}/transactioncontacts

Body :{  
“transactionMeta”: {    
“batchId”: “transactionId from the previous API response“,    
“fields”: [      “CaseNumber”,    “AgentName”    ]  },  
“contacts”: [{ 
     “firstName”: “Rahul”,  “lastName”: “Tiwary”, “email”: “rtiwary@microsoft.com”,  
“extRef”: “rtiwary@microsoft.com”,      “unsubscribed”: false,
 “transactionData”: {        “CaseNumber”: “CAS-99999-C1H0W3”,   “AgentName”: “Harsh Tiwary” }   
 } ]
}

In response you will get import Id , tracking URL and status. Initially the status will be In-Progress and we can’t go to the next step till the import status is not completed. Now how to check the status ? Using tracking URL. So it its best idea to store this tracking URL and check the status accordingly.

  • Step 3: Tracking Import Contact Status
    You can call the same Tracking URL that you get in response to import contact. Below is the format of the same

Get: https://fra1.qualtrics.com/API/v3/directories/{directoryId}/mailinglists/{mailingListId}/transactioncontacts/{importContactStatusID}

Decommissioning a System Administrator is Dynamic CRM

Sometime back I faced an issue after decommissioning an user who was part of all the phases of project life cycle. Though we have used service accounts wherever possible but still we faced some issues.

I have tried to remove all the dependencies of this user from CRM but even after doing that I used to get errors like “You need a Microsoft Dynamics 365 license to continue”

Below are the steps that I have done

  1. Reassign all the workflows and business process flows that are owned by the former system admin to a service account.
  2. Email  : To and From has been changed wherever applicable
  3. Integrations:
    a) batch Jobs
    b) Emial Router
    c) SSIS
    d) Any other custom integration
  4. Reassign Record – By clicking Reassign records: This is tricky, for me I want to reassign all the records, but it keeps on failing as the record volume was very high.
  5. Check al the plugin running context, update it to calling user or service account if it requires and admin privileges

Once completing these steps, when I tested a case creation scenario, I again received below error:D365 license error

This issue was only coming on create of case. So, I thought of digging more into the all the code and workflows that are running around case entity —– No luck

Finally, I saw that there are SLA’s and SLA items that are owned by the disabled user which was creating the issue. After reassigning it to service account it worked smoothly.

I have wasted couple of my hours and effort to identify. So though of sharing this with all.

[Resolved] – “Server time out error while using AddListMembersListRequest” D365 CRM

Many of us would have used AddListMembersListRequest to add members to marketing list via plugins, workflows, batch jobs etc. Are you aware that there was an issue reported on April 2019. If you are trying to add 5000 records in 2 minutes CRM platform throws server time out error. We raised support ticket with Microsoft on the same front. Ticket ID “119040323001150“.

1-AddToMarketing

According to them, their is a deadlock error in SQL causing this issue.

Finally, this has been fixed by Microsoft. There is no max limit that Microsoft has provided/committed, but as per my test results you can add 3.5 lac members in one go with service proxy time out being set to 10 minutes.

If you are also facing this issue make sure to check the solution  “msdynce_MarketingSalesPatch”  version “9.0.1906.3003” has been deployed or not in your environment under Internal Solution History.

2.Solution Import

Interactive Service Hub – Security role and privileges

In most of the implementation we do for interactive service hub, we came across scenarios where we need to use security role which are not a part of OOB security role and we know ISH icon comes only when you have these roles (System Administrator, System Customizer and CSR Manager). So generally, to achieve a functionality where we need a custom role to be able to access ISH, we create an icon in sitemap and provide ISH URL. For visibility control is handled by checking privilege access on the entities we can to show.

1-ISH

But that not enough for Interactive service hub to work properly. If you have configured, you own custom security role and ISH is not getting loaded properly or just keeps downloading customizations without an end then it may be because you don’t have specific privilege.

2-ISH

You need to give read privilege on Relationship entity under Customization tab in the specific role you are looking for

3-ISH

Optimize CRM performace by using Data Return

This blog will help you to reduces multiple calls to sever and reduces loading time of CRM forms.

There are scenarios where you want to retrieve an entity right after you create or update it. With December 2016 update (online and on-premise) this capability has been added. You can compose your POST request so that data from the created record will be returned with a status of 201 (Created). To get this result, you must use the return=representation preference in the request headers. Note if you don’t define the return type as representation then the status for creation will be 204 .

To control which properties are returned, append the $select query option to the URL to the entity set. The $expand query option will be ignored if used.

This is the header that needs to be configured for Create 

Request
POST [Organization URI]/api/data/v9.0/accounts?$select=name,creditonhold,address1_latitude,description,revenue,accountcategorycode,createdon HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
Accept: application/json
Content-Type: application/json; charset=utf-8
Prefer: return=representation
{
“name”: “Sample Account”,
“creditonhold”: false,
“address1_latitude”: 47.639583,
“description”: “This is the description of the sample account”,
“revenue”: 5000000,
“accountcategorycode”: 1
}

Response
HTTP/1.1 201 Created
Content-Type: application/json; odata.metadata=minimal
Preference-Applied: return=representation
OData-Version: 4.0
{
“@odata.context”: “[Organization URI]/api/data/v9.0/$metadata#accounts/$entity”,
“@odata.etag”: “W/\”536530\””,
“accountid”: “d6f193fc-ce85-e611-80d8-00155d2a68de”,
“accountcategorycode”: 1,
“description”: “This is the description of the sample account”,
“address1_latitude”: 47.63958,
“creditonhold”: false,
“name”: “Sample Account”,
“createdon”: “2016-09-28T22:57:53Z”,
“revenue”: 5000000.0000,
“_transactioncurrencyid_value”: “048dddaa-6f7f-e611-80d3-00155db5e0b6”
}

Header for Update with return value

To retrieve data from an entity you are updating you can compose your PATCH request so that data from the created record will be returned with a status of 200 (Update). To get his result, you must use the return=representation preference in the request headers. Note if you don’t define the return type as representation then the status for creation will be 1223.
Request

PATCH [Organization URI]/api/data/v9.0/accounts(00000000-0000-0000-0000-000000000001)?$select=name,creditonhold,address1_latitude,description,revenue,accountcategorycode,createdon HTTP/1.1 OData-MaxVersion: 4.0 OData-Version: 4.0 Accept: application/json Content-Type: application/json; charset=utf-8 Prefer: return=representation {“name”:”Updated Sample Account”}

Define all the attribute you want returned under $select.

Response
HTTP/1.1 200 OK Content-Type: application/json; odata.metadata=minimal Preference-Applied: return=representation OData-Version: 4.0

{
“@odata.context”: “[Organization URI]/api/data/v9.0/$metadata#accounts/$entity”, “@odata.etag”: “W/\”536537\””, “accountid”: “00000000-0000-0000-0000-000000000001”, “accountcategorycode”: 1, “description”: “This is the description of the sample account”, “address1_latitude”: 47.63958, “creditonhold”: false, “name”: “Updated Sample Account”, “createdon”: “2016-09-28T23:14:00Z”, “revenue”: 5000000.0000, “_transactioncurrencyid_value”: “048dddaa-6f7f-e611-80d3-00155db5e0b6”
}

This works for 8.2 and 9.x as well

For more details : https://msdn.microsoft.com/en-us/library/mt770366.aspx

 

Reporting Error : The expected parameter has not been supplied for the report When running report form the record

Reporting Error

Reporting Error

The expected parameter has not been supplied for the report

When running report form the record

OR

If you want to run your report only on active records or only for the specific condition

If you defined parameters in SSRS reports and using it into the report then you may come across with this error because the dataset doesn’t return any data

Define a parameter which contains the attribute value like statecode for Active inactive records or any other attribute based on your requirement.

Go To Table properties -> Hidden

Check for the condition you want to hide your table.

Hidden properties

 

Go to Record -> Run Report

run report

Debugging Custom Workflow activity in CRM (Online Version)

This is Alternative approach which uses Exception as a storage rather than using persist to entity

  1. Open plugin registration tool and check if profiler is installed in the current instance or not ? If not click on the install profiler buttonInstallProfiller
  2. Call your custom activity inside a workflow1
  3. Convert it to a real time workflow
    7
  4. Select the “Plug -in Profiler” and then click the “Profile Workflow” button to open the profile setting window. It will list you down all the workflows owned by you. Select the one you want to debug. Specify profile storage as Exception and then click “OK”
    4
  5. Internally plugin registration tool will create a profiled workflow with the same details and deactivate the original one
    5
  6. Here you need to do some change in the newly created profiled workflow. First deactivate this workflow
    6
  7. Second convert this workflow in real-time and activate it.
    7
  8. Now execute your workflow. I have made my workflow to run on demand
    9
  9. Download log file.
    10
  10. Open “Plug-in Registration” tool , click “Plug-in Profiler”  and then Click on Debug.
    11
  11. Attach the downloaded ErrorDetails.txt file in profile and specify the workflow assembly in assembly location.
    12
  12. Open visual studio and attach PluginRegistration.exe as a process
    13
  13. Click on start Execution14
  14. Here you go with the debugging
    15