Skip to content

Commit 96c9e1a

Browse files
authored
HMA-2602: Added HTS calculation for final bonus calculator (#7)
* HMA-2602: Added HTS calculation for final bonus calculator * Format readme * Added an extra response to show if user is possible to earn final bonus * Address comment to inline format * Missed an inline format
1 parent c23e980 commit 96c9e1a

File tree

9 files changed

+502
-46
lines changed

9 files changed

+502
-46
lines changed

README.md

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ FirstBonusTermCalculator.runFirstBonusCalculator(input)
5252
```
5353
Where `input` have the following object:
5454
```
55-
regularPayment: Double, // 25.0
56-
currentBalance: Double, // 25.0
57-
paidInThisMonth: Double, // 50.0
58-
thisMonthEndDate: YearMonthDayInput, // YearMonthDayInput(2020, 3, 31)
59-
firstTermEndDate: YearMonthDayInput, // YearMonthDayInput(2022, 2, 28)
60-
secondTermEndDate: YearMonthDayInput, // YearMonthDayInput(2024, 2, 28)
61-
balanceMustBeMoreThanForBonus: Double // 50.0
55+
regularPayment: Double, // 25.0
56+
currentBalance: Double, // 25.0
57+
paidInThisMonth: Double, // 50.0
58+
thisMonthEndDate: YearMonthDayInput, // YearMonthDayInput(2020, 3, 31)
59+
firstTermEndDate: YearMonthDayInput, // YearMonthDayInput(2022, 2, 28)
60+
secondTermEndDate: YearMonthDayInput, // YearMonthDayInput(2024, 2, 28)
61+
balanceMustBeMoreThanForBonus: Double // 50.0
6262
```
6363

6464
## Response
@@ -71,6 +71,28 @@ This will returns an object of type `FirstBonusCalculatorResponse`.
7171
* `projectedAdditionalSavingsFinalBonusPeriod: Double`
7272
* `projectedFinalBonus: Double`
7373

74+
### For existing accounts in final term
75+
```kotlin
76+
FinalBonusTermCalculator.runFinalBonusCalculator(input)
77+
```
78+
Where `input` have the following object:
79+
```
80+
regularPayment: Double, // 25.0
81+
currentBalance: Double, // 25.0
82+
paidInThisMonth: Double, // 50.0
83+
thisMonthEndDate: YearMonthDayInput, // YearMonthDayInput(2022, 3, 31)
84+
secondTermEndDate: YearMonthDayInput, // YearMonthDayInput(2024, 2, 28)
85+
balanceMustBeMoreThanForBonus: Double, // 50.0
86+
secondTermBonusEstimate: Double // 25.0
87+
```
88+
89+
## Response
90+
This will returns an object of type `FinalBonusCalculatorResponse`.
91+
* `totalProjectedSavingsIncludingBonuses: Double`
92+
* `totalProjectedSavings: Double`
93+
* `totalProjectedBonuses: Double`
94+
* `canEarnFinalBonus: Boolean`
95+
7496
## Validation
7597

7698
To validate the monthly contributions:
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2020 HM Revenue & Customs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package uk.gov.hmrc.helptosavecalculator
17+
18+
import uk.gov.hmrc.helptosavecalculator.models.FinalBonusInput
19+
import uk.gov.hmrc.helptosavecalculator.utils.monthsSince
20+
21+
internal class FinalBonusTermCalculation {
22+
23+
fun calculateAdditionalSavingsThisMonth(input: FinalBonusInput) =
24+
if (input.regularPayment > input.paidInThisMonth) {
25+
input.regularPayment - input.paidInThisMonth
26+
} else 0.0
27+
28+
fun calculateTotalProjectedSavingsIncludeBonuses(
29+
totalProjectedSavings: Double,
30+
totalProjectedBonuses: Double
31+
) = totalProjectedSavings + totalProjectedBonuses
32+
33+
fun calculateTotalProjectedSavings(
34+
input: FinalBonusInput,
35+
additionalSavingsThisMonth: Double,
36+
monthsLeftInScheme: Int
37+
) = input.currentBalance + additionalSavingsThisMonth + (input.regularPayment * monthsLeftInScheme)
38+
39+
fun calculateTotalProjectedBonuses(
40+
highestBalanceFinalBonusPeriod: Double,
41+
input: FinalBonusInput
42+
) = if (highestBalanceFinalBonusPeriod > input.balanceMustBeMoreThanForBonus) {
43+
(highestBalanceFinalBonusPeriod - input.balanceMustBeMoreThanForBonus) / 2
44+
} else 0.0
45+
46+
fun calculateMaybeHighestBalanceSoFar(input: FinalBonusInput) =
47+
input.balanceMustBeMoreThanForBonus + (input.secondTermBonusEstimate * 2)
48+
49+
fun calculateHighestBalanceFinalBonusPeriod(
50+
highestBalanceSoFar: Double,
51+
totalProjectedSavings: Double
52+
) = (highestBalanceSoFar).takeIf {
53+
it > totalProjectedSavings
54+
} ?: totalProjectedSavings
55+
56+
fun calculateMonthsLeftInScheme(input: FinalBonusInput): Int {
57+
val thisMonthEndDate = input.thisMonthEndDate.convertToDateTime()
58+
val secondTermEndDate = input.secondTermEndDate.convertToDateTime()
59+
return thisMonthEndDate.monthsSince(secondTermEndDate)
60+
}
61+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2020 HM Revenue & Customs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package uk.gov.hmrc.helptosavecalculator
17+
18+
import uk.gov.hmrc.helptosavecalculator.exceptions.InvalidRegularPaymentException
19+
import uk.gov.hmrc.helptosavecalculator.models.FinalBonusCalculatorResponse
20+
import uk.gov.hmrc.helptosavecalculator.models.FinalBonusInput
21+
import uk.gov.hmrc.helptosavecalculator.validation.RegularPaymentValidators
22+
23+
object FinalBonusTermCalculator {
24+
25+
private val calculation = FinalBonusTermCalculation()
26+
27+
fun runFinalBonusCalculator(input: FinalBonusInput): FinalBonusCalculatorResponse = calculateFinalBonus(input)
28+
29+
private fun calculateFinalBonus(input: FinalBonusInput): FinalBonusCalculatorResponse {
30+
var canEarnFinalBonus = true
31+
validateUserInput(input.regularPayment)
32+
33+
val monthLeftInScheme = calculation.calculateMonthsLeftInScheme(input)
34+
val additionalSavingsThisMonth = calculation.calculateAdditionalSavingsThisMonth(input)
35+
val totalProjectedSavings = calculation.calculateTotalProjectedSavings(input,
36+
additionalSavingsThisMonth,
37+
monthLeftInScheme)
38+
val maybeHighestBalanceSoFar = calculation.calculateMaybeHighestBalanceSoFar(input)
39+
val highestBalanceFinalBonusPeriod = calculation.calculateHighestBalanceFinalBonusPeriod(
40+
maybeHighestBalanceSoFar,
41+
totalProjectedSavings)
42+
val totalProjectedBonuses = calculation.calculateTotalProjectedBonuses(
43+
highestBalanceFinalBonusPeriod,
44+
input)
45+
val totalProjectedSavingsIncludingBonuses = calculation.calculateTotalProjectedSavingsIncludeBonuses(
46+
totalProjectedSavings,
47+
totalProjectedBonuses)
48+
49+
if (totalProjectedBonuses == 0.0) {
50+
canEarnFinalBonus = false
51+
}
52+
53+
return FinalBonusCalculatorResponse(
54+
totalProjectedSavingsIncludingBonuses,
55+
totalProjectedSavings,
56+
totalProjectedBonuses,
57+
canEarnFinalBonus
58+
)
59+
}
60+
61+
private fun validateUserInput(regularPayment: Double) {
62+
if (!RegularPaymentValidators.isValidRegularPayments(regularPayment)) {
63+
throw InvalidRegularPaymentException(regularPayment)
64+
}
65+
}
66+
}

src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt

Lines changed: 16 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -23,68 +23,48 @@ internal class FirstBonusTermCalculation {
2323
fun calculateTotalProjectedSavingsIncludeBonuses(
2424
totalProjectedSavings: Double,
2525
totalProjectedBonuses: Double
26-
): Double {
27-
return totalProjectedSavings + totalProjectedBonuses
28-
}
26+
) = totalProjectedSavings + totalProjectedBonuses
2927

30-
fun calculateAdditionalSavingsThisMonth(input: FirstBonusInput): Double {
31-
return if (input.regularPayment > input.paidInThisMonth) {
28+
fun calculateAdditionalSavingsThisMonth(input: FirstBonusInput) =
29+
if (input.regularPayment > input.paidInThisMonth) {
3230
input.regularPayment - input.paidInThisMonth
33-
} else {
34-
0.0
35-
}
36-
}
31+
} else 0.0
3732

3833
fun calculateTotalProjectedSavings(
3934
input: FirstBonusInput,
4035
additionalSavingsThisMonth: Double,
4136
monthsLeftInScheme: Int
42-
): Double {
43-
return input.currentBalance + additionalSavingsThisMonth + (input.regularPayment * monthsLeftInScheme)
44-
}
37+
) = input.currentBalance + additionalSavingsThisMonth + (input.regularPayment * monthsLeftInScheme)
4538

4639
fun calculateTotalProjectedBonuses(
4740
projectedFirstBonus: Double,
4841
projectedFinalBonus: Double
49-
): Double {
50-
return projectedFirstBonus + projectedFinalBonus
51-
}
42+
) = projectedFirstBonus + projectedFinalBonus
5243

5344
fun calculateProjectedSavingsFirstBonusPeriod(
5445
input: FirstBonusInput,
5546
additionalSavingsThisMonth: Double,
5647
monthsLeftInFirstTerm: Int
57-
): Double {
58-
return input.currentBalance + additionalSavingsThisMonth + (input.regularPayment * monthsLeftInFirstTerm)
59-
}
48+
) = input.currentBalance + additionalSavingsThisMonth + (input.regularPayment * monthsLeftInFirstTerm)
6049

6150
fun calculateHighestBalanceFirstBonusPeriod(
6251
input: FirstBonusInput,
6352
projectedSavingsFirstBonusPeriod: Double
64-
): Double {
65-
return input.balanceMustBeMoreThanForBonus.takeIf {
66-
it > projectedSavingsFirstBonusPeriod
67-
} ?: projectedSavingsFirstBonusPeriod
68-
}
53+
) = input.balanceMustBeMoreThanForBonus.takeIf {
54+
it > projectedSavingsFirstBonusPeriod
55+
} ?: projectedSavingsFirstBonusPeriod
6956

70-
fun calculateProjectedFirstBonus(highestBalanceFirstBonusPeriod: Double): Double {
71-
return highestBalanceFirstBonusPeriod / 2
72-
}
57+
fun calculateProjectedFirstBonus(highestBalanceFirstBonusPeriod: Double) =
58+
highestBalanceFirstBonusPeriod / 2
7359

74-
fun calculateProjectedAdditionalSavingsFinalBonusPeriod(input: FirstBonusInput): Double {
75-
return input.regularPayment * 24
76-
}
60+
fun calculateProjectedAdditionalSavingsFinalBonusPeriod(input: FirstBonusInput) = input.regularPayment * 24
7761

7862
fun calculateProjectedFinalBonus(
7963
highestBalanceFinalBonusPeriod: Double,
8064
highestBalanceFirstBonusPeriod: Double
81-
): Double {
82-
return if (highestBalanceFinalBonusPeriod > highestBalanceFirstBonusPeriod) {
83-
(highestBalanceFinalBonusPeriod - highestBalanceFirstBonusPeriod) / 2
84-
} else {
85-
0.0
86-
}
87-
}
65+
) = if (highestBalanceFinalBonusPeriod > highestBalanceFirstBonusPeriod) {
66+
(highestBalanceFinalBonusPeriod - highestBalanceFirstBonusPeriod) / 2
67+
} else 0.0
8868

8969
fun calculateMonthsLeftInScheme(input: FirstBonusInput): Pair<Int, Int> {
9070
val thisMonthEndDate = input.thisMonthEndDate.convertToDateTime()

src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ object FirstBonusTermCalculator {
2424

2525
private val calculation = FirstBonusTermCalculation()
2626

27-
fun runFirstBonusCalculator(input: FirstBonusInput): FirstBonusCalculatorResponse {
28-
return calculateFirstBonus(input)
29-
}
27+
fun runFirstBonusCalculator(input: FirstBonusInput) = calculateFirstBonus(input)
3028

3129
private fun calculateFirstBonus(input: FirstBonusInput): FirstBonusCalculatorResponse {
3230
validateUserInput(input.regularPayment)

src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/CalculatorResponse.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,10 @@ data class FirstBonusCalculatorResponse(
4848
val projectedAdditionalSavingsFinalBonusPeriod: Double,
4949
val projectedFinalBonus: Double
5050
)
51+
52+
data class FinalBonusCalculatorResponse(
53+
val totalProjectedSavingsIncludingBonuses: Double,
54+
val totalProjectedSavings: Double,
55+
val totalProjectedBonuses: Double,
56+
val canEarnFinalBonus: Boolean
57+
)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2020 HM Revenue & Customs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package uk.gov.hmrc.helptosavecalculator.models
17+
18+
data class FinalBonusInput(
19+
val regularPayment: Double,
20+
val currentBalance: Double,
21+
val paidInThisMonth: Double,
22+
val thisMonthEndDate: YearMonthDayInput,
23+
val secondTermEndDate: YearMonthDayInput,
24+
val balanceMustBeMoreThanForBonus: Double,
25+
val secondTermBonusEstimate: Double
26+
)

0 commit comments

Comments
 (0)