Skip to content

Commit

Permalink
Merge pull request #5 from hmrc/feature/HMA-2552-first-bonus-period-c…
Browse files Browse the repository at this point in the history
…alculator

HMA-2552: Added HTS calculation for first bonus calculator
  • Loading branch information
ngoulongkam authored Mar 18, 2020
2 parents 31b1408 + 2d24898 commit f24123c
Show file tree
Hide file tree
Showing 9 changed files with 582 additions and 1 deletion.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ yarn-error.log
/.idea/
/.gradle/
credentials.properties
gradlew.bat
gradlew.bat
local.properties
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,31 @@ This will returns an object of type `CalculatorResponse`. This provide headline
* `endOfPeriod2Savings: Double`
* `endOfPeriod2Total: Double`

### For existing accounts in first term
```kotlin
FirstBonusTermCalculator.runFirstBonusCalculator(input)
```
Where `input` have the following object:
```
regularPayment: Double, // 25.0
currentBalance: Double, // 25.0
paidInThisMonth: Double, // 50.0
accountStartDate: YearMonthDayInput, // YearMonthDayInput(2020, 3)
firstTermEndDate: YearMonthDayInput, // YearMonthDayInput(2022, 2, 28)
secondTermEndDate: YearMonthDayInput, // YearMonthDayInput(2024, 2, 28)
balanceMustBeMoreThanForBonus: Double // 50.0
```

## Response
This will returns an object of type `FirstBonusCalculatorResponse`.
* `totalProjectedSavingsIncludingBonuses: Double`
* `totalProjectedSavings: Double`
* `totalProjectedBonuses: Double`
* `projectedSavingsFirstBonusPeriod: Double`
* `projectedFirstBonus: Double`
* `projectedAdditionalSavingsFinalBonusPeriod: Double`
* `projectedFinalBonus: Double`

## Validation

To validate the monthly contributions:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright 2020 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package uk.gov.hmrc.helptosavecalculator

import uk.gov.hmrc.helptosavecalculator.models.FirstBonusInput
import uk.gov.hmrc.helptosavecalculator.utils.monthsSince

