scripting guide

81
Oracle Fusion CRM Application Composer Scripting Guide Revision History June 11th, 2013 Revision 1.23 Table of Contents Introduction ........................................................................................................................ 2 Terminology ............................................................................................................... 3 Where You'll Use Groovy in Your Application ................................................................... 3 Groovy Basics .................................................................................................................... 4 Commenting Your Scripts .............................................................................................. 4 Defining Variables ....................................................................................................... 5 Referencing the Value of a Field in the Current Object ........................................................ 6 Working with Numbers, Dates, and Strings ....................................................................... 7 Using Substitution Expressions in Strings ......................................................................... 7 Using Conditional Expressions ....................................................................................... 7 Using the Switch Statement ........................................................................................... 8 Returning a Boolean Result ........................................................................................... 9 Assigning a Value to a Field in the Current Object ............................................................ 10 Writing Null-Aware Expressions ................................................................................... 11 Understanding the Difference Between Null and Empty String ........................................... 11 Understanding Secondary Fields Related to a Lookup ....................................................... 12 Using Groovy's Safe Navigation Operator ....................................................................... 12 Assigning a Value to a Field in a Related Object ............................................................... 13 Printing and Viewing Diagnostic Messages ..................................................................... 13 Working with Lists ..................................................................................................... 14 Working with Maps .................................................................................................... 15 Working with Ranges .................................................................................................. 17 Examples of Each Context Where You Can Use Groovy ........................................................... 17 Providing an Expression to Calculate a Custom Formula Field's Value ................................. 17 Providing an Expression to Calculate a Custom Field's Default Value ................................... 18 Providing an Expression to Make a Custom Field Conditionally Updateable .......................... 18 Providing an Expression to Make a Custom Field Conditionally Required ............................. 19 Defining a Field-Level Validation Rule ........................................................................... 19 Defining an Object-Level Validation Rule ....................................................................... 20 Defining Utility Code in a Global Function ..................................................................... 21 Defining Reusable Behavior with an Object Function ....................................................... 22 Defining an Object-Level Trigger to Complement Default Processing .................................. 23 Defining a Field-Level Trigger to React to Value Changes .................................................. 24 Groovy Tips and Techniques ................................................................................................ 24 Simplifying Code Authoring with the Expression Palette ................................................... 24 Using the Related Object Accessor Field to Work with a Parent Object ................................. 28 Using the Related Object Accessor Field to Work with a Referenced Object .......................... 29 Using the Related Collection Accessor Field to Work with Child Rows ................................. 29 Accessing Current Date and Time from the Application Server ........................................... 31 Accessing Current Date and Time from the Database ........................................................ 31 Understanding ADF's Additional Built-in Groovy Functions ............................................... 31 Understanding Groovy's Null-Safe Comparison Operators ................................................. 37 1

Post on 05-Jan-2016

248 views

Category:

Documents


1 download

DESCRIPTION

Sales Cloud R7 Scripting Guide

TRANSCRIPT

Page 1: Scripting Guide

Oracle Fusion CRM ApplicationComposer Scripting Guide

Revision HistoryJune 11th, 2013Revision 1.23

Table of ContentsIntroduction ........................................................................................................................ 2

Terminology ............................................................................................................... 3Where You'll Use Groovy in Your Application ................................................................... 3

Groovy Basics .................................................................................................................... 4Commenting Your Scripts .............................................................................................. 4Defining Variables ....................................................................................................... 5Referencing the Value of a Field in the Current Object ........................................................ 6Working with Numbers, Dates, and Strings ....................................................................... 7Using Substitution Expressions in Strings ......................................................................... 7Using Conditional Expressions ....................................................................................... 7Using the Switch Statement ........................................................................................... 8Returning a Boolean Result ........................................................................................... 9Assigning a Value to a Field in the Current Object ............................................................ 10Writing Null-Aware Expressions ................................................................................... 11Understanding the Difference Between Null and Empty String ........................................... 11Understanding Secondary Fields Related to a Lookup ....................................................... 12Using Groovy's Safe Navigation Operator ....................................................................... 12Assigning a Value to a Field in a Related Object ............................................................... 13Printing and Viewing Diagnostic Messages ..................................................................... 13Working with Lists ..................................................................................................... 14Working with Maps .................................................................................................... 15Working with Ranges .................................................................................................. 17

Examples of Each Context Where You Can Use Groovy ........................................................... 17Providing an Expression to Calculate a Custom Formula Field's Value ................................. 17Providing an Expression to Calculate a Custom Field's Default Value ................................... 18Providing an Expression to Make a Custom Field Conditionally Updateable .......................... 18Providing an Expression to Make a Custom Field Conditionally Required ............................. 19Defining a Field-Level Validation Rule ........................................................................... 19Defining an Object-Level Validation Rule ....................................................................... 20Defining Utility Code in a Global Function ..................................................................... 21Defining Reusable Behavior with an Object Function ....................................................... 22Defining an Object-Level Trigger to Complement Default Processing .................................. 23Defining a Field-Level Trigger to React to Value Changes .................................................. 24

Groovy Tips and Techniques ................................................................................................ 24Simplifying Code Authoring with the Expression Palette ................................................... 24Using the Related Object Accessor Field to Work with a Parent Object ................................. 28Using the Related Object Accessor Field to Work with a Referenced Object .......................... 29Using the Related Collection Accessor Field to Work with Child Rows ................................. 29Accessing Current Date and Time from the Application Server ........................................... 31Accessing Current Date and Time from the Database ........................................................ 31Understanding ADF's Additional Built-in Groovy Functions ............................................... 31Understanding Groovy's Null-Safe Comparison Operators ................................................. 37

1

Page 2: Scripting Guide

Testing Whether an Field's Value Is Changed ................................................................... 38Avoiding Validation Threshold Errors By Conditionally Assigning Values ............................. 38Passing the Current Object to a Global Function .............................................................. 39Referencing Original Values of Changed Fields ............................................................... 39Raising a Warning From a Validation Rule Instead of an Error ............................................ 39Throwing a Custom Validation Exception ....................................................................... 39Returning Locale-Sensitive Custom Strings ..................................................................... 40Raising a Trigger's Optional Declaratively-Configured Error Message .................................. 40Accessing the View Object for Programmatic Access to Business Objects ............................. 41Finding an Object by Id ............................................................................................... 42Finding Objects Using a View Criteria ........................................................................... 43Creating a New Object ................................................................................................ 49Updating an Existing Object ........................................................................................ 49Permanently Removing an Existing Object ..................................................................... 50Reverting Changes in a Single Row ............................................................................... 50Understanding Why Using Commit or Rollback In Scripts Is Strongly Discouraged ................ 50Using the User Data Map ............................................................................................ 50Referencing Information About the Current User ............................................................. 51Using Aggregate Functions .......................................................................................... 51Understanding When to Configure Field Dependencies ..................................................... 53Enforcing Conditional Updateability of Custom Fields for Web Service Access ..................... 55Implementing Non-Formula Field Changeable Only From Script ........................................ 56Understanding When Field Default Value Expressions Are Evaluated ................................... 56Understanding the Difference Between Default Expression and Create Trigger ...................... 56Deriving Values of a Field When Other Fields Change Value .............................................. 57Setting Invalid Fields for the UI in an Object-Level Validation Rule ..................................... 57Determining the State of a Row ................................................................................... 58Understanding How Local Variables Hide Object Fields .................................................... 59Invoking Web Services from Your Scripts ....................................................................... 59

Understanding Common JBO Exceptions in Groovy Scripts ....................................................... 63JBO-25030: Detail entity X with row key Y cannot find or invalidate its owning entity ............ 63JBO-26020: Attempting to insert row with no matching EO base ......................................... 64

Supported Classes and Methods for Use in Groovy Scripts ......................................................... 64

Introduction

Groovy is a standard, dynamic scripting language for the Java platform for which the CRM ApplicationComposer provides deep support. This document explains the basics of how you will use the Groovyscripting language to enhance your applications. This section provides a brief overview of the differentcontexts in which you can use Groovy scripts. The second section covers the basics of Groovy. The thirdsection gives a concrete example of each type of Groovy script you can write. The fourth section offers acompendium of tips and techniques for getting the most out of Groovy in your applications, and the finalsection documents the supported classes and methods you are allowed to use in your Groovy code.

Note

Please read the section called “Supported Classes and Methods for Use in Groovy Scripts” thatdocuments the only classes and methods you may use in your Groovy scripts. Using any otherclass or method will raise a security violation error when you migrate your code to later FusionCRM maintenance releases. Therefore, we strongly suggest that the Groovy code you write usesonly the classes and methods shown there to avoid the unpleasant and possibly time-consumingtask of having to rewrite your code in the future.

2

Oracle Fusion CRM Application Composer Scripting Guide

Page 3: Scripting Guide

Terminology

Throughout the document the term script is used to describe one or more lines of Groovy code that theOracle ADF framework executes at runtime. Often a very-short script is all that is required. For example,to validate that a CommissionPercentage field's value does not exceed 40%, you might use a one-linescript like:

return CommissionPercentage < 0.40

In fact, this one-liner can be conveniently shortened by dropping the return keyword since the returnkeyword is always implied on the last line of a script:

CommissionPercentage < 0.40

For slightly more complicated logic, your script might require some conditional handling. For example,suppose the maximum commission percentage is 40% if the salesperson's job grade is less than or equalto 3, but 60% if the job grade is higher. Your script would grow a little to look like this:

if (JobGrade <= 3) { return CommissionPercentage < 0.40}else { return CommissionPercentage < 0.60}

Scripts that you'll write for other purposes like complex validation rules or reusable functions may spanmultiple pages, depending on your needs.

When a context requiring a Groovy script will typically use a short (often, one-line) script, we emphasizethat fact by calling it an expression, however technically the terms script and expression are interchangeable.Anywhere you can provide a one-line expression is also a valid context for providing a multi-line script ifthe need arises. Whether you provide a short expression or a multi-line script, the syntax and features atyour disposal are the same. You need only pay attention that your code returns a value of the appropriatetype for the context in which you use it. Each section below highlights the expected return type for thescript in question.

Where You'll Use Groovy in Your Application

There are a number of different contexts where you will use Groovy scripts as you customize existing objectsor create new custom ones. You will write shorter scripts to provide an expression to:

• calculate a custom formula field's value

• calculate a custom field's default value

• make a custom field conditionally updateable, or

• make a custom field conditionally required

• define the condition for executing an object workflow

You will generally write somewhat longer scripts to define:

• a field-level validation rule

• an object-level validation rule

• a trigger to complement default processing

3

Oracle Fusion CRM Application Composer Scripting Guide

Page 4: Scripting Guide

• utility code in a global function, or

• reusable behavior in an object function

If you anticipate calling the same code from multiple different contexts, any of your scripts can call thereusable code you write in either global functions or object functions. As their name implies, global functionscan be called from scripts in any object or from other global functions. Object functions can be called byany scripts in the same object, or even triggered by a button in the user interface.

After exploring the Groovy basic techniques needed to understand the examples, see the section called“Examples of Each Context Where You Can Use Groovy ” for a concrete example of each of these usages,and the section called “Groovy Tips and Techniques” for additional tips and techniques on getting the mostout of Groovy in your application.

Groovy Basics

This section highlights some important aspects of Groovy to allow you to better understand the examplesin the sections that follow.

Commenting Your Scripts

It is important that you document your scripts so that you and your colleagues who might view the codemonths from now will remember what the logic is doing. You can use either a double-slash combination// which makes the rest of the current line a comment, or you can use the open-comment and close-commentcombination of /* followed later by */. The latter style can span multiple lines.

Here is an example of both styles in action:

// Loop over the names in the listfor (name in listOfNames) { /* * Update the location for the current name. * If the name passed in does not exist, will result in a no-op */ adf.util.updateLocationFor(name, // name of contact 'Default', /* location style */ )}

When using multi-line comments, it is illegal for a nested /* ... */ comment to appear inside of anotherone. So, for example, the following is not allowed:

// Nested, multi-line comment below is not legaldef interest = 0/* 18-MAY-2001 (smuench) Temporarily commented out calculation!

/* * Interest Accrual Calculation Here */ interest = complexInterestCalculation()*/

Instead, you can comment out an existing multi-line block like this:

// Nested, multi-line comment below is legaldef interest = 0//// 18-MAY-2001 (smuench) Temporarily commented out calculation!//

4

Oracle Fusion CRM Application Composer Scripting Guide

Page 5: Scripting Guide

// /*// * Interest Accrual Calculation Here// */// interest = complexInterestCalculation()//

Or, alternatively had your initial code used the // style of comments, the following is also legal:

// Nested, multi-line comment below is not legaldef interest = 0/* 18-MAY-2001 (smuench) Temporarily commented out calculation!

// // Interest Accrual Calculation Here // interest = complexInterestCalculation()*/

The most common style-guide for comments would suggest to use multi-line comments at the beginningof the script, and single-line comments on subsequent lines. This allows you to most easily comment outcode for debugging purposes. Thus, you typical script would look like this:

/* * Object validation rule for BankAccount * * Ensures that account is not overdrawn */def balance = CurrentBalance_c// Use an object function to calculate uncleared chargesdef unclearedCharges = unclearedChargesAmountForAccount()// Perform some other complicated processingperformComplicatedProcessing()// return true if the account is not overdrawnreturn balance > unclearedCharges

Defining Variables

Groovy is a dynamic language, so variables in your scripts can be typed dynamically using the def keywordas follows:

// Assign the number 10 to a variable named "counter"def counter = 10

// Assign the string "Hello" to a variable named "salutation"def salutation = 'Hello'

// Assign the current date and time to a variable named "currentTime"def currentTime = now()

Using the def keyword you can define a local variable of the right type to store any kind of value, not onlythe three examples above. Alternatively you can declare a specific type for variables to make your intentionmore explicit in the code. For example, the above could be written like this instead:

// Assign the number 10 to a variable of type Integer named "counter"Integer counter = 10

// Assign the string "Hello" to a variable named "salutation"String salutation = 'Hello'

// Assign the current date and time to a variable named "currentTime"Date currentTime = now()

5

Oracle Fusion CRM Application Composer Scripting Guide

Page 6: Scripting Guide

Note

You can generally choose to use the def keyword or to use a specific type for your variables ac-cording to your own preference, however when working with ADF objects you must to use thedef keyword to define a variable to hold them. See the tip in the the section called “Using theRelated Collection Accessor Field to Work with Child Rows” section below for more details.

Referencing the Value of a Field in the Current Object

When writing scripts that execute in the context of the current business object, you can reference the valueof any field in the current object by simply using its API name. This includes all of the following contexts:

• object validation rules

• field-level validation rules

• formula field expressions

• custom field conditionally updateable expressions

• custom field conditionally required expressions

• object triggers

• field triggers

• object functions, and

• conditions for executing an object workflow

The API name of custom fields that you have added to a standard object will be suffixed with _c to distin-guish them from standard field names. So, for example to write a script that references the value of astandard field named ContactPhoneNumber and a custom field named ContactTwitterName, you woulduse the following code:

// Assign value of standard field "ContactPhoneNumber" to "phone" vardef phone = ContactPhoneNumber

// Assign value of custom field "ContactTwitterName" to "twitterName" vardef twitterName = ContactTwitterName_c

// Assemble text fragment by concatenating static text and variablesdef textFragment = 'We will try to call you at ' + phone + ' or send you a tweet at ' + twitterName

Defining a local variable to hold the value of a field is a good practice if you will be referencing its valuemore than once in your code. If you only need to use it once, you can directly reference a field's namewithout defining a local variable for it, like this:

def textFragment = 'We will try to call you at ' + ContactPhoneNumber + ' or send you a tweet at ' + ContactTwitterName_c

Note

When referencing a field value multiple times, you can generally choose to use or not to use alocal variable according to your own preference, however when working with an ADF RowIter-ator object, you must to use the def keyword to define a variable to hold it. See the tip in the the

6

Oracle Fusion CRM Application Composer Scripting Guide

Page 7: Scripting Guide

section called “Using the Related Collection Accessor Field to Work with Child Rows” sectionbelow for more details.

Working with Numbers, Dates, and Strings

Groovy makes it easy to work with numbers, dates and strings. You can use the normal + and - operatorsto do date, number, and string arithmetic like this:

// Assign a date three days after the CreatedDatedef targetDate = CreatedDate + 3

// Assign a date one week (seven days) before the value// of the custom SubmittedDate fielddef earliestAcceptedDate = SubmittedDate_c - 7

// Increase an employee's custom Salary field value by 100 dollarssetAttribute('Salary_c', Salary_c + 100)

// Decrement an salesman's commission field value by 100 dollarssetAttribute('Commission_c', Commission_c - 100)

// Subtract (i.e. remove) any "@"-sign that might be present// in the contact's twitter namedef twitNameWithoutAtSign = ContactTwitterName_c - '@'

// Add the value of the twitter name to the messagedef message = 'Follow this user on Twitter at @' + twitNameWithoutAtSign

Using Substitution Expressions in Strings

Groovy supports using two kinds of string literals, normal strings and strings with substitution expressions.To define a normal string literal, use single quotes to surround the contents like this:

