Part 2: Using Tables and Formulas
Using Formulas
So far, our home contents model does not offer much flexibility to the business user. A product can have precisely one base coverage and the rate is determined from the rate table. Next, we will allow the business user to flexibly define extra coverages without having to modify the model or the code. We will now use the formula language of Faktor-IPS to compute the insurance premiums.
We will use extra coverages against bicycle theft and overvoltage damage as examples:
Bicycle Theft | Overvoltage Damage | |
---|---|---|
Extra coverage sum insured |
1% of the contract’s sumInsured, maximum 3000 Euro |
5% of the contract’s sumInsured. No maximum value. |
Annual base premium |
10% of sum insured in bicycle theft coverage |
10 Euro + 3% of sum insured in overvoltage coverage |
Extra coverages of this type have their own sum insured that is dependent on the sum insured agreed upon in the contract. The annual base premium, on the other hand, is dependent on the sum insured in the coverage. In order to be able to represent these sorts of extra coverages, we extend our model as shown in the following class diagram:
One HomeContract can contain any number of extra coverages. The configuration class pertaining to HomeExtraCoverage will be named HomeExtraCoverageType. It includes the properties factorSumInsured
and maxSumInsured
. The sum insured in the extra coverage is computed by the sum insured in the contract times the factor, and it can not exceed the maximum sum insured.
Both example coverages are instances of the HomeExtraCoverageType class, as shown in the following diagram.
Before we look at the premium computation, we first have to create the new classes HomeExtraCoverage
and HomeExtraCoverageType
. The contract class creation wizard enables you to create both classes in the same process. Start the wizard and enter the data shown in the following picture on the first wizard page.
On the second page, enter the class name HomeExtraCoverageType and click Finish. Faktor-IPS will now create both classes as well as their references to one another.
Next, we have to define the relationships HomeContract
and HomeExtraCoverage
, and between HomeProduct
and HomeExtraCoverageType
, respectively. To do this, you can use the wizard for creating new relationships in the contract class editor. The wizard enables you to create the relationship on the product side at the same time.
Once the relationships have been defined, add the derived sumInsured
attribute (of type Money) to the HomeExtraCoverage
class and the attributes name
(String), factorSumInsured
(Decimal) and maxSumInsured
(Money) to the HomeExtraCoverageType
class. When you have completed the attributes, you can go on and implement the computation of the sum insured in the HomeExtraCoverage class, as follows:
/**
* Returns the sumInsured.
*
* @restrainedmodifiable
*/
@IpsAttribute(name = "sumInsured", kind = AttributeKind.DERIVED_ON_THE_FLY, valueSetKind = ValueSetKind.AllValues)
@IpsGenerated
public Money getSumInsured() {
// begin-user-code
HomeExtraCoverageType type = getHomeExtraCoverageType();
if(type == null) {
return Money.NULL;
}
Decimal factor = type.getFactorSumInsured();
Money sumInsuredContract = getHomeContract().getSumInsured();
Money sumInsured = sumInsuredContract.multiply(factor, RoundingMode.HALF_UP);
if(sumInsured.isNull()) {
return sumInsured;
}
Money maxSumInsured = type.getMaxSumInsured();
if(sumInsured.greaterThan(maxSumInsured)) {
return maxSumInsured;
}
return sumInsured;
// end-user-code
}
We will then create the coverage types for bicycle theft and overvoltage damage. Go back to the Product Definition Perspective and, in the Product Definition Explorer, select the coverages
package of the HomeProducts
project. Based on the HomeExtraCoverageType
class, you will now create two product components named BicycleTheft 2021-12 and OvervoltageDamage 2021-12, respectively, and define their properties in the editor.
Bicycle Theft 2019-07 | OvervoltageDamage 2019-07 | |
---|---|---|
Name |
Bicycle Theft |
Overvoltage Damage |
SumInsuredFactor |
0.01 |
0.05 |
MaxSumInsured |
3000 EUR |
<null> |
The next step is to assign the coverages to the products, just as we have done before with the base coverages. Contracts based on the HC-Optimal product should always include both coverages, whereas with the HC-Compact product, they are optional. You can set this by means of the association type in the component editor.
Computing the Premiums for Extra Coverages
The computation of the annual base premium should be defined with a formula by the business users. The premium due for an extra coverage will usually depend on the sum insured and any other risk-related characteristics [5]. Hence, the formula needs to access these properties. There are essentially two possible ways to do this:
-
The formula language allows random navigation of the object graph.
-
The parameters used in the formula are defined explicitly.
Faktor-IPS uses the second alternative, essentially for two reasons:
-
The syntax for navigating the object graph can quickly get too complex. How, for example, could you write a syntax determining the coverage with the highest sum insured in a way that is still easily comprehensible for business users?
-
With the second approach, the derived attributes are always up to date
5 In a home contents insurance, these could be aspects such as whether it is a house for one family or for multiple families, or which construction method has been applied.
The second aspect is best explained by means of an example. In order to compute the premium for an extra coverage, you need to know the sum insured, which is a derived attribute. If it is also a cached attribute, you have to ensure that the sum insured has been computed before calling the premium computation formula. If you want to enable any navigation through the object graph, you have to ensure that all available objects use correct values for their derived attributes. As this is error-prone and negatively affects performance, Faktor-IPS requires you to explicitly define all parameters that can be used in a formula.
Formula parameters can be of a simple type, such as the sum insured, but they can also be complex objects like, for example, the contract itself. The upside of using objects as parameters is that the parameter list does not have to be extended each time the business users need to access attributes that have not been used before. To compute the annual base premium of the extra coverage, we will use the extra coverage itself and its underlying home contract as parameters.
Before we can define the premium computation formula in the extra coverages, we have to define the formula signature with its parameters in the HomeExtraCoverageType class. To do this, open the editor for the HomeExtraCoverageType
class. On the second page click the New button in the Methods and Formula Signatures section to create a formula signature and enter the data according to the following screenshot.
Close the dialog box and save everything. The HomeExtraCoveragesType
now now includes a computeAnnualBasePremium(…)
method to compute the base premium.
Let us now open the bicycle theft coverage in order to define the premium computation formula. When you do this, you will first see a dialog box telling you that the model contains a new formula that has not yet been captured in the product definition.
Click Fix to confirm that the formula should be added. In the Tables and Formulas section, the premium rate formula will be displayed, as yet empty. Click the button next to the formula box to edit the formula. The following dialog box will open, enabling you to edit the formula and to view the available parameters:
The bicycle theft insurance premium shall amount to 10% of its sum insured. Press Ctrl-Space in the middle input box and you will now see all the parameters and functions that are available to you. Choose the "coverage" parameter and enter a dot (.) at the end. Next, you will see a choice of properties pertaining to the coverage. Choose "sumInsured" and multiply the sum insured by 0.1.
Close the dialog box and save. Similarly, you can then define that the premium for overvoltage coverage is 10Euro + 3% of the sum insured (the formula is: 10EUR + coverage.sumInsured * 0.03).
Faktor-IPS has now generated the subclasses of both product components with the formula translated in Java code. You can find the two classes in the package org.faktorips.tutorial.productdata.internal.coverage
in the Java source folder src/main/resources
[6]. The following code shows the generated computeAnnualBasePremium(…)
method for the bicycle theft coverage.
6 Names of product components can contain special characters like spaces or hyphens. As these are not allowed in Java class names, they have been replaced by underscores. This replacement operation can be configured in the ProductCmptNamingStrategy section of the ".ipsproject" file.
public Money computeAnnualBasePremium(final HomeContract contract, final HomeExtraCoverage coverage)
throws FormulaExecutionException {
try {
return coverage.getSumInsured().multiply(Decimal.valueOf("0.1"), RoundingMode.HALF_UP);
} catch (Exception e) {
StringBuilder parameterValues = new StringBuilder();
parameterValues.append("contract=");
parameterValues.append(contract == null ? "null" : contract.toString());
parameterValues.append(", ");
parameterValues.append("coverage=");
parameterValues.append(coverage == null ? "null" : coverage.toString());
throw new FormulaExecutionException(toString(), "coverage.sumInsured*0.1", parameterValues.toString(), e);
}
}
If an error is encountered while running the compiled formula in Java, Faktor-IPS will throw a RuntimeException containing the formula text and the String representation of the parameters passed in.
The only remaining task is to ensure that the formula is called when calculating the premium. To enable this, we will implement the computeAnnualBasePremium()
method in the HomeExtraCoverage
class. We can achieve this simply by delegating to the computation method in the extra coverage type, passing in as parameters the extra coverage (this) and the contract to which it belongs.
Define the method computeAnnualBasePremium
in the model class HomeExtraCoverage
with the return value Money, visibility published and without parameters and save everything.
Now open the HomeExtraCoverage
Java class and implement your method as follows:
/**
* @generated NOT
*/
@IpsGenerated
public Money computeAnnualBasePremium() {
return getHomeExtraCoverageType().computeAnnualBasePremium(getHomeContract(), this);
}
In order to have the extra coverages premium added to the total premium of the home contract, we will extend the premium computation in the HomeContract
class accordingly:
private void computeAnnualBasePremium() {
annualBasePremium = Money.euro(0, 0);
HomeBaseCoverage baseCoverage = getHomeBaseCoverage();
baseCoverage.computeAnnualBasePremium();
annualBasePremium = annualBasePremium.add(baseCoverage.getAnnualBasePremium());
// Iterate over extra coverages and sum their base premiums
for (HomeExtraCoverage coverage : getHomeExtraCoverages()) {
annualBasePremium = annualBasePremium.add(coverage.computeAnnualBasePremium());
}
}
Finally, at the end of this chapter, we will test our new functionality by extending our JUnit test once more:
Unresolved directive in <stdin> - include::../../../../../SampleSolution/HomeProducts/src/test/java/TutorialTest.java[tags=testComputePremiumBicycleTheft]
In this second part of the tutorial we have taken a look at how tables are used in Faktor-IPS, how to implement premium computation and, using extra coverages as an example, we examined how to design a model in a way which allows it to be extended flexibly.
A further tutorial demonstrates how to work with the model classes created here in a practical application (Tutorial "Home Contents Offer System").
The tutorial on model partitioning shows how to divide complex models into meaningful parts and how to handle these. Specifically, by way of various examples, the tutorial illustrates the separation into different lines of business (lob) and how to separate lob-specific from cross-lob aspects.
The support available in testing Faktor-IPS is shown in the tutorial "Software tests with Faktor-IPS".