internal class FirstBonusTermCalculation {

fun calculateTotalProjectedSavingsIncludeBonuses(
totalProjectedSavings: Double,
totalProjectedBonuses: Double
): Double {
return totalProjectedSavings + totalProjectedBonuses
}

fun calculateAdditionalSavingsThisMonth(input: FirstBonusInput): Double {
return if (input.regularPayment > input.paidInThisMonth) {
input.regularPayment - input.paidInThisMonth
} else {
0.0
}
}

fun calculateTotalProjectedSavings(
input: FirstBonusInput,
additionalSavingsThisMonth: Double,
monthsLeftInScheme: Int
): Double {
return input.currentBalance + additionalSavingsThisMonth + (input.regularPayment * monthsLeftInScheme)
}

fun calculateTotalProjectedBonuses(
projectedFirstBonus: Double,
projectedFinalBonus: Double
): Double {
return projectedFirstBonus + projectedFinalBonus
}

fun calculateProjectedSavingsFirstBonusPeriod(
input: FirstBonusInput,
additionalSavingsThisMonth: Double,
monthsLeftInFirstTerm: Int
): Double {
return input.currentBalance + additionalSavingsThisMonth + (input.regularPayment * monthsLeftInFirstTerm)
}

fun calculateHighestBalanceFirstBonusPeriod(
input: FirstBonusInput,
projectedSavingsFirstBonusPeriod: Double
): Double {
return input.balanceMustBeMoreThanForBonus.takeIf {
it > projectedSavingsFirstBonusPeriod
} ?: projectedSavingsFirstBonusPeriod
}

fun calculateProjectedFirstBonus(highestBalanceFirstBonusPeriod: Double): Double {
return highestBalanceFirstBonusPeriod / 2
}

fun calculateProjectedAdditionalSavingsFinalBonusPeriod(input: FirstBonusInput): Double {
return input.regularPayment * 24
}

fun calculateProjectedFinalBonus(
highestBalanceFinalBonusPeriod: Double,
highestBalanceFirstBonusPeriod: Double
): Double {
return if (highestBalanceFinalBonusPeriod > highestBalanceFirstBonusPeriod) {
(highestBalanceFinalBonusPeriod - highestBalanceFirstBonusPeriod) / 2
} else {
0.0
}
}

fun calculateMonthsLeftInScheme(input: FirstBonusInput): Pair<Int, Int> {
val startDate = input.accountStartDate.convertToDateTime()
val secondTermEndDate = input.secondTermEndDate.convertToDateTime()
val firstTermEndDate = input.firstTermEndDate.convertToDateTime()
val monthsLeftInScheme = startDate.monthsSince(secondTermEndDate)
val monthsLeftInFirstTerm = startDate.monthsSince(firstTermEndDate)
return Pair(monthsLeftInScheme, monthsLeftInFirstTerm)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2020 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package uk.gov.hmrc.helptosavecalculator

import uk.gov.hmrc.helptosavecalculator.exceptions.InvalidRegularPaymentException
import uk.gov.hmrc.helptosavecalculator.models.FirstBonusCalculatorResponse
import uk.gov.hmrc.helptosavecalculator.models.FirstBonusInput
import uk.gov.hmrc.helptosavecalculator.validation.RegularPaymentValidators

object FirstBonusTermCalculator {

private val calculation = FirstBonusTermCalculation()

fun runFirstBonusCalculator(input: FirstBonusInput): FirstBonusCalculatorResponse {
return calculateFirstBonus(input)
}

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

val (monthLeftInScheme, monthLeftInFirstTerm) = calculation.calculateMonthsLeftInScheme(input)
val additionalSavingsThisMonth = calculation.calculateAdditionalSavingsThisMonth(input)
val totalProjectedSavings = calculation.calculateTotalProjectedSavings(input,
additionalSavingsThisMonth,
monthLeftInScheme)
val projectedSavingsFirstBonusPeriod = calculation.calculateProjectedSavingsFirstBonusPeriod(input,
additionalSavingsThisMonth,
monthLeftInFirstTerm)
val highestBalanceFirstBonusPeriod = calculation.calculateHighestBalanceFirstBonusPeriod(input,
projectedSavingsFirstBonusPeriod)
val projectedFirstBonus = calculation.calculateProjectedFirstBonus(highestBalanceFirstBonusPeriod)
val projectedAdditionalSavingsFinalBonusPeriod =
calculation.calculateProjectedAdditionalSavingsFinalBonusPeriod(input)
val projectedFinalBonus = calculation.calculateProjectedFinalBonus(totalProjectedSavings,
highestBalanceFirstBonusPeriod)
val totalProjectedBonuses = calculation.calculateTotalProjectedBonuses(projectedFirstBonus,
projectedFinalBonus)
val totalProjectedSavingsIncludingBonuses =
calculation.calculateTotalProjectedSavingsIncludeBonuses(totalProjectedSavings,
totalProjectedBonuses)

return FirstBonusCalculatorResponse(
totalProjectedSavingsIncludingBonuses,
totalProjectedSavings,
totalProjectedBonuses,
projectedSavingsFirstBonusPeriod,
projectedFirstBonus,
projectedAdditionalSavingsFinalBonusPeriod,
projectedFinalBonus
)
}

private fun validateUserInput(regularPayment: Double) {
if (!RegularPaymentValidators.isValidRegularPayments(regularPayment)) {
throw InvalidRegularPaymentException(regularPayment)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,13 @@ data class MonthlyBreakdown(
) {
val bonusToDate: Double = period1Bonus + period2Bonus
}

data class FirstBonusCalculatorResponse(
val totalProjectedSavingsIncludingBonuses: Double,
val totalProjectedSavings: Double,
val totalProjectedBonuses: Double,
val projectedSavingsFirstBonusPeriod: Double,
val projectedFirstBonus: Double,
val projectedAdditionalSavingsFinalBonusPeriod: Double,
val projectedFinalBonus: Double
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2020 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package uk.gov.hmrc.helptosavecalculator.models

data class FirstBonusInput(
val regularPayment: Double,
val currentBalance: Double,
val paidInThisMonth: Double,
val accountStartDate: YearMonthDayInput,
val firstTermEndDate: YearMonthDayInput,
val secondTermEndDate: YearMonthDayInput,
val balanceMustBeMoreThanForBonus: Double
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2020 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package uk.gov.hmrc.helptosavecalculator.models

import com.soywiz.klock.DateTime

data class YearMonthDayInput(
val year: Int,
val month: Int,
val day: Int = 1
) {
internal fun convertToDateTime(): DateTime {
return DateTime(year, month, day)
}
}
Loading

0 comments on commit f24123c

Please sign in to comment.