3Examples of Each Context Where You Can Use Groovy

Examples of Each Context Where You Can Use Groovy

This section provides a simple example of using Groovy in all of the different supported contexts in your application.

Providing an Expression to Calculate a Custom Formula Field's Value

To compute a field’s value using an formula, set the field’s Value Calculation property to Calculate value with a formula and provide an appropriate Groovy expression.

Read-Only Calculated Fields

A field whose value calculation is configured to use a formula is read-only. The expression you supply is evaluated at runtime to return the field’s value each time it is accessed. The expected return type of the formula field's expression must be compatible with the type of the field on which you’ve configured that formula. (e.g. Number, Date, or String).

For example, consider a custom TroubleTicket object. If you add a number field named daysOpen, you can configure it to calculate its value with a formula and provide an expression like this:

(today() - creationDate) as Integer /* truncate to whole number of days */

Providing an Expression to Calculate a Custom Field's Default Value

When a new row is created for an object, the value of a custom field defaults to null unless you configure a default value for it. You can configure a default by setting its Value Calculation property to supply an expression to Set to default if value not provided. You can either specify a static default value, or use a Groovy expression. The default value expression is evaluated at the time the new row is created. The expected return type of your field's default value expression must be compatible with the field's type (Number, Date, String, etc.)

For example, consider a callbackDate field in a TroubleTicket object. If you want the callback back for a new trouble ticket to default to 3 days after it was created, then you can provide a default expression of:

creationDate + 3

Defining a Field-Level Validation Rule

A field-level validation rule is a constraint you can define on any standard or custom field. It is evaluated whenever the corresponding field's value is set. When the rule executes, the field's value has not been assigned yet and your rule acts as a gatekeeper to its successful assignment. The expression (or longer script) you write must return a boolean value that indicates whether the value is valid. If the rule returns true, then the field assignment will succeed so long as all other field-level rules on the same field also return true. If the rule returns false, then this prevents the field assignment from occurring, the invalid field is visually highlighted in the UI, and the configured error message is displayed to the end user. Since the assignment fails in this situation, the field retains its current value (possibly null, if the value was null before), however the UI component in the web page allows the user to see and correct their invalid entry to try again. Your script can use the newValue keyword to reference the new value that will be assigned if validation passes. To reference the existing field value, use the oldValue keyword. A field-level rule is appropriate when the rule to enforce only depends on the new value being set.

For example, consider a TroubleTicket object with a Priority field. To validate that the number entered is between 1 and 5, your field-level validation rule would look like this:

  • Field Name: Priority

  • Rule Name: Validate_Priority_Range

  • Error Message: The priority must be in the range from 1 to 5

Rule Body

newValue == null || (1..5).contains(newValue as Integer)
Tip: If a validation rule for field A depends on the values of one or more other fields (e.g. Y and Z), then create an object-level rule and programmatically signal which field or fields should be highlighted as invalid to the user as explained in Setting Invalid Fields for the UI in an Object-Level Validation Rule.

Defining an Object-Level Validation Rule

An object-level validation rule is a constraint you can define on any business object. It is evaluated whenever the framework attempts to validate the object. Use object-level rules to enforce conditions that depend on two or more fields in the object. This ensures that regardless of the order in which the user assigns the values, the rule will be consistently enforced. The expression (or longer script) you write must return a boolean value that indicates whether the object is valid. If the rule returns true, then the object validation will succeed so long as all other object-level rules on the same object return true. If the rule returns false, then this prevents the object from being saved, and the configured error message is displayed to the end user.

For example, consider a TroubleTicket object with Priority and DueDate fields. To validate that a trouble ticket of priority 1 or 2 cannot be saved without a due date, your object-level rule would look like this:

  • Rule Name: Validate_High_Priority_Ticket_Has_DueDate

  • Error Message: A trouble ticket of priority 1 or 2 must have a due date

Rule Body

// Rule depends on two fields, so must be written as object-level rule
if (Priority <= 2 && DueDate == null) {
  // Signal to highlight the DueDate field on the UI as being in error
  adf.error.addAttribute('DueDate')
  return false
}
return true

Defining Reusable Behavior with an Object Function

Object functions are useful for code that encapsulates business logic specific to a given object. You can call object functions by name from any other script code related to the same object or from any other scripts that work programmatically with the object in question. When defining a function, you specify a return value and can optionally specify one or more typed parameters that the caller will be required to pass in when invoked. The most common types for function return values and parameters are the following:

  • String: a text value

  • Boolean: a logical true or false value

  • Long: an integer value in the range of ±263-1

  • BigInteger: a integer of arbitrary precision

  • Double: a floating-point decimal value in the range of ±1.79769313486231570 x 10308

  • BigDecimal: a decimal number of arbitrary precision

  • Date: a date value with optional time component

  • List: an ordered collection of objects

  • Map: an unordered collection of name/value pairs

  • Object: any object

In addition, a function can define a void return type which indicates that it returns no value.