// These are normal stringsdef name = 'Steve'def confirmation = '2 message(s) sent to ' + name

To define a string with substitution expressions, use double-quotes to surround the contents. The stringvalue can contain any number of embedded expressions using the ${expression} syntax. For example,you could write:

// The confirmation variable is a string with substitution expressionsdef name = 'Steve'def numMessages = 2def confirmation = "${numMessages} message(s) sent to ${name}"

Executing the code above will end up assigning the value 2 messages(s) sent to Steve to the variablenamed confirmation. It does no harm to use double-quotes all the time, however if your string literal containsno substitution expressions it is slightly more efficient to use the normal string with single-quotes.

Tip

As a rule of thumb, use normal (single-quoted) strings as your default kind of string, unless yourequire the substitution expressions in the string.

Using Conditional Expressions

When you need to perform the conditional logic, you use the familiar if/else construct. For example, inthe text fragment example in the previous section, if the current object's ContactTwitterName_c returns

7

Oracle Fusion CRM Application Composer Scripting Guide

Page 8: Scripting Guide

null, then you won't want to include the static text related to a twitter name. You can accomplish thisconditional text inclusion using if/else like this:

def textFragment = 'We will try to call you at ' + ContactPhoneNumberif (ContactTwitterName_c != null) { textFragment += ', or send you a tweet at '+ContactTwitterName_c}else { textFragment += '. Give us your twitter name to get a tweet'}textFragment += '.'

While sometimes the traditional if/else block is more easy to read, in other cases it can be quite verbose.Consider an example where you want to define an emailToUse variable whose value depends on whetherthe EmailAddress custom field ends with a .gov suffix. If the primary email ends with .gov, then youwant to use the AlternateEmailAddress instead. Using the traditional if/else block your script wouldlook like this:

// Define emailToUse variable whose value is conditionally// assigned. If the primary email address contains a '.gov'// domain, then use the alternate email, otherwise use the// primary email.def emailToUseif (endsWith(EmailAddress_c,'.gov') { emailToUse = AlternateEmailAddress_c }else { emailToUse = EmailAddress_c}

Using Groovy's handy inline if/then/else operator, you can write the same code in a lot fewer lines:

def emailToUse = endsWith(EmailAddress_c,'.gov') ? AlternateEmailAddress_c : EmailAddress_c

The inline if/then/else operator has the following general syntax:

BooleanExpression ? If_True_Use_This_Expression : If_False_Use_This_Expression

Using the Switch Statement

If the expression on which your conditional logic depends may take on many different values, and for eachdifferent value you'd like a different block of code to execute, use the switch statement to simplify thetask. As shown in the example below, the expression passed as the single argument to the switch statementis compared with the value in each case block. The code inside the first matching case block will execute.Notice the use of the break statement inside of each case block. Failure to include this break statementresults in the execution of code from subsequent case blocks, which will typically lead to bugs in yourapplication.

Notice, further, that in addition to using a specific value like 'A' or 'B' you can also use a range of valueslike 'C'..'P' or a list of values like ['Q','X','Z']. The switch expression is not restricted to being astring as is used in this example; it can be any object type.

def logMsgdef maxDiscount = 0// warehouse code is first letter of product SKU// uppercase the letter before using it in switchdef warehouseCode = upperCase(left(SKU_c,1))// Switch on warehouseCode to invoke appropriate// object function to calculate max discountswitch (warehouseCode) { case 'A': maxDiscount = Warehouse_A_Discount()

8

Oracle Fusion CRM Application Composer Scripting Guide

Page 9: Scripting Guide

logMsg = "Used warehouse A calculation" break case 'B': maxDiscount = Warehouse_B_Discount() logMsg = "Used warehouse B calculation" case 'C'..'P': maxDiscount = Warehouse_C_through__P_Discount() logMsg = "Used warehouse C-through-P calculation" break case ['Q','X','Z']: maxDiscount = Warehouse_Q_X_Z_Discount() logMsg = "Used warehouse Q-X-Z calculation" break default: maxDiscount = Default_Discount() logMsg = "Used default max discount"}adf.util.log(logMsg+' ['+maxDiscount+']')// return expression that will be true when rule is validreturn Discount_c == null || Discount_c <= maxDiscount

Returning a Boolean Result

Several different contexts in ADF runtime expect your groovy script to return a boolean true or false result.These include:

• custom field's conditionally updateable expressions

• custom field's conditionally required expressions

• object-level validation rules

• field-level validation rules

• conditions for executing an object workflow

Groovy makes this easy. One approach is to use the groovy true and false keywords to indicate your returnas in the following example:

// Return true if value of the Commission custom field is greater than 1000if (Commission_c > 1000) { return true}else { return false}

However, since the expression Commission_c > 1000 being tested above in the if statement is itself aboolean-valued expression, you can write the above logic in a more concise way by simply returning theexpression itself like this:

return Commission_c > 1000

Furthermore, since Groovy will implicitly change the last statement in your code to be a return, you couldeven remove the return keyword and just say:

Commission_c > 1000

This is especially convenient for simple comparisons that are the only statement in a validation rule, con-ditionally updateable expression, conditionally required expression, formula expression, or the conditionto execute an object workflow.

9

Oracle Fusion CRM Application Composer Scripting Guide

Page 10: Scripting Guide

Assigning a Value to a Field in the Current Object

The most logical way to assign the value of a field would be to using the groovy assignment operator likethis:

// Logical, but incorrect, way to assign the value "steve"// to a custom field named ContactTwitterName in the current objectContactTwitterName_c = 'steve'

However, since it is easy for you to inadvertently substitute the assignment operator = with the equals op-erator == ADF requires that your Groovy scripts explicitly use the setAttribute() function to avoid anyaccidental assignments from being silently performed without your knowledge. It has the following syntax:

// Correct way to assign the value "steve" to a custom field// named ContactTwitterName in the current objectsetAttribute('ContactTwitterName_c', 'steve')

Notice above that the second argument 'steve' passed to setAttribute() is a literal string, so it requiresthe single quotes around it. However, if the value to be assigned to the field is a literal number, or a groovyexpression, or a value that is already stored in a variable myNewVal then the second argument to setAttrib-ute() does not require any quotes like this:

// Correct way to assign the value 3 to a custom field// named ContactNumberOfChildren in the current objectsetAttribute('ContactNumberOfChildren_c', 3)

// Correct way to assign the value "one year from today's date"// to a custom field ContactCallbackDate in the current objectsetAttribute('ContactCallbackDate_c', today() + 365)

// Correct way to assign the value in the variable myNewVal// to a custom field ContactNumberOfChildren in the current objectdef myNewVal = /* compute new number of children value here */setAttribute('ContactNumberOfChildren_c', myNewVal)

Notice that the name of the field whose value you want to assign is passed as a literal string (enclosed inquotes). Failure to enclose the value in quotes will result in either an error or unexpected functionality. Forexample, if you did the following:

// Incorrect way to assign the value "steve" to a custom field// named ContactTwitterName in the current object.// The name of the field is not being passed as a literal stringsetAttribute( ContactTwitterName_c, 'steve')

Using this incorrect syntax, at runtime, ADF will evaluate the value of the ContactTwitterName customfield and use its value as the name of the field to assign. So, as a silly example, if the ContactTwitterNamefield currently had the value "NoComment", then ADF would attempt to assign the value "steve" to thefield in the current object named NoComment. If there were such a field, its value will be assigned withouterror. If no such field exists, then a runtime exception will be thrown.

Tip

See the section called “Avoiding Validation Threshold Errors By Conditionally Assigning Values”for a tip about how to avoid your field assignments from causing an object to hit its validationthreshold.

10

Oracle Fusion CRM Application Composer Scripting Guide

Page 11: Scripting Guide

Writing Null-Aware Expressions

When writing your scripts, be aware that field values can be null. You can use the nvl() null valuefunction to easily define a value to use instead of null as part of any script expressions you use. Considerthe following examples:

// Assign a date three days after the PostedDate// Use the current date instead of the PostedDate if the// PostedDate is nulldef targetDate = nvl(PostedDate_c,now()) + 3

// Increase an employee's custom Salary field value by 10 percent// Use zero if current Salary is nullsetAttribute('Salary_c', nvl(Salary_c,0) * 1.1)

Understanding the Difference Between Null and Empty String

In Groovy, there is a subtle difference between a variable whose value is null and a variable whose valueis the empty string. The value null represents the absence of any object, while the empty string is an objectof type String with zero characters. If you try to compare the two, they are not the same. For example,any code inside the following conditional block will not execute because the value of varA (null) does notequals the value of varB (the empty string).

