This document is the eighth in a set of tutorials on writing calculations in Service Desk. Each topic can be read independently but for an introduction to calculations please see the following document: Calculation Writing Tutorial - 1. What is a calculation? What is Boo?.
This tutorial covers the techniques to set the value of a dropdown with a calculation.
Some experience in object and process design is recommended, as well as the basic concept of calculations as covered in the first tutorial (linked above) and also tutorial number 5: Calculation Writing Tutorial - 5. Boo language features versus .NET classes.
Understanding Objects in Calculations
Setting the value of a String, Int32, DateTime, etc. attribute with a calculation is easy. You just return your value and as long as it is the right data type it sets the attribute. However if you want to set the value of a dropdown list then the calculation needs to be more intelligent about what it returns.
Consider trying to set a Group dropdown on an Incident to "Helpdesk". This will fail:
This will generate the error message "Type mismatch when attempting to set attribute..." because the Group dropdown is a one-to-one relationship to the Group object. That object has a String attribute to hold the value "Helpdesk" but it also has other attributes and so to set the dropdown value we need to return an actual Group object.
This might look like it will work but will also fail:
This looks like you are going to return the PrimaryGroup relationship from RaiseUser which is indeed a relationship to the Group object. However this will also cause the same "type mismatch" error. This is because when you access a one-to-one relationship in a calculation you are only given access to a read-only proxy to the underlying object. If you try to return PrimaryGroup you are actually returning a proxy object which is just as foreign to the Group object as returning a String.
Returning a Real Object
Service Desk supplies some functions for use in calculations to access real object so you can return them into a dropdown. Some of these functions are not available in all versions so this document covers multiple techniques to account for this.
Setting an Ordered List with GetRankedObject() - 7.3 onwards
The first function that was made available at the same time that calculations themselves were was the GetRankedObject() function which is used to get an ordered list object record based on its rank.
return Incident.GetRankedObject("IncidentManagement._Impact", 2)
The first thing to note is that before the function name there is an object name, in this example Incident.GetRankedObject(...). This is the name of the object your calculation is based on and is nothing to do with the dropdown you are setting.
This function itself takes two parameters:
- The object to look up the record from - In the example above this is the _Impact object in the IncidentManagement module. This is in the format ModuleName.ObjectName and it is very important this is correct. If this is wrong then the function will always return a blank value.
The object name can be found by looking at the object properties in Object Designer, note that you are looking for the Name and not the Title of the object, and you are looking for the Ordered List object itself, not a relationship to it from another object. The module name is harder to find - in most cases it is the same as the Title as you see it listed in the tree in Object Designer but without the spaces but this is not always the case. To confirm the name you can write a query against the Metadata -> Module object to see a list of all modules and their respective Name and Title values.
- The second parameter is the rank of the record you want to get. In the example above the 2nd highest Impact record is returned for setting an Impact dropdown on an Incident.
A more complex example of this function is to make the second parameter a dynamic value based on other logic:
Rank = 3 if Incident.Category != null and Incident.Category.FullName = "Hardware - Server": Rank = 2 if Incident.RaiseUser._IsVIP == true: Rank = 1 return Incident.GetRankedObject("IncidentManagement._Impact", Rank)
This chooses the Impact of an Incident based on the Category and if the Raise User is flagged as a "VIP" or not.
Setting any dropdown with GetNamedObject() - 7.3.1 onwards
The next function added was to enable setting any type of dropdown by looking up an object record by its name:
return Incident.GetNamedObject("System.Group", "Helpdesk")
This function is very similar to GetRankedObject() except that the second parameter is a String instead of a Rank. The String is used to look up the object record however it is very important to understand which attribute of the object it is looking up. As the function name suggests, it looks up an object record by the value of its Named attribute, but this is not to say it uses the attribute called Name. It uses whichever attribute has the Is Name? property set to True and is the same attribute that gets displayed in the dropdown list.
This is designed to be used with reference lists more than anything else although it can be used for any object with some care. In the example above we are looking up a Group object record. On the Group object the Named attribute is Title, so it is a Title we enter as the second parameter which may or may not be the same as the Name attribute for your groups. Consider this example:
return Incident.GetNamedObject("System.User", "John Smith")
This is looking up a User object record, and as with the Group object the Named attribute for the User object is Title. On the User object you have the Name attribute to store the username of the person and this must be unique, however the Title attribute can be the same for multiple records. So in this example if you have two John Smiths the function will return the first John Smith it finds which may or may not be the John Smith you wanted.
If you are confident that the function will always only find one result then you can use it in conjunction with other logic to create complex calculations:
GroupName = "Helpdesk" if Incident.Category != null and Incident.Category.FullName.StartWith("Local Suppot -"): GroupName = Incident.RaiseUser._LocalSupportGroup.Title return Incident.GetNamedObject("System.Group", GroupName)
This chooses which group to set based on some logic around which Category is selected.
The limitation of which attribute to look up was addressed in Service Desk 7.6 with the introduction of the following new function.
The recommended way to set any dropdown with GetObjectByAttribute() - 7.6 onwards
The most recent addition to the functions is GetObjectByAtrribute() which extends on what GetNamedObject() does by allowing you to specify which attribute to use when looking up the object record:
return Incident.GetObjectByAttribute("System.User", "Name", "JSmith1")
The first parameter is the same as the other functions and specifies the object to look up. The second parameter specifies the attribute to use to look up the record and the third is the value of that attribute. This allows for a far greater flexibility than GetNamedObject() because you can look up object records by the Name attribute on User and Group objects instead of Title, or any other attribute you need to find the record. If there are multiple records matched then the first one found is returned.
Combining GetObjectByAttribute() and GetCurrentUserName() or GetCurrentGroupName()
Other new functions in 7.6 are to get the username of the current user and the group name of the current user's current group. This returns the value as a String which means it can be used for comparisons:
RaiseUserIsCurrentUser = false if Incident.RaiseUser.Name.ToLower() == Incident.GetCurrentUserName().ToLower(): RaiseUserIsCurrentUser = true
Note the use of the .ToLower() methods on each side of the comparison to convert the value to all lowercase. This is required because GetCurrentUserName() gets the username as it was typed when logging in which may or may not match the case of how it is stored on the user record.
The value can't be used directly to set a User or Group dropdown value. Instead you must combine it with GetObjectByAttribute():
Username = Incident.CreationUser.Name if Incident.Category != null and Incident.Category.FullName.StartsWith("Hardware -"): Username = Incident.GetCurrentUserName() return Incident.GetObjectByAttribute("System.User", "Name", Username)
Using the function result
As well as using the result of the above functions as the result of the calculation, you can also use them as you would any other attribute:
John = Incident.GetObjectByAttribute("System.User", "Name", "JSmith1") if Incident.RaiseUser.CurrentGroup.Name == John.CurrentGroup.Name: RaiseUserGroupMatchesJohn = true
The only difference is if you want to access a user-defined attribute (one whose Name begins with an underscore) you need to do this in a different format than normal due to the way the functions work:
John = Incident.GetObjectByAttribute("System.User", "Name", "JSmith1") if Incident.RaiseUser._OfficeName == John["_OfficeName"]: RaiseUserOfficeMatchesJohn = true
The name of the attribute to look up is put inside [" "].