Unified Service Desk (USD) for Microsoft Dynamics CRM provides an action called CloseActivity but how does this work, when might you use it?
CloseActivity is an action associated with Global Manager.
Closes an activity record in CRM.
You need to supply the following details in the data parameters of the action;
Parameter | Description |
Id | The GUID of the activity record to close. |
LogicalName | The logical name of the activity to close. |
StatusCode | The display name of the final status code after the activity is closed. |
StateCode | The display name of the final state code after the activity is closed. |
When might this action might come in handy? A couple of examples might include on an agent script to give the operator an answer that closes down the call. Or maybe when the session closes you might want to close a phone call that could be accidently left open. Or how about closing a task from a toolbar button etc.
CloseActivity – From an Agent Script
In my “not simple” example I decided to show an agent script answer to close a phone call activity.
Getting this example spot on presented me with several challenges;
Challenge One: My phone call could be incoming or outgoing and I wanted to be confident that the status reason of “Made” or “Received” was set correctly.
Challenge Two: When the user clicks close in the agent script I wanted to ensure any unsaved changes aren’t lost.
Challenge Three: I wanted the user to be able to make repetitive phone calls in one session. (For example, two outbound calls one to someones mobile and then immediately a second to their home number.)
Challenge Four: After saving and closing the phone call I wanted to refresh the screen so that the user saw the read only version.
Challenge Five: I didn’t want any annoying prompts! If I did anything in the wrong sequence, I found I could get IE messages about leaving or staying on the page. I hate seeing them in USD! This challenge in particular made things difficult!
These challenges made creating this example a little harder than I first expected! But I wanted to create a solution that was as robust as possible.
I could have simply shown how to create a CreateActivity action and completed this post in 5 minutes but I felt it better to try to give a real world example. I’m sorry if providing a detailed example has also made this more complicated.
Warning: This post is now aimed at the experienced USD user!
The final steps involved were this;
- Create a UII Action or two. (Maybe)
- Create some actions calls.
- Add sub actions on the “special actions”.
- Create an agent script answer.
- Add action calls to the agent script.
NOTE:
I’m going to assume you already have the ability to open a phone call in a session and that an agent script on phone call exists. I will “simply” show how to add an answer to that script.
Step One – Create a UII Action or Two.
In a second we are going to use some “special” actions called ExecuteOneExpressionTrue and ExecuteOnTimeout. Why? Well, it was mainly because of my fifth challenge! I found that if I tried to refresh the phone call tab before the save or close actions had completed I got those annoying popups I hate.
I’ll explain how this is used these in a second but the first step is to check the action exists.
In my install of USD this action didn’t exist out of the box. Check before creating as you might already have it. Go to hosted controls, load your global manager and then open uii actions from the navigation bar.
If you see these great, skip to step two. If you don’t click “ADD NEW UII ACTION” and simply create an actions with the names ExecuteOnExpressionTrue and ExecuteOnTimeout. Mine is shown below.
Step Two – Create Some Actions Calls
You will need eight actions in my example. Why so many? …… short answer because of all my challenges. If you look at each one in turn you will start to see that none of them are that complicated.
Action One – Clear Data Parameters
Why? If you only create one phone call per session then this action is not needed. I wanted to create multiple calls. So I needed to clear any parameters from the previous call before starting a fresh one.
To do this I used a ClearDataParameter action. You can see my copy below. Notice that the hosted control is Global Manager and that the action field is set to ClearDataParameter.
The data field then contains the command to define which parameter you need to clear. In my example it is the contents of $Return I wish to wipe clean. So my command if “Name=$Return”.
Notice the order has been set to 5. This is important and will become clear later.
Action Two – RunXrmCommand
Next we’ll need to use a RunXrmCommand, this injects javascript into my phone call form. Why? The reason is because I want to look at the form to see if any data has been changed but not yet saved. The getIsDirty() command lets me do that.
The result I want is to decide how long to wait for my save to run. So I return either 300 or 10. These values being the number of milliseconds I wanted to wait.
Tip:
You might need to increase or decrease the 300 value depending on the speed of your system. 3 seconds (i.e. 300 milliseconds) felt about right for my application.
The hosted control this time was “phonecall” and the action is “RunXrmCommand“.
The code in my data field was;
function RunXrmCommand() { var a = Xrm.Page.data.entity.getIsDirty(); if(a == true) { return "300"; } else { return "10"; } } RunXrmCommand();
Notice the order has been set to 6. This is important and will become clear later.
Action Three – Save
My next action was pretty simple, I created an action on my phone call hosted control that would be used to force a save. So my hosted control was set to Phonecall and my action is Save. The save action has no parameters so the data field is left blank.
Notice the order has been set to 7. This is important and will become clear later.
Action Four – ExecuteOnTimeout
My next action was ExecuteOnTimeout. One of my two “special” actions. Special because the action will wait “n” milliseconds and then run its sub actions. Allowing me to add a pause for the save to complete.
Notice the order has been set to 8. This is important and will become clear later.
My hosted control was my global manager (CRM Global Manager) and the action was ExecuteOnTimeout.
The data field that specifies how long to wait. I set this to “milliseconds=[[$Return.Phonecall – RunXrmCommand(getIsDirty)]]”. To explain what is happening here, the results of the RunXrmCommand action will be used to decide how long to wait. Either 10 milliseconds or 3 seconds in this example.
NOTE:
I don’t like timers in applications! A 3 second delay is not the most reliable approach and I hate deliberately slowing an application down. But my save action simply starts the save on the CRM form. I couldn’t find a better method for USD to know when the CRM form had finished saving. So I have used a delay as a last resort.
Action Five – Close Activity (Outgoing)
Action five closes the phone call when the call is an outgoing phone call. Meaning the status needs to show “Made”
I called this action CRM Global Manager – Close Activity (Phonecall, Outgoing), normally I wouldn’t mention the name of an action. As you can call them whatever you want. But remember this one as it will come into play later.
My hosted control is my global manager and the action is CloseActivity.
Notice the order has been set to 10. This is important and will become clear later.
The data field contains the ID of the activity to close, its logical name and the status you want to assign.
Id=[[phonecall.Id]] LogicalName=phonecall statuscode=Made statecode=Completed
My action looked like this;
Importantly I also added a condition to this action. I am checking that a phonecall.Id is available. (As the action would fail if one didn’t) And I am also checking the direction of the phone call. A directioncode of true indicates that the phone call is an outbound call.
“[[phonecall.Id]]”!=”” && “[[phonecall.directioncode]]”==”true”
Action Six – Close Activity (Incoming)
My sixth action is a clone of my fifth! Except this time the statecode is different.
This time my name was CRM Global Manager – Close Activity (Phonecall, Incoming). And notice that this time my status is going to be “Recieved”.
Notice the order has also been set to 10. This is still important and will still become clear later.
Again I had a condition but this time the directioncode is false.
Action Seven – ExecuteOnExpressionTrue
Now we are going to use the UII action that you created in step one. ExecuteOnExpressionTrue is a “special” action call so I guess I better quickly explain why. This action will wait until the expression it contains returns true. And when it does it will fire the sub action(s) attached to the expression.
So how are we going to use it? ….. the actions you created earlier to call CloseActivity will return a value of true when they have succeeded. I want to “wait” until this happens before triggering the final action to refresh the phone call screen. And in doing so I will avoid those annoying popups I hate.
Notice the order has been set to 20. This is still important and will still become clear later.
The hosted control is my global manager. Which I’ve called CRM Global Manager. Then my action is ExecuteOnExpressionTrue.
The data field then contains some code that will check the values returned by the actions we created to close the activity. You will recall that each action had a condition based on the direction of the call. So each time my agent script answer is run only one of those actions will return a value. Meaning if either of them return true, I want to continue to the next step.
function Expression() { var a = "[[$Return.CRM Global Manager - Close Activity (Phonecall, Outgoing)]+]" var b = "[[$Return.CRM Global Manager - Close Activity (Phonecall, Incoming)]+]" if(a=="True" || b=="True") { return true; } else { return false; } } Expression();
Action Eight – Refresh Phonecall
And so to the final action. This is a simple one to refresh the phone call. The hosted control is phonecall and the action is refresh. No data parameters are needed. So nice and simple.
I haven’t given this action an order, simply as I don’t care! It is going to be the only sub action I call from the ExecuteOnExpressionTrue action.
Step Three – Add sub actions on the “special” actions.
I have already mentioned that I’ve used two special actions ExecuteOnExpressionTrue and ExecuteOnTimeout. Both of these actions will have sub actions.
It is important to associate the refresh action with our ExecuteOnExpressionTrue action. To so that navigate to the sub actions on your ExecuteOnExpressionTrue action and add this refresh action.
The ExecuteOnTimeout will have three sub actions. One of which is the ExecuteOnExpressionTrue action! You can see below that I have added the close activity actions and the execute on expression true actions as sub actions.
Step Four – Create an Agent Script Answer
You’ve done the hard bits now. Below you can see my example agent script and that I have created an answer called “Phone Call Close Activity”.
I have shown my close activity answer below. It is pretty straight forward. I have set the show tab to phonecall, as I’d like to give it focus.
I have also added an enable and show condition of “[[phonecall.Id]]”!=””, this will mean the answer wouldn’t show if a phone call had been created but not saved. As the user couldn’t close a call that doesn’t yet exist!
Step Five – Add Actions To The Agent Script
And now the final step, we simply need to associate the remaining actions with the agent script which has just been created. You can see below that I have added four actions to the script.
Notice that the order should now make sense. The logic is;
Clear out any old parameters (Order 5) Find out if the phone call is dirty (Order 6) Save the phone call (Order 7) Execute a timer (Order 8) If phone call was dirty, wait 3 seconds If phone call wasn't dirty wait 10 milliseconds THEN ..... If phone call was incoming, close the phone call activity (Order 10) If phone call was outgoing, close the phone call activity (Order 10) Wait until close has happened (Order 20) THEN …. Refresh the phone call tab
I am sorry if this example became more complicated than I expected but hopefully you have seen some useful concepts. In this one post we have looked at;
- Injecting JavaScript into CRM forms,
- Closing activities,
- Waiting based on a timeout,
- Waiting based on an expression,
- Refreshing tabs
Even if you don’t follow my example exactly I hope I have demonstrated quite a few advanced concepts of USD. And I hope many of them will be useful to you. J
Pingback: USD – The Book | Microsoft Dynamics CRM and Unified Service Desk
Hi Neil,
I am new to USD , i have a requirement that when ever closing a session if the new case is there means just case details are filled by agent but he did’t save that case.at this situation session should give some confirmation and session should not close . i am able to give some alert while closing the session but i am not able to stop the close session.
Please can you help on this.
LikeLike
Thanks for reading my blog and getting in touch.
To stop a session closing you need to add some actions to the SessionCloseRequested event. In here you can validate your case and then optionally fire the close if valid. I have described this approach in the following blog post.
https://neilparkhurst.com/2016/03/19/usd-sessioncloserequested/
Using this can be quite involved but I have managed to successfully implement this in a project I worked on.
Hopefully this will answer your question.
Thanks
Neil.
LikeLike