The access to data fields is an essential part of ConSol CM programming. It is potentially required in all scripts of the system, workflow as well as Admin Tool scripts, no matter of which type. Here, we will set the focus on workflow programming, but the access to data fields is basically the same in all scripts.
There are three types of data fields:
Rules for work with data fields
When you work with ticket fields, customer fields or resource fields, there are two main rules you have to keep in mind:
A data field is always of a certain data type. As for any variable in programming, it depends on the data type how you have to handle the value of the field, e.g. a string field cannot be used for calculating numbers, an enum field needs a specific access method.
The following data types are available for ticket fields, customer fields and resource fields.
If you work with scripts, either in CM workflows or in the Admin Tool, please note that the behavior of boolean fields which are represented as checkboxes, i.e. with annotation boolean-type = checkbox (default) is different depending on the CM version!
Fields which have already been filled with values in the database will not be changed during an update from a version prior to 6.9.4.0 to a version 6.9.4.0 and up.
Boolean fields represented as radio buttons (annotation boolean-type = radio) or drop-down menu (annotation boolean-type = select) are always shown and behave as described for versions 6.9.4.0 and up, i.e., with NULL value if untouched.
Figure 130: Scheme: List of structs
Technically spoken, the list is an array which contains a map (= key:value pairs) in each field.
Figure 131: List of structs, technical principle
Restriction when using an Oracle database: at most 4000 bytes can be saved in UTF encoding. Starting with Oracle12c.
For long strings the limit depends on the database system used for ConSol CM: MS SQL Server: 2 GByte; MySQL: 4 GByte; Oracle: 16 - 64 GByte (depending on page size of tablespace).
For string fields, you can use specific annotations to fine-tune the field definition. For example, a string field can be defined to contain a URL which will automatically be displayed as hyperlink or can be the hook for an autocomplete list. Please read the following section.
The data type you choose on creating a data field cannot be changed afterwards!
String fields are widely used for customer, ticket, and resource data and strings can be used to contain various content, for example, a text box with a comment, a simple input field with only 20 characters, a URL or a password. The fine-tuning of string fields is implemented using specific annotations which are all listed on the Annotations page. However, since work with these annotations is an every-day task of CM administrators, the most important and most commonly used annotations will be explained here as well.
Value for annotation text-type: textarea
The size of the text box can be adjusted, displayed as standard text box depending on web browser. Use the field-size annotation in case a specific size of the text box is required.
Value for annotation text-type: password
Only dots will be displayed. This annotation does not define the field to contain a password! It only defines the display mode! Use the password annotation to define a string field to contain the CM/Track password.
Value for annotation text-type: url
Input will be displayed as a hyperlink in view mode. String has to match a specific URL pattern:
First part of the string is the link (url), second part is the name which should be displayed.
Example: "http://consol.de ConSol"
Value for annotation text-type: file-url
Input will be displayed as a link to a file on the file system. The web browser has to allow/support those links!
Example: Enabling file:// URLs in a Firefox browser
Add the following lines to either the configuration file prefs.js or to user.js in the user profile. On a Windows system usually in a folder like C:\Users\<USERNAME>\AppData\Roaming\Mozilla\Firefox\Profiles\uvubg4fj.default
Alternatively a Firefox browser add-on like Local Filesystem Links can be installed for better access to the referenced files and folders.
The link will also be displayed as tooltip.
The URL is correctly formed if the following conditions are met:
Example URLs:
See also the explanation about file-url in the section text-type
Value for annotation text-type: label
This will be a read-only field which is displayed in gray, use the label-group annotation to link label and input fields which belong together. Please take a look at the annotations for labels (show-label-in-edit, show-label-in-view) before implementing special label fields!
Value for annotation email: true
The field may only contain valid email addresses. Input will be validated according to standard email format <name>@<domain>.
Value for the annotation text-type = autocomplete
Optional: value for the annotation autocomplete-script = <name of the respective script>
A scripted autocomplete list is used to provide a drop-down menu which is filled dynamically using the input the engineer has provided so far. For example, when the user types "Mil", the possible values "Miller", "Milberg", and "Milhouse" are displayed as list and the engineer can select the one required for the field. You know this behavior from other autocomplete fields, e.g., the search for engineers for a ticket or the search for customers while creating a ticket. However, in these cases, CM generates the list automatically. The behavior cannot be influenced or customized. Scripted autocomplete lists, on the contrary, can be implemented by the CM administrator. The values are based on a result set which is dynamically created. The result set can contain strings, engineers, customers (Units), and resources.
A detailed description of scripted autocomplete lists is provided in section Scripted Autocomplete Lists in the Administrator Manual.
Value for annotation username: true
Will be used for authentication against CM/Track server. Only for customer fields in a contact object.
Value for annotation password: true
Will be used for authentication against CM/Track server (in DATABASE mode). Only for customer fields in a contact object.
Value for annotation personal-data: true
This annotation can be assigned to ticket and contact fields. Contact fields with this annotation will be deleted when a contact is anonymized. Ticket fields with this annotation will be deleted when the main customer of the ticket is anonymized.
When defining a field to contain personal data, please take into account that the deletion of the field during the anonymization process is treated as a regular update. Therefore, business event triggers reacting on changes to ticket fields fire, and the contact update action script is executed.
This might lead to unwanted side-effects.
In the Admin Tool, the ticket fields are defined in the navigation group Tickets, navigation item Ticket Fields.
Figure 132: ConSol CM Admin Tool - Ticket field administration for ticket data (CM version 6.10)
Three methods are of major importance for programming ticket field access in CM scripts. They all are methods of the class Ticket:
Another method might be used when a field should be emptied, i.e., when its value should be set to null:
To retrieve data from a ticket field in a script, you have to reference it by using the technical names of the ticket field group and of the ticket field. The method which has to be used can vary depending on the data type of the ticket field.
The following examples refer to the ticket fields in the figure above. The method which should to be used (because it is the most convenient way) is:
ticket.get("<ticketFieldGroupName>.<ticketFieldName>")
Please keep in mind that the getter method for a field might return either a value or an object, depending on the type of data field! Please see the following example for an explanation
The following Admin Tool script, e.g., called from a workflow, will display ticket data:
// display info from custom types of various data types:
import com.consol.cmas.common.model.ticket.Ticket
Ticket ticket = workflowApi.ticket
log.info 'Display enum from helpdesk CF group ... '
def myfield = ticket.get("helpdesk_standard.priority")
log.info 'Class of helpdesk_standard.priority field is ' + myfield.getClass()
log.info 'Value of helpdesk_standard.priority field is ' + myfield.getName()
log.info 'Retrieve value directly, method 1, using getter method ... '
def my_new_field1 = ticket.get("helpdesk_standard.priority").getName()
log.info 'Result is ' + my_new_field1
log.info 'Retrieve value directly, method 2, using direct access to attribute ... '
def my_new_field2 = ticket.get("helpdesk_standard.priority").name
log.info 'Result is ' + my_new_field2
log.info 'Display value of simple data field ...'
def fb = ticket.get("helpdesk_standard.feedback")
log.info 'Value of feedback boolean field is ' + fb
Code example 28: Admin Tool script used to display ticket fields
The following output will be displayed in the server.log file:
Explanation:
If you want to retrieve the value of a ticket field which contains an object (e.g., an EnumValue), you have to use methods like getName() to retrieve the actual value, because ticket.get ... only provides the object. The .name notation is a simplified (Groovy) version of the getter method.
If you want to retrieve the value of a simple data field, you can do this "directly": ticket.get ... will provide the value.
A precondition script of a workflow activity could look like the following code:
boolean vip_info = ticket.get("am_fields.vip")
if(vip_info == true){
return true
}
else {
return false
}
Code example 29: Precondition script where boolean value is checked
Or shorter:
return ticket.get("am_fields.vip")
Code example 30: Precondition script where boolean value is checked, short version
An enum (ordered list) field is a field where the value is one of various list values. For example, a list with priorities is the basis for an enum field. To retrieve the value of an enum field, you can use the same syntax as for simple data types. The get method provides the enum list value, the getName() method provides the string attribute with the name of the value.
def prio = ticket.get("helpdesk_standard.priority")
log.info 'Priority is now ' + prio.getName()
//or shorter:
log.info 'Priority is now ' + prio.name
Code example 31: Retrieving an enum value for a ticket field
The Java class for MLAs (Multi Level Attributes) is MultiEnumField. It is handled like a regular enum field, i.e. to retrieve the current value of an MLA field, you can use the Java/Groovy code like the one shown in the example below.
Figure 133: ConSol CM Admin Tool - Definition of a ticket field of type MLA
Figure 134: ConSol CM Admin Tool - MLA definition
Figure 135: ConSol CM Web Client - Setting a value (technically: name) of the MLA
// Get
def myEnumValueName = ticket.get("GROUP_NAME.MLA_FIELD_NAME").name
// Set
EnumValue enumValue = enumService.getValueByName("ENUM_NAME","ENUM_VALUE_NAME")
ticket.set("GROUP_NAME.MLA_FIELD_NAME", enumValue)
Example with technical name of the field and localized name:
import com.consol.cmas.common.model.customfield.enums.EnumValue
log.info 'Displaying MLA value ...'
def my_mla_field = ticket.get("helpdesk_standard.categories")
log.info 'MLA value categories (technical name) is now ' + my_mla_field.name
def my_mla_field_localized = localizationService.getLocalizedProperty(EnumValue.class, "name", my_mla_field.id, engineerService.getCurrentLocale())
log.info 'MLA value categories (localized name) is now ' + my_mla_field_localized
Code example 32: Code (Workflow or Admin Tool script) for displaying MLA data
Figure 136: Log output for the example above
If you need to retrieve the entire path to the selected MLA value, you can proceed as follows:
log.info 'Displaying MLA branch ...'
List<EnumValue> mlaPathElements = mlaService.getAssignedMla(ticket, "helpdesk_standard", "categories")
def mlaPath1=""
mlaPathElements.each() {elem ->
mlaPath1 = elem.name + ' -- ' + mlaPath1
}
log.info 'mlaPath1 is now ' + mlaPath1
//or shorter:
def mlaPath2 = mlaService.getAssignedMla(ticket, "helpdesk_standard", "categories").reverse()*.name.join(" -- ")
log.info 'mlaPath2 is now ' + mlaPath2
Code example 33: Retrieving the entire path to the selected MLA value
Figure 137: Log output for the example above
A list of simple data types consists of a list (= array) which has a value of a simple data type in each line, a date in our example. The ticket field of type date has to have the parameter Belongs to which points to the list.
Figure 138: ConSol CM Admin Tool - Ticket fields for a list of date fields
Figure 139: ConSol CM Web Client - List of date fields in a ticket (edit mode)
For access to each date ticket field within a list use the following lines of code:
def convs = ticket.get("conversation_data.conversation_list").each() { conv ->
log.info "NEXT DATE is :" + conv
log.info "CLASS of NEXT DATE is " + conv.getClass()
}
Code example 34: Displaying the content of a list of date objects
Figure 140: Log output of the script above
To access a certain line, you can use the following syntax:
def mydate = ticket.get("conversation_data.conversation_list[1]")
Code example 35: Retrieve a certain value from a list of simple data types
The data construct list of structs is the technical basis for a multi-column table structure in the Web Client. The list is the parent object which contains lines. Each line is an instance of a struct. Each line (struct) contains as many data fields (table columns) as required. Please see the figures in the introductory part of this section.
To retrieve the data from a list of structs you can work with an iteration over the lines (= structs). In the following example (from an order system, not displayed in the figure above) we work with a table where ...
Figure 141: ConSol CM Admin Tool - ticket fields for a multi-column list (= table)
Figure 142: ConSol CM Web Client - Ticket with filled-in table
def structs = ticket.get("order_data.orders_list").each() { str ->
log.info("CLASS of LINE is " + str.getClass())
log.info("FIELD VALUE HARDWARE is " + str.orders_hardware.getName())
log.info("CLASS of FIELD VALUE HARDWARE is " + str.orders_hardware.getName().getClass())
log.info("FIELD VALUE CONTACT is " + str.orders_contact)
log.info("CLASS of FIELD VALUE CONTACT is " + str.orders_contact.getClass())
log.info("FIELD VALUE NUMBER is " + str.orders_number)
log.info("CLASS of FIELD VALUE NUMBER is " + str.orders_number.getClass())
}
Code example 36: Retrieve data from a list of structs
Figure 143: Log File - Script output
To set values for ticket fields, you follow the same principle as for getting data: use the ticket field group name and the technical name of the ticket field as a reference. Of course, additionally, the new value is required. And of course it has to be of the correct data type.
ticket.set("<ticketFieldGroupName>.<ticketFieldName>", <value>)
ticket.set("fields.reaction_time", new Date());
Code example 37: Set a ticket field value for a field of type Date
When you work with number or date fields, you can even calculate with the ticket field values in a very comfortable way, see following example.
//add 24 hours (in millis) to current field value
ticket.add("fields.deadline", 24*60*60*1000)
Code example 38: Calculate with the value of a ticket field of type date
Setting a value to null (i.e. emptying the field) is the same as removing the value:
ticket.set("fields.numberOfEmployees", null)
Code example 39: Setting a ticket field value to null
Or shorter:
ticket.remove("fields.numberOfEmployees" )
Code example 40: Setting a ticket field value to null via removing the value
To set an enum value use the following syntax. Of course, the new value has to be present in the ordered list (enum) which is referenced by the ticket field.
ticket.set("ticketFieldGroupName.ticketFieldName",<technical name of value>)
ticket.set("fields.priority", "URGENT");
Code example 41: Setting an enum value
When you want to add a line, you can simply use the add method:
ticket.add("fields.tags", "my new String")
Code example 42: Adding a new line in a list of strings
When you want to refer to a certain value to set a new value for it, you have to use the syntax for an array:
ticket.set("fields.tags[last]", "consol cm6")
Code example 43: Setting a value in a list of strings
Working with structs, you always have to work with the key of the value you would like to add or set. When you want to add a new line, you have to build a new struct as new line. The set method can be used one after another for each new field.
ticket.add("order_data.orders_list", new Struct()
.set("tA_Id", id).set("orders_hardware",mynewhardware_model)
.set("orders_contact", thenewcontactname)
.set("orders_number",thenewnumber)
Code example 44: Adding a new line in a list of structs
A ticket field group can be faded in (made visible) and faded out (made invisible) using a workflowApi method. This works for ticket field groups which are displayed in the main ticket data section as well as for ticket field groups which are displayed in the tabbed (Details) section.
A typical use case is a ticket field group which is invisible at first (ticket field group annotation group-visibility = false) and is faded in when the engineer needs to work with the data in the process. For example, a ticket field group which contains reasons for the dismissal of a request is only displayed (faded in) when the engineer has used the workflow activity Dismiss ticket .... This prevents an information overload of the ticket.
workflowApi.setGroupProperty("CF_Group_Dismissal",GroupPropertyType.VISIBLE,"true")
Code example 45: Fade in a ticket field group
To fade out some ticket field groups, e.g. when the ticket has been qualified and some of the ticket field groups will no longer be required in the process, use code according to the following example:
workflowApi.setGroupProperty("CF_Group_HardwareInfo",GroupPropertyType.VISIBLE,"false")
workflowApi.setGroupProperty("CF_Group_SoftwareInfo",GroupPropertyType.VISIBLE,"false")
Code example 46: Fade out ticket field groups
The customer data model (FlexCDM) contains customer field groups and customer field. They are both defined in the Admin Tool, navigation group Customers, navigation item Data Models.
Figure 144: ConSol CM Admin Tool - Customer field administration for customer data (CM version 6.10)
Three methods are of major importance for programming access to customer fields in ConSol CM scripts. They all are methods of the class Unit.
For retrieving data from a customer field:
Unit.get()
For setting data in an already existing customer field:
Unit.set()
For calculating with a value within a customer field, e.g., to add a certain time range to a date field and for adding a new line in list fields (simple lists and tables).
Unit.add()
Another method might be used when a field should be emptied, i.e. when its value should be set to null:
Unit.remove() // Sets the value of the field to null.
Because the name of a customer field might appear in more than one customer field group, the name of the customer field group has to be provided when accessing the customer data. For example, in the customer data model shown in the figure above, the customer field groups ResellerCompanyData and DirCustCompanyData could have a customer field named city. Therefore, it is important to mention group name and field name.
Please use the following syntax:
For example:
Code example 47: Retrieving a field value for a company
Methods to access the values of customer fields:
There are various objects and methods to work with data on different levels of the FlexCDM. Please see the following example where several common objects and methods have been applied. It is an Admin Tool script which is accessed from a workflow activity. The only purpose is to display some data of the ticket's main customer. The following figure shows the Java objects used in the script and the ConSol CM objects in the Admin Tool which are referenced.
Figure 145: ConSol CM customer objects in script and Admin Tool
Please keep in mind that you might also use the short notation like unit.definition.type for getter methods like unit.getDefinition().getType().
import com.consol.cmas.common.model.ticket.Ticket
import com.consol.cmas.common.model.customfield.meta.UnitDefinitionType
def ticket = workflowApi.ticket
def mcont = ticket.mainContact
log.info "CustomerGroup of main contact is now " + mcont.customerGroup.name
def custmod = mcont.customerDefinition.name
log.info "Customer definition of main contact is now " + custmod
log.info "UnitDefinition of main contact is now " + mcont.definition.name
def cityfield
switch (custmod) {
case "BasicModel" : cityfield = "company:city";
break;
case "DirectCustomerModel" : cityfield = "DirCustCompanyData:dir_cust_company_city";
break;
case "ResellerModel": cityfield = "ResellerCompanyData:city";
break;
default: cityfield = null;
break
}
log.info "CITYFIELD is now " + cityfield
def utype1 = mcont.getDefinition().getType()
def utype2 = mcont.definition.type
log.info "UTYPE1 is now " + utype1
log.info "UTYPE2 is now " + utype2
def company = mcont
if (utype2 == UnitDefinitionType.CONTACT) {
company = mcont.get("company()")
}
def mycity = company.get(cityfield)
log.info " CITY is now " + mycity
Code example 48: Admin Tool script (called from workflow) for displaying customer data
For the following data set the log file output is shown below. The Reseller model of the figure above is used.
Figure 146: ConSol CM Web Client - Customer data
Figure 147: Log file - Script output FlexCDM
The set and add methods work as described for ticket fields. For example:
//set a string field
unit.set("ResellerCompanyData:service_status","ok")
//set number field
company.set("ResellerCompanyData:numberOfEmployees", 1);
//add 1 to field value, afterwards the value of the field is 2
company.add("numberOfEmployees", 1);
Code example 49: Set and add values for a customer field of type integer and set a value for a customer field of type string
company.set("consultantsFields:responsibleConsultants", [
new Struct().set("lastName", "Miller").set("email", "miller@consol.com"),
new Struct().set("lastName", "Smith").set("email", "smith@consol.com"),
new Struct().set("lastName", "Burger").set("email", "burger@consol.com")
]);
Code example 50: Creating a new list of structs, version 2
company.add("consultantsFields:responsibleConsultants", new Struct().set("lastName", " Nowitzki ").set("email", "dnowitzki@consol.us"));
Code example 51: Adding a new line in a list of structs for company data
company.set("consultantsFields:responsibleConsultants[0].firstName", "John");
Code example 52: Setting a value in a list of structs using index notation
company.set("consultantsFields:responsibleConsultants[last]", null);
Code example 53: Removing a struct (= line) from a list of structs (= table)
Unit mainContact = ticket.mainContact
// "company" extension returns company for contact
Unit company = mainContact.get("company()");
// it is also possible to set company using "company" extension
mainContact.set("company()", company);
// "contacts" extension returns list of contacts for company
List contacts = company.get("contacts()");
// "tickets" extension returns list of tickets for contact or company
List tickets = company.get("tickets()");
tickets = mainContact.get("tickets()");
// extensions can be chained
Integer count = contact.get("company().contacts()[0].tickets()[count]");
// parentheses can be omitted, but it is not recommended (possible collision with name of group or field)
count = contact.get("company.contacts[0].tickets[count]"); // here "company" is not extension but name of field
Code example 54: Convenience methods for access to customer data
Starting with version 6.10.0, ConSol CM can contain the optional module CM/Resource Pool. This module allows it to extend the CM database and store resource objects. This can be various objects like IT assets, contracts, shop items or whatever is required in the respective company. Similar to the customer data model, the resource data model is defined using the Admin Tool. In the navigation group Resources, navigation item Data Models, the model is defined. In order to be able to work with resource data in workflow scripts, you should first get a profound knowledge of the Resource Pool principle and the setup of the data model, so please read the section about the CM/Resource Pool in the ConSol CM Administrator Manual first.
Similar to ticket and customer data, resource data is stored in resource field groups which contain resource fields.
Figure 148: ConSol CM Admin Tool - Definition of the resource data model
To retrieve data from resource fields, you can use methods as the ones shown in the following examples.
// Display info about HP Printer, printing infos about resource fields into server.log
def my_resource_name = resource.get("HP_Printer_Fields_basic.name")
log.info 'Displaying resource information ...'
log.info ' Name is : ' + my_resource_name
def my_resource_inv_number = resource.get("HP_Printer_Fields_basic.inventory_number")
log.info ' Inventory number is : ' + my_resource_inv_number
Code example 55: Simple resource action script which displays information about the resource in the log file
Figure 149: ConSol CM Web Client - Resource page of a HP printer resource
Figure 150: Log output of the script for the printer shown in the GUI example
In the real world, the circumstances usually are more complex. Often it is required to retrieve all resources of a certain type which are linked by a certain type of relation to a ticket or to a resource. This topic is treated in detail in the section Working With Resource Relations.
More coding examples, taken from the ConSol CM Action Framework, are provided in the ConSol CM Administrator Manual, section Scripts for the Action Framework.
A resource field group can be faded in (made visible) and faded out (made invisible) using a workflowApi method. This works for resource field groups which are displayed in the main resource page data section as well as for resource field groups which are displayed in the tabbed (Details) section.
In the following example, the resource HP_Printers contains a resource field group with a list of tickets. Each maintenance ticket which is created for the resource using a resource action (see ConSol CM Administrator Manual for a detailed explanation of resource actions) should be entered in the list. Initially, the resource field group is configured as invisible (group annotation group-visibility = false). Only when the action has been performed, the resource field group should be made visible on the resource page.
The code which is shown is an excerpt taken from the resource action script, but the same or similar code can be used in workflow scripts.
// set ticket number in list in resource, newtic is the newly created maintenance ticket
def newtic_id = newtic.id.toString()
def newtic_name = newtic.name
//resource is the HP printer from which the action script is started
resource.add("HP_Printer_MaintenanceTickets.MaintenanceTicketsList",new Struct().set("MaintenanceTicketID", newtic_id)
.set("MaintenanceTicketName", newtic_name)
)
if (!executionContext) {
return actionScriptResultFactory.getPostAction(PostActionType.FAILURE, "action.fail.wrong.activity")
}
// if ticktes are in the list in the resource, the field group should be visible
def groupName = "HP_Printer_MaintenanceTickets"
def fieldGroupDefinition = fieldDefinitionService.getGroupByName(groupName)
if (fieldGroupDefinition == null) {
throw new IllegalArgumentException("There is no group definition with name '" + groupName + "'.")
}
resource.getGroupsConfiguration().setProperty(fieldGroupDefinition, GroupPropertyType.VISIBLE, "true")
Code example 56: Excerpt from a script which adds a line to a list of tickets on a resource page and makes the resource field group which contains the list visible
Sometimes it is necessary to work with variables which are not used as values for ticket fields, customer fields or resource fields, which are not visible on the GUI, but which are only used as containers for internal programming variables.
The values of these data fields represent global variables. You implement global variables by creating regular ticket fields, customer fields or resource fields with the required data type and setting the field to invisible. This has to be done by using the annotation visibility = none. You can even let the variable be visible during the development of the process and control the field's value. Then you can set it to invisible when the system is handed over to QA and users.