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 Developer 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 intended for data modifications, in which case the function’s signature description contains more details.

Reading

This 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 (=):

claimLine.benefitsInputDate = claimLine.startDate

Dynamic Fields

An object’s dynamic field can be accessed through the dynamic field’s usage name. The dynamic field’s usage not only depends on the field’s definition (for example a character field with maximum length of 30 characters), but also on the configured time-valid and multi-value indicators. Whenever one or both indicators are checked, a dynamic field can have multiple values. If an object’s dynamic field can have multiple values, the dynamic field it is represented as a list on the object.

Multi-value and Time-valid Dynamic Fields

  1. A Single-Value Non-Time-Valid dynamic field can hold at most one value for an object’s property. Editing the value will replace the old value.

    Example

    Defining a person’s occupation as a single-value non-time-valid dynamic field allows you to specify at most one occupation for that person.
    If the person changes it’s occupation, you overwrite the old occupation.

  2. A Multi-Value Non-Time-Valid dynamic field can hold multiple values for an object’s property.

    Example

    Defining a person’s occupation as a multi-value non-time-valid dynamic field allows you to specify multiple occupations for that person.
    If the person changes it’s occupation, you can choose to either add the new occupation and keep or delete the old occupation. You could also decide to overwrite the old occupation.

  3. A Single-Value Time-Valid dynamic field can hold multiple values, however each of these values is only valid for a time period. Each value holds a mandatory start date and an optional end date. The time period of each value cannot overlap with the time period of another value for this dynamic field on this object.

    Example

    Defining a person’s occupation as a single-value time-valid dynamic field allows you to specify multiple occupations for that person however only one occupation ia allowed at a certain moment in time.
    If the person changes it’s occupation, you end-date the old occupation and add the new occupation with a start date that does not overlap with the old occupation. If the existing occupation is not correct you can edit that occupation and update either the occupation itself or it’s start- or enddate.

  4. A Multi-Value Time-Valid dynamic field allows you to specify multiple values that each have a time valid period and the time validity of each of the values is allowed to overlap.

    Example

    Defining a person’s occupation as a multi-value time-valid dynamic field allows you to specify multiple occupations for that person where that person can have multiple occupations at a certain moment in time.

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>

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/Create (if dynamic field does not have any values yet)

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

Set/Replace (if dynamic field already has some values)

Clean-up or nullify the existing values first, for example: claimLine.dependentNames[0] = null
claimLine.dependentNames[1] = null

and then set the new values, for example:
claimLine.dependentNames = [ new DynamicFieldPeriod("Peter", startDate1, endDate1)
, new DynamicFieldPeriod("Paul", startDate2, endDate2) ]

Add

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

Update

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

The below table shows some dynamic logic examples of manipulating a Code dynamic field.

Configuration Dynamic logic

Time-valid

Multi-value

Operation

Example

N

N

Set

person.SVNTV_FC = "Y"

Update

person.SVNTV_FC = "N"

Remove

person.SVNTV_FC = null

N

Y

Set

person.MVNTV_FC = ["BLACK","BLUE"]

Add

person.MVNTV_FC = "RED"

Update

person.MVNTV_FC[0] = "BROWN"

Remove

person.MVNTV_FC[0] = null

Y

Y

Set

person.MVTV_FC = [["endDate": Date.valueOf("2015-07-20"), 'startDate": Date.valueOf("2015-07-20"), "value": "BLACK"], ["endDate": Date.valueOf("2015-07-20"), 'startDate": Date.valueOf("2015-07-20"), "value": "BLUE"]]

Set/Replace

Clean-up or nullify the existing values first, for example:

person.MVTV_FC[0] = null
person.MVTV_FC[1] = null
person.MVTV_FC[2] = null and so on for all the entries

and then set the new values, for example:

person.MVTV_FC = [["endDate": Date.valueOf("2015-07-20"), 'startDate": Date.valueOf("2015-07-20"), "value": "BLACK"], ["endDate": Date.valueOf("2015-07-20"), 'startDate": Date.valueOf("2015-07-20"), "value": "BLUE"]]

Add

person.MVTV_FC = ["endDate": Date.valueOf("2015-07-20"), 'startDate": Date.valueOf("2015-07-20"), "value": "RED"]

Update

person.MVTV_FC[0] = "RED"

Y

N

Set

person.SVTV_FC = [["endDate": Date.valueOf("2015-07-20"), 'startDate": Date.valueOf("2015-07-20"), "value": "BLACK"]]

Set/Replace

Clean-up or nullify the existing values first, for example:

person.MVTV_FC[0] = null
person.MVTV_FC[1] = null
person.MVTV_FC[2] = null and so on for all the entries

and then set the new values, for example:

person.SVTV_FC = [["endDate": Date.valueOf("2015-07-20"), 'startDate": Date.valueOf("2015-07-20"), "value": "BLACK"]]

Add

person.SVTV_FC = ["endDate": Date.valueOf("2015-07-20"), 'startDate": Date.valueOf("2015-07-20"), "value": "RED"]

Update

person.SVTV_FC[0] = "RED"

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?

No

Yes

Multivalue?

No

assert claimLine.accidentState.type == "CAR"

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

Yes

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.

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.