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.
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
-
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.
-
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.
-
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.
-
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 |
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 becomesList<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 alwaysList<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) |
Set/Replace (if dynamic field already has some values) |
Clean-up or nullify the existing values first, for example:
claimLine.dependentNames[0] = null and then set the new values, for example: |
||
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 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 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" |
Yes |
assert claimLine.accidentStates[0].type == "CAR" |
assert claimLine.accidentStates[0].type == "CAR" |
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.