Object Model

The object model describes the structure of all known entities that are available in dynamic logic. Each entity is modeled using a Groovy class, which consists of methods and fields (sometimes also referred to as attributes or properties). All entities contain fixed fields, which are predefined by Oracle Health Insurance, and certain entities also support dynamic field, which can be configured by the user. An entity’s fixed fields are documented in the corresponding implementation guide. A complete overview of all fields (that is, including the dynamic ones) is automatically generated on-the-fly by the system, and can be found in the system’s View Object page. This chapter describes how to access both kind of attributes from dynamic logic.

Fixed fields

Attributes that are predefined as part of the system’s data model are fixed. In dynamic logic, they can always be read, but cannot always be written. Whether a particular attribute can be written or not, depends on the purpose of the dynamic logic. Dynamic logic validations and conditions are solely meant to return either true or false, whereas certain dynamic logic functions are indented for data modifications, in which case the function’s signature description contains more details.

Reading

The Dynamic Logic guide assumes that the reader is familiar with the general aspects of the Groovy programming language, so the following syntax example should come as no surprise:

assert claimLine.sequence == 1

Writing

Similarly, writing a fixed field can be done by using the assignment operator (<tt>=</tt>):

claimLine.benefitsInputDate = claimLine.startDate

Dynamic fields

Accessing an object’s dynamic field is done using the configured dynamic field usage name. The dynamic attribute’s type not only depends on the configured data type, but also on the configured time-valid and multi-value indicators. Whenever one or both indicators are checked, a dynamic field can have multiple different values. Those values are stored inside a list object. This is the case regardless of the actual number of values, so even if a field only has one value, it is still represented as a list.

Reading

The below table shows examples of accessing dynamic fields with different configuration.

Configuration Dynamic logic

Time-valid

Multi-value

Data type

Attribute type

Example

N

N

Character

String

assert claimLine.dependentName == 'John'

N

N

Number

BigDecimal

assert claimLine.patientWeight > 100

N

N

Date

Date

assert claimLine.accidentDate.after(claimLine.startDate)

N

N

Code

FlexCode

// Assuming the flex code field usages "name" and "severity" are configured assert claimLine.accidentType.name == 'CAR' assert claimLine.accidentType.severity == 17

N

N

Provider

Provider

assert claimLine.preferredProvider.code == '1970000011'

N

N

Procedure

Procedure

assert claimLine.extraProcedure.code == '08.14'

N

N

Diagnosis

Diagnosis

assert claimLine.secondaryDiagnosis.code == '690.02'

N

Y

All

List<Object> // Explained further below

assert claimLine.dependentNames.size() == 1

Y

N

All

List<DynamicFieldPeriod>

assert claimLine.familyTiers.size() == 2

Y

Y

All

List<DynamicFieldPeriod>

assert claimLine.insuranceCategories.size() == 3

The above table illustrates that for single-value non-time-valid dynamic fields the resulting attribute type is directly related to the configured data type. If a dynamic field is either multi-value or time-valid, then the attribute’s type becomes a List. The type of the elements in that list further depend on the time-valid indicator:

  • For non-time-valid dynamic fields, the element type simply depends on the configured data type, equivalent to the single-value case. For example, a Character dynamic field becomes List<String>, and a Code dynamic field becomes List<FlexCode>.

  • For time-valid dynamic fields, each value coincides with a time validity: a start and end date. This information is captured within an DynamicFieldPeriod object, so the dynamic field’s type is always List<DynamicFieldPeriod>. This class is structured as follows:

class DynamicFieldPeriod {
  Date startDate
  Date endDate
  Object value
}

To illustrate the aforementioned list mechanics further, the following table contains more detailed examples related to accessing such dynamic fields:

Configuration Example

Time-valid

Multi-value

Data type

N

N

Character

assert claimLine.dependentName == 'John'

N

Y

Character

assert claimLine.dependentNames[0] == 'John'

Y

N

Character

assert claimLine.dependentNames[0].value == 'John' assert claimLine.dependentNames[0].startDate >= claimLine.startDate assert claimLine.dependentNames.asOf(date) == 'John'

Y

Y

Character

assert claimLine.dependentNames[0].value == 'John' assert claimLine.dependentNames.asOf(date)[0] == 'John'

N

N

Code

// Assuming the flex code field usages "name" and "severity" are configured assert claimLine.accidentType.name == 'CAR'

N

Y

Code

assert claimLine.accidentTypes[0].name == 'CAR'

Y

N

Code

assert claimLine.accidentTypes[0].value.name == 'CAR' assert claimLine.accidentTypes[0].value.severity == 17 assert claimLine.accidentTypes[0].startDate >= claimLine.startDate assert claimLine.accidentTypes.asOf(date).name == 'CAR' assert claimLine.accidentTypes.asOf(date).severity == 17

Y

Y

Code

assert claimLine.accidentTypes[0].value.name == 'CAR' assert claimLine.accidentTypes.asOf(date)[0].name == 'CAR'

As the examples illustrate, the value of a time-valid dynamic field cannot be accessed directly, but has to be accessed through the value attribute of the DynamicFieldPeriod class. The example also shows that finding the value that is valid on a particular date, can be done with the asOf convenience method (see the Predefined Methods section for details). For single-value dynamic fields, the asOf method directly returns the value, not a DynamicFieldPeriod object. For multi-value dynamic fields, the asOf method returns a list of values, not a List<DynamicFieldPeriod>.

Writing

Similar to fixed fields, the assignment operator is also used to write dynamic fields. For single-value non-time-valid dynamic fields, it operates the same as for fixed fields. For multi-value and/or time-valid dynamic fields, however, the operator is used for setting the initial value and for adding subsequent values, so it is not idempotent. For time-valid dynamic fields, the value is specified alongside the start and end date. This is done using the DynamicFieldPeriod class described above. The below table shows some dynamic logic examples of manipulating a Character dynamic field.

Configuration Dynamic logic

Time-valid

Multi-value

Operation

Example

N

N

Set

claimLine.dependentName = 'John'

Update

claimLine.dependentName = 'Bob'

Remove

claimLine.dependentName = null

N

Y

Set

claimLine.dependentNames = ['John', 'Bob']

Add

claimLine.dependentNames = 'Jim'

Update

claimLine.dependentNames[0] = 'Peter'

Remove

claimLine.dependentNames[0] = null

Y

Y/N

Set

claimLine.dependentNames = [ new DynamicFieldPeriod('John', startDate1, endDate1) , new DynamicFieldPeriod('Bob', startDate2, endDate2) ]

Add

claimLine.dependentNames = new DynamicFieldPeriod('Jim', startDate, endDate)

Update

claimLine.dependentNames[0].value = 'Peter' claimLine.dependentNames[1].endDate = newEndDate

Remove

// Updating a field’s value to null results in its removal claimLine.dependentNames[0].value = null

The syntax for time-valid dynamic fields is the same for both the single-value and the multi-value case. The difference between the two is in the fact that values are allowed to overlap in time whenever the time-valid indicator is checked. A business rule violation exception is raised when they overlap and the indicator is not checked.

Dynamic records

Records can be accessed through the configured usage name, just like dynamic fields. Similarly, the data type also depends on the configured multi-value and time-valid indicators. The only difference being that a dynamic record consists of multiple fields, so unlike dynamic fields, they do not have a single value attribute. Instead, the records configured fields can be accessed individually.

Reading

The below table again shows the different examples, assuming the accidentState record has a type field.

Time-valid

N

Y

Multivalue

N

assert claimLine.accidentState.type == 'CAR'

assert claimLine.accidentStates[0].type == 'CAR' assert claimLine.accidentStates.asOf(date)[0].type == 'CAR'

Y

assert claimLine.accidentStates[0].type == 'CAR'

assert claimLine.accidentStates[0].type == 'CAR' assert claimLine.accidentStates.asOf(date)[0].type == 'CAR'

Writing

Dynamic records can be written using the predefined methods addDynamicRecord, updateDynamicRecord, and deleteDynamicRecord. Refer to the Predefined Methods chapter for more details and examples.

Optional attributes

Attributes that are optional might not have a value. In Groovy, the value null is used to indicate that fact. It is a special value that deserves a mention in this chapter. Accessing an attribute or a method on a null value results in a technical error, and should therefore be avoided. This is done by simply checking whether an attribute is equal to null or not, before further accessing it:

if (claimLine.condition != null) {
  assert claimLine.condition.code == 'REGULAR'
}

The Groovy language support a shorter way of writing such checks, using the ?-symbol, which is called the Safe navigation operator:

assert claimLine.condition == null
assert claimLine.condition?.code == null
assert claimLine.condition?.code?.toLowerCase() == null

As you can see, the ?-operator short-circuits further navigation as soon as a null value is encountered, and immediately returns null, thus avoiding a technical error. The operator can be used to reduce code complexity, but if used profusely, might also reduce readability or even lead to undesirable results.

For Example

Assume some dynamic logic is applying a bonus percentage to a claim line, based on the line’s condition, which is optional. If a line’s condition is irregular, a bonus of 10% should be applied. Since the condition is optional, before checking the condition’s type, it should first be checked that the claim line actually has a condition. The following two pieces of dynamic logic code are equivalent:

if (claimLine.condition != null && claimLine.condition.type == 'IRREGULAR') {
  claimLine.bonusPercentage = 10
} else {
  claimLine.bonusPercentage = 0
}
if (claimLine.condition?.type == 'IRREGULAR') {
  claimLine.bonusPercentage = 10
} else {
  claimLine.bonusPercentage = 0
}

In both cases, only claim lines with an irregular condition receive a bonus. Lines with a regular condition, and lines without a condition at all, do not receive a bonus. The latter code works, because claimLine.condition?.code yields null, and it so happens to be that null is not equal to the string IRREGULAR.

Now, instead of doing an exact match on the IRREGULAR condition, the above pieces of logic are extended to match all conditions other than REGULAR. These two are no longer equivalent:

if (claimLine.condition != null && claimLine.condition.type != 'REGULAR') {
  claimLine.bonusPercentage = 10
} else {
  claimLine.bonusPercentage = 0
}
if (claimLine.condition?.type != 'REGULAR') {
  claimLine.bonusPercentage = 10
} else {
  claimLine.bonusPercentage = 0
}

As before, the first piece of logic only applies the bonus to claim lines that have a condition, specifically an irregular condition. However, the second logic now applies the bonus to lines without any condition. It illustrates that carelessly applying the ?-operator to each navigation, is not always the correct solution to deal with possible null values.