From dda67913ee9a03ab58a91d7ee2d0daf0c84c9dd8 Mon Sep 17 00:00:00 2001 From: ngoulongkam <25244131+ngoulongkam@users.noreply.github.com> Date: Tue, 17 Mar 2020 09:38:51 +0000 Subject: [PATCH 01/11] HMA-2552: Added HTS calculation for first bonus calculator --- .gitignore | 3 +- .../FirstBonusTermCalculator.kt | 146 ++++++++++++++++++ .../models/CalculatorResponse.kt | 10 ++ .../gov/hmrc/FirstBonusTermCalculatorTest.kt | 115 ++++++++++++++ 4 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt create mode 100644 src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt diff --git a/.gitignore b/.gitignore index 14c9e65..1d8854f 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,5 @@ yarn-error.log /.idea/ /.gradle/ credentials.properties -gradlew.bat \ No newline at end of file +gradlew.bat +local.properties \ No newline at end of file diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt new file mode 100644 index 0000000..58f7b02 --- /dev/null +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt @@ -0,0 +1,146 @@ +/* + * 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 com.soywiz.klock.DateFormat +import com.soywiz.klock.DateTime +import com.soywiz.klock.parse +import uk.gov.hmrc.helptosavecalculator.exceptions.InvalidRegularPaymentException +import uk.gov.hmrc.helptosavecalculator.models.FirstBonusCalculatorResponse +import uk.gov.hmrc.helptosavecalculator.utils.monthsSince +import uk.gov.hmrc.helptosavecalculator.validation.RegularPaymentValidators + +object FirstBonusTermCalculator { + + fun runFirstBonusCalculator( + regularPayment: Double, + currentBalance: Double, + paidInThisMonth: Double, + accountStartDate: String, + firstTermEndDate: String, + secondTermEndDate: String, + BalanceMustBeMoreThanForBonus: Double + ): FirstBonusCalculatorResponse { + return calculateFirstBonus(regularPayment, currentBalance, paidInThisMonth, accountStartDate, firstTermEndDate, secondTermEndDate, BalanceMustBeMoreThanForBonus) + } + + private fun calculateFirstBonus( + regularPayment: Double, + currentBalance: Double, + paidInThisMonth: Double, + accountStartDate: String, + firstTermEndDate: String, + secondTermEndDate: String, + BalanceMustBeMoreThanForBonus: Double + ): FirstBonusCalculatorResponse { + validateUserInput(regularPayment) + + val accountStartDateInDateTime = convertYearMonthToDateTime(accountStartDate) + val accountFirstTermEndDateInDateTime = convertYearMonthDayToDateTime(firstTermEndDate) + val accountSecondTermEndDateInDateTime = convertYearMonthDayToDateTime(secondTermEndDate) + val monthLeftInScheme = calculateMonthsLeftInScheme(accountStartDateInDateTime, accountSecondTermEndDateInDateTime) + val monthLetInFirstTerm = calculateMonthsLeftInFirstTerm(accountStartDateInDateTime, accountFirstTermEndDateInDateTime) + val additionalSavingsThisMonth = calculateAdditionalSavingsThisMonth(regularPayment, paidInThisMonth) + val totalProjectedSavings = calculateTotalProjectedSavings(currentBalance, additionalSavingsThisMonth, regularPayment, monthLeftInScheme) + val projectedSavingsFirstBonusPeriod = calculateProjectedSavingsFirstBonusPeriod(currentBalance, additionalSavingsThisMonth, regularPayment, monthLetInFirstTerm) + val highestBalanceFirstBonusPeriod = calculateHighestBalanceFirstBonusPeriod(BalanceMustBeMoreThanForBonus, projectedSavingsFirstBonusPeriod) + val projectedFirstBonus = calculateProjectedFirstBonus(highestBalanceFirstBonusPeriod) + val projectedAdditionalSavingsFinalBonusPeriod = calculateProjectedAdditionalSavingsFinalBonusPeriod(regularPayment) + val projectedFinalBonus = calculateProjectedFinalBonus(totalProjectedSavings, highestBalanceFirstBonusPeriod) + val totalProjectedBonuses = calculateTotalProjectedBonuses(projectedFirstBonus, projectedFinalBonus) + val totalProjectedSavingsIncludingBonuses = calculateTotalProjectedSavingsIncludeBonuses(totalProjectedSavings, totalProjectedBonuses) + + return FirstBonusCalculatorResponse( + totalProjectedSavingsIncludingBonuses = totalProjectedSavingsIncludingBonuses, + totalProjectedSavings = totalProjectedSavings, + totalProjectedBonuses = totalProjectedBonuses, + projectedSavingsFirstBonusPeriod = projectedSavingsFirstBonusPeriod, + projectedFirstBonus = projectedFirstBonus, + projectedAdditionalSavingsFinalBonusPeriod = projectedAdditionalSavingsFinalBonusPeriod, + projectedFinalBonus = projectedFinalBonus + ) + } + + private fun calculateTotalProjectedSavingsIncludeBonuses(totalProjectedSavings: Double, totalProjectedBonuses: Double): Double { + return totalProjectedSavings + totalProjectedBonuses + } + + private fun calculateAdditionalSavingsThisMonth(regularPayment: Double, paidInThisMonth: Double): Double { + return if (regularPayment > paidInThisMonth) { + regularPayment - paidInThisMonth + } else { + 0.0 + } + } + + private fun calculateTotalProjectedSavings(currentBalance: Double, additionalSavingsThisMonth: Double, regularPayment: Double, monthsLeftInScheme: Int): Double { + return currentBalance + additionalSavingsThisMonth + (regularPayment * monthsLeftInScheme) + } + + private fun calculateTotalProjectedBonuses(projectedFirstBonus: Double, projectedFinalBonus: Double): Double { + return projectedFirstBonus + projectedFinalBonus + } + + private fun calculateProjectedSavingsFirstBonusPeriod(currentBalance: Double, additionalSavingsThisMonth: Double, regularPayment: Double, monthsLeftInFirstTerm: Int): Double { + return currentBalance + additionalSavingsThisMonth + (regularPayment * monthsLeftInFirstTerm) + } + + private fun calculateHighestBalanceFirstBonusPeriod(balanceMustBeMoreThanValue: Double, projectedSavingsFirstBonusPeriod: Double): Double { + return balanceMustBeMoreThanValue.takeIf { + it > projectedSavingsFirstBonusPeriod + } ?: projectedSavingsFirstBonusPeriod + } + + private fun calculateProjectedFirstBonus(highestBalanceFirstBonusPeriod: Double): Double { + return highestBalanceFirstBonusPeriod / 2 + } + + private fun calculateProjectedAdditionalSavingsFinalBonusPeriod(regularPayment: Double): Double { + return regularPayment * 24 + } + + private fun calculateProjectedFinalBonus(highestBalanceFinalBonusPeriod: Double, highestBalanceFirstBonusPeriod: Double): Double { + return if (highestBalanceFinalBonusPeriod > highestBalanceFirstBonusPeriod) { + (highestBalanceFinalBonusPeriod - highestBalanceFirstBonusPeriod) / 2 + } else { + 0.0 + } + } + + private fun calculateMonthsLeftInScheme(accountStartDate: DateTime, secondTermEndDate: DateTime): Int { + return accountStartDate.monthsSince(secondTermEndDate) + } + + private fun calculateMonthsLeftInFirstTerm(accountStartDate: DateTime, firstTermEndDate: DateTime): Int { + return accountStartDate.monthsSince(firstTermEndDate) + } + + private fun convertYearMonthToDateTime(yearMonthFormat: String): DateTime { + val dateFormat = DateFormat("yyyy-MM-dd") + return dateFormat.parse("$yearMonthFormat-01").local + } + + private fun convertYearMonthDayToDateTime(yearMonthDayFormat: String): DateTime { + val dateFormat = DateFormat("yyyy-MM-dd") + return dateFormat.parse(yearMonthDayFormat).local + } + + private fun validateUserInput(regularPayment: Double) { + if (!RegularPaymentValidators.isValidRegularPayments(regularPayment)) { + throw InvalidRegularPaymentException(regularPayment) + } + } +} \ No newline at end of file diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/CalculatorResponse.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/CalculatorResponse.kt index 070ff8b..bc9dd40 100644 --- a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/CalculatorResponse.kt +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/CalculatorResponse.kt @@ -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 +) diff --git a/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt b/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt new file mode 100644 index 0000000..0c0fc65 --- /dev/null +++ b/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt @@ -0,0 +1,115 @@ +/* + * 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 + +import uk.gov.hmrc.helptosavecalculator.Calculator +import uk.gov.hmrc.helptosavecalculator.FirstBonusTermCalculator.runFirstBonusCalculator +import uk.gov.hmrc.helptosavecalculator.exceptions.InvalidRegularPaymentException +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +class FirstBonusTermCalculatorTest { + @Test + fun `Throw Exception when regular payment is below 1`() { + assertFailsWith { + Calculator.run(regularPayment = 0.0) + } + } + + @Test + fun `Throw Exception when regular payment is above 50`() { + assertFailsWith { + Calculator.run(regularPayment = 51.0) + } + } + + @Test + fun `A new account with no payment AND 1 pound regular payment`() { + val calculator = runFirstBonusCalculator(1.0, + 0.0, + 0.0, + "2020-03", + "2022-02-28", + "2024-02-28", + 0.0) + + assertEquals(72.00, calculator.totalProjectedSavingsIncludingBonuses) + assertEquals(48.00, calculator.totalProjectedSavings) + assertEquals(24.00, calculator.totalProjectedBonuses) + assertEquals(24.00, calculator.projectedSavingsFirstBonusPeriod) + assertEquals(12.00, calculator.projectedFirstBonus) + assertEquals(24.00, calculator.projectedAdditionalSavingsFinalBonusPeriod) + assertEquals(12.00, calculator.projectedFinalBonus) + } + + @Test + fun `A new account with no payment AND 25 pound regular payment`() { + val calculator = runFirstBonusCalculator(25.0, + 0.0, + 0.0, + "2020-03", + "2022-02-28", + "2024-02-28", + 0.0) + + assertEquals(1800.00, calculator.totalProjectedSavingsIncludingBonuses) + assertEquals(1200.00, calculator.totalProjectedSavings) + assertEquals(600.00, calculator.totalProjectedBonuses) + assertEquals(600.00, calculator.projectedSavingsFirstBonusPeriod) + assertEquals(300.00, calculator.projectedFirstBonus) + assertEquals(600.00, calculator.projectedAdditionalSavingsFinalBonusPeriod) + assertEquals(300.00, calculator.projectedFinalBonus) + } + + @Test + fun `A new account with no payment AND 50 pound regular payment`() { + val calculator = runFirstBonusCalculator(50.0, + 0.0, + 0.0, + "2020-03", + "2022-02-28", + "2024-02-28", + 0.0) + + assertEquals(3600.00, calculator.totalProjectedSavingsIncludingBonuses) + assertEquals(2400.00, calculator.totalProjectedSavings) + assertEquals(1200.00, calculator.totalProjectedBonuses) + assertEquals(1200.00, calculator.projectedSavingsFirstBonusPeriod) + assertEquals(600.00, calculator.projectedFirstBonus) + assertEquals(1200.00, calculator.projectedAdditionalSavingsFinalBonusPeriod) + assertEquals(600.00, calculator.projectedFinalBonus) + } + + @Test + fun `A new account paid in 50 first month, withdrawn 256 AND 25 pounds regular payment`() { + val calculator = runFirstBonusCalculator(25.0, + 25.0, + 50.0, + "2020-03", + "2022-02-28", + "2024-02-28", + 50.0) + + assertEquals(1800.00, calculator.totalProjectedSavingsIncludingBonuses) + assertEquals(1200.00, calculator.totalProjectedSavings) + assertEquals(600.00, calculator.totalProjectedBonuses) + assertEquals(600.00, calculator.projectedSavingsFirstBonusPeriod) + assertEquals(300.00, calculator.projectedFirstBonus) + assertEquals(600.00, calculator.projectedAdditionalSavingsFinalBonusPeriod) + assertEquals(300.00, calculator.projectedFinalBonus) + } +} \ No newline at end of file From c1d517b9a49d150e23882b983be02a9af1ad47f0 Mon Sep 17 00:00:00 2001 From: ngoulongkam <25244131+ngoulongkam@users.noreply.github.com> Date: Tue, 17 Mar 2020 11:53:05 +0000 Subject: [PATCH 02/11] Address feedback --- local.properties.example | 8 ++ .../FirstBonusTermCalculation.kt | 90 ++++++++++++ .../FirstBonusTermCalculator.kt | 134 +++--------------- .../models/FirstBonusInput.kt | 26 ++++ .../utils/String.DateTime.kt | 15 ++ .../gov/hmrc/FirstBonusTermCalculatorTest.kt | 13 +- 6 files changed, 168 insertions(+), 118 deletions(-) create mode 100644 local.properties.example create mode 100644 src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt create mode 100644 src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/FirstBonusInput.kt create mode 100644 src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/utils/String.DateTime.kt diff --git a/local.properties.example b/local.properties.example new file mode 100644 index 0000000..15f064a --- /dev/null +++ b/local.properties.example @@ -0,0 +1,8 @@ +## This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +#Wed Mar 11 14:00:21 GMT 2020 +sdk.dir=/sdk diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt new file mode 100644 index 0000000..d1c6b0f --- /dev/null +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt @@ -0,0 +1,90 @@ +/* + * 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.FirstBonusInput +import uk.gov.hmrc.helptosavecalculator.utils.convertYearMonthDayToDateTime +import uk.gov.hmrc.helptosavecalculator.utils.convertYearMonthToDateTime +import uk.gov.hmrc.helptosavecalculator.utils.monthsSince +import uk.gov.hmrc.helptosavecalculator.validation.RegularPaymentValidators + +open class FirstBonusTermCalculation { + + protected fun calculateTotalProjectedSavingsIncludeBonuses(totalProjectedSavings: Double, totalProjectedBonuses: Double): Double { + return totalProjectedSavings + totalProjectedBonuses + } + + protected fun calculateAdditionalSavingsThisMonth(input: FirstBonusInput): Double { + return if (input.regularPayment > input.paidInThisMonth) { + input.regularPayment - input.paidInThisMonth + } else { + 0.0 + } + } + + protected fun calculateTotalProjectedSavings(input: FirstBonusInput, additionalSavingsThisMonth: Double, monthsLeftInScheme: Int): Double { + return input.currentBalance + additionalSavingsThisMonth + (input.regularPayment * monthsLeftInScheme) + } + + protected fun calculateTotalProjectedBonuses(projectedFirstBonus: Double, projectedFinalBonus: Double): Double { + return projectedFirstBonus + projectedFinalBonus + } + + protected fun calculateProjectedSavingsFirstBonusPeriod(input: FirstBonusInput, additionalSavingsThisMonth: Double, monthsLeftInFirstTerm: Int): Double { + return input.currentBalance + additionalSavingsThisMonth + (input.regularPayment * monthsLeftInFirstTerm) + } + + protected fun calculateHighestBalanceFirstBonusPeriod(input: FirstBonusInput, projectedSavingsFirstBonusPeriod: Double): Double { + return input.balanceMustBeMoreThanForBonus.takeIf { + it > projectedSavingsFirstBonusPeriod + } ?: projectedSavingsFirstBonusPeriod + } + + protected fun calculateProjectedFirstBonus(highestBalanceFirstBonusPeriod: Double): Double { + return highestBalanceFirstBonusPeriod / 2 + } + + protected fun calculateProjectedAdditionalSavingsFinalBonusPeriod(input: FirstBonusInput): Double { + return input.regularPayment * 24 + } + + protected fun calculateProjectedFinalBonus(highestBalanceFinalBonusPeriod: Double, highestBalanceFirstBonusPeriod: Double): Double { + return if (highestBalanceFinalBonusPeriod > highestBalanceFirstBonusPeriod) { + (highestBalanceFinalBonusPeriod - highestBalanceFirstBonusPeriod) / 2 + } else { + 0.0 + } + } + + protected fun calculateMonthsLeftInScheme(input: FirstBonusInput): Int { + val accountStartDateInDateTime = input.accountStartDate.convertYearMonthToDateTime() + val accountSecondTermEndDateInDateTime = input.secondTermEndDate.convertYearMonthDayToDateTime() + return accountStartDateInDateTime.monthsSince(accountSecondTermEndDateInDateTime) + } + + protected fun calculateMonthsLeftInFirstTerm(input: FirstBonusInput): Int { + val accountStartDateInDateTime = input.accountStartDate.convertYearMonthToDateTime() + val accountFirstTermEndDateInDateTime = input.firstTermEndDate.convertYearMonthDayToDateTime() + return accountStartDateInDateTime.monthsSince(accountFirstTermEndDateInDateTime) + } + + protected fun validateUserInput(regularPayment: Double) { + if (!RegularPaymentValidators.isValidRegularPayments(regularPayment)) { + throw InvalidRegularPaymentException(regularPayment) + } + } +} \ No newline at end of file diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt index 58f7b02..77e23d3 100644 --- a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt @@ -15,132 +15,38 @@ */ package uk.gov.hmrc.helptosavecalculator -import com.soywiz.klock.DateFormat -import com.soywiz.klock.DateTime -import com.soywiz.klock.parse -import uk.gov.hmrc.helptosavecalculator.exceptions.InvalidRegularPaymentException import uk.gov.hmrc.helptosavecalculator.models.FirstBonusCalculatorResponse -import uk.gov.hmrc.helptosavecalculator.utils.monthsSince -import uk.gov.hmrc.helptosavecalculator.validation.RegularPaymentValidators +import uk.gov.hmrc.helptosavecalculator.models.FirstBonusInput -object FirstBonusTermCalculator { +object FirstBonusTermCalculator : FirstBonusTermCalculation() { - fun runFirstBonusCalculator( - regularPayment: Double, - currentBalance: Double, - paidInThisMonth: Double, - accountStartDate: String, - firstTermEndDate: String, - secondTermEndDate: String, - BalanceMustBeMoreThanForBonus: Double - ): FirstBonusCalculatorResponse { - return calculateFirstBonus(regularPayment, currentBalance, paidInThisMonth, accountStartDate, firstTermEndDate, secondTermEndDate, BalanceMustBeMoreThanForBonus) + fun runFirstBonusCalculator(input: FirstBonusInput): FirstBonusCalculatorResponse { + return calculateFirstBonus(input) } - private fun calculateFirstBonus( - regularPayment: Double, - currentBalance: Double, - paidInThisMonth: Double, - accountStartDate: String, - firstTermEndDate: String, - secondTermEndDate: String, - BalanceMustBeMoreThanForBonus: Double - ): FirstBonusCalculatorResponse { - validateUserInput(regularPayment) + private fun calculateFirstBonus(input: FirstBonusInput): FirstBonusCalculatorResponse { + validateUserInput(input.regularPayment) - val accountStartDateInDateTime = convertYearMonthToDateTime(accountStartDate) - val accountFirstTermEndDateInDateTime = convertYearMonthDayToDateTime(firstTermEndDate) - val accountSecondTermEndDateInDateTime = convertYearMonthDayToDateTime(secondTermEndDate) - val monthLeftInScheme = calculateMonthsLeftInScheme(accountStartDateInDateTime, accountSecondTermEndDateInDateTime) - val monthLetInFirstTerm = calculateMonthsLeftInFirstTerm(accountStartDateInDateTime, accountFirstTermEndDateInDateTime) - val additionalSavingsThisMonth = calculateAdditionalSavingsThisMonth(regularPayment, paidInThisMonth) - val totalProjectedSavings = calculateTotalProjectedSavings(currentBalance, additionalSavingsThisMonth, regularPayment, monthLeftInScheme) - val projectedSavingsFirstBonusPeriod = calculateProjectedSavingsFirstBonusPeriod(currentBalance, additionalSavingsThisMonth, regularPayment, monthLetInFirstTerm) - val highestBalanceFirstBonusPeriod = calculateHighestBalanceFirstBonusPeriod(BalanceMustBeMoreThanForBonus, projectedSavingsFirstBonusPeriod) + val monthLeftInScheme = calculateMonthsLeftInScheme(input) + val monthLetInFirstTerm = calculateMonthsLeftInFirstTerm(input) + val additionalSavingsThisMonth = calculateAdditionalSavingsThisMonth(input) + val totalProjectedSavings = calculateTotalProjectedSavings(input, additionalSavingsThisMonth, monthLeftInScheme) + val projectedSavingsFirstBonusPeriod = calculateProjectedSavingsFirstBonusPeriod(input, additionalSavingsThisMonth, monthLetInFirstTerm) + val highestBalanceFirstBonusPeriod = calculateHighestBalanceFirstBonusPeriod(input, projectedSavingsFirstBonusPeriod) val projectedFirstBonus = calculateProjectedFirstBonus(highestBalanceFirstBonusPeriod) - val projectedAdditionalSavingsFinalBonusPeriod = calculateProjectedAdditionalSavingsFinalBonusPeriod(regularPayment) + val projectedAdditionalSavingsFinalBonusPeriod = calculateProjectedAdditionalSavingsFinalBonusPeriod(input) val projectedFinalBonus = calculateProjectedFinalBonus(totalProjectedSavings, highestBalanceFirstBonusPeriod) val totalProjectedBonuses = calculateTotalProjectedBonuses(projectedFirstBonus, projectedFinalBonus) val totalProjectedSavingsIncludingBonuses = calculateTotalProjectedSavingsIncludeBonuses(totalProjectedSavings, totalProjectedBonuses) return FirstBonusCalculatorResponse( - totalProjectedSavingsIncludingBonuses = totalProjectedSavingsIncludingBonuses, - totalProjectedSavings = totalProjectedSavings, - totalProjectedBonuses = totalProjectedBonuses, - projectedSavingsFirstBonusPeriod = projectedSavingsFirstBonusPeriod, - projectedFirstBonus = projectedFirstBonus, - projectedAdditionalSavingsFinalBonusPeriod = projectedAdditionalSavingsFinalBonusPeriod, - projectedFinalBonus = projectedFinalBonus + totalProjectedSavingsIncludingBonuses, + totalProjectedSavings, + totalProjectedBonuses, + projectedSavingsFirstBonusPeriod, + projectedFirstBonus, + projectedAdditionalSavingsFinalBonusPeriod, + projectedFinalBonus ) } - - private fun calculateTotalProjectedSavingsIncludeBonuses(totalProjectedSavings: Double, totalProjectedBonuses: Double): Double { - return totalProjectedSavings + totalProjectedBonuses - } - - private fun calculateAdditionalSavingsThisMonth(regularPayment: Double, paidInThisMonth: Double): Double { - return if (regularPayment > paidInThisMonth) { - regularPayment - paidInThisMonth - } else { - 0.0 - } - } - - private fun calculateTotalProjectedSavings(currentBalance: Double, additionalSavingsThisMonth: Double, regularPayment: Double, monthsLeftInScheme: Int): Double { - return currentBalance + additionalSavingsThisMonth + (regularPayment * monthsLeftInScheme) - } - - private fun calculateTotalProjectedBonuses(projectedFirstBonus: Double, projectedFinalBonus: Double): Double { - return projectedFirstBonus + projectedFinalBonus - } - - private fun calculateProjectedSavingsFirstBonusPeriod(currentBalance: Double, additionalSavingsThisMonth: Double, regularPayment: Double, monthsLeftInFirstTerm: Int): Double { - return currentBalance + additionalSavingsThisMonth + (regularPayment * monthsLeftInFirstTerm) - } - - private fun calculateHighestBalanceFirstBonusPeriod(balanceMustBeMoreThanValue: Double, projectedSavingsFirstBonusPeriod: Double): Double { - return balanceMustBeMoreThanValue.takeIf { - it > projectedSavingsFirstBonusPeriod - } ?: projectedSavingsFirstBonusPeriod - } - - private fun calculateProjectedFirstBonus(highestBalanceFirstBonusPeriod: Double): Double { - return highestBalanceFirstBonusPeriod / 2 - } - - private fun calculateProjectedAdditionalSavingsFinalBonusPeriod(regularPayment: Double): Double { - return regularPayment * 24 - } - - private fun calculateProjectedFinalBonus(highestBalanceFinalBonusPeriod: Double, highestBalanceFirstBonusPeriod: Double): Double { - return if (highestBalanceFinalBonusPeriod > highestBalanceFirstBonusPeriod) { - (highestBalanceFinalBonusPeriod - highestBalanceFirstBonusPeriod) / 2 - } else { - 0.0 - } - } - - private fun calculateMonthsLeftInScheme(accountStartDate: DateTime, secondTermEndDate: DateTime): Int { - return accountStartDate.monthsSince(secondTermEndDate) - } - - private fun calculateMonthsLeftInFirstTerm(accountStartDate: DateTime, firstTermEndDate: DateTime): Int { - return accountStartDate.monthsSince(firstTermEndDate) - } - - private fun convertYearMonthToDateTime(yearMonthFormat: String): DateTime { - val dateFormat = DateFormat("yyyy-MM-dd") - return dateFormat.parse("$yearMonthFormat-01").local - } - - private fun convertYearMonthDayToDateTime(yearMonthDayFormat: String): DateTime { - val dateFormat = DateFormat("yyyy-MM-dd") - return dateFormat.parse(yearMonthDayFormat).local - } - - private fun validateUserInput(regularPayment: Double) { - if (!RegularPaymentValidators.isValidRegularPayments(regularPayment)) { - throw InvalidRegularPaymentException(regularPayment) - } - } } \ No newline at end of file diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/FirstBonusInput.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/FirstBonusInput.kt new file mode 100644 index 0000000..cf5468a --- /dev/null +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/FirstBonusInput.kt @@ -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: String, + val firstTermEndDate: String, + val secondTermEndDate: String, + val balanceMustBeMoreThanForBonus: Double +) \ No newline at end of file diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/utils/String.DateTime.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/utils/String.DateTime.kt new file mode 100644 index 0000000..23141aa --- /dev/null +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/utils/String.DateTime.kt @@ -0,0 +1,15 @@ +package uk.gov.hmrc.helptosavecalculator.utils + +import com.soywiz.klock.DateFormat +import com.soywiz.klock.DateTime +import com.soywiz.klock.parse + +internal fun String.convertYearMonthToDateTime(): DateTime { + val dateFormat = DateFormat("yyyy-MM-dd") + return dateFormat.parse("$this-01").local +} + +internal fun String.convertYearMonthDayToDateTime(): DateTime { + val dateFormat = DateFormat("yyyy-MM-dd") + return dateFormat.parse(this).local +} \ No newline at end of file diff --git a/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt b/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt index 0c0fc65..1a31288 100644 --- a/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt +++ b/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt @@ -18,6 +18,7 @@ package uk.gov.hmrc import uk.gov.hmrc.helptosavecalculator.Calculator import uk.gov.hmrc.helptosavecalculator.FirstBonusTermCalculator.runFirstBonusCalculator import uk.gov.hmrc.helptosavecalculator.exceptions.InvalidRegularPaymentException +import uk.gov.hmrc.helptosavecalculator.models.FirstBonusInput import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -39,13 +40,14 @@ class FirstBonusTermCalculatorTest { @Test fun `A new account with no payment AND 1 pound regular payment`() { - val calculator = runFirstBonusCalculator(1.0, + val input = FirstBonusInput(1.0, 0.0, 0.0, "2020-03", "2022-02-28", "2024-02-28", 0.0) + val calculator = runFirstBonusCalculator(input) assertEquals(72.00, calculator.totalProjectedSavingsIncludingBonuses) assertEquals(48.00, calculator.totalProjectedSavings) @@ -58,13 +60,14 @@ class FirstBonusTermCalculatorTest { @Test fun `A new account with no payment AND 25 pound regular payment`() { - val calculator = runFirstBonusCalculator(25.0, + val input = FirstBonusInput(25.0, 0.0, 0.0, "2020-03", "2022-02-28", "2024-02-28", 0.0) + val calculator = runFirstBonusCalculator(input) assertEquals(1800.00, calculator.totalProjectedSavingsIncludingBonuses) assertEquals(1200.00, calculator.totalProjectedSavings) @@ -77,13 +80,14 @@ class FirstBonusTermCalculatorTest { @Test fun `A new account with no payment AND 50 pound regular payment`() { - val calculator = runFirstBonusCalculator(50.0, + val input = FirstBonusInput(50.0, 0.0, 0.0, "2020-03", "2022-02-28", "2024-02-28", 0.0) + val calculator = runFirstBonusCalculator(input) assertEquals(3600.00, calculator.totalProjectedSavingsIncludingBonuses) assertEquals(2400.00, calculator.totalProjectedSavings) @@ -96,13 +100,14 @@ class FirstBonusTermCalculatorTest { @Test fun `A new account paid in 50 first month, withdrawn 256 AND 25 pounds regular payment`() { - val calculator = runFirstBonusCalculator(25.0, + val input = FirstBonusInput(25.0, 25.0, 50.0, "2020-03", "2022-02-28", "2024-02-28", 50.0) + val calculator = runFirstBonusCalculator(input) assertEquals(1800.00, calculator.totalProjectedSavingsIncludingBonuses) assertEquals(1200.00, calculator.totalProjectedSavings) From 9499c3bf9f5d71efebb917056c1fb92b0db1c530 Mon Sep 17 00:00:00 2001 From: ngoulongkam <25244131+ngoulongkam@users.noreply.github.com> Date: Tue, 17 Mar 2020 13:24:51 +0000 Subject: [PATCH 03/11] Copy right --- .../helptosavecalculator/utils/String.DateTime.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/utils/String.DateTime.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/utils/String.DateTime.kt index 23141aa..4ffdd54 100644 --- a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/utils/String.DateTime.kt +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/utils/String.DateTime.kt @@ -1,3 +1,18 @@ +/* + * 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.utils import com.soywiz.klock.DateFormat From cf8134670bea119db8e58e25c721159082b5d9fb Mon Sep 17 00:00:00 2001 From: ngoulongkam <25244131+ngoulongkam@users.noreply.github.com> Date: Tue, 17 Mar 2020 14:00:27 +0000 Subject: [PATCH 04/11] Added readme and spotless --- README.md | 25 +++++++++++++++++++ .../FirstBonusTermCalculation.kt | 2 +- .../FirstBonusTermCalculator.kt | 2 +- .../models/CalculatorResponse.kt | 14 +++++------ .../models/FirstBonusInput.kt | 2 +- .../utils/String.DateTime.kt | 2 +- .../gov/hmrc/FirstBonusTermCalculatorTest.kt | 8 +++--- 7 files changed, 40 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 2364f25..00d1fb9 100644 --- a/README.md +++ b/README.md @@ -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: String, // "2020-03" +firstTermEndDate: String, // "2022-02-28" +secondTermEndDate: String, // "2024-02-28" +balanceMustBeMoreThanForBonus: Double // 50.0 +``` + +## Response +This will returns an object of type `FirstBonusCalculatorResponse`. +* `val totalProjectedSavingsIncludingBonuses: Double` +* `val totalProjectedSavings: Double` +* `val totalProjectedBonuses: Double` +* `val projectedSavingsFirstBonusPeriod: Double` +* `val projectedFirstBonus: Double` +* `val projectedAdditionalSavingsFinalBonusPeriod: Double` +* `val projectedFinalBonus: Double` + ## Validation To validate the monthly contributions: diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt index d1c6b0f..6f4ca0d 100644 --- a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt @@ -87,4 +87,4 @@ open class FirstBonusTermCalculation { throw InvalidRegularPaymentException(regularPayment) } } -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt index 77e23d3..c42460f 100644 --- a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt @@ -49,4 +49,4 @@ object FirstBonusTermCalculator : FirstBonusTermCalculation() { projectedFinalBonus ) } -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/CalculatorResponse.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/CalculatorResponse.kt index bc9dd40..9f64fb0 100644 --- a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/CalculatorResponse.kt +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/CalculatorResponse.kt @@ -40,11 +40,11 @@ data class MonthlyBreakdown( } data class FirstBonusCalculatorResponse( - val totalProjectedSavingsIncludingBonuses: Double, - val totalProjectedSavings: Double, - val totalProjectedBonuses: Double, - val projectedSavingsFirstBonusPeriod: Double, - val projectedFirstBonus: Double, - val projectedAdditionalSavingsFinalBonusPeriod: Double, - val projectedFinalBonus: Double + val totalProjectedSavingsIncludingBonuses: Double, + val totalProjectedSavings: Double, + val totalProjectedBonuses: Double, + val projectedSavingsFirstBonusPeriod: Double, + val projectedFirstBonus: Double, + val projectedAdditionalSavingsFinalBonusPeriod: Double, + val projectedFinalBonus: Double ) diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/FirstBonusInput.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/FirstBonusInput.kt index cf5468a..eb464d2 100644 --- a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/FirstBonusInput.kt +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/FirstBonusInput.kt @@ -23,4 +23,4 @@ data class FirstBonusInput( val firstTermEndDate: String, val secondTermEndDate: String, val balanceMustBeMoreThanForBonus: Double -) \ No newline at end of file +) diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/utils/String.DateTime.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/utils/String.DateTime.kt index 4ffdd54..16d9c4e 100644 --- a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/utils/String.DateTime.kt +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/utils/String.DateTime.kt @@ -27,4 +27,4 @@ internal fun String.convertYearMonthToDateTime(): DateTime { internal fun String.convertYearMonthDayToDateTime(): DateTime { val dateFormat = DateFormat("yyyy-MM-dd") return dateFormat.parse(this).local -} \ No newline at end of file +} diff --git a/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt b/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt index 1a31288..591b6e7 100644 --- a/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt +++ b/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt @@ -15,13 +15,13 @@ */ package uk.gov.hmrc +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith import uk.gov.hmrc.helptosavecalculator.Calculator import uk.gov.hmrc.helptosavecalculator.FirstBonusTermCalculator.runFirstBonusCalculator import uk.gov.hmrc.helptosavecalculator.exceptions.InvalidRegularPaymentException import uk.gov.hmrc.helptosavecalculator.models.FirstBonusInput -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith class FirstBonusTermCalculatorTest { @Test @@ -117,4 +117,4 @@ class FirstBonusTermCalculatorTest { assertEquals(600.00, calculator.projectedAdditionalSavingsFinalBonusPeriod) assertEquals(300.00, calculator.projectedFinalBonus) } -} \ No newline at end of file +} From 28495c700c013887d515c88c75bcd73feeed42ed Mon Sep 17 00:00:00 2001 From: ngoulongkam <25244131+ngoulongkam@users.noreply.github.com> Date: Tue, 17 Mar 2020 14:01:28 +0000 Subject: [PATCH 05/11] fix readme --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 00d1fb9..902bbad 100644 --- a/README.md +++ b/README.md @@ -63,13 +63,13 @@ balanceMustBeMoreThanForBonus: Double // 50.0 ## Response This will returns an object of type `FirstBonusCalculatorResponse`. -* `val totalProjectedSavingsIncludingBonuses: Double` -* `val totalProjectedSavings: Double` -* `val totalProjectedBonuses: Double` -* `val projectedSavingsFirstBonusPeriod: Double` -* `val projectedFirstBonus: Double` -* `val projectedAdditionalSavingsFinalBonusPeriod: Double` -* `val projectedFinalBonus: Double` +* `totalProjectedSavingsIncludingBonuses: Double` +* `totalProjectedSavings: Double` +* `totalProjectedBonuses: Double` +* `projectedSavingsFirstBonusPeriod: Double` +* `projectedFirstBonus: Double` +* `projectedAdditionalSavingsFinalBonusPeriod: Double` +* `projectedFinalBonus: Double` ## Validation From c0dc91f0b3ac5a1ab3dc4687d33feb7e9e8865e8 Mon Sep 17 00:00:00 2001 From: ngoulongkam <25244131+ngoulongkam@users.noreply.github.com> Date: Tue, 17 Mar 2020 14:37:14 +0000 Subject: [PATCH 06/11] Refactor/detekt/spotless --- .../FirstBonusTermCalculation.kt | 50 +++++++++++-------- .../FirstBonusTermCalculator.kt | 21 ++++++-- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt index 6f4ca0d..cbbe37c 100644 --- a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt @@ -15,16 +15,17 @@ */ package uk.gov.hmrc.helptosavecalculator -import uk.gov.hmrc.helptosavecalculator.exceptions.InvalidRegularPaymentException import uk.gov.hmrc.helptosavecalculator.models.FirstBonusInput import uk.gov.hmrc.helptosavecalculator.utils.convertYearMonthDayToDateTime import uk.gov.hmrc.helptosavecalculator.utils.convertYearMonthToDateTime import uk.gov.hmrc.helptosavecalculator.utils.monthsSince -import uk.gov.hmrc.helptosavecalculator.validation.RegularPaymentValidators open class FirstBonusTermCalculation { - protected fun calculateTotalProjectedSavingsIncludeBonuses(totalProjectedSavings: Double, totalProjectedBonuses: Double): Double { + protected fun calculateTotalProjectedSavingsIncludeBonuses( + totalProjectedSavings: Double, + totalProjectedBonuses: Double + ): Double { return totalProjectedSavings + totalProjectedBonuses } @@ -36,19 +37,33 @@ open class FirstBonusTermCalculation { } } - protected fun calculateTotalProjectedSavings(input: FirstBonusInput, additionalSavingsThisMonth: Double, monthsLeftInScheme: Int): Double { + protected fun calculateTotalProjectedSavings( + input: FirstBonusInput, + additionalSavingsThisMonth: Double, + monthsLeftInScheme: Int + ): Double { return input.currentBalance + additionalSavingsThisMonth + (input.regularPayment * monthsLeftInScheme) } - protected fun calculateTotalProjectedBonuses(projectedFirstBonus: Double, projectedFinalBonus: Double): Double { + protected fun calculateTotalProjectedBonuses( + projectedFirstBonus: Double, + projectedFinalBonus: Double + ): Double { return projectedFirstBonus + projectedFinalBonus } - protected fun calculateProjectedSavingsFirstBonusPeriod(input: FirstBonusInput, additionalSavingsThisMonth: Double, monthsLeftInFirstTerm: Int): Double { + protected fun calculateProjectedSavingsFirstBonusPeriod( + input: FirstBonusInput, + additionalSavingsThisMonth: Double, + monthsLeftInFirstTerm: Int + ): Double { return input.currentBalance + additionalSavingsThisMonth + (input.regularPayment * monthsLeftInFirstTerm) } - protected fun calculateHighestBalanceFirstBonusPeriod(input: FirstBonusInput, projectedSavingsFirstBonusPeriod: Double): Double { + protected fun calculateHighestBalanceFirstBonusPeriod( + input: FirstBonusInput, + projectedSavingsFirstBonusPeriod: Double + ): Double { return input.balanceMustBeMoreThanForBonus.takeIf { it > projectedSavingsFirstBonusPeriod } ?: projectedSavingsFirstBonusPeriod @@ -62,7 +77,10 @@ open class FirstBonusTermCalculation { return input.regularPayment * 24 } - protected fun calculateProjectedFinalBonus(highestBalanceFinalBonusPeriod: Double, highestBalanceFirstBonusPeriod: Double): Double { + protected fun calculateProjectedFinalBonus( + highestBalanceFinalBonusPeriod: Double, + highestBalanceFirstBonusPeriod: Double + ): Double { return if (highestBalanceFinalBonusPeriod > highestBalanceFirstBonusPeriod) { (highestBalanceFinalBonusPeriod - highestBalanceFirstBonusPeriod) / 2 } else { @@ -70,21 +88,11 @@ open class FirstBonusTermCalculation { } } - protected fun calculateMonthsLeftInScheme(input: FirstBonusInput): Int { + protected fun calculateMonthsLeftInScheme(input: FirstBonusInput): Pair { val accountStartDateInDateTime = input.accountStartDate.convertYearMonthToDateTime() val accountSecondTermEndDateInDateTime = input.secondTermEndDate.convertYearMonthDayToDateTime() - return accountStartDateInDateTime.monthsSince(accountSecondTermEndDateInDateTime) - } - - protected fun calculateMonthsLeftInFirstTerm(input: FirstBonusInput): Int { - val accountStartDateInDateTime = input.accountStartDate.convertYearMonthToDateTime() val accountFirstTermEndDateInDateTime = input.firstTermEndDate.convertYearMonthDayToDateTime() - return accountStartDateInDateTime.monthsSince(accountFirstTermEndDateInDateTime) - } - - protected fun validateUserInput(regularPayment: Double) { - if (!RegularPaymentValidators.isValidRegularPayments(regularPayment)) { - throw InvalidRegularPaymentException(regularPayment) - } + return Pair(accountStartDateInDateTime.monthsSince(accountSecondTermEndDateInDateTime), + accountStartDateInDateTime.monthsSince(accountFirstTermEndDateInDateTime)) } } diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt index c42460f..bd1593b 100644 --- a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt @@ -15,8 +15,10 @@ */ 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 : FirstBonusTermCalculation() { @@ -27,17 +29,20 @@ object FirstBonusTermCalculator : FirstBonusTermCalculation() { private fun calculateFirstBonus(input: FirstBonusInput): FirstBonusCalculatorResponse { validateUserInput(input.regularPayment) - val monthLeftInScheme = calculateMonthsLeftInScheme(input) - val monthLetInFirstTerm = calculateMonthsLeftInFirstTerm(input) + val (monthLeftInScheme, monthLetInFirstTerm) = calculateMonthsLeftInScheme(input) val additionalSavingsThisMonth = calculateAdditionalSavingsThisMonth(input) val totalProjectedSavings = calculateTotalProjectedSavings(input, additionalSavingsThisMonth, monthLeftInScheme) - val projectedSavingsFirstBonusPeriod = calculateProjectedSavingsFirstBonusPeriod(input, additionalSavingsThisMonth, monthLetInFirstTerm) - val highestBalanceFirstBonusPeriod = calculateHighestBalanceFirstBonusPeriod(input, projectedSavingsFirstBonusPeriod) + val projectedSavingsFirstBonusPeriod = calculateProjectedSavingsFirstBonusPeriod(input, + additionalSavingsThisMonth, + monthLetInFirstTerm) + val highestBalanceFirstBonusPeriod = calculateHighestBalanceFirstBonusPeriod(input, + projectedSavingsFirstBonusPeriod) val projectedFirstBonus = calculateProjectedFirstBonus(highestBalanceFirstBonusPeriod) val projectedAdditionalSavingsFinalBonusPeriod = calculateProjectedAdditionalSavingsFinalBonusPeriod(input) val projectedFinalBonus = calculateProjectedFinalBonus(totalProjectedSavings, highestBalanceFirstBonusPeriod) val totalProjectedBonuses = calculateTotalProjectedBonuses(projectedFirstBonus, projectedFinalBonus) - val totalProjectedSavingsIncludingBonuses = calculateTotalProjectedSavingsIncludeBonuses(totalProjectedSavings, totalProjectedBonuses) + val totalProjectedSavingsIncludingBonuses = calculateTotalProjectedSavingsIncludeBonuses(totalProjectedSavings, + totalProjectedBonuses) return FirstBonusCalculatorResponse( totalProjectedSavingsIncludingBonuses, @@ -49,4 +54,10 @@ object FirstBonusTermCalculator : FirstBonusTermCalculation() { projectedFinalBonus ) } + + private fun validateUserInput(regularPayment: Double) { + if (!RegularPaymentValidators.isValidRegularPayments(regularPayment)) { + throw InvalidRegularPaymentException(regularPayment) + } + } } From c1128128ecd320759f71fd8765fd214350d24993 Mon Sep 17 00:00:00 2001 From: Ngou Long Kam <25244131+ngoulongkam@users.noreply.github.com> Date: Tue, 17 Mar 2020 15:58:56 +0000 Subject: [PATCH 07/11] Update src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt Co-Authored-By: Mark Webb --- .../gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt index bd1593b..55dbfce 100644 --- a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt @@ -29,7 +29,7 @@ object FirstBonusTermCalculator : FirstBonusTermCalculation() { private fun calculateFirstBonus(input: FirstBonusInput): FirstBonusCalculatorResponse { validateUserInput(input.regularPayment) - val (monthLeftInScheme, monthLetInFirstTerm) = calculateMonthsLeftInScheme(input) + val (monthLeftInScheme, monthLeftInFirstTerm) = calculateMonthsLeftInScheme(input) val additionalSavingsThisMonth = calculateAdditionalSavingsThisMonth(input) val totalProjectedSavings = calculateTotalProjectedSavings(input, additionalSavingsThisMonth, monthLeftInScheme) val projectedSavingsFirstBonusPeriod = calculateProjectedSavingsFirstBonusPeriod(input, From 181cb9382d25ee07aeef5dffcf0a96949249d473 Mon Sep 17 00:00:00 2001 From: ngoulongkam <25244131+ngoulongkam@users.noreply.github.com> Date: Tue, 17 Mar 2020 16:33:10 +0000 Subject: [PATCH 08/11] Fix spelling and remove local.properties file --- local.properties.example | 8 -------- .../hmrc/helptosavecalculator/FirstBonusTermCalculator.kt | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 local.properties.example diff --git a/local.properties.example b/local.properties.example deleted file mode 100644 index 15f064a..0000000 --- a/local.properties.example +++ /dev/null @@ -1,8 +0,0 @@ -## This file must *NOT* be checked into Version Control Systems, -# as it contains information specific to your local configuration. -# -# Location of the SDK. This is only used by Gradle. -# For customization when using a Version Control System, please read the -# header note. -#Wed Mar 11 14:00:21 GMT 2020 -sdk.dir=/sdk diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt index 55dbfce..f9790e2 100644 --- a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt @@ -34,7 +34,7 @@ object FirstBonusTermCalculator : FirstBonusTermCalculation() { val totalProjectedSavings = calculateTotalProjectedSavings(input, additionalSavingsThisMonth, monthLeftInScheme) val projectedSavingsFirstBonusPeriod = calculateProjectedSavingsFirstBonusPeriod(input, additionalSavingsThisMonth, - monthLetInFirstTerm) + monthLeftInFirstTerm) val highestBalanceFirstBonusPeriod = calculateHighestBalanceFirstBonusPeriod(input, projectedSavingsFirstBonusPeriod) val projectedFirstBonus = calculateProjectedFirstBonus(highestBalanceFirstBonusPeriod) From fbdb55531773380281ab521f05a963de64a973ca Mon Sep 17 00:00:00 2001 From: ngoulongkam <25244131+ngoulongkam@users.noreply.github.com> Date: Wed, 18 Mar 2020 10:48:17 +0000 Subject: [PATCH 09/11] Modify input value from String to custom data class and convert to DateTime --- README.md | 6 ++--- .../FirstBonusTermCalculation.kt | 13 +++++----- .../models/FirstBonusInput.kt | 6 ++--- .../YearMonthDayInput.kt} | 20 +++++++-------- .../gov/hmrc/FirstBonusTermCalculatorTest.kt | 25 ++++++++++--------- 5 files changed, 34 insertions(+), 36 deletions(-) rename src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/{utils/String.DateTime.kt => models/YearMonthDayInput.kt} (60%) diff --git a/README.md b/README.md index 902bbad..032ca3b 100644 --- a/README.md +++ b/README.md @@ -55,9 +55,9 @@ Where `input` have the following object: regularPayment: Double, // 25.0 currentBalance: Double, // 25.0 paidInThisMonth: Double, // 50.0 -accountStartDate: String, // "2020-03" -firstTermEndDate: String, // "2022-02-28" -secondTermEndDate: String, // "2024-02-28" +accountStartDate: YearMonthDayInput, // YearMonthDayInput(2020, 3) +firstTermEndDate: YearMonthDayInput, // YearMonthDayInput(2022, 2, 28) +secondTermEndDate: YearMonthDayInput, // YearMonthDayInput(2024, 2, 28) balanceMustBeMoreThanForBonus: Double // 50.0 ``` diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt index cbbe37c..285e83b 100644 --- a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt @@ -16,8 +16,6 @@ package uk.gov.hmrc.helptosavecalculator import uk.gov.hmrc.helptosavecalculator.models.FirstBonusInput -import uk.gov.hmrc.helptosavecalculator.utils.convertYearMonthDayToDateTime -import uk.gov.hmrc.helptosavecalculator.utils.convertYearMonthToDateTime import uk.gov.hmrc.helptosavecalculator.utils.monthsSince open class FirstBonusTermCalculation { @@ -89,10 +87,11 @@ open class FirstBonusTermCalculation { } protected fun calculateMonthsLeftInScheme(input: FirstBonusInput): Pair { - val accountStartDateInDateTime = input.accountStartDate.convertYearMonthToDateTime() - val accountSecondTermEndDateInDateTime = input.secondTermEndDate.convertYearMonthDayToDateTime() - val accountFirstTermEndDateInDateTime = input.firstTermEndDate.convertYearMonthDayToDateTime() - return Pair(accountStartDateInDateTime.monthsSince(accountSecondTermEndDateInDateTime), - accountStartDateInDateTime.monthsSince(accountFirstTermEndDateInDateTime)) + 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) } } diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/FirstBonusInput.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/FirstBonusInput.kt index eb464d2..1cb19c8 100644 --- a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/FirstBonusInput.kt +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/FirstBonusInput.kt @@ -19,8 +19,8 @@ data class FirstBonusInput( val regularPayment: Double, val currentBalance: Double, val paidInThisMonth: Double, - val accountStartDate: String, - val firstTermEndDate: String, - val secondTermEndDate: String, + val accountStartDate: YearMonthDayInput, + val firstTermEndDate: YearMonthDayInput, + val secondTermEndDate: YearMonthDayInput, val balanceMustBeMoreThanForBonus: Double ) diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/utils/String.DateTime.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/YearMonthDayInput.kt similarity index 60% rename from src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/utils/String.DateTime.kt rename to src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/YearMonthDayInput.kt index 16d9c4e..18d8cda 100644 --- a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/utils/String.DateTime.kt +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/models/YearMonthDayInput.kt @@ -13,18 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package uk.gov.hmrc.helptosavecalculator.utils +package uk.gov.hmrc.helptosavecalculator.models -import com.soywiz.klock.DateFormat import com.soywiz.klock.DateTime -import com.soywiz.klock.parse -internal fun String.convertYearMonthToDateTime(): DateTime { - val dateFormat = DateFormat("yyyy-MM-dd") - return dateFormat.parse("$this-01").local -} - -internal fun String.convertYearMonthDayToDateTime(): DateTime { - val dateFormat = DateFormat("yyyy-MM-dd") - return dateFormat.parse(this).local +data class YearMonthDayInput( + val year: Int, + val month: Int, + val day: Int = 1 +) { + internal fun convertToDateTime(): DateTime { + return DateTime(year, month, day) + } } diff --git a/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt b/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt index 591b6e7..2701d94 100644 --- a/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt +++ b/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt @@ -22,6 +22,7 @@ import uk.gov.hmrc.helptosavecalculator.Calculator import uk.gov.hmrc.helptosavecalculator.FirstBonusTermCalculator.runFirstBonusCalculator import uk.gov.hmrc.helptosavecalculator.exceptions.InvalidRegularPaymentException import uk.gov.hmrc.helptosavecalculator.models.FirstBonusInput +import uk.gov.hmrc.helptosavecalculator.models.YearMonthDayInput class FirstBonusTermCalculatorTest { @Test @@ -43,9 +44,9 @@ class FirstBonusTermCalculatorTest { val input = FirstBonusInput(1.0, 0.0, 0.0, - "2020-03", - "2022-02-28", - "2024-02-28", + YearMonthDayInput(2020, 3), + YearMonthDayInput(2022, 2, 28), + YearMonthDayInput(2024, 2, 28), 0.0) val calculator = runFirstBonusCalculator(input) @@ -63,9 +64,9 @@ class FirstBonusTermCalculatorTest { val input = FirstBonusInput(25.0, 0.0, 0.0, - "2020-03", - "2022-02-28", - "2024-02-28", + YearMonthDayInput(2020, 3), + YearMonthDayInput(2022, 2, 28), + YearMonthDayInput(2024, 2, 28), 0.0) val calculator = runFirstBonusCalculator(input) @@ -83,9 +84,9 @@ class FirstBonusTermCalculatorTest { val input = FirstBonusInput(50.0, 0.0, 0.0, - "2020-03", - "2022-02-28", - "2024-02-28", + YearMonthDayInput(2020, 3), + YearMonthDayInput(2022, 2, 28), + YearMonthDayInput(2024, 2, 28), 0.0) val calculator = runFirstBonusCalculator(input) @@ -103,9 +104,9 @@ class FirstBonusTermCalculatorTest { val input = FirstBonusInput(25.0, 25.0, 50.0, - "2020-03", - "2022-02-28", - "2024-02-28", + YearMonthDayInput(2020, 3), + YearMonthDayInput(2022, 2, 28), + YearMonthDayInput(2024, 2, 28), 50.0) val calculator = runFirstBonusCalculator(input) From 286250a3741a140b3fdd391d75558ceda645eb81 Mon Sep 17 00:00:00 2001 From: ngoulongkam <25244131+ngoulongkam@users.noreply.github.com> Date: Wed, 18 Mar 2020 11:10:47 +0000 Subject: [PATCH 10/11] Address feedback --- .../FirstBonusTermCalculation.kt | 22 +++++++------- .../FirstBonusTermCalculator.kt | 30 ++++++++++++------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt index 285e83b..cdda6d7 100644 --- a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculation.kt @@ -18,16 +18,16 @@ package uk.gov.hmrc.helptosavecalculator import uk.gov.hmrc.helptosavecalculator.models.FirstBonusInput import uk.gov.hmrc.helptosavecalculator.utils.monthsSince -open class FirstBonusTermCalculation { +internal class FirstBonusTermCalculation { - protected fun calculateTotalProjectedSavingsIncludeBonuses( + fun calculateTotalProjectedSavingsIncludeBonuses( totalProjectedSavings: Double, totalProjectedBonuses: Double ): Double { return totalProjectedSavings + totalProjectedBonuses } - protected fun calculateAdditionalSavingsThisMonth(input: FirstBonusInput): Double { + fun calculateAdditionalSavingsThisMonth(input: FirstBonusInput): Double { return if (input.regularPayment > input.paidInThisMonth) { input.regularPayment - input.paidInThisMonth } else { @@ -35,7 +35,7 @@ open class FirstBonusTermCalculation { } } - protected fun calculateTotalProjectedSavings( + fun calculateTotalProjectedSavings( input: FirstBonusInput, additionalSavingsThisMonth: Double, monthsLeftInScheme: Int @@ -43,14 +43,14 @@ open class FirstBonusTermCalculation { return input.currentBalance + additionalSavingsThisMonth + (input.regularPayment * monthsLeftInScheme) } - protected fun calculateTotalProjectedBonuses( + fun calculateTotalProjectedBonuses( projectedFirstBonus: Double, projectedFinalBonus: Double ): Double { return projectedFirstBonus + projectedFinalBonus } - protected fun calculateProjectedSavingsFirstBonusPeriod( + fun calculateProjectedSavingsFirstBonusPeriod( input: FirstBonusInput, additionalSavingsThisMonth: Double, monthsLeftInFirstTerm: Int @@ -58,7 +58,7 @@ open class FirstBonusTermCalculation { return input.currentBalance + additionalSavingsThisMonth + (input.regularPayment * monthsLeftInFirstTerm) } - protected fun calculateHighestBalanceFirstBonusPeriod( + fun calculateHighestBalanceFirstBonusPeriod( input: FirstBonusInput, projectedSavingsFirstBonusPeriod: Double ): Double { @@ -67,15 +67,15 @@ open class FirstBonusTermCalculation { } ?: projectedSavingsFirstBonusPeriod } - protected fun calculateProjectedFirstBonus(highestBalanceFirstBonusPeriod: Double): Double { + fun calculateProjectedFirstBonus(highestBalanceFirstBonusPeriod: Double): Double { return highestBalanceFirstBonusPeriod / 2 } - protected fun calculateProjectedAdditionalSavingsFinalBonusPeriod(input: FirstBonusInput): Double { + fun calculateProjectedAdditionalSavingsFinalBonusPeriod(input: FirstBonusInput): Double { return input.regularPayment * 24 } - protected fun calculateProjectedFinalBonus( + fun calculateProjectedFinalBonus( highestBalanceFinalBonusPeriod: Double, highestBalanceFirstBonusPeriod: Double ): Double { @@ -86,7 +86,7 @@ open class FirstBonusTermCalculation { } } - protected fun calculateMonthsLeftInScheme(input: FirstBonusInput): Pair { + fun calculateMonthsLeftInScheme(input: FirstBonusInput): Pair { val startDate = input.accountStartDate.convertToDateTime() val secondTermEndDate = input.secondTermEndDate.convertToDateTime() val firstTermEndDate = input.firstTermEndDate.convertToDateTime() diff --git a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt index f9790e2..471bfea 100644 --- a/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt +++ b/src/commonMain/kotlin/uk/gov/hmrc/helptosavecalculator/FirstBonusTermCalculator.kt @@ -20,7 +20,9 @@ import uk.gov.hmrc.helptosavecalculator.models.FirstBonusCalculatorResponse import uk.gov.hmrc.helptosavecalculator.models.FirstBonusInput import uk.gov.hmrc.helptosavecalculator.validation.RegularPaymentValidators -object FirstBonusTermCalculator : FirstBonusTermCalculation() { +object FirstBonusTermCalculator { + + private val calculation = FirstBonusTermCalculation() fun runFirstBonusCalculator(input: FirstBonusInput): FirstBonusCalculatorResponse { return calculateFirstBonus(input) @@ -29,19 +31,25 @@ object FirstBonusTermCalculator : FirstBonusTermCalculation() { private fun calculateFirstBonus(input: FirstBonusInput): FirstBonusCalculatorResponse { validateUserInput(input.regularPayment) - val (monthLeftInScheme, monthLeftInFirstTerm) = calculateMonthsLeftInScheme(input) - val additionalSavingsThisMonth = calculateAdditionalSavingsThisMonth(input) - val totalProjectedSavings = calculateTotalProjectedSavings(input, additionalSavingsThisMonth, monthLeftInScheme) - val projectedSavingsFirstBonusPeriod = calculateProjectedSavingsFirstBonusPeriod(input, + 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 = calculateHighestBalanceFirstBonusPeriod(input, + val highestBalanceFirstBonusPeriod = calculation.calculateHighestBalanceFirstBonusPeriod(input, projectedSavingsFirstBonusPeriod) - val projectedFirstBonus = calculateProjectedFirstBonus(highestBalanceFirstBonusPeriod) - val projectedAdditionalSavingsFinalBonusPeriod = calculateProjectedAdditionalSavingsFinalBonusPeriod(input) - val projectedFinalBonus = calculateProjectedFinalBonus(totalProjectedSavings, highestBalanceFirstBonusPeriod) - val totalProjectedBonuses = calculateTotalProjectedBonuses(projectedFirstBonus, projectedFinalBonus) - val totalProjectedSavingsIncludingBonuses = calculateTotalProjectedSavingsIncludeBonuses(totalProjectedSavings, + 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( From 2d24898fc44f688d8c8d4adc26b0aa65744c4dc9 Mon Sep 17 00:00:00 2001 From: ngoulongkam <25244131+ngoulongkam@users.noreply.github.com> Date: Wed, 18 Mar 2020 12:27:51 +0000 Subject: [PATCH 11/11] Improved tests --- .../gov/hmrc/FirstBonusTermCalculationTest.kt | 189 ++++++++++++++++++ .../gov/hmrc/FirstBonusTermCalculatorTest.kt | 31 ++- 2 files changed, 211 insertions(+), 9 deletions(-) create mode 100644 src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculationTest.kt diff --git a/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculationTest.kt b/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculationTest.kt new file mode 100644 index 0000000..300492c --- /dev/null +++ b/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculationTest.kt @@ -0,0 +1,189 @@ +/* + * 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 + +import kotlin.test.Test +import kotlin.test.assertEquals +import uk.gov.hmrc.helptosavecalculator.FirstBonusTermCalculation +import uk.gov.hmrc.helptosavecalculator.models.FirstBonusInput +import uk.gov.hmrc.helptosavecalculator.models.YearMonthDayInput + +class FirstBonusTermCalculationTest { + + @Test + fun `GIVEN calculateTotalProjectedSavingsIncludeBonuses called THEN return the total`() { + val valueOne = 1.0 + val valueTwo = 2.0 + val calculation = FirstBonusTermCalculation() + val result = calculation.calculateTotalProjectedSavingsIncludeBonuses(valueOne, valueTwo) + + assertEquals(3.0, result) + } + + @Test + fun `GIVEN regular payment is above paid in this month WHEN calculateAdditionalSavingsThisMonth called THEN return the total`() { + val input = FirstBonusInput(50.0, + 25.0, + 25.0, + YearMonthDayInput(2020, 3), + YearMonthDayInput(2022, 2, 28), + YearMonthDayInput(2024, 2, 28), + 50.0) + val calculation = FirstBonusTermCalculation() + val result = calculation.calculateAdditionalSavingsThisMonth(input) + + assertEquals(25.0, result) + } + + @Test + fun `GIVEN regular payment is below paid in this month WHEN calculateAdditionalSavingsThisMonth called THEN return zero`() { + val input = FirstBonusInput(25.0, + 25.0, + 50.0, + YearMonthDayInput(2020, 3), + YearMonthDayInput(2022, 2, 28), + YearMonthDayInput(2024, 2, 28), + 50.0) + val calculation = FirstBonusTermCalculation() + val result = calculation.calculateAdditionalSavingsThisMonth(input) + + assertEquals(0.0, result) + } + + @Test + fun `GIVEN calculateTotalProjectedSavings called THEN return the total`() { + val input = FirstBonusInput(25.0, + 25.0, + 50.0, + YearMonthDayInput(2020, 3), + YearMonthDayInput(2022, 2, 28), + YearMonthDayInput(2024, 2, 28), + 50.0) + val calculation = FirstBonusTermCalculation() + val result = calculation.calculateTotalProjectedSavings(input, 2.0, 10) + + assertEquals(277.0, result) + } + + @Test + fun `GIVEN calculateTotalProjectedBonuses called THEN return the total`() { + val valueOne = 1.0 + val valueTwo = 2.0 + val calculation = FirstBonusTermCalculation() + val result = calculation.calculateTotalProjectedBonuses(valueOne, valueTwo) + + assertEquals(3.0, result) + } + + @Test + fun `GIVEN calculateProjectedSavingsFirstBonusPeriod called THEN return the total`() { + val input = FirstBonusInput(25.0, + 25.0, + 50.0, + YearMonthDayInput(2020, 3), + YearMonthDayInput(2022, 2, 28), + YearMonthDayInput(2024, 2, 28), + 50.0) + val calculation = FirstBonusTermCalculation() + val result = calculation.calculateProjectedSavingsFirstBonusPeriod(input, 2.0, 10) + + assertEquals(277.0, result) + } + + @Test + fun `GIVEN balanceMustBeMoreThanForBonus is above projectedSavingsFirstBonusPeriod WHEN calculateHighestBalanceFirstBonusPeriod called THEN return balanceMustBeMoreThanForBonus`() { + val input = FirstBonusInput(25.0, + 25.0, + 50.0, + YearMonthDayInput(2020, 3), + YearMonthDayInput(2022, 2, 28), + YearMonthDayInput(2024, 2, 28), + 50.0) + val calculation = FirstBonusTermCalculation() + val result = calculation.calculateHighestBalanceFirstBonusPeriod(input, 2.0) + + assertEquals(50.0, result) + } + + @Test + fun `GIVEN balanceMustBeMoreThanForBonus is below projectedSavingsFirstBonusPeriod WHEN calculateHighestBalanceFirstBonusPeriod called THEN return projectedSavingsFirstBonusPeriod`() { + val input = FirstBonusInput(25.0, + 25.0, + 50.0, + YearMonthDayInput(2020, 3), + YearMonthDayInput(2022, 2, 28), + YearMonthDayInput(2024, 2, 28), + 1.0) + val calculation = FirstBonusTermCalculation() + val result = calculation.calculateHighestBalanceFirstBonusPeriod(input, 2.0) + + assertEquals(2.0, result) + } + + @Test + fun `GIVEN calculateProjectedFirstBonus called THEN return the total`() { + val calculation = FirstBonusTermCalculation() + val result = calculation.calculateProjectedFirstBonus(10.0) + + assertEquals(5.0, result) + } + + @Test + fun `GIVEN calculateProjectedAdditionalSavingsFinalBonusPeriod called THEN return the total`() { + val input = FirstBonusInput(10.0, + 25.0, + 50.0, + YearMonthDayInput(2020, 3), + YearMonthDayInput(2022, 2, 28), + YearMonthDayInput(2024, 2, 28), + 1.0) + val calculation = FirstBonusTermCalculation() + val result = calculation.calculateProjectedAdditionalSavingsFinalBonusPeriod(input) + + assertEquals(240.0, result) + } + + @Test + fun `GIVEN highestBalanceFinalBonusPeriod is above highestBalanceFirstBonusPeriod WHEN calculateProjectedFinalBonus called THEN return the total`() { + val calculation = FirstBonusTermCalculation() + val result = calculation.calculateProjectedFinalBonus(10.0, 5.0) + + assertEquals(2.5, result) + } + + @Test + fun `GIVEN highestBalanceFinalBonusPeriod is below highestBalanceFirstBonusPeriod WHEN calculateProjectedFinalBonus called THEN return zero`() { + val calculation = FirstBonusTermCalculation() + val result = calculation.calculateProjectedFinalBonus(5.0, 10.0) + + assertEquals(0.0, result) + } + + @Test + fun `GIVEN calculateMonthsLeftInScheme called THEN return the remaining months left in scheme`() { + val input = FirstBonusInput(10.0, + 25.0, + 50.0, + YearMonthDayInput(2020, 3), + YearMonthDayInput(2022, 2, 28), + YearMonthDayInput(2024, 2, 28), + 1.0) + val calculation = FirstBonusTermCalculation() + val result = calculation.calculateMonthsLeftInScheme(input) + + assertEquals(Pair(47, 23), result) + } +} diff --git a/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt b/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt index 2701d94..1dc47b2 100644 --- a/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt +++ b/src/commonTest/kotlin/uk/gov/hmrc/FirstBonusTermCalculatorTest.kt @@ -18,7 +18,6 @@ package uk.gov.hmrc import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith -import uk.gov.hmrc.helptosavecalculator.Calculator import uk.gov.hmrc.helptosavecalculator.FirstBonusTermCalculator.runFirstBonusCalculator import uk.gov.hmrc.helptosavecalculator.exceptions.InvalidRegularPaymentException import uk.gov.hmrc.helptosavecalculator.models.FirstBonusInput @@ -26,21 +25,35 @@ import uk.gov.hmrc.helptosavecalculator.models.YearMonthDayInput class FirstBonusTermCalculatorTest { @Test - fun `Throw Exception when regular payment is below 1`() { + fun `GIVEN regular payment is below 1 THEN InvalidRegularPaymentException thrown`() { + val input = FirstBonusInput(0.0, + 0.0, + 0.0, + YearMonthDayInput(2020, 3), + YearMonthDayInput(2022, 2, 28), + YearMonthDayInput(2024, 2, 28), + 0.0) assertFailsWith { - Calculator.run(regularPayment = 0.0) + runFirstBonusCalculator(input) } } @Test - fun `Throw Exception when regular payment is above 50`() { + fun `GIVEN regular payment is above 50 THEN InvalidRegularPaymentException thrown`() { + val input = FirstBonusInput(51.0, + 0.0, + 0.0, + YearMonthDayInput(2020, 3), + YearMonthDayInput(2022, 2, 28), + YearMonthDayInput(2024, 2, 28), + 0.0) assertFailsWith { - Calculator.run(regularPayment = 51.0) + runFirstBonusCalculator(input) } } @Test - fun `A new account with no payment AND 1 pound regular payment`() { + fun `GIVEN new account with no payment WHEN 1 pound regular payment added THEN correct calculation displayed`() { val input = FirstBonusInput(1.0, 0.0, 0.0, @@ -60,7 +73,7 @@ class FirstBonusTermCalculatorTest { } @Test - fun `A new account with no payment AND 25 pound regular payment`() { + fun `GIVEN new account with no payment WHEN 25 pound regular payment added THEN correct calculation displayed`() { val input = FirstBonusInput(25.0, 0.0, 0.0, @@ -80,7 +93,7 @@ class FirstBonusTermCalculatorTest { } @Test - fun `A new account with no payment AND 50 pound regular payment`() { + fun `GIVEN new account with no payment WHEN 50 pound regular payment added THEN correct calculation displayed`() { val input = FirstBonusInput(50.0, 0.0, 0.0, @@ -100,7 +113,7 @@ class FirstBonusTermCalculatorTest { } @Test - fun `A new account paid in 50 first month, withdrawn 256 AND 25 pounds regular payment`() { + fun `GIVEN new account with 50 pounds first month AND withdrawn 25 WHEN 25 pound regular payment added THEN correct calculation displayed`() { val input = FirstBonusInput(25.0, 25.0, 50.0,