For example, you might define the following updateOpenTroubleTicketCount() object function on a Contact object. It calls the newView() built-in function (described in Accessing the View Object for Programmatic Access to Business Objects) to access the view object for programmatic access of trouble tickets, then appends a view criteria to find trouble tickets related to the current contact's id and having either 'Working' or 'Waiting' as their current status. Finally, it calls getEstimatedRowCount() to retrieve the count of trouble tickets that qualify for the filter criteria. Finally, if the new count is different from the existing value of the openTroubleTickets field, it updates this field’s value to be the new count computed.

  • Function Name: updateOpenTroubleTicketCount

  • Return Type: void

  • Parameters: None

Function Definition

// Access the view object for TroubleTicket programmatic access
def tickets = newView('TroubleTicket')
tickets.appendViewCriteria("""
contact = ${Id} and status in ('Working','Waiting')
"""
// Update OpenTroubleTickets field value
def newCount = tickets.getEstimatedRowCount()
if (openTroubleTickets != newCount) {
  openTroubleTickets = newCount
}

Enabling External Visibility of an Object Function

When you create an object function named doSomething() on an object named Example, the following is true by default:

  • other scripts on the same object can call it,

  • any script written on another object that obtains a row of type Example can call it

  • external systems working with an Example object via REST service, cannot call it

  • it displays in the Object category of the Functions tab on the Code Helpers Palette.

If you check the Callable by External Systems checkbox, then an external system working with an Example object will be able to invoke your doSomething() via REST service. Do this when the business logic it contains should be accessible to external systems.

Defining an Object-Level Trigger to Complement Default Processing

Triggers are scripts that you can write to complement the default processing logic for a standard or custom object. You can define triggers both at the object-level and the field-level. The object-level triggers that are available are described below. See Defining a Field-Level Trigger to React to Value Changes for the available field-level triggers.

  • On Initialize

    Fires when a new instance of an object is created. Use to assign programmatic default values to one or more fields in the object.

  • On Invalidate

    Fires on a valid parent object when a child row is created, removed, or modified, or also when the first persistent field is changed in an unmodified row.

  • On Remove

    Fires when an attempt is made to delete an object. Returning false stops the row from being deleted and displays the optional trigger error message.

  • Before Insert

    Fires before a new object is inserted into the database.

  • Before Update

    Fires before an existing object is modified in the database

  • Before Delete

    Fires before an existing object is deleted from the database

  • Before Commit

    Fires after all changes have been posted to the database, but before they are permanently committed. Can be used to make additional changes that will be saved as part of the current transaction.

  • Before Rollback

    Fires before the change pending for the current object (insert, update, delete) is rolled back

For example, consider a Contact object with a openTroubleTickets field that needs to be updated any time a trouble ticket is created or modified. You can create the following trigger on the TroubleTicket object that invokes the updateOpenTroubleTicketCount() object function described above.

  • Trigger Object: TroubleTicket

  • Trigger: Before Commit

  • Trigger Name: Before_Commit_Set_Open_Trouble_Tickets

Trigger Definition

// Get the related contact for this trouble ticket
def relatedContact = contactObject
// Update its openTroubleTickets field value
relatedContact?.updateOpenTroubleTicketCount()

Defining a Field-Level Trigger to React to Value Changes

Field-level triggers are scripts that you can write to complement the default processing logic for a standard or custom field. The following field-level trigger is available:

  • After Field Changed

    Fires when the value of the related field has changed (implying that it has passed any field-level validation rules that might be present).

Use the After Field Changed trigger to calculate other derived field values when another field changes value. Do not use a field-level validation rule to achieve this purpose because while your field-level validation rule may succeed, other field-level validation rules may fail and stop the field's value from actually being changed. Since generally you only want your field-change derivation logic to run when the field's value actually changes, the After Field Changed trigger guarantees that you get this desired behavior.

See Deriving Values of a Field When Other Fields Change Value for tips on using this trigger.

Converting a Trigger to Custom Code

When you create a new trigger, Visual Builder uses the visual editor shown in the figure below. This interface allows you to solve many common use cases with point and click by configuring one or more conditional expressions and one or more actions that should execute if a given condition is true.


Visual Trigger Editor

As you use the designer interface, Visual Builder keeps the trigger’s equivalent Groovy script in sync. At any time you can peek at a read-only view of the script code by clicking on the Code Editor button in the toolbar. Click again on the Designer toolbar button to return to the visual view. If your trigger requires a small amount of custom Groovy code, use the Custom Groovy Code action as shown below. This action is useful for adding small amounts of manually authored Groovy script inside an otherwise declaratively-configured trigger. For example here we’ve written one line of Groovy code in the “Upcase Subject” custom Groovy code action to uppercase the subject of the trouble ticket being inserted.


Configuring an Action Group

If the complexity of your task demands it, or you simply prefer it that way, you can convert any trigger you create into a custom code trigger. While in the read-only code editor view, as shown in the figure below, you can click on the convert to Custom Code Trigger link to change the current trigger into a manually authored script. Once you’ve performed this step, the code editor becomes editable. After performing this step, you have full control over the contents of the current trigger’s groovy script. This conversion is a one-way street, however. A custom code trigger cannot be switched back into visual design mode. You’ll need to create a new trigger to start again with the point-and-click approach in the visual editor.


Converting Trigger to Custom Code