def varA = nulldef varB = "" /* The empty string */if (varA == varB) { // Do something here when varA equals varB}

Another common gotcha related to this subtle difference is that trying to compare a variable to the emptystring does not test whether it is null. For example, the code inside the following conditional block willexecute (and cause a NullPointerException at runtime) because the null value of varA is not equal tothe empty string:

def varA = nullif (varA != "") { // set varB to the first character in varA def varB = varA.charAt(0)}

To test whether a string variable is neither null nor empty, you could explicitly write out both conditionslike this:

if (varA != null && varA != "") { // Do something when varA is neither null nor empty}

However, Groovy provides an even simpler way. Since both null and the empty string evaluate to falsewhen interpreted as a boolean, you can use the following instead:

if (varA) { // Do something when varA has a non-null and non-empty value}

If varA is null, the condition block is skipped. The same will occur if varA is equal to the empty stringbecause either condition will evaluate to boolean false. This more compact syntax is the recommendedapproach.

11

Oracle Fusion CRM Application Composer Scripting Guide

Page 12: Scripting Guide

Understanding Secondary Fields Related to a Lookup

A lookup field represents a many-to-1 foreign key reference between one object and a another object ofthe same or different type. For example, a TroubleTicket object might have a lookup field named Contactthat represents a foreign key reference to the specific Contact object that reported the trouble ticket. Whendefining a lookup field, you specify a primary display field name from the reference object. For example,while defining the Contact lookup field referencing the Contact object, you might specify the ContactName field.

When you define a lookup field like Contact, you get one primary field and two secondary fields:

The Lookup Field This primary field is named Contact_c and it holds the value of theprimary display field related to the referenced object, for examplethe name of the related contact.

The Foreign Key Field This secondary field is named Contact_Id_c and it holds the valueof the primary key of the reference contact.

The Related Object Accessor Field This secondary field is named Contact_Obj_c and it allows you toprogrammatically access the related contact object in script code

To access other fields on the related object, you can use the related object accessor field like this:

// Assume script runs in context of TroubleTicket objectdef contactEmail = Contact_Obj_c?.EmailAddress_c

If you know the primary key of the new

To change which contact the TroubleTicket is related to, you can set a new contact by using one of thefollowing techniques:

If you know the primary key valueof the new contact...

// Assume script runs in context of TroubleTicket objectdef newId = /* ... Get the Id of the New Contact Here */setAttribute('Contact_Id_c', newId)

If you know the value of the contact'sprimary display field...

// Assume script runs in context of TroubleTicket objectsetAttribute('Contact_c', 'James Smith')

Using Groovy's Safe Navigation Operator

If you are using "dot" notation to navigate to reference the value of a related object, you should use Groovy'ssafe-navigation operator ?. instead of just using the . operator. This will avoid a NullPointerExceptionat runtime if the left-hand-side of the operator happens to evaluate to null. For example, consider aTroubleTicket object with a lookup field named AssignedTo representing the staff member assigned towork on the trouble ticket. Since the AssignedTo field may be null before the ticket gets assigned, anycode referencing fields from the related object should use the safe-navigation operator as shown here:

// access related lookup object and access its record name// Using the ?. operator, if related object is null,// the expression evaluates to null instead of throwing// NullPointerExceptiondef assignedToName = AssignedTo_Obj_c?.RecordName

12

Oracle Fusion CRM Application Composer Scripting Guide

Page 13: Scripting Guide

Tip

For more information on why the code here accesses AssignedTo_Obj_c instead of a field namedAssignedTo_c, see the section called “Understanding Secondary Fields Related to a Lookup”

Assigning a Value to a Field in a Related Object

When using the setAttribute() method — see the section called “Assigning a Value to a Field in theCurrent Object” — the first argument must be a string that represents the name of a single field in objecton which you invoke it. For example, the following is not a correct way to use the setAttribute() functionto set the Status field of the parent TroubleTicket object for an activity because TroubleTick-et_c?.Status_c is not the name of a field on the current activity object:

// Assume script runs in context of Activity object// which is a child object of parent TroubleTicket

// INCORRECT way to set a parent field's valuesetAttribute('TroubleTicket_c?.Status_c', 'Open')

Instead, you must first assign the parent object to a variable, then invoke the setAttribute() method onthis parent object as shown here:

// Assume script runs in context of Activity object// which is a child object of parent TroubleTicket

// First access the parent objectdef parentTicket = TroubleTicket_c// Then call the setAttribute on that parent objectparentTicket?.setAttribute('Status_c', 'Open')

Tip

For more information on accessing related objects see the section called “Using the Related ObjectAccessor Field to Work with a Parent Object” and the section called “Using the Related ObjectAccessor Field to Work with a Referenced Object”.

Printing and Viewing Diagnostic Messages

To write messages to the diagnostic log, use the print or println function. The former writes its valuewithout any newline character, while the latter writes it value along with a newline. For example:

// Write a diagnostic message to the log. Notice how// convenient string substitution expressions areprintln("Status = ${Status_c}")

Use the Diagnostic Dashboard shown in to view the diagnostic messages your scripts have written to thelog to assist with debugging. As shown in Figure 1, “Viewing Messages Using the Diagnostics Dashboard”,after accessing the diagnostic dashboard, click the Get Latest Log Messages button to retrieve the latestGroovy script messages from the log file. To sort in reverse chronological order — to see the most recentmessages first — click the down-pointing arrow in the Timestamp column header.

13

Oracle Fusion CRM Application Composer Scripting Guide

Page 14: Scripting Guide

Figure 1. Viewing Messages Using the Diagnostics Dashboard

In this release, the diagnostic messages in the log are not identified by context, so it can be helpful to includeinformation in the printed diagnostic messages to identify what code was executing when the diagnosticmessage was written. For example:

// Write a diagnostic message to the log, including info about the contextprintln("[In: BeforeInsert] Status = ${Status_c}")

Working with Lists

A list is an ordered collection of objects. You can create list of objects using Groovy's square-bracketnotation and a comma separating each list element like this:

// Define a list of numbersdef list = [101, 334, 1208, 20]

Of course, the list can be of strings as well:

// Define a list of stringsdef names = ['Steve','Paul','Jane','Josie']

If needed, the list can contain objects of any type, including a heterogeneous set of object types, for examplea mix of strings and numbers.

To refer to a specific element in the list, use the square brackets with an integer argument like this.

// Store the third name in the list in a variabledef thirdName = names[2] // zero based index!

Remember that the list is zero-based so list[0] is the first element of the list and list[5] is the sixelement. Of course you can also pass a variable as the value of the operand like this:

14

Oracle Fusion CRM Application Composer Scripting Guide

Page 15: Scripting Guide

for (j in 2..3) { def curName = names[j] // do something with curName value here}

To update a specific list item's value, you can use the combination of the subscript and the assignmentoperator:

names[2] = 'John'

To add an entry to the end of the list, use the add() method:

names.add('Ringo')

A list can contain duplicates, so if you write code like the following, then the string Ringo will be addedtwice to the list:

// This will add 'Ringo' twice to the list!names.add('Ringo')names.add('Ringo')

To test if an entry already exists in the list, use the contains() function. This way, you can ensure thatyou don't add the same item twice if duplicates are not desirable for your purposes:

// The exclamation point is the "not" operator, so this// first checks if the 'names' list does NOT contain 'Ringo' before// adding it to the listif (!names.contains('Ringo')) { names.add('Ringo')}

To remove an entry from the list, use the remove() method.

names.remove('Ringo')

Note that this only removes the first occurrence of the item in the list, returning a boolean result indicatingtrue if the desired item was found and removed. Therefore, if your list allows duplicates and you need toremove them all, you'll need to write a loop to call remove() until it returns false.

You can iterate over the entries in a list using the for...in loop like this:

// Process each name in the list, returning// false if any restricted name is encounteredfor (name in names) { // call a custom global function for each name processed if (adf.util.isNameRestricted(name)) { return false }}return true

You can define an empty list using the square-bracket notation with nothing inside like this:

def foundElements = [] // empty list!

Working with Maps

A map is an unordered collection of name/value pairs. The name in each name/value pair is called themap's key for that entry since it is the key to looking up the value in the map later. You can create a mapusing Groovy's square-bracket notation, using a colon to separate each key and value, and a comma betweeneach key/value pair like this:

15

Oracle Fusion CRM Application Composer Scripting Guide

Page 16: Scripting Guide

// Define a map of name/value pairs that associate// a status value (e.g. "Open", "Closed", "Pending") with a// maximum number of daysdef maxDaysByStatus = [Open:30, Closed:90, Pending:45]

Notice that by default, the map key is assumed to be a string so you don't need to include the key valuesin quotes. However, if any key value contains spaces you will need to use quotes around it like this:

def maxDaysByStatus = [Open:30, Closed:90, Pending:45, 'On Backorder':10]

If you want to use another type as the map key, you need to surround the key with parentheses. Considerthe following example without the parentheses:

def x = 1def y = 2def xvalue = 'One'def yvalue = 'Two'// this creates a map with entries ('x'->'One') and ('y'->'Two')def m = [x:xvalue,y:yvalue]

The above example creates a map with key values of the strings x and y, rather than using the value of thevariable x and the value of the variable y as map keys. To obtain this effect, surround the key expressionswith parentheses like this:

def x = 1def y = 2def xvalue = 'One'def yvalue = 'Two'// this creates a map with entries (1->'One') and (2->'Two')def m = [(x):xvalue,(y):yvalue]

This creates a map with key values of the numbers 1 and 2.

To reference the value of a map entry, use dot notation like this, using the may key value as if it were afield name on the map object:

def closedDayLimit = maxDaysByStatus.Closed

If the key value contains a literal dot character or contains spaces or special characters, you can also usethe square-bracket notation, passing the key value as the operand:

def onBackorderDayLimit = maxDaysByStatus['On Backorder']

This square bracket notation is also handy if the key value is coming from the value of a variable insteadof a literal string, for example:

// Loop over a list of statuses to processfor (curStatus in ['Open','On Backorder']) { def limitForCurStatus = maxDaysByStatus[curStatus] // do something here with the current status' limit}

To add an new key/value pair to the map, use the put() method:

// Add an additional status to the mapmaxDaysByStatus.put('Ringo')

A map cannot contain duplicate key entries, so if you use put() to put the value of an existing element,the existing value for that key is overwritten. You can use the containsKey() function to test whether ornot a particular map entry already exists with a given key value, or you can use the containsValue()function to test if any map entry exists that has a given value — there might be zero, one, or multipleentries!

16

Oracle Fusion CRM Application Composer Scripting Guide

Page 17: Scripting Guide

// Test whether a map key matching the value of the// curKey variable exists or notif (maxDaysByStatus.containsKey(curKey)) { def dayLimit = maxDaysByStatus[curKey] // do something with dayLimit here}else { println("Unexpected error: key ${curKey} not found in maxDaysByStatusMap!")}

To remove an entry from the map, use the remove() method. It returns the value that was previously asso-ciated with the key passed in, otherwise it returns null if it did not find the given key value in the map toremove.

maxDaysByStatus.remove('On Backorder')

You can define an empty map using the square-bracket notation with only a colon inside like this:

def foundItemCounts = [:] // empty map!

Working with Ranges

Using ranges, you can conveniently creates lists of sequential values. If you need to work with a list ofintegers from 1 to 100, rather than creating a list with 100 literal numbers in it, you can use the .. operatorto create a range like this:

def indexes = 1..100

The range is particularly useful in performing iterations over a list of items in combination with a for looplike this:

def indexes = 1..100for (j in indexes) { // do something with j here}

Of course, you need not assign the range to a variable to use it in a loop, you can use it inline like this:

for (j in 1..100) { // do something with j here}

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 yourapplication.

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

When you need a calculated field or a transient value-holder field with an optional initial, calculated value,use a formula field.

Read-Only Calculated Fields

A formula field defaults to being a read-only, calculated value. It displays the value resulting from theruntime evaluation of the calculation expression you supply. By using the Depends On multi-select list inthe field create or edit page, you can configure the names of fields on which your expression depends. Bydoing this, its calculated value will update dynamically when any of those Depends On fields' value changes.

17

Oracle Fusion CRM Application Composer Scripting Guide

Page 18: Scripting Guide

The expected return type of the formula field's expression must be compatible with the formula field typeyou specified (Number, Date, or Text).

For example, consider a custom TroubleTicket object. If you add a formula field named DaysOpen, youcan provide its calculated value with the expression:

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

Transient Value Holder Fields with Optional Calculated Initial Value

If you want to allow the end user to override the calculated value, then mark your formula to be updateable.An updateable formula field is a "transient value holder" whose expression provides the value of the fielduntil the user overrides it. If the user overrides the value, the object remembers this user-entered value forthe duration of the current transaction so that your validation rules and triggers can reference it. If youhave configured one or more Depends On fields for your updateable formula field, then note that the valueof the formula will revert back to the calculated value should any of the dependent fields' value change. Ifyou want a transient field whose initial value is null until the user fills it in, simply provide no formulaexpression for your updateable formula field to achieve this.

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 configurea default value for it. You can supply a literal default value of appropriate type or supply an expression tocalculate the default value for new rows. The default value expression is evaluated at the time the new rowis created. The expected return type of your field's default value expression must be compatible with thefield's type (Number, Date, Text, etc.)

For example, consider a custom CallbackDate field in a TroubleTicket object. If you want the callbackback for a new trouble ticket to default to 3 days after it was created, then you can provide a default expres-sion of:

CreationDate + 3

Providing an Expression to Make a Custom Field Conditionally Update-able

A custom field can be updateable or read-only. By default, any non-formula field is updateable. Alternatively,you can configure a conditionally updateable expression. If you do this, it is evaluated each time a pagedisplaying the field is rendered or refreshed. The expected return type the expression is boolean. If youdefine one for a field, you must also configure the Depends On list to indicate the names of any fields onwhich your conditionally updateable expression depends. By doing this, your conditionally updateablefield will interactively enable or disable as appropriate when the user changes the values of fields on whichthe conditionally updateable expression depends.

For example, consider a custom TroubleTicket object with Status and Justification fields. Assumeyou want to prevent a user from editing the justification of a closed trouble ticket. To achieve this, configurethe conditionally updateable expression for the Justification field as follows:

Status_c != 'Closed'

After configuring this expression, you must then indicate that the Justification field depends on theStatus field as described in the section called “Understanding When to Configure Field Dependencies”.This ensures that if a trouble ticket is closed during the current transaction, or if a closed trouble ticket isreopened, that the Justification field becomes enable or disabled as appropriate.

18

Oracle Fusion CRM Application Composer Scripting Guide

Page 19: Scripting Guide

Tip

A field configured with a conditionally updateable expression only enforces the conditional up-dateability through the web user interface in this release. See the section called “Enforcing Con-ditional Updateability of Custom Fields for Web Service Access” for more information on howto ensure it gets enforced for web service access as well.

Providing an Expression to Make a Custom Field Conditionally Required

A custom field can be optional or required. By default it is optional. Alternatively, you can configure aconditionally required expression. If you do this, it is evaluated each time a page displaying the field isrendered or refreshed, as well as when the object is validated. The expected return type of the expressionboolean. If you define one for a field, you must also configure the Depends On list to indicate the namesof any fields on which your conditionally required expression depends. By doing this, your conditionallyrequired field will interactively show or hide the visual indicator of the field's being required as appropriatewhen the user changes the values of fields on which the conditionally required expression depends.

For example, consider a custom TroubleTicket object with Priority and Justification fields. Assumethat priority is an integer from 1 to 5 with priority 1 being the most critical kind of problem to resolve. Toenforce that a justification is required for trouble tickets whose priority is 1 or 2, configure the conditionallyrequired expression for the Justification field as follows:

Priority_c <= 2

After configuring this expression, you must then indicate that the Justification field depends on thePriority field as described in the section called “Understanding When to Configure Field Dependencies”.This ensures that if a trouble ticket is created with priority 2, or an existing trouble ticket is updated to in-crease the priority from 3 to 2, that the Justification field becomes mandatory.

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 evaluatedwhenever the corresponding field's value is set. When the rule executes, the field's value has not been as-signed 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 returntrue. If the rule returns false, then this prevents the field assignment from occurring, the invalid field isvisually highlighted in the UI, and the configured error message is displayed to the end user. Since the as-signment 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 tryagain. Your script can use the newValue keyword to reference the new value that will be assigned if valid-ation passes. To reference the existing field value, use the oldValue keyword. A field-level rule is appro-priate when the rule to enforce only depends on the new value being set. As shown in Figure 2, “ExpressionPalette Showing the Keywords Tab”, you can use the Keywords tab of the Expression Palette to insert thenewValue and oldValue keywords.

19

Oracle Fusion CRM Application Composer Scripting Guide

Page 20: Scripting Guide

Figure 2. Expression Palette Showing the Keywords Tab

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

Field Name Priority

Rule Name Validate_Priority_Range

Rule Body newValue == null || (1..5).contains(newValue as Integer)

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

Tip

If a validation rule for field A depends on the values of one or more other fields (e.g. Y andZ ), then create an object-level rule and programmatically signal which field or fields should behighlighted as invalid to the user as explained in the section called “Setting Invalid Fields for theUI 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 standard or custom object. It is evaluatedwhenever the framework attempts to validate the object. This can occur upon submitting changes in a webform, when navigating from one row to another, as well as when changes to an object are saved. Use object-level rules to enforce conditions that depend on two or more fields in the object. This ensures that regardlessof the order in which the user assigns the values, the rule will be consistently enforced. The expression (orlonger script) you write must return a boolean value that indicates whether the object is valid. If the rulereturns true, then the object validation will succeed so long as all other object-level rules on the sameobject return true. If the rule returns false, then this prevents the object from being saved, and the con-figured error message is displayed to the end user.

For example, consider a TroubleTicket object with Priority and AssignedTo fields, where the latter is adynamic choice list field referencing Contact objects whose Type field is a 'Staff Member'. To validatethat a trouble ticket of priority 1 or 2 cannot be saved without being assigned to a staff member, your object-level rule would look like this:

20

Oracle Fusion CRM Application Composer Scripting Guide

Page 21: Scripting Guide

Rule Name Validate_High_Priority_Ticket_Has_Owner

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

Error Message A trouble ticket of priority 1 or 2 must have a staff member assigned

to it

Defining Utility Code in a Global Function

Global functions are useful for code that multiple objects want to share. To call a global function, prefacethe function name with the adf.util. prefix. When defining a function, you specify a return value andcan 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.

Tip

A global function has no current object context. To write global functions that work on a particularobject, refer to the section called “Passing the Current Object to a Global Function”.

For example, you could create the following two global functions to define standard helper routines to logthe start of a block of groovy script and to log a diagnostic message. Examples later in this document willmake use of them.

Function Name logStart

Return Type void

21

Oracle Fusion CRM Application Composer Scripting Guide

Page 22: Scripting Guide

Parameters TypeName

StringscriptName

Function Definition // Log the name of the scriptprintln("[In: ${scriptName}]")

Function Name log

Return Type void

Parameters TypeName

Stringmessage

Function Definition // Log the message, could add other infoprintln(message)

Defining Reusable Behavior with an Object Function

Object functions are useful for code that encapsulates business logic specific to a given object. You cancall object functions by name from any other script code related to the same object. In addition, you caninvoke them using a button or link in the user interface. The supported return types and optional parametertypes are the same as for global functions (described above).

For example, you might define the following updateOpenTroubleTicketCount() object function on aContact custom object. It begins by calling the logStart() global function above to log a diagnosticmessage in a standard format to signal the beginning of a block of custom Groovy script. It calls thenewView() built-in function (described in the section called “Accessing the View Object for ProgrammaticAccess to Business Objects” ) to access the view object for programmatic access of trouble tickets, thencalls another global function applyFilter() (described in the section called “Simplifying Most CommonView Criteria Creation with a Global Function” ) to apply a filter to find trouble tickets related to the currentcontact's id and having either 'Working' or 'Waiting' as their current status. Finally, it calls getEstimated-RowCount() to retrieve the count of trouble tickets that qualify for the filter criteria.

Function Name updateOpenTroubleTicketCount

Return Type void

Parameters None

Function Definition adf.util.logStart('updateOpenTroubleTicketCount')// Access the view object for TroubleTicket programmatic accessdef tickets = newView('TroubleTicket_c')// Use a global function to apply a query filter to Trouble Ticket// view object where Contact_Id = Id of current Contact and // Status is either 'Working' or 'Waiting'adf.util.applyFilter(tickets,[Status_c:['Working','Waiting'], Contact_Id__c:Id])// Update OpenTroubleTickets field valuesetAttribute('OpenTroubleTickets_c',tickets.getEstimatedRowCount())

22

Oracle Fusion CRM Application Composer Scripting Guide

Page 23: Scripting Guide

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 customobject. You can define triggers both at the object-level and the field-level. The object-level triggers thatare available are described below. See the section called “Defining a Field-Level Trigger to React to ValueChanges” for the available field-level triggers.

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

Before Modify Fires when the end-user first modifies a persistent field in an exist-ing, queried row

Before Invalidate Fires on an existing object when its first persistent field is modified.Fires on a valid parent object when a child row is created, removed,or modified.

Before Remove Fires when an attempt is made to delete an object. Returning falsestops the row from being deleted and displays the optional triggererror message.

Before Insert in Database Fires before a new object is inserted into the database.

After Insert in Database Fires after a new object is inserted into the database

Before Update in Database Fires before an existing object is modified in the database

After Update in Database Fires after an existing object is modified in the database

Before Delete in Database Fires before an existing object is deleted from the database

After Delete in Database Fires after an existing object is deleted from the database

After Changes Posted to Database Fires after all changes have been posted to the database, but beforethey are permanently committed. Can be used to make additionalchanges that will be saved as part of the current transaction.

Before Commit in Database Fires before the change pending for the current object (insert, update,delete) is made permanent in the current transaction. Any changesmade in this trigger will not be part of the current transaction. Use"After Changed Posted to Database" trigger if your trigger needs tomake changes.

After Commit in Database Fires after the change pending for the current object (insert, update,delete) is made permanent in the current transaction.

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

After Rollback in Database Fires after 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 anytime a trouble ticket is created or modified. You can create the following two triggers on theTroubleTicket object which both invoke the updateOpenTroubleTicketCount() object function describedabove.

23

Oracle Fusion CRM Application Composer Scripting Guide

Page 24: Scripting Guide

Trigger Object TroubleTicket

Trigger After Insert In Database

Trigger Name After_Insert_Set_Open_Trouble_Tickets

Trigger Definition adf.util.logStart('After_Insert_Set_Open_Trouble_Tickets')// Get the related contact for this trouble ticketdef relatedContact = Contact_Obj_c// Update its OpenTroubleTickets field valuerelatedContact?.updateOpenTroubleTicketCount()

Trigger Object TroubleTicket

Trigger After Update In Database

Trigger Name After_Update_Set_Open_Trouble_Tickets

Trigger Definition // Get the related contact for this trouble ticketdef relatedContact = Contact_Obj_c// Update its OpenTroubleTickets field valuerelatedContact?.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 standardor custom field. The following field-level trigger is available:

After Field Changed Fires when the value of the related field has changed (implying thatit 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 changesvalue. Do not use a field-level validation rule to achieve this purpose because while your field-level valid-ation rule may succeed, other field-level validation rules may fail and stop the field's value from actuallybeing changed. Since generally you only want your field-change derivation logic to run when the field'svalue actually changes, the After Field Changed trigger guarantees that you get this desired behavior.

See the section called “Deriving a Non-Formula Field Using a Validation Rule” for tips on using thistrigger.

Groovy Tips and Techniques

This section provides a compendium of tips and techniques for getting the most out of Groovy in yourapplication.

Simplifying Code Authoring with the Expression Palette

Before diving into the specifics of each example, keep in mind that the CRM Application Composer alwaysmakes an Expression Palette available to assist you. It helps you insert the names of built-in functions,object fields, or function names.

24

Oracle Fusion CRM Application Composer Scripting Guide

Page 25: Scripting Guide

The Expression Editor Toolbar

Typically the expression editor opens with the expression palette collapsed to give you more space to yourscript. In this minimized, hidden state, as shown in Figure 3, “Expression Palette Toolbar”, you have anumber of useful toolbar buttons that can assist you to :

• insert one of the common comparison operators like equals (==), not equals (!=), less than (<), greaterthan (>), etc.

• erase the code editor to start over with a clean slate

• validate the correct syntax of your script, or

• detach the code editor into a floating window to give you even more space to see your script.

Figure 3. Expression Palette Toolbar

Showing the Expression Palette

To expand the amount of assistance available to you, you can show the expression palette. As shown inFigure 4, “Expand the Expression Palette to Show It”, click the right-pointing arrow to the left of the ShowPalette label to expand the palette, revealing its three tab display.

Figure 4. Expand the Expression Palette to Show It

Inserting an Expression for Fields of Related Objects

As shown in Figure 5, “Steps to Insert a Field Name Using Expression Palette's Fields Tab”, the Fieldstab of the palette shows you the current object at the root of the tree, and allows you to explore the relatedobjects and collections of related objects by expanding the tree nodes. If the selected node has the single-

row icon then it represents a single, related object. If the selected node has the multiple-row icon then it represents a collection of related objects. For example, in the figure the tree shows Activities withthe multiple-row icon, so this represents the collection of Activity objects related to the current troubleticket. The Contact node has a single-row icon so it represents a single Contact object that is the customerwho reported the trouble ticket. The Assigned To node represents the staff member to whom the troubleticket has been assigned, and lastly, the Manager node represents the manager of that staff member.

As shown in the numbered steps in the figure, to insert the name of a field, do the following:

25

Oracle Fusion CRM Application Composer Scripting Guide

Page 26: Scripting Guide

1. Select the object you want to work with, for example the Manager of the staff member Assigned Tothis Trouble Ticket

2. In the fields table, select the field you want to insert, for example Contact Name

3. Click the Insert button to insert the field's name.

Figure 5. Steps to Insert a Field Name Using Expression Palette's Fields Tab

If the field on the selected object is directly accessible from the current object through "dot" navigation,then the expression palette will insert the expression for you. For example, if you click Insert after selectingthe Contact Name field of the Manager object related to the Assigned To staff member assigned to thecurrent Trouble Ticket, then the expression inserted will be:

AssignedTo_c?.Manager_c?.RecordName

Inserting Field Name of Object in Related Collection

In contrast, as shown in Figure 6, “Selecting a Field from an Object in a Related Collection”, if you wereto select the Activities collection and then select the Activity Status field from the object in that collection,when you click Insert only the API name ActivityStatus_c of the selected field will be inserted. Thereason a complete expression cannot be inserted in this case is due to the fact that you cannot use "dot"notation to work with a specific row in a collection in order to access its field value. You need to useGroovy code to access the collection of rows and then work with one row at a time.

26

Oracle Fusion CRM Application Composer Scripting Guide

Page 27: Scripting Guide

Figure 6. Selecting a Field from an Object in a Related Collection

Inserting a Related Object Accessor or Related Collection Accessor Field Name

As shown in Figure 7, “Selecting a Field from an Object in a Related Collection”, to insert the name of anRelated Object or Related Collection field, select the object and field in the express ion palette, and clickInsert as you do with any other type of field. For example, to insert the API name of the collection ofActivity objects related to a TroubleTicket, do the following:

1. Select the Trouble Ticket node in the tree (representing the current object in this example)

2. Select the desired Related Collection or Related Object field in the table at the right, for example, theActivityCollection_c field.

3. Click the Insert button.

Figure 7. Selecting a Field from an Object in a Related Collection

Once you have inserted the name of the related collection field, you can write a loop to process each rowin the collection like this:

27

Oracle Fusion CRM Application Composer Scripting Guide

Page 28: Scripting Guide

def activities = ActivityCollection_cwhile (activities.hasNext()) { def activity = activities.next() // if the activity status is 'Open'... if (activity.Status_c == 'Open') { // do something here to the current child activity }}

When writing the code in the loop that processes Activity rows, you can use the Expression Palette againto insert field names of the object in that collection by selecting the Activities collection in the tree,choosing the desired field, then clicking Insert.

Hiding the Expression Palette

When you're done using the Expression Palette, as shown in Figure 8, “Collapsing the Expression Paletteto Hide It”, click the down-pointing arrow to the left of the Hide Palette label to collapse the palette.

Figure 8. Collapsing the Expression Palette to Hide It

Using the Related Object Accessor Field to Work with a Parent Object

When writing business logic in a child object like Activity, you can access its owning parentTroubleTicket object using the related object accessor field. If the parent object is named TroubleTicket,the related object accessor field in Activity will be named TroubleTicket_c. Example 1, “Accessing aField from Parent Object” shows how to reference the parent TroubleTicket object and access one of itsfields.

Example 1. Accessing a Field from Parent Object

// Assume code in context of Activityif (TroubleTicket_c.Status_c == 'Open') { // Do something here because the owning parent // trouble ticket's status is open}

Notice that since the child object cannot exist without an owning parent object, the reference to the parentobject will never be null, so here instead of the Groovy safe navigation operator (?.) we can just use thenormal dot operator in the expression TroubleTicket_c.Status_c.

If you will be referencing multiple fields from the parent, or calling any of its object functions, then it isbest practice to store the parent object in a local variable as shown in Example 2, “Storing Parent Objectin Local Variable Before Multiple References”.

Example 2. Storing Parent Object in Local Variable Before Multiple References

// Store the parent object in a local variabledef ticket = TroubleTicket_c// Now reference one or more fields from the parentif (ticket.Status_c == 'Working' && ticket.Priority_c >= 2) { // Do something here because the owning parent // trouble ticket is high priority and being worked on.}

28

Oracle Fusion CRM Application Composer Scripting Guide

Page 29: Scripting Guide

Using the Related Object Accessor Field to Work with a ReferencedObject

When writing business logic for an object like TroubleTicket that has a lookup field like Contact or As-signedTo, you can access the object referenced by the lookup field using the respective lookup field'ssecondary related object accessor field. See the section called “Understanding Secondary Fields Relatedto a Lookup” for more information on this. For the Contact and AssignedTo lookup fields, the secondaryrelated object accessor fields are named Contact_Obj_c and AssignedTo_Obj_c, respectively. Example 3,“Accessing a Field from Two Lookup Objects” shows how to reference the two lookup objects from scriptwritten in the context of TroubleTicket, and access one of each's fields.

Example 3. Accessing a Field from Two Lookup Objects

// Assume code in context of TroubleTicketif (endsWith(Contact_Obj_c?.EmailAddress_c,'.gov') && startsWith(AssignedTo_Obj_c?.PhoneNumber_c,'202')) { // Do something here because contact's email address // is a government mail address and assigned-to staff member's // phone number is in the 202 area code for Washington DC.}

If you will be referencing multiple fields from the referenced lookup object, or calling any of its objectfunctions, then it is best practice to store the referenced lookup object in a local variable as shown in Ex-ample 4, “Storing Related Lookup Objects in Local Variable Before Multiple References”.

Example 4. Storing Related Lookup Objects in Local Variable Before MultipleReferences

// Store the contact object and assignedTo object in a local variabledef customer = Contact_Obj_cdef supportRep = AssignedTo_Obj_c// Now reference one or more fields from the parentif ((endsWith(customer?.EmailAddress_c,'.gov') || endsWith(customer?.EmailAddress_c,'.com') ) && (startsWith(supportRep?.PhoneNumber_c,'(202)')|| startsWith(supportRep?.PhoneNumber_c,'(206)') ) ) { // Do something here because contact's email address // is a government or business email and assigned-to // support rep is in 202 or 206 Washington DC area code}

Using the Related Collection Accessor Field to Work with Child Rows

When an parent object like TroubleTicket has a child object Activity, the parent object will have a relatedcollection accessor field whose name you decided when you created the child object. For example, if whencreating the child Activity object for the TroubleTicket parent, you decided to name the related collectionaccessor field ActivityCollection, then you can write business logic in the context of the parentTroubleTicket object that works with the one or more Activity child rows. To do this, your code accessesthe related collection accessor field by name like this:

// Assume code in context of TroubleTicket// define a variable to hold activities collectiondef activities = ActivityCollection_c // work with activities here...

29

Oracle Fusion CRM Application Composer Scripting Guide

Page 30: Scripting Guide

Tip

Always store a child collection you want to work with in a local variable. Failure to do this willresult in your code that does not behave as you expect.

The related collection accessor field returns a row iterator object, so you can use methods like those listedin Table 1, “Most Commonly Used RowIterator Methods” to work with the rows. The row iterator tracksthe current row in the collection that your code is working with.

Table 1. Most Commonly Used RowIterator Methods

DescriptionMethod Name

Returns: - true if the row iterator has more rows toiterate over, false if there are no rows in the iterat-

hasNext()

or's row set or if the iterator is already on or beyondthe last row.

Returns: - the next row in the row iteratornext()

Returns: - void. Resets the row iterator to the "slot"before the first row.

reset()

Returns: - the first row in the row iterator, or nullif the iterator's row set is empty

first()

Putting the commonly used row iterator methods from Table 1, “Most Commonly Used RowIteratorMethods” into practice, Example 5, “Processing Each Row in a Related Child Collection” shows the typ-ical code you will use to work with the child row iterator. This example accesses the child row iteratorusing the related collection field's API name, and saves it in a local variable. Then, it resets the iterator sothat it sits on the "slot" before the first row in the row iterator. Next, it uses a while loop in combinationwith the hasNext() method to iterate over each row in the row iterator.

Example 5. Processing Each Row in a Related Child Collection

// store the child row iterator in a local variabledef activities = ActivityCollection_c// ensure iterator is on slot before first rowactivities.reset()// loop while there are more rows to processwhile (activities.hasNext()) { // access the next row in the row iterator def curActivity = activities.next() // reference fields or object functions from the current row if (curActivity.Status_c == 'Open') { // do something here to the current child activity }}// to process the same row iterator again in this block of code, // call activities.reset() method again to reset the// iterator to the slot before the first row

To detect whether the child row iterator is empty or not, you can use the first() method. If it returns nullthen the row iterator's row set is empty. As shown in Example 6, “Testing Whether a Related Child RowIterator is Empty”, if you call the first() method and there are rows in the row iterator's row set, thismethod sets the iterator to point at the first row. So, if your script uses the first() method, then plans toiterate over all the rows in the iterator again using the typical while(rowiterator.hasNext()) idiom, youneed to call the reset() method on the row iterator to move the current row pointer back to the slot beforethe first row. Failure to do this could result in inadvertently not processing the first row in the row set.

30

Oracle Fusion CRM Application Composer Scripting Guide

Page 31: Scripting Guide

Example 6. Testing Whether a Related Child Row Iterator is Empty

def activities = ActivityCollection_c// If there are no child activities...if (activities.first() == null) { // Do something here because there are no child activities}else { // There are some child activities, call reset() to set // iterator back to slot before first row activities.reset() while (activities.hasNext()) { def curActivity = activities.next(); // Do something here with the current activity }}

Accessing Current Date and Time from the Application Server

To reference the application server's current date in any groovy expression, use:

adf.currentDate

To reference the application server's current date including the current time, use the expression:

adf.currentDateTime

Note

This function cannot currently be referenced inside code for a Global Function. An object functioncan reference it and pass the value, if necessary, into the global function.

Accessing Current Date and Time from the Database

To reference the database's current date in any groovy expression, use:

adf.currentDBDate

To reference the application server's current date including the current time, use the expression:

adf.currentDBDateTime

Note

This function cannot currently be referenced inside code for a Global Function. An object functioncan reference it and pass the value, if necessary, into the global function.

Understanding ADF's Additional Built-in Groovy Functions

Oracle ADF adds a number of additional helper functions that you can use in your Groovy scripts. Thissection defines each one and provides a simple example of its use. As shown in Figure 9, “ExpressionPalette Showing the Functions Tab”, you can use the Functions tab of the Expression Palette to insert anyof the built-in functions and get a quick example of their use.

31

Oracle Fusion CRM Application Composer Scripting Guide

Page 32: Scripting Guide

Figure 9. Expression Palette Showing the Functions Tab

Table 2. Built-in Date Functions

DescriptionFunction

Returns: the current date, with no timetoday()

Return Type: Date

The current date and timenow()

Return Type: Timestamp

Returns: a date, given the year, month, and daydate(year,month,day)

Return Type: Date

Parameters:

• year - a positive integer

• month - a positive integer between 1 and 12

• day - a positive integer between 1 and 31

Example: to return a date for February 8th, 1998,use date(1998,2,8)

Returns: a timestamp, given the year, month, day,hour, minute, and second

dateTime(year,month,day,hour,minute,second)

Return Type: Date

Parameters:

• year - a positive integer

• month - a positive integer between 1 and 12

• day - a positive integer between 1 and 31

32

Oracle Fusion CRM Application Composer Scripting Guide

Page 33: Scripting Guide

DescriptionFunction

• hour - a positive integer between 0 and 23

• minute - a positive integer between 0 and 59

• second - a positive integer between 0 and 59

Example: to return a timestamp for February 8th,1998, at 23:42:01, use date(1998,2,8,23,42,1)

Returns: the year of a given dateyear(date)

Return Type: Integer

Parameters:

• date - date

Example: if curDate represents April 19th, 1996,then year(curDate) returns 1996.

Returns: the month of a given datemonth(date)

Return Type: Integer

Parameters:

• date - a date

Example: if curDate represents April 12th, 1962,then month(curDate) returns 4.

Returns: the day for a given dateday(date)

Return Type: Integer

Parameters:

• date - a date

Example: if curDate represents July 15th, 1968,then day(curDate) returns 15.

Table 3. Built-in String Functions

DescriptionFunction

Returns: true, if string s1 contains string s2, falseotherwise

contains(s1,s2)

Return Type: boolean

Parameters:

• s1 - a string to search in

• s2 - a string to search for

33

Oracle Fusion CRM Application Composer Scripting Guide

Page 34: Scripting Guide

DescriptionFunction

Example: if twitterName holds the value @steve,then contains(twitterName,'@') returns true.

Returns: true, if string s1 ends with string s2,false otherwise

endsWith(s1,s2)

Return Type: boolean

Parameters:

• s1 - a string to search in

• s2 - a string to search for

For example, if twitterName holds the value @steve,then endsWith(twitterName,'@') returns false.

Returns: the integer position of the first characterin string s1 where string s2 is found, or zero (0) ifthe string is not found

find(s1,s2)

Return Type: Integer

Parameters:

• s1 - a string to search in

• s2 - a string to search for

Example: if twitterName holds the value @steve,then find(twitterName,'@') returns 1 andfind(twitterName,'ev') returns 4.

Returns: the first len characters of the string sleft(s,len)

Return Type: String

Parameters:

• s - a string

• len - an integer number of characters to return

Example: if postcode holds the value 94549-5114,then left(postcode,5) returns 94549.

Returns: the length of string slength(s)

Return Type: Integer

Parameters:

• s - a string

Example: if name holds the value Julian Croissant,then len(name) returns 16.

34

Oracle Fusion CRM Application Composer Scripting Guide

Page 35: Scripting Guide

DescriptionFunction

Returns: the string s with any uppercase lettersconverted to lowercase

lowerCase(s)

Return Type: String

Parameters:

• s - a string

Example: if sku holds the value 12345-10-WHT-XS, then lowerCase(sku) returns 12345-10-wht-xs.

Returns: the last len characters of the string sright(s,len)

Return Type: String

Parameters:

• s - a string

• len - an integer number of characters to return

Example: if sku holds the value 12345-10-WHT-XS, then right(sku,2) returns XS.

Returns: true, if string s1 starts with s2, falseotherwise

startsWith(s1,s2)

Return Type: boolean

Parameters:

• s1 - a string to search in

• s2 - a string to search for

Example: if twitterName holds the value @steve,then startsWith(twitterName,'@') returns true.

Returns: the substring of s1 that precedes the firstoccurrence of s2, otherwise an empty string

substringBefore(s1, s2)

Return Type: String

Parameters:

• s1 - a string to search in

• s2 - a string to search for

Examples: if sku holds the value 12345-10-WHT-XS, then substringBefore(sku,'-') returns thevalue 12345, substringBefore(sku,'12345') re-turns an empty string, and substringBe-

fore(sku,'16-BLK') also returns an empty string.

Returns: the substring of s1 that follows the firstoccurrence of s2. otherwise an empty string

substringAfter(s1,s2)

35

Oracle Fusion CRM Application Composer Scripting Guide

Page 36: Scripting Guide

DescriptionFunction

Return Type: String

Parameters:

• s1 - a string to search in

• s2 - a string to search for

Example: if sku holds the value 12345-10-WHT-XS, then substringAfter(sku,'-') returns the value10-WHT-XS, substringAfter(sku,'WHT-') returnsthe value XS, substringAfter(sku,'XS') returnsan empty string, and substringAfter(sku,'BLK')also returns an empty string.

Returns: the string s with any lowercase lettersconverted to uppercase

upperCase(s)

Return Type: String

Parameters:

• s - a string

Example: if sku holds the value 12345-10-Wht-xs,then upperCase(sku) returns 12345-10-WHT-XS.

Table 4. Other Built-in Functions

DescriptionFunction

Returns: a ViewObject reserved for programmaticuse, or null if not available.

newView(objectAPIName)

Return Type: ViewObject

Parameters:

• objectAPIName - the object API name whose rowsyou want to find, create, update, or remove

Example: if TroubleTicket is a custom object, thennewView('TroubleTicket_c') returns a new viewobject instance you can use to find, create, update,or delete TroubleTicket rows.

Tip

For important information about using thenewView() function correctly, see the sec-tion called “Accessing the View Object forProgrammatic Access to Business Objects”.

Returns: a multi-valued key object for use in theViewObject's findByKey() method.

key(list)

36

Oracle Fusion CRM Application Composer Scripting Guide

Page 37: Scripting Guide

DescriptionFunction

Return Type: Key

Parameters:

• list - a list of values for a multi-field key

Example: if a standard object has a two-field key,use key([101,'SAMBA'])

Returns: a key object for use in the ViewObject'sfindByKey() method.

key(val)

Return Type: Key

Parameters:

• val - a value to use as the key field

Example: if a standard object has a single-field key,as all custom objects do, use key(123456789)

Returns: the object o1 if it is not null, otherwise theobject o2.

nvl(o1,o2)

Return Type: Object

Parameters:

• o1 - a value to use if not null

• o2 - a value to use instead if o1 is null

Example: to calculate the sum of Salary and Com-mission custom fields that might be null, usenvl(Salary_c,0) + nvl(Commission_c,0)

Understanding Groovy's Null-Safe Comparison Operators

It's important to know that Groovy's comparison operators == and != handle nulls gracefully so you don'thave to worry about protecting null values in equality or inequality comparisons. Furthermore, the >, >=,<, and <= operators are also designed to avoid null-related exceptions, however you need to be consciousof how Groovy treats null in these order-dependent comparisons. Effectively, a null value is "less than"any other non-null value in the natural ordering, so for example observe the following comparison results.

Table 5. Examples of How null Is Less Than Everything

Comparison ResultRight-Side ExpressionOperatorLeft-Side Expression

truenull>'a'

falsenull<'a'

truenull>100

falsenull<100

truenull>-100

falsenull<-100

37

Oracle Fusion CRM Application Composer Scripting Guide

Page 38: Scripting Guide

Comparison ResultRight-Side ExpressionOperatorLeft-Side Expression

truenull>now()

falsenull<now()

truenull>now() - 7

falsenull<now() - 7

If you want a comparison to treat a null-valued field with different semantics — for example, treating anull MaximumOverdraftAmount field as if it were zero (0) like a spreadsheet user might expect — then usethe nvl() function as part of your comparison logic as shown in the following example:

// Change default comparison semantics for the MaximumOverdraftAmount custom field in// case its value is null by using nvl() to treat null like zero (0)if (nvl(MaximumOverdraftAmount_c,0) < -2000) { // do something for suspiciously large overdraft amount}

As illustrated by the table above, without the nvl() function in the comparison any MaximumOverdraf-tAmount_c value of null would always be less than -2000 — since by default null is less than everything.

Testing Whether an Field's Value Is Changed

You can test whether an field's value has changed in the current transaction by using the built-in isAttrib-uteChanged() function. As shown in this example, it takes a single string argument that provides the nameof the field whose changed status you want to evaluate:

if (isAttributeChanged('Status_c')) { // perform some logic here in light of the fact // that status has changed in this transaction}

Avoiding Validation Threshold Errors By Conditionally Assigning Values

When you write scripts for validation rules or triggers that modify the values of fields in the current object,you must be aware of how this affects the object's so-called "validation cycle". Before allowing an objectto be saved to the database, the Oracle ADF framework ensures that its data passes all validation rules.The act of successfully running all defined validation rules results in the object's being marked as validand allows the object to be saved along with all other valid objects that have been modified in the currenttransaction. If as part of executing a validation rule or trigger your script modifies the value of a field, thismarks the object "dirty" again. This results in ADF's subjecting the object again to all of the defined valid-ation rules to ensure that your new changes do not result in an invalid object. If the act of re-validating theobject runs your scripts that modify the field values again, this process could result in a cycle that wouldappear to be an infinite loop. ADF avoids this possibility by imposing a limit of 10 validation cycles onany given object. If after 10 attempts at running all the rules the object still has not been able to be success-fully validated due to the object's being continually modified by its validation or trigger logic, ADF willthrow an exception complaining that you have exceeded the validation threshold.

A simple way to avoid this from happening is to test the value of the field your script is about to assignand ensure that you perform the setAttribute() call to modify its value only if the value you intend toassign is different from its current value. An example script employing this approach would look like this:

// "before insert in database" trigger on a PurchaseOrder object// to derive the default purchasing rep based on a custom// algorithm defined in an object function named // determinePurchasingRep()def defaultRep = determinePurchasingRep()// If the current purchasing rep is not the default, assign it

38

Oracle Fusion CRM Application Composer Scripting Guide

Page 39: Scripting Guide

if (PurchasingRep_c != defaultRep) { setAttribute('PurchasingRep_c',defaultRep)}

Passing the Current Object to a Global Function

As you begin to recognize that you are writing repetive code in more than one object's functions, triggers,or validation rules, you can refactor that common code into a global function that accepts a parameter oftype Object and then have your various usages call the global function. Since a global function does nothave a "current object" context, you need to pass the current object as an argument to your global functionto provide it the context you want it to work with. When you define the global function, the Object typeis not shown in the dropdown list of most commonly used argument types, but you can type it in yourself.Note that the value is case-sensitive, so Object must have an initial capital letter "O".

When writing code in an object trigger, object function, or other object script, you can use the expressionadf.source to pass the current object to a global function that you invoke. the section called “ReferencingOriginal Values of Changed Fields” shows an example of putting these two techniques into practice.

Referencing Original Values of Changed Fields

When the value of a field gets changed during the current transaction, the ADF framework remembers theso-called "original value" of the field. This is the value it had when the existing object was retrieved fromthe database. Sometimes it can be useful to reference this original value as part of your business logic. Todo so, use the getOriginalAttributeValue() function as shown below (substituting your field's namefor the example's Priority_c):

// Assume we're in context of a TroubleTicketif (isAttributeChanged('Priority_c')) { def curPri = Priority_c def origPri = getOriginalAttributeValue("Priority_c") adf.util.log("Priority changed: ${origPri} -> ${curPri}") // do something with the curPri and origPri values here}

Raising a Warning From a Validation Rule Instead of an Error

When your validation rule returns false, it causes a validation error that stops normal processing. If insteadyou want to show the user a warning that does not prevent the data from being saved successfully, thenyour rule can signal a warning and then return true. For example, your validation rule would look likethis:

// if the discount is over 50%, give a warningif (Discount > 0.50) { // raise a warning using the default declarative error message adf.error.warn(null)}return true

Throwing a Custom Validation Exception

When defining object level validation rules or triggers, normally the declaratively-configured error messagewill be sufficient for your needs. When your validation rule returns false to signal that the validation hasfailed, the error message you've configured is automatically shown to the user. The same occurs for atrigger when it calls the adf.error.raise(null) function. If you have a number of different conditionsyou want to enforce, rather than writing one big, long block of code that enforces several distinct conditions,instead define a separate validation rule or trigger (as appropriate) for each one so that each separate checkcan have its own appropriate error message.

39

Oracle Fusion CRM Application Composer Scripting Guide

Page 40: Scripting Guide

That said, on occasion you may require writing business logic that does not make sense to separate intoindividual rules, and which needs to conditionally determine which among several possible error messagesto show to the user. In this case, you can throw a custom validation exception with an error string that youcompose on the fly using the following technique:

// Throw a custom object-level validation rule exception// The message can be any string valuethrow new oracle.jbo.ValidationException('Your custom message goes here')

Note that choose this approach, your error message is not translatable in the standard way, so it becomesyour responsibility to provide translated versions of the custom-thrown error messages. You could use asolution like the one presented in the section called “Returning Locale-Sensitive Custom Strings” for ac-complishing the job.

Returning Locale-Sensitive Custom Strings

When you throw custom validation error messages, if your end users are multi-lingual, you may need toworry about providing a locale-specific error message string. To accomplish this, you can reference theend user's current locale as part of global function that encapsulates all of your error strings. Consider agetMessage function like the one below. Once it is defined, your validation rule or trigger can throw alocale-sensitive error message by passing in the appropriate message key:

// context is trigger or object-level validation rulethrow new oracle.jbo.ValidationException(adf.util.getMessage('BIG_ERROR'))

The global function is defined as follows.

Function Name getMessage

Return Type String

Parameters TypeName

StringstringKey

Function Definition // Let "en" be the default lang// Get the language part of the locale// e.g. for locale "en_US" lang part is "en"def defaultLang = 'en';def userLocale = adf.context.getLocale() as Stringdef userLang = left(userLocale,2)def supportedLangs=['en','it']def lookupLang = supportedLangs.contains(userLang) ? userLang : defaultLangdef messages = [BIG_ERROR: [en:'A big error occurred', it:'È successo un grande errore'], SMALL_ERROR:[en:'A small error occurred', it:'È successo un piccolo errore']]return messages[stringKey][lookupLang]

Raising a Trigger's Optional Declaratively-Configured Error Message

In contrast with a validation rule where the declarative error message is mandatory, when you write atrigger it is optional. Since any return value from a trigger's script is ignored, the way to cause the optionalerror message to be shown to the user is by calling the adf.error.raise() method, passing null as thesingle argument to the function. This causes the default declarative error message to be shown to the user

40

Oracle Fusion CRM Application Composer Scripting Guide

Page 41: Scripting Guide

and stops the current transaction from being saved successfully. For example, you trigger would look likethis:

// Assume this is in a Before Insert triggerif (someComplexCalculation() == -1) { // raise an exception using the default declarative error message adf.error.raise(null)}

Accessing the View Object for Programmatic Access to Business Objects

A "view object" is an Oracle ADF component that simplifies querying and working with business objectrows. The newView() function allows you to access a view object dedicated to programmatic access for agiven business object. By default, any custom object you create is enabled to have such a view object, andselected standard objects will be so-enabled by the developers of the original application you are custom-izing. Each time the newView(objectAPIName) function is invoked for a given value of object API name,a new view object instance is created for its programmatic access. This new view object instance is in apredictable initial state. Typically, the first thing you will then do with this new view object instance is:

• Call the findByKey() function on the view object to find a row by key, or

• Use a view criteria to restrict the view object to only return some desired subset of business objects rowsthat meet your needs. Filtering the rows returned by the view object involves creating and configuringa new view criteria, appending this view criteria to the view object, and then executing the view objectas described in the section called “Finding Objects Using a View Criteria”.

A view object instance for programmatic access to a business object is guaranteed not to be used by anyapplication user interface pages. This means that any iteration you perform on the view object in yourscript will not inadvertently affect the current row seen in the user interface. That said, the end user willsee the results of any field values that you change, any new rows that you add, and any existing rows thatyou modify, presuming that they are presently on a page where said objects and fields are visible.

For example, suppose the user interface is displaying an employee along with the number and associatedname of the department in which she works. If a script that you write...

• uses newView() to obtain the view object for programmatic access for the Department object, then

• uses findByKey() to find the department whose id matches the current employee's department, and finally

• changes the name of the current employee's department

then this change should be reflected in the screen the next time it is updated. Once you've accessed theview object, in addition to your use of the built-in newViewCriteria() function, the most common methodsthat you will use on the view object are shown in Table 6, “Most Commonly Used View Object Methods”.

Table 6. Most Commonly Used View Object Methods

DescriptionMethod Name

Allows you to find a row by unique id.findByKey()

Returns: an array of rows having the given key,typically containing either zero or one row.

Parameters:

41

Oracle Fusion CRM Application Composer Scripting Guide

Page 42: Scripting Guide

DescriptionMethod Name

• key - a key object representing the unique identifi-er for the desired row

• maxRows - an integer representing the maximumnumber of rows to find (typically 1 is used)

Example: See the section called “Finding an Objectby Id”

Appends an additional view criteria query filter.appendViewCriteria()

Returns: - void.

Executes the view object's query with any currentlyapplied view criteria filters.

executeQuery()

Returns: - void.

Returns: - true if the row iterator has more rows toiterate over, false if there are no further rows in theiterator or it is already on or beyond the last row.

hasNext()

Returns: - the next row in the iteratornext()

Resets the view object's iterator to the "slot" beforethe first row.

reset()

Returns: - void.

Returns: - the first row in the row iterator, or nullif the iterator's row set is empty

first()

Creates a new row, automatically populating itssystem-generated Id primary key field.

createRow()

Returns: - the new row

Inserts a new row into the view object's set of rows.insertRow()

Returns: - void

Finding an Object by Id

To find an object by id, follow these steps:

1. Use the newView() function to obtain the view object for programmatic access for the business objectin question

2. Call findByKey(), passing in a key object that you construct using the key() function

The new object will be saved the next time you save your work as part of the current transaction. Example 7,“Finding an Object by Id” shows how the steps fit together in practice.

42

Oracle Fusion CRM Application Composer Scripting Guide

Page 43: Scripting Guide

Example 7. Finding an Object by Id

// Access the view object for the custom TroubleTicket objectdef vo = newView('TroubleTicket_c')def foundRows = vo.findByKey(key(100000000272002),1)def found = foundRows.size() == 1 ? foundRows[0] : null;if (found != null) { // Do something here with the found row}

To simplify the code involved in this common operation, you could consider defining the following find-RowByKey() global helper function:

Function Name findRowByKey

Return Type Object

Parameters TypeName

Objectvo

ObjectidValue

Function Definition adf.util.logStart('findRowByKey')def found = vo.findByKey(key(idValue),1)return found.size() == 1 ? found[0] : null;

After defining this helper function, Example 8, “Finding an Object by Id Using Global Function” showsthe simplified code for finding a row by key.

Example 8. Finding an Object by Id Using Global Function

// Access the view object for the custom TroubleTicket objectdef vo = newView('TroubleTicket_c')def found = adf.util.findRowByKey(vo,100000000272002)if (found != null) { // Do something here with the found row}

Finding Objects Using a View Criteria

A "view criteria" is a collection of one or more "query-by-example" (QBE) rows that define a data filterfor a view object. Each QBE row in a view criteria is called a view criteria row. Each view criteria rowhas the same fields as the object you are filtering, and you assign values to the desired fields in the viewcriteria row to provide an example of what you want to find. At runtime, the Oracle ADF framework turnsthe view criteria into an appropriate SQL WHERE clause to pass your filter criteria to the database for execu-tion.

Overview of Basic Steps to Use a View Criteria

To find objects using a view criteria, perform the following steps:

1. Use the newView() function to obtain the view object for programmatic access for the business objectin question

2. Use the newViewCriteria() function to create a new view criteria on the view object

3. Create a new view criteria row by calling createRow() on the view criteria

43

Oracle Fusion CRM Application Composer Scripting Guide

Page 44: Scripting Guide

4. Define one or more view criteria items by doing the following for each one:

a. Create a new view criteria item by calling ensureCriteriaItem() on the view criteria row

b. Define the criteria operator on the view criteria item by calling setOperator() on the view criteriaitem

Tip

See Table 7, “View Criteria Item Operators (Case-Sensitive!)” in the section called “Under-standing View Criteria Item Operators” for a list of valid operators.

c. Set the value of the view criteria item operand by calling setValue() on the view criteria item

d. For text field criteria items, if the search should be case-insensitive, then call setUpperColumns(true)on the view criteria item

5. Add the new view criteria row by calling insertRow() on the view criteria

6. Append the view criteria by calling appendViewCriteria() on the view object

7. Execute the query by calling executeQuery()

Example 9, “Basic Example of Using View Criteria” demonstrates how all the pieces fit together in anexample for querying the TroubleTicket custom object to find the trouble tickets assigned to a particularstaff member and which have a status of Working.

Example 9. Basic Example of Using View Criteria

/* * Query all 'Working'-status trouble tickets assigned to a given staff member */def staffMemberId = 100000000089003 // 1. Use the newView() function to get a view objectdef vo = newView('TroubleTicket_c')// 2. Create a new view criteriadef vc = newViewCriteria(vo)// 3. Create a new view criteria rowdef vcr = vc.createRow()// 4. Define two view criteria items// 4a. (#1) Create a view criteria item for AssignedTo_Iddef vci1 = vcr.ensureCriteriaItem('AssignedTo_Id_c')// 4b. (#1) Set the operator of the first view criteria itemvci1.setOperator('=')// 4c. (#1) Set the value of the first view criteria itemvci1.setValue(staffMemberId)// 4a. (#2) Create a view criteria item for Statusdef vci2 = vcr.ensureCriteriaItem('Status_c')// 4b. (#2) Set the operator of the first view criteria itemvci2.setOperator('=')// 4c. (#2) Set the value of the first view criteria itemvci2.setValue('working')// 4d. (#2) Set the text field criteria to be case-insensitivevci2.setUpperColumns(true)// 5. Add the new view criteria rowvc.insertRow(vcr)// 6. Apply the view criteria to the view objectvo.appendViewCriteria(vc)// 7. Execute the queryvo.executeQuery()

44

Oracle Fusion CRM Application Composer Scripting Guide

Page 45: Scripting Guide

Simplifying Most Common View Criteria Creation with a Global Function

Very often, the kinds of a view criteria that you will use will be simple ones with equals operators, so youcan create a global function to simplify creating common data filters like this. The applyFilter() functionshown below would allow you to simplify the code in Example 9, “Basic Example of Using View Criteria”to look like this:

/* * Query all 'Working'-status trouble tickets assigned to a given staff member */def staffMemberId = 100000000089003 // 1. Use the newView() function to get a view objectdef vo = newView('TroubleTicket_c')// 2. Filter on AssignedTo_Id = Create a new view criteriaadf.util.applyFilter(vo,[Status_c:'Working',AssignedTo_Id_c:staffMemberId])// 3. Execute the queryvo.executeQuery()

The global function looks like this:

Function Name applyFilter

Return Type void

Parameters TypeName

Objectvo

MapfieldsToFilter

Function Definition /* * Defines a new view criteria with one view criteria row * having a view criteria item for each map entry in * the 'fieldsToFilter' parameter */adf.util.logStart('applyFilter')def criteria = newViewCriteria(vo)def criteriaRow = criteria.createRow()adf.util.log("fieldsToFilter = ${fieldsToFilter}")criteria.insertRow(criteriaRow)for (curField in fieldsToFilter?.keySet()) { def filterValue = fieldsToFilter[curField] adf.util.log("Filter on ${curField} = ${filterValue}") // if the value to filter on is a List then if (filterValue instanceof List) { adf.util.addMultiValueCriteriaItem(criteriaRow, curField, filterValue, true) } else { def criteriaItem = criteriaRow.ensureCriteriaItem(curField) criteriaItem.setValue(filterValue) }} vo.appendViewCriteria(criteria)

Understanding View Criteria Item Operators

When you a create view criteria item, Table 7, “View Criteria Item Operators (Case-Sensitive!)” providesthe list of valid values that you can provide as the single argument to the setOperator() method on the

45

Oracle Fusion CRM Application Composer Scripting Guide

Page 46: Scripting Guide

view criteria item object. Notice that some operators are valid only for fields of certain data types. Forexample, for a field {Priority of type number, to set the "Greater than or equals" operator on the relatedview criteria item, you would write a line of code like this:

// Use the 'greater than or equals' operatorpriorityVCItem.setOperator('>=')

In contrast, for a view criteria item related to a ResolutionDate field of type Date, you would use theONORAFTER operator like this:

// Use the 'on or after' operatorresolutionDateVCItem.setOperator('ONORAFTER')

Table 7. View Criteria Item Operators (Case-Sensitive!)

Valid for Field TypesDescriptionOperator

Number, Text, DateEquals=

Number, Text, DateNot equals<>

Number, Text, DateIs null (no view criteria itemvalue required)

ISBLANK

Number, Text, DateIs not null (no view criteriaitem value required)

ISNOTBLANK

TextLikeLIKE

TextStarts withSTARTSWITH

Number, TextGreater than>

DateDate comes afterAFTER

Number, TextGreater than or equal to>=

DateDate is on or comes afterONORAFTER

Number, TextLess than<

DateDate comes beforeBEFORE

Number, TextLess than or equal to<=

DateDate is on or comes beforeONORBEFORE

Number, Text, DateIs between (two view criteriaitem values required)

BETWEEN

Number, Text, DateIs not between (two view cri-teria item values required)

NOTBETWEEN

Using View Criteria Items with the ISBLANK Operator

The ISBLANK and ISNOTBLANK view criteria item operators do not require any operand value to be set. Forexample, assuming that vcr represents a view criteria row in which you want to create a criteria item tofilter where Justification is null, you would write script like this:

// Assume vcr is a view criteria rowdef vci = vcr.ensureCriteriaItem('Justification_c')vci.setOperator('ISBLANK')// no need to call vci.setValue() with the ISBLANK operators

The same consideration holds for the ISNOTBLANK operator.

46

Oracle Fusion CRM Application Composer Scripting Guide

Page 47: Scripting Guide

Using View Criteria Items with the BETWEEN Operator

The BETWEEN and NOTBETWEEN view criteria item operators require two arguments. For example, assumingthat vcr represents a view criteria row in which you want to create a criteria item to filter where Priorityis between 2 and 4, you would write script like this:

// Assume vcr is a view criteria rowdef vci = vcr.ensureCriteriaItem('Priority_c')vci.setOperator('BETWEEN')// The BETWEEN operator requires two values...// Set first value to 2 (zero-based index!)vci.setValue(0 /* index */, 2 /* value */)// Set second value to 4 (zero-based index!)vci.setValue(1,4)

Understanding View Criteria Row and Item Conjunctions

For many purposes, a view criteria with a single view criteria row will be ample. By default, when theview criteria is converted into a SQL WHERE clause, all of the view criteria items in the same view criteriarow are joined by an AND conjunction to create a SQL fragment that looks like this (assuming the simplestequals operator on each view criteria item):

(FieldName1 = Value1 AND FieldName2 = Value2) /* from View Criteria Row#1 */

If you need to define multiple sets of criteria, you can add additional view criteria rows to the view criteria.By default, when the view criteria is converted into a SQL WHERE clause, each of the SQL fragments createdfor the individual view criteria rows is joined by an OR conjunction like this:

(FieldName1 = Value1 AND FieldName2 = Value2) /* from View Criteria Row#1 */OR(FieldName3 = Value3 AND FieldName4 = Value4) /* from View Criteria Row#2 */

If you need to change the default AND conjunction used to join a view criteria item with the one processedjust before it in the same view criteria row, then you can call the setConjunction() method on it to changethe conjunction to an OR. The method accepts a numerical argument with number 1 corresponding to theAND conjunction and the number 0 (zero) corresponding to the OR conjunction. Therefore, to set a particularview criteria item to use the OR conjunction instead, use the code:

vci.setConjunction(0 /* OR */)

If you need to change the default OR conjunction used to join the SQL fragment produced by a given viewcriteria row with the view criteria row processed just before it in the same view criteria, then you can callthe setConjunction() method on it to change the conjunction to an AND. Therefore, to set a particularview criteria row to use the AND conjunction instead, use the code:

vcr.setConjunction(1 /* AND */)

Using Compound Criteria Item to Search for Multiple Alternatives

To create a view criteria item that includes multiple values for the same field — for example, to querywhere the value of the Status field is either Working or Waiting, then you can use a compound criteriaitem value. You make a criteria item into a compound item by calling the makeCompound() method on it.Once you've done this, you use the ensureItem() method on the compound criteria item to create nestedcriteria items.

For example, the addMultiValueCriteriaItem function below simplifies creating multi-valued criteriaitem. You may have noticed that it is used by the applyFilter() global function in the section called“Simplifying Most Common View Criteria Creation with a Global Function”. Once you put the two global

47

Oracle Fusion CRM Application Composer Scripting Guide

Page 48: Scripting Guide

functions to work together, you can create a criteria like "find the trouble tickets assigned to Jane Jedeyewith a status Working or Waiting" by using a script like:

/* * Query all 'Working'- or 'Waiting'-status trouble tickets assigned to a staff member */def staffMemberId = 100000000089003 // 1. Use the newView() function to get a view objectdef vo = newView('TroubleTicket_c')// 2. Filter on AssignedTo_Id = Create a new view criteriaadf.util.applyFilter(vo,[Status_c:['Working','Waiting],AssignedTo_Id_c:staffMemberId])// 3. Execute the queryvo.executeQuery()

The code in the applyFilter() global function detects that the map entry for Status_c has a list as itsvalue. When it detects this it uses addMultiValueCriteriaItem() to add the compound view criteria itemto query for where Status = 'Working' OR Status = 'Waiting'. The relevant SQL WHERE clause criteriawill look something like:

(STAFF_MEMBER_ID = 100000000089003 AND (UPPER(STATUS) = UPPER('Working') OR UPPER(STATUS) = UPPER('Waiting') ))

The function definition looks like this:

Function Name addMultiValueCriteriaItem

Return Type void

Parameters TypeName

ObjectcriteriaRow

StringfieldName

ListlistOfValues

BooleancaseInsensitive

Function Definition adf.util.logStart('addMultiValueCriteriaItem')def compoundVCI = criteriaRow.ensureCriteriaItem(fieldName).makeCompound();compoundVCI.setOperator('=');if (listOfValues != null) { for (int z=0; z < listOfValues.size(); z++) { def nestedVCI = compoundVCI.ensureItem(z); nestedVCI.setOperator('='); nestedVCI.setUpperColumns(caseInsensitive); // Nested items beyond first use "OR" instead of "AND" if (z > 0) { nestedVCI.setConjunction(0 /* OR */); } nestedVCI.setValue(listOfValues.get(z)); }}

48

Oracle Fusion CRM Application Composer Scripting Guide

Page 49: Scripting Guide

Creating a New Object

To create a new object, follow these steps:

1. Use the newView() function to obtain the view object for programmatic access for the business objectin question

2. Call the createRow() function on the view object to create a new row

3. Call setAttribute() as needed to set the desired field values in the new row

4. Call insertRow() on the view object to insert the row.

The new object will be saved the next time you save your work as part of the current transaction. Example 10,“Creating a New Object” shows how the steps fit together in practice.

Example 10. Creating a New Object

// Access the view object for the custom TroubleTicket objectdef vo = newView('TroubleTicket_c')// Create the new rowdef newTicket = vo.createRow()// Set the problem summarynewTicket.setAttribute('ProblemSummary_c','Cannot insert floppy disk')// Assign the ticket a prioritynewTicket.setAttribute('Priority_c',2)// Relate the ticket to a customer by namenewTicket.setAttribute('Contact_c','Tim Tubbington')// Relate the ticket to a staff member by namenewTicket.setAttribute('AssignedTo_c','Jane Jedeye')// Insert the new row into the view objectvo.insertRow(newTicket)// The new data will be saved to the database as part of the current// transaction when it is committed.

Updating an Existing Object

If the object you want to update is the current row in which your script is executing, then you need onlyuse the setAttribute() function to assign new values to the fields as needed.

However, if you need to update an object that is different from the current row, perform these steps:

1. Use newView() to access the appropriate view object for programmatic access

2. Find the object by id or find one or more objects using a view criteria, depending on your use requirements

3. Call the setAttribute() method on the row or rows to assign new values as needed

The changes will be saved as part of the current transaction when the user commits it.

Tip

See the section called “Avoiding Validation Threshold Errors By Conditionally Assigning Values”for a tip about how to avoid your field assignments from causing an object to hit its validationthreshold.

49

Oracle Fusion CRM Application Composer Scripting Guide

Page 50: Scripting Guide

Permanently Removing an Existing Object

To permanently remove an existing object, perform these steps:

1. Use newView() to access the appropriate view object for programmatic access

2. Find the object by id or find one or more objects using a view criteria, depending on your use requirements

3. Call the remove() method on the row or rows as needed

The changes will be saved as part of the current transaction when the user commits it.

Reverting Changes in a Single Row

To revert pending changes to an existing object, perform these steps:

1. Use newView() to access the appropriate view object for programmatic access

2. Find the object by id

3. Call the revertRowAndContainees() method as follows on the row

yourRow.revertRowAndContainees()

Understanding Why Using Commit or Rollback In Scripts Is StronglyDiscouraged

It is strongly advised to not commit or rollback the transaction from within your scripts, allowing insteadthat any changes made by your script to be committed or rolled-back along with the rest of the currenttransaction. If your script code were to call commit() or rollback(), this would affect all changes pendingin the current transaction, not only those performed by your script. Forewarned is forearmed.

Using the User Data Map

The Oracle ADF framework provides a map of name/value pairs that is associated with the current user'ssession. You can use this map to temporarily save name/value pairs for use by your business logic. Beaware that the information that you put in the user data map is never written out to a permanent store, andis not replicated under failover conditions. If the code that reads the value in the user data map executesas part of different web request from the code that put the value in the map, then that code reading the mapshould be written to expect that the value that has been put their may not be there as expected (due toserver failover).

To access the server map from a validation rule or trigger, use the expression adf.userSession.userDataas shown in the following example:

// Put a name/value pair in the user data mapadf.userSession.userData.put('SomeKey', someValue)

// Get a value by key from the user data mapdef val = adf.userSession.userData.SomeKey

Tip

See the section called “Working with Maps” for more information on using maps in your scripts.

50

Oracle Fusion CRM Application Composer Scripting Guide

Page 51: Scripting Guide

Referencing Information About the Current User

The adf.context.getSecurityContext() expression provides access to the security context, from whichyou can access information about the current user like her user name or whether she belongs to a particularrole. The following code illustrates how to reference these two pieces of information:

// Get the security contextdef secCtx = adf.context.getSecurityContext()// Check if user has a given roleif (secCtx.isUserInRole('MyAppRole')) { // get the current user's name def user = secCtx.userName // Do something if user belongs to MyAppRole}

Using Aggregate Functions

Oracle ADF's built-in support for row iterator aggregate functions can simplify a number of common cal-culations you will perform in your scripts, especially in the context of scripts written in a parent objectwhich has one or more collections of child objects.

Understanding the Supported Aggregate Functions

Oracle ADF supports five built-in aggregate functions over all the rows in a row set. The most commonuse case is to calculate an aggregate value of a child collection in the context of a parent object. Table 8,“Supported Aggregate Functions” provides a description and example of the supported functions.

Table 8. Supported Aggregate Functions

Example (in Context ofTroubleTicket Parent Object

DescriptionAggregate Function

ActivityCollection_c.avg('Dur-

ation_c')

Average value of an expressionavg

ActivityCollection_c.min('Dur-

ation_c')

Minimum value of an expressionmin

ActivityCollection_c.max('Dur-

ation_c')

Maximum value of an expressionmax

ActivityCollection_c.sum('Dur-

ation_c')

Sum of the value of an expressionsum

A c t i v i t y C o l l e c -

tion_c.count('Duration_c')

Count of rows having a non-nullexpression value

count

Understanding How Null Values Behave in Aggregate Calculation

When an ADF aggregate function executes, it iterates over each row in the row set. For each row, it eval-uates the Groovy expression provided as an argument to the function in the context of the current row. Ifyou want a null value to be considered as zero for the purposes of the aggregate calculation, then use thenvl() function like this:

// Use nvl() Function in aggregate expressiondef avgDuration = ActivityCollection_c.min('nvl(Duration_c,0)')

51

Oracle Fusion CRM Application Composer Scripting Guide

Page 52: Scripting Guide

Performing Conditional Counting

In the case of the count() function, if the expression evaluates to null then the row is not counted. Youcan supply a conditional expression to the count() function which will count only the rows where the ex-pression returns a non-null value. For example, to count the number of child activities for the currenttrouble-ticket where the Duration was over half an hour, you can use the following expression:

// Conditional expression returns non-null for rows to count// Use the inline if/then/else operator to return 1 if the// duration is over 0.5 hours, otherwise return null to avoid// counting that the non-qualifying row.def overHalfHourCount = ActivityCollection_c.count('nvl(Duration_c,0) > 0.5 ? 1 : null')

Inserting an Aggregate Expression Using the Expression Palette

As shown in Figure 10, “Selecting an Aggregate Function to Insert”, the Expression Palette's Functionstab allows you to insert any of the aggregate functions. They appear in the Number category.

Figure 10. Selecting an Aggregate Function to Insert

After selecting the desired function and clicking the Insert button, as shown in Figure 11, “Configuringthe Field on Which to Calculate the Sum”, and additional Configure Function dialog appears to allow youto select the field name over which the aggregate function will be performed. Note that in this dialog, theFields table only shows field names when you select an object representing a multi-row collection in thetree at the left.

52

Oracle Fusion CRM Application Composer Scripting Guide

Page 53: Scripting Guide

Figure 11. Configuring the Field on Which to Calculate the Sum

Understanding When to Configure Field Dependencies

You need to correctly configure dependent field information when you define a formula field or when youconfigure conditionally updateable and/or conditionally required expressions. If you fail to correctly con-figure this information, your application will not behave correctly at runtime.

Configuring Depends On Fields for a Formula

The ADF framework — unlike, say, a spreadsheet program — does not automatically infer what field(s)your Groovy scripts depend on. For example, suppose that in a OrderLineItem object, you added a formulafield named LineTotal object having the following Groovy formula:

(nvl(UnitPrice_c,0) * nvl(Quantity_c,0)) * (1.00 - nvl(LineDiscountPercentage_c,0))

On its own, the ADF framework would not be able to automatically recalculate the line total when the userchanged the UnitPrice, Quantity, or LineDiscountPercentage Instead, you manually configure the De-pends On information for any field formulas need it. For example, Figure 12, “Configuring Depends OnFields for a Formula” shows the Depends On multi-select list configured to reflect the fields on which theLineTotal formula depends. With this information correctly configured, the LineTotal will automaticallyupdate to reflect the new value whenever any of the values of the field on which it depends is changed.

53

Oracle Fusion CRM Application Composer Scripting Guide

Page 54: Scripting Guide

Figure 12. Configuring Depends On Fields for a Formula

Configuring Depends On Fields for Conditional Updateability or Conditional Man-datory

When you define a formula field, the Depends On multi-select list is always visible. However, for otherfield types it is only visible when you have configured either a conditionally updateable expression or aconditionally required expression. In this latter case, it appears to remind you that you must configure theinformation that tells the Oracle ADF framework on which other fields your conditionally updateableand/or conditionally required expression depends. If you fail to configure this information, your applicationwill not behave correctly.

Understanding Automatic Dependency Information for Cascading Fixed-ChoiceLists

Assume that you have defined two fixed-choice fields on the OrderLineItem objects named Fulfillment-Center and FulfillmentWarehouse. When defining the FulfillmentWarehouse field, further assume thatyou configured it to be constrained based on the value of the FulfillmentCenter field. This combinationallows the end-user to first pick a fulfillment center, then to pick an appropriate fulfillment warehouse relatedto that choice of fulfillment center. If the user changes the value of the fulfillment center, then the currentvalue of the fulfillment warehouse is set to null to force the end-user to select an appropriate fulfillmentwarehouse based on the new value of the constraining FulfillmentCenter field. This type of cascadingfixed-choice list dependency is automatically configured when you setup the "constrained by" field as partof defining the second fixed-choice field. In this example, the FulfilmentWarehouse field depends on theFulfillmentCenter field.

Next, assume that your application has the requirement to allow configuring the fulfillment warehouseonly for line items with a quantity greater than five. To support this functionality, you would configurethe following conditionally updateable expression on the FulfillmentWarehouse field:

Quantity_c > 5

After doing this, the Depends On multi-select list will appear in the Constraints section so you can configurethe fact that your FulfillmentWarehouse now also depends on the Quantity field. After doing this, whenthe user changes the value of the quantity at runtime, this will dynamically affect whether the fulfillmentwarehouse field is updateable or not. Figure 13, “Cascading Fixed-Choice Dependencies Cannot Be Re-

54

Oracle Fusion CRM Application Composer Scripting Guide

Page 55: Scripting Guide

moved” shows the process of configuring the Quantity field as one on which the FulfillmentWarehousefield now depends. Notice that the Fulfillment Center field appears selected in the Depends On list, yet isdisabled. This is because that dependency is required for the correct runtime functionality of the constrainedfixed-choice fields and so you are not allowed to remove it.

Figure 13. Cascading Fixed-Choice Dependencies Cannot Be Removed

Enforcing Conditional Updateability of Custom Fields for Web ServiceAccess

In this release any conditional updateability expression you provide for a field is enforced only within theweb application's user interface. Depending on the condition you've provided — assuming you have correctlyconfigured the Depends On information as described in the section called “Configuring Depends On Fieldsfor Conditional Updateability or Conditional Mandatory” — the field in the user interface will enable ordisable as appropriate. However, the same cannot be said for updates to that field performed through theweb service interface. Using the example from the section called “Understanding Automatic DependencyInformation for Cascading Fixed-Choice Lists”, if the line item's quantity field is less than or equal to five(5), then the fulfillment warehouse field will be disabled in the user interface. In contrast, if a system attemptsto update the same line item using the web service interface, an attempt to update FulfillmentWarehousewill succeed regardless of the value of the Quantity field!

To enforce this conditional updateability through the web service as well, you need to add an object-levelvalidation rule that enforces the same condition. For example, you would configure the following rule toprevent web service updates to the fulfillment warehouse value if the quantity is not greater than five:

Rule Name Warehouse_Updateable_Only_With_Quantity_Over_Five

Rule Body // If the fulfillment warehouse is changed, ensure the// quantity is greater than five (5)if (isAttributeChanged('FulfillmentWarehouse_c')) { if (Quantity_c <= 5) { return false}return true

Error Message A warehouse can be specified only for quantities over five

55

Oracle Fusion CRM Application Composer Scripting Guide

Page 56: Scripting Guide

Implementing Non-Formula Field Changeable Only From Script

Assume you want to add a LineEditedCount field to the OrderLineItem object to track how many timesit has been edited. The field value needs to be stored in the database, so a formula field is not appropriate.Start by adding a custom field of type Number to the object, configuring its default value to be the literalvalue 0 (zero). Next, configure a conditionally updateable expression for the new LineEditedCount withthe expression:

false

This will cause the user interface to always see that the field is not updateable, and the value will only beupdateable from script. Note that configuring the conditionally updateable expression to always returnfalse is semantically different from unchecking the Updateable checkbox. Doing the latter, your fieldwould never be updateable (neither from the user interface nor from script). Using the conditionally update-able expression, the updateability enforcement is done only at the user interface level.

Finally, to derive the value of the LineEditedCount you would add the following trigger:

Trigger Object OrderLineItem

Trigger Before Update In Database

Trigger Name Before_Update_Adjust_Edited_Count

Trigger Definition adf.util.logStart('Before_Update_Adjust_Edited_Count')// Get the original value of the LineEditedCount fielddef origCount = getOriginalAttributeValue('LineEditedCount_c')def newCount = origCount + 1// Only assign the value if it's not already what we want it to beif (LineEditedCount_c != newCount) { setAttribute('LineEditedCount_c',newCount)}

Understanding When Field Default Value Expressions Are Evaluated

A default value expression provides you the ability to dynamically calculate the initial value of a field ina newly-created row. If you configure a default value expression for a field, it is evaluated only when anew row is created. The expression is not evaluated at any other time. If your use case requires the valueof a field to change automatically when the value of one or more other fields is changed, see the sectioncalled “Deriving Values of a Field When Other Fields Change Value”.

Understanding the Difference Between Default Expression and CreateTrigger

There are two ways you can assign default values to fields in a newly-created row and it is important tounderstand the difference between them.

The first way is to provide a default value expression for one or more fields in your object. Your defaultvalue expression should not depend on other fields in the same object since you cannot be certain of theorder in which the fields are assigned their default values. The default value expression should evaluateto a legal value for the field in question and it should not contain any setAttribute() calls as part of theexpression. The framework evaluates your default expression and assigns it to the field to which it is asso-ciated automatically at row creation time.

On the other hand, If you need to assign default values to one or more fields after first allowing theframework to assign each field's literal default values or default value expression, then the second way is

56

Oracle Fusion CRM Application Composer Scripting Guide

Page 57: Scripting Guide

more appropriate. Define a Create trigger on the object and inside that trigger you can reference any fieldin the object as well as perform any setAttribute() calls to assign default values to one or more fields.

Deriving Values of a Field When Other Fields Change Value

There are three different use cases where you might want to derive the value of a field. This section assistsyou in determining which one is appropriate for your needs.

Deriving the Value of a Formula Field When Other Fields Change Value

If the value of your derived field is calculated based on other fields and its calculated value does not needto be permanently stored, then use a formula field. To derive the value of the formula field, perform thesetwo steps:

1. Configure the formula expression

2. Configure the Depends On information to indicate the fields on which your formula expressions depends

Deriving the Value of Non-Formula Field When Other Fields Change Value

If the value of your derived field must be stored, then use one of strategies in this section to derive its value.

The value of the field is derived by save-time triggers and cannot be set by the user

In this case, follow the steps described in

Deriving a Non-Formula Field Using a Before Trigger

The simplest way to derive a stored field's value is to create an appropriate "before" trigger (Before Insertin Database and/or Before Update in Database) which sets the field's value to your calculated value bycalling the setAttribute(). See the section called “Assigning a Value to a Field in the Current Object”for more information on this function and the section called “Avoiding Validation Threshold Errors ByConditionally Assigning Values” for a tip about how to avoid your field assignments from causing an objectto hit its validation threshold.

Deriving a Non-Formula Field Using a Validation Rule

If you want the end-user to see the derived field's value update on the user interface immediately, then youneed to perform the assignment of the derived field's value inside an After Field Changed trigger. Whenthis trigger fires, the value of the field in question has already changed. Therefore, you can simply referencethe new value of the field by name instead of using the special newValue expression (as would be requiredin a field-level validation rule to reference the field's candidate new value that is attempting to be set).

Setting Invalid Fields for the UI in an Object-Level Validation Rule

When a field-level validation rule that you've written returns false, ADF signals the failed validation withan error and the field is highlighted in red in the user interface to call the problem to the user's attention.However, since object-level validation rules involve multiple fields, the framework does not know whichfield to highlight in the user interface as having the problematic value. If you want your object-level valid-ation rule to highlight one or more fields as being in need of user review to resolve the validation error,you need to assist the framework in this process. You do this by adding a call to the adf.error.addAttrib-ute() function in your validation rule script before returning false to signal the failure. For example,consider the following rule to enforce: A contact cannot be his/her own manager. Since the Id field of theContact object cannot be changed, it will make sense to flag the Manager_Id field — a secondary field

57

Oracle Fusion CRM Application Composer Scripting Guide

Page 58: Scripting Guide

related to the Manager lookup field — as the field in error to highlight in the user interface. Here is theexample validation rule.

Rule Name Contact_Cannot_Be_Own_Manager

Rule Body // Rule depends on two fields, so must be// written as object-level ruleif (Manager_Id_c == Id) { // Signal to highlight the Manager field on the UI // as being in error. Note that Manager_Id field // is not shown in the user interface! adf.error.addAttribute('Manager_c') return false}return true

Error Message A contact cannot be his/her own manager

Determining the State of a Row

A row of data can be in any one of the following states:

Initialized A new row that has been created in the user interface, but has not had any user editsapplied to it yet

New A new row that will be inserted into the database during the next save operation.

Unmodified An existing row that has not been modified

Modified An existing row where one or more values has been changed and will be updated in thedatabase during the next save operation

Deleted An existing row that will be deleted from the database during the next save operation

Dead A row that was new and got removed before being saved, or a deleted row after it hasbeen saved

To determine the state of a row in your Groovy scripts, use the function getPrimaryPostState() and itsrelated helper methods as shown in Example 11, “Making Logic Conditional Based on the Row State”.

Example 11. Making Logic Conditional Based on the Row State

// Only perform this business logic if the row is newif (getPrimaryPostState().isNew()) { // conditional logic here}

The complete list of helper methods that you can use on the return value of getPrimaryPostState() is shownbelow:

isInitialized() Returns boolean true if the row state is initialized, false otherwise.

isNew() Returns boolean true if the row state is new, false otherwise.

isUnmodified() Returns boolean true if the row state is unmodified, false otherwise.

isModified() Returns boolean true if the row state is modified, false otherwise.

58

Oracle Fusion CRM Application Composer Scripting Guide

Page 59: Scripting Guide

isDeleted() Returns boolean true if the row state is deleted, false otherwise.

isDead() Returns boolean true if the row state is dead, false otherwise.

Understanding How Local Variables Hide Object Fields

If you define a local variable whose name is the same as the name of a field in your object, then be awarethat this local variable will take precedence over the current object's field name when evaluated in yourscript. For example, assuming your object has a custom field named Status_c, then consider the followingobject validation script:

// Assuming current object has a Status_c fielddef Status_c = 'Closed'/* * : * imagine pages full of complex code here * : */// If the object's current status is Open, then change it to 'Pending'// ------------------// POTENTIAL BUG HERE: The Status_c local variable takes precedence// ------------------ so the Status_c field value is not used!//if (Status_c == 'Open') { setAttribute('Status_c','Pending') }

At the top of the example, a variable named Status_c is defined. After pages full of complex code, laterin the script the author references the custom field named Status_c without remembering that there is alsoa local variable named Status_c defined above. Since the local variable named Status_c will always takeprecedence, the script will never enter into the conditional block here, regardless of the current value ofthe Status_c field in the current object. As a rule of thumb, use a naming scheme for your local variablesto ensure their names never clash with object field names.

Invoking Web Services from Your Scripts

Calling web service methods from your scripts involves two high-level steps:

1. Registering a variable name for the web service you want to invoke

2. Writing Groovy code that prepares the inbound arguments, calls the web service function, and thenprocesses the return value

Using Groovy Maps and Lists with Web Services

When passing and receiving structured data from a web service, a Groovy Map represents an object and itsproperties. For example, an Employee object with properties named Empno , Ename, Sal, and Hiredatewould be represented by a Map object having four key/value pairs, where the names of the properties arethe keys.

You can create an empty Map using the syntax:

def newEmp = [:]

Then, you can add properties to the map using the explicit put() method like this:

newEmp.put("Empno",1234)newEmp.put("Ename","Sean")newEmp.put("Sal",9876)newEmp.put("Hiredate",date(2013,8,11))

59

Oracle Fusion CRM Application Composer Scripting Guide

Page 60: Scripting Guide

Alternatively, and more conveniently, you can assign and/or update map key/value pairs using a simplerdirect assignment notation like this:

newEmp.Empno = 1234newEmp.Ename = "Sean"newEmp.Sal = 9876newEmp.Hiredate = date(2013,8,11)

Finally, you can also create a new map and assign some or all of its properties at once using the constructorsyntax:

def newEmp = [Empno : 1234, Ename : "Sean", Sal : 9876, Hiredate : date(2013,8,11)]

To create a collection of objects you use the Groovy List object. You can create one object at a time andthen create an empty list, and call the list's add() method to add both objects to the list:

def dependent1 = [Name : "Dave", BirthYear : 1996]def dependent2 = [Name : "Jenna", BirthYear : 1999]def listOfDependents = []listOfDependents.add(dependent1)listOfDependents.add(dependent2)

To save a few steps, the last three lines above can be done in a single line by constructing a new list withthe two desired elements in one line like this:

def listOfDependents = [dependent1, dependent2]

You can also create the list of maps in a single go using a combination of list constructor syntax and mapconstructor syntax:

def listOfDependents = [[Name : "Dave", BirthYear : 1996], [Name : "Jenna", BirthYear : 1999]]

If the employee object above had a property named Dependents that was a list of objects representing de-pendent children, you can assign the property using the same syntax as shown above (using a list of mapsas the value assigned):

newEmp.Dependents = [[Name : "Dave", BirthYear : 1996], [Name : "Jenna", BirthYear : 1999]]

Lastly, note that you can also construct a new employee with nested dependents all in one statement byfurther nesting the constructor syntax:

def newEmp = [Empno : 1234, Ename : "Sean", Sal : 9876, Hiredate : date(2013,8,11), Dependents : [ [Name : "Dave", BirthYear : 1996], [Name : "Jenna", BirthYear : 1999]] ]

For more information on Maps and Lists, see the section called “Working with Maps”.

60

Oracle Fusion CRM Application Composer Scripting Guide

Page 61: Scripting Guide

Registering a Web Service Variable

The composer allows you to register a web service for use in your scripts. This capability lets you associatea web service variable name with a URL that provides the location of the Web Service Description Language(WSDL) resource that represents the service you want to invoke. For example, you might register a webservice variable name of EmployeeService for a web service that your application needs to invoke forworking with employee data from another system. This service's WSDL resource URL might look somethinglike:

http://example.com:8099/Services/EmployeeService?WSDL

Of course, the server name, the port number, and path name for your actual service will be different. If theport number is omitted, then it will assume the service is listening on the default HTTP port number 80.

Browsing Available Web Service Methods

When writing your scripts, the Web Services tab in the expression builder shown in Figure 14, “Web ServicesTab in Expression Builder Palette” displays the available web service methods for a given web service.The Web Services drop down list displays the set of registered web service variable names. After you selecta particular web service, the Functions list displays the function names that are available to invoke for thatservice. Click on the Insert button to insert the syntax to invoke the web service method.

Figure 14. Web Services Tab in Expression Builder Palette

Examples of Calling Web Service Methods in Groovy

To invoke a web service method named someMethodName() on a web service that you registered with thevariable name YourServiceVariableName , use the syntax:

adf.webServices.YourServiceVariableName.someMethodName(args)

For instance, Example 12, “Retrieving an Employee by Id” shows how to invoke a getEmployee() methodon a web service registered with the web service variable name EmployeeService, passing the integer 7839as the single argument to the function.

61

Oracle Fusion CRM Application Composer Scripting Guide

Page 62: Scripting Guide

Example 12. Retrieving an Employee by Id

// retrieve Employee object by id from remote systemdef emp = adf.webServices.EmployeeService.getEmployee(7839)// log a message, referencing employee fields with "dot" notationprintln('Got employee '+emp.Ename+' with id '+emp.Empno)// access the nested list of Dependent objects for this employeedef deps = emp.Dependentsif (deps != null) { println("Found "+deps.size()+" dependents") for (dep in deps) { println("Dependent:"+dep.Name) }}

The code in Example 13, “Creating a new Employee, Including new Dependents” illustrates how to useGroovy's convenient Map and List construction notation to create a new employee with two nested depend-ents. The newEmp object is then passed as the argument to the createEmployee() method on the service.

Example 13. Creating a new Employee, Including new Dependents

// Create a new employee object using a Groovy map. The// nested collection of dependents is a Groovy list of mapsdef newEmp = [ Ename:"Steve", Deptno:10, Job:"CLERK", Sal:1234, Dependents:[[Name:"Timmy",BirthYear:1996], [Name:"Sally",BirthYear:1998]]]// Create the new employee by passing this object to a web servicenewEmp = adf.webServices.EmployeeService.createEmployee(newEmp)// The service returns a new employee object which may have // other attributes defaulted/assigned by the service, like the Empnoprintln("New employee created was assigned Empno = "+ newEmp.Empno)

The script in Example 14, “Merging Updates to an Employee, Adding a New Child Dependent Object”shows how to use the mergeEmployee() method to update fields in an employee object that is retrieved atthe outset via another call to the getEmployee() method. The script updates the Ename field on the empobject retrieved, updates the names of the existing depedents, and then adds a new dependent before callingthe mergeEmployee() method on the same service to save the changes.

Example 14. Merging Updates to an Employee, Adding a New Child DependentObject

// Merge updates and inserts on Employee and nested Dependentsdef emp = adf.webServices.EmployeeService.getEmployee(7839)// update employee's name to add an exclamation point!emp.Ename = emp.Ename + '!'def deps = emp.Dependents// Update dependent names to add an exclamation point!for (dep in deps) { dep.Name = dep.Name + '!'}// Add a new dependentdef newChild = [Name:"Jane", BirthYear:1997]deps.add(newChild)emp = adf.webServices.EmployeeService.mergeEmployee(emp)

62

Oracle Fusion CRM Application Composer Scripting Guide

Page 63: Scripting Guide

Understanding Common JBO Exceptions in Groovy Scripts

This section provides some background information on ADF exceptions that might occur while yourGroovy scripts are executing and attempts to explain the most common causes.

JBO-25030: Detail entity X with row key Y cannot find or invalidate itsowning entity

Problem Description You tried to create a new child object row of type X row without providingthe necessary context information to identify its owning parent object. Atthe moment of child row creation the correct owning parent context mustbe provided, otherwise the new child row created would be an "orphan".

For example, consider a custom object named TroubleTicket that has achild object named Activity. The following script that tries to create a newactivity would generate this error:

def activityVO = newView('Activity_c')// PROBLEM: Attempting to create a new child activity row// ------- without providing context about which owning// TroubleTicket row this activity belongs to.def newActivity = activityVO.createRow()

This generates a JBO-25030: Detail entity Activity_c with row key nullcannot find or invalidate its owning entity exception because the script istrying to create a new Activity row without providing the context that allowsthat new row to know which TroubleTicket it should belong to.

Resolution There are two ways to provide the appropriate parent object context whencreating a new child object row. The first approach is to get the owningparent row to which you want to add a new child row and use the parentrow's child collection attribute to perform the createRow() and insertRow()combination. For example, to create a new Activity row in the context ofTroubleTicket with an Id of 100000000272002 you can do the following,using the helper function mentioned in Example 7, “Finding an Object byId”. When you use this approach, the parent object context is implicit sinceyou're performing the action on the parent row's child collection.

def idParent = 100000000272002def ttVO = newView('TroubleTicket_c')def parent = adf.util.findRowByKey(ttVO,idParent)if (parent != null) { // Access the collection of Activity child rows for // this TroubleTicket parent object def activities = parent.ActivityCollection_c // Use this child collection to create/insert the new row def newActivity = activities.createRow() activities.insertRow(newActivity); // Set other field values of the new activity here...}

The second approach you can use is to pass the context that identifies theid of the parent TroubleTicket row when you create the child row. Youdo that using an alternative function named createAndInitRow() as shownbelow. In this case, you don't need to have the parent row in hand or evenuse the parent TroubleTicket view object. Providing the id to the owningparent row at the moment of child activity row creation is good enough.

63

Oracle Fusion CRM Application Composer Scripting Guide

Page 64: Scripting Guide

def idParent = 100000000272002// Create an name/value pairs object to pass the parent iddef parentAttrs = new NameValuePairs()parentAttrs.setAttribute("Id",idParent)// Use this name/value pairs object to pass the parent// context information while creating the new child rowdef activityVO = newView('Activity_c')def newActivity = activityVO.createAndInitRow(parentAttrs)activityVO.insertRow(newActivity);// Set other field values of the new activity here...

JBO-26020: Attempting to insert row with no matching EO base

Problem Description You inadvertently added a row that was created or queried from one viewobject to another view object of a different type. For example, the followingscript would generate this error:

def empVO = newView("Employees")def newEmp = empVO.createRow()def deptVO = newView("Department")// PROBLEM: Incorrectly adding a row of type "Employees"// ------- to the view of type "Department"deptVO.insertRow(newEmp)

This generates a JBO-26020: Attempting to insert row with no matchingEO base exception because the script is trying to insert a row from "Employ-ees" view into a view that is expecting rows of type "Department". Thisleads to a type mismatch that is not supported.

Resolution Ensure that when you call insertRow() on a view object that the row youare trying to insert into the collection is of the correct view type.

Supported Classes and Methods for Use in Groovy Scripts

When writing Groovy scripts, you may only use the classes and methods that are documented in Table 9,“Classes/Methods Allowed for Groovy Scripts”. Using any other class or method may work initially, butwill throw a runtime exception when you migrate your code to later versions. Therefore, we strongly suggestthat you ensure the Groovy code you write adheres to the classes and methods shown here. For each class,in addition to the method names listed in the table, the following method names are also allowed:

• equals()

• hashCode()

• toString()

In contrast, the following methods are never allowed on any object:

• finalize()

• getClass()

• getMetaClass()

• notify()

• notifyAll()

64

Oracle Fusion CRM Application Composer Scripting Guide

Page 65: Scripting Guide

• wait()

Table 9. Classes/Methods Allowed for Groovy Scripts

PackageAllowed MethodsClass Name

oracle.adf.shareADFContext • getLocale()

• getSecurityContext()

• getUserRoles()

• isUserInRole()

java.sqlArray • Any constructor

• Any method

oracle.jbo.domainArray • getArray()

• getElemType()

• getList()

java.utilArrayList • Any constructor

• Any method

java.utilArrays • Any constructor

• Any method

oracle.jboAttributeDef • getAttributeKind()

• getIndex()

• getJavaType()

• getName()

• getPrecision()

• getProperty()

• getScale()

• getUIHelper()

• getUpdateableFlag()

• isMandatory()

• isQueriable()

oracle.jboAttributeHints • getControlType()

65

Oracle Fusion CRM Application Composer Scripting Guide

Page 66: Scripting Guide

PackageAllowed MethodsClass Name

• getDisplayHeight()

• getDisplayHint()

• getDisplayWidth()

• getFormat()

• getFormattedAttribute()

• getFormatter()

• getFormatterClassName()

• getHint()

• getLocaleName()

• parseFormattedAttribute()

oracle.jboAttributeList • getAttribute()

• getAttributeIndexOf()

• getAttributeNames()

• setAttribute()

oracle.jbo.domainBaseLobDomain • closeCharacterStream()

• closeInputStream()

• closeOutputStream()

• getInputStream()

• getLength()

• getOutputStream()

• getcharacterStream()

java.mathBigDecimal • Any constructor

• Any method

java.mathBigInteger • Any constructor

• Any method

java.utilBitSet • Any constructor

• Any method

66

Oracle Fusion CRM Application Composer Scripting Guide

Page 67: Scripting Guide

PackageAllowed MethodsClass Name

java.sqlBlob • Any constructor

• Any method

oracle.jbo.domainBlobDomain • Any constructor

• getBinaryOutputStream()

• getBinaryStream()

• getBufferSize()

java.langBoolean • Any constructor

• Any method

java.langByte • Any constructor

• Any method

java.utilCalendar • Any constructor

• Any method

oracle.jbo.domainChar • Any constructor

• bigDecimalValue()

• bigIntegerValue()

• booleanValue()

• doubleValue()

• floatValue()

• getValue()

• intValue()

• longValue()

java.sqlClob • Any constructor

• Any method

oracle.jbo.domainClobDomain • Any constructor

• toCharArray()

java.utilCollection • Any constructor

• Any method

67

Oracle Fusion CRM Application Composer Scripting Guide

Page 68: Scripting Guide

PackageAllowed MethodsClass Name

java.utilCollections • Any constructor

• Any method

java.utilComparator • Any constructor

• Any method

java.utilCurrency • Any constructor

• Any method

oracle.jbo.domainDBSequence • Any constructor

• getValue()

java.utilDate • Any constructor

• Any method

java.sqlDate • Any constructor

• Any method

oracle.jbo.domainDate • Any constructor

• compareTo()

• dateValue()

• getValue()

• stringValue()

• timeValue()

• timestampValue()

java.utilDictionary • Any constructor

• Any method

java.langDouble • Any constructor

• Any method

java.langEnum • Any constructor

• Any method

java.utilEnumMap • Any constructor

• Any method

68

Oracle Fusion CRM Application Composer Scripting Guide

Page 69: Scripting Guide

PackageAllowed MethodsClass Name

java.utilEnumSet • Any constructor

• Any method

java.utilEnumeration • Any constructor

• Any method

java.utilEventListener • Any constructor

• Any method

java.utilEventListenerProxy • Any constructor

• Any method

java.utilEventObject • Any constructor

• Any method

java.langException • Any constructor

• Any method

oracle.jboExprValueErrorHandler • addAttribute()

• clearAttributes()

• raise()

• raiseLater()

• warn()

java.langFloat • Any constructor

• Any method

java.utilFormattable • Any constructor

• Any method

java.utilFormattableFlags • Any constructor

• Any method

java.utilFormatter • Any constructor

• Any method

java.utilGregorianCalendar • Any constructor

69

Oracle Fusion CRM Application Composer Scripting Guide

Page 70: Scripting Guide

PackageAllowed MethodsClass Name

• Any method

java.utilHashMap • Any constructor

• Any method

java.utilHashSet • Any constructor

• Any method

java.utilHashtable • Any constructor

• Any method

java.utilIdentityHashMap • Any constructor

• Any method

java.langInteger • Any constructor

• Any method

java.utilIterator • Any constructor

• Any method

oracle.jboJboException • getDetails()

• getErrorCode()

• getErrorParameters()

• getLocalizedMessage()

• getMessage()

• getProductCode()

• getProperty()

oracle.jboJboWarning • Any constructor

• getDetails()

• getErrorCode()

• getErrorParameters()

• getLocalizedMessage()

• getMessage()

• getProductCode()

70

Oracle Fusion CRM Application Composer Scripting Guide

Page 71: Scripting Guide

PackageAllowed MethodsClass Name

• getProperty()

oracle.jboKey • toStringFormat()

java.utilLinkedHashMap • Any constructor

• Any method

java.utilLinkedHashSet • Any constructor

• Any method

java.utilLinkedList • Any constructor

• Any method

java.utilList • Any constructor

• Any method

java.utilListIterator • Any constructor

• Any method

java.utilListResourceBundle • Any constructor

• Any method

java.utilLocale • Any constructor

• Any method

java.langLong • Any constructor

• Any method

java.utilMap • Any constructor

• Any method

java.langMath • Any constructor

• Any method

java.mathMathContext • Any constructor

• Any method

java.sqlNClob • Any constructor

• Any method

71

Oracle Fusion CRM Application Composer Scripting Guide

Page 72: Scripting Guide

PackageAllowed MethodsClass Name

oracle.jboNameValuePairs • Any constructor

• getAttribute()

• getAttributeIndexOf()

• getAttributeNames()

• setAttribute()

oracle.jbo.domainNativeTypeDomainInterface • getNativeObject()

oracle.jbo.domainNumber • Any constructor

• bigDecimalValue()

• bigIntegerValue()

• booleanValue()

• byteValue()

• doubleValue()

• floatValue()

• getValue()

• intValue()

• longValue()

• shortValue()

java.utilObservable • Any constructor

• Any method

java.utilObserver • Any constructor

• Any method

java.utilPriorityQueue • Any constructor

• Any method

java.utilProperties • Any constructor

• Any method

java.utilPropertyPermission • Any constructor

• Any method

72

Oracle Fusion CRM Application Composer Scripting Guide

Page 73: Scripting Guide

PackageAllowed MethodsClass Name

java.utilPropertyResourceBundle • Any constructor

• Any method

java.utilQueue • Any constructor

• Any method

java.utilRandom • Any constructor

• Any method

java.utilRandomAccess • Any constructor

• Any method

java.sqlRef • Any constructor

• Any method

java.utilResourceBundle • Any constructor

• Any method

oracle.jboRow • getAttribute()

• getAttributeHints()

• getKey()

• getLookupDescription()

• getOriginalAttributeValue()

• getPrimaryPostState()

• isAttributeChanged()

• isAttributeUpdateable()

• remove()

• revertRow()

• revertRowAndContainees()

• setAttribute()

• validate()

java.sqlRowId • Any constructor

• Any method

73

Oracle Fusion CRM Application Composer Scripting Guide

Page 74: Scripting Guide

PackageAllowed MethodsClass Name

oracle.jboRowIterator • createAndInitRow()

• createRow()

• findByKey()

• findRowsMatchingCriteria()

• first()

• getAllRowsInRange()

• getCurrentRow()

• getEstimatedRowCount()

• hasNext()

• hasPrevious()

• insertRow()

• last()

• next()

• previous()

• reset()

oracle.jboRowSet • avg()

• count()

• createAndInitRow()

• createRow()

• executeQuery()

• findByKey()

• findRowsMatchingCriteria()

• first()

• getAllRowsInRange()

• getCurrentRow()

• getEstimatedRowCount()

• hasNext()

• hasPrevious()

74

Oracle Fusion CRM Application Composer Scripting Guide

Page 75: Scripting Guide

PackageAllowed MethodsClass Name

• insertRow()

• last()

• max()

• min()

• next()

• previous()

• reset()

• sum()

java.utilScanner • Any constructor

• Any method

oracle.adf.share.securitySecurityContext • getUserName()

• getUserProfile()

oracle.jboSession • getLocale()

• getLocaleContext()

• getUserData()

java.utilSet • Any constructor

• Any method

java.langShort • Any constructor

• Any method

java.langShort • Any constructor

• Any method

java.utilSimpleTimeZone • Any constructor

• Any method

java.utilSortedMap • Any constructor

• Any method

java.utilSortedSet • Any constructor

• Any method

75

Oracle Fusion CRM Application Composer Scripting Guide

Page 76: Scripting Guide

PackageAllowed MethodsClass Name

java.utilStack • Any constructor

• Any method

java.langStackTraceElement • Any constructor

• Any method

java.langStrictMath • Any constructor

• Any method

java.langString • Any constructor

• Any method

java.langStringBuffer • Any constructor

• Any method

java.langStringBuilder • Any constructor

• Any method

java.utilStringTokenizer • Any constructor

• Any method

java.sqlStruct • Any constructor

• Any method

oracle.jbo.domainStruct • getAttribute()

• setAttribute()

oracle.jboStructureDef • findAttributeDef()

• getAttributeIndexOf()

java.sqlTime • Any constructor

• Any method

java.utilTimeZone • Any constructor

• Any method

java.utilTimer • Any constructor

• Any method

76

Oracle Fusion CRM Application Composer Scripting Guide

Page 77: Scripting Guide

PackageAllowed MethodsClass Name

java.utilTimerTask • Any constructor

• Any method

java.sqlTimestamp • Any constructor

• Any method

oracle.jbo.domainTimestamp • Any constructor

• compareTo()

• dateValue()

• getValue()

• stringValue()

• timeValue()

• timestampValue()

java.utilTreeMap • Any constructor

• Any method

java.utilTreeSet • Any constructor

• Any method

java.utilUUID • Any constructor

• Any method

oracle.adf.share.secur-

ity.identitymanagment

Note

Some of these methodsmay return null if the

UserProfile • getBusinessCity()

• getBusinessCountry()

• getBusinessEmail()

• getBusinessFax()

corresponding attribute of• getBusinessMobile()the user record is notpopulated in the identity• getBusinessPOBox()

store or if the identity• getBusinessPager() provider does not support

them.• getBusinessPhone()

• getBusinessPostalAddr()

• getBusinessPostalCode()

77

Oracle Fusion CRM Application Composer Scripting Guide

Page 78: Scripting Guide

PackageAllowed MethodsClass Name

• getBusinessState()

• getBusinessStreet()

• getDateofBirth()

• getDateofHire()

• getDefaultGroup()

• getDepartment()

• getDepartmentNumber()

• getDescription()

• getDisplayName()

• getEmployeeNumber()

• getEmployeeType()

• getFirstName()

• getGUID()

• getGivenName()

• getHomeAddress()

• getHomePhone()

• getInitials()

• getJpegPhoto()

• getLastName()

• getMaidenName()

• getManager()

• getMiddleName()

• getName()

• getNameSuffix()

• getOrganization()

• getOrganizationalUnit()

• getPreferredLanguage()

• getPrinciple()

78

Oracle Fusion CRM Application Composer Scripting Guide

Page 79: Scripting Guide

PackageAllowed MethodsClass Name

• getProperties()

• getProperty()

• getTimeZone()

• getTitle()

• getUIAccessMode()

• getUniqueName()

• getUserID()

• getUserName()

• getWirelessAccountNumber()

oracle.jboValidationException • getDetails()

• getErrorCode()

• getErrorParameters()

• getLocalizedMessage()

• getMessage()

• getProductCode()

• getProperty()

java.utilVector • Any constructor

• Any method

oracle.jboViewCriteria • createAndInitRow()

• createRow()

• createViewCriteriaRow()

• findByKey()

• findRowsMatchingCriteria()

• first()

• getAllRowsInRange()

• getCurrentRow()

• getEstimatedRowCount()

• hasNext()

79

Oracle Fusion CRM Application Composer Scripting Guide

Page 80: Scripting Guide

PackageAllowed MethodsClass Name

• hasPrevious()

• insertRow()

• last()

• next()

• previous()

• reset()

oracle.jboViewCriteriaItem • getValue()

• makeCompound()

• setOperator()

• setUpperColumns()

• setValue()

oracle.jboViewCriteriaItemCompound • ensureItem()

• getValue()

• makeCompound()

• setOperator()

• setUpperColumns()

• setValue()

oracle.jboViewCriteriaRow • ensureCriteriaItem()

• getConjunction()

• isUpperColumns()

• setConjunction()

• setUpperColumns()

oracle.jboViewObject • appendViewCriteria()

• avg()

• count()

• createAndInitRow()

• createRow()

• createViewCriteria()

80

Oracle Fusion CRM Application Composer Scripting Guide

Page 81: Scripting Guide

PackageAllowed MethodsClass Name

• executeQuery()

• findByKey()

• findRowsMatchingCriteria()

• first()

• getAllRowsInRange()

• getCurrentRow()

• getEstimatedRowCount()

• getMaxFetchSize()

• hasNext()

• hasPrevious()

• insertRow()

• last()

• max()

• min()

• next()

• previous()

• reset()

• setMaxFetchSize()

• sum()

java.utilWeakHashMap • Any constructor

• Any method

81

Oracle Fusion CRM Application Composer Scripting Guide