calculate future value of fixed term deposit on opening or creation of deposit account.2
This commit is contained in:
+13
-7
@@ -118,14 +118,19 @@ public class DepositAccount extends AbstractAuditableCustom<AppUser, Long> {
|
||||
|
||||
public static DepositAccount openNew(
|
||||
final Client client, final DepositProduct product,
|
||||
final String externalId, final Money deposit, final BigDecimal interestRate, final Integer termInMonths,
|
||||
final String externalId, final Money deposit, final BigDecimal maturityInterestRate, final Integer tenureInMonths,
|
||||
final Integer interestCompoundedEvery,
|
||||
final PeriodFrequencyType interestCompoundedFrequencyPeriodType,
|
||||
final LocalDate commencementDate,
|
||||
final boolean renewalAllowed,
|
||||
final boolean preClosureAllowed) {
|
||||
return new DepositAccount(client, product, externalId, deposit, interestRate, termInMonths,
|
||||
interestCompoundedEvery, interestCompoundedFrequencyPeriodType, commencementDate, renewalAllowed, preClosureAllowed);
|
||||
final boolean preClosureAllowed,
|
||||
final FixedTermDepositInterestCalculator fixedTermDepositInterestCalculator) {
|
||||
|
||||
Money futureValueOnMaturity = fixedTermDepositInterestCalculator.calculateInterestOnMaturityFor(deposit, tenureInMonths,
|
||||
maturityInterestRate, interestCompoundedEvery, interestCompoundedFrequencyPeriodType);
|
||||
|
||||
return new DepositAccount(client, product, externalId, deposit, maturityInterestRate, tenureInMonths,
|
||||
interestCompoundedEvery, interestCompoundedFrequencyPeriodType, commencementDate, renewalAllowed, preClosureAllowed, futureValueOnMaturity);
|
||||
}
|
||||
|
||||
protected DepositAccount() {
|
||||
@@ -139,7 +144,8 @@ public class DepositAccount extends AbstractAuditableCustom<AppUser, Long> {
|
||||
final PeriodFrequencyType interestCompoundedFrequencyPeriodType,
|
||||
final LocalDate commencementDate,
|
||||
final boolean renewalAllowed,
|
||||
final boolean preClosureAllowed) {
|
||||
final boolean preClosureAllowed,
|
||||
final Money futureValueOnMaturity) {
|
||||
this.client = client;
|
||||
this.product = product;
|
||||
this.externalId = externalId;
|
||||
@@ -163,8 +169,8 @@ public class DepositAccount extends AbstractAuditableCustom<AppUser, Long> {
|
||||
this.preClosureInterestRate = BigDecimal.ZERO;
|
||||
|
||||
// derived fields
|
||||
this.projectedInterestAccruedOnMaturity = BigDecimal.ZERO;
|
||||
this.projectedTotalOnMaturity = BigDecimal.ZERO;
|
||||
this.projectedInterestAccruedOnMaturity = futureValueOnMaturity.minus(deposit).getAmount();
|
||||
this.projectedTotalOnMaturity = futureValueOnMaturity.getAmount();
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package org.mifosng.platform.saving.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
import org.mifosng.platform.currency.domain.Money;
|
||||
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class FixedTermDepositInterestCalculator {
|
||||
|
||||
public Money calculateInterestOnMaturityFor(
|
||||
final Money deposit,
|
||||
final Integer tenureInMonths,
|
||||
final BigDecimal maturityInterestRate,
|
||||
final Integer interestCompoundedEvery,
|
||||
final PeriodFrequencyType interestCompoundedFrequencyPeriodType) {
|
||||
|
||||
// TODO - Can change 'interest compounding' fields into something simpler like interestCompoundedFrequency {None (Simple Interest), Monthly, Quarterly, Half-Yearly, Annually}
|
||||
// - the 'interestCompoundedEvery' would then be inferred form the selected type.
|
||||
// - the special case of 'simple interest' or 'no compounding' would use simple formula rather than 'future value' function.
|
||||
|
||||
// FIXME - assuming only monthly compounding period for now
|
||||
MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
|
||||
Integer monthsInYear = 12;
|
||||
|
||||
BigDecimal interestRateAsFraction = maturityInterestRate.divide(BigDecimal.valueOf(100), mc);
|
||||
BigDecimal interestRateForOneMonth = interestRateAsFraction.divide(BigDecimal.valueOf(monthsInYear.doubleValue()), mc);
|
||||
|
||||
BigDecimal ratePerCompoundingPeriod = interestRateForOneMonth.multiply(BigDecimal.valueOf(interestCompoundedEvery.doubleValue()), mc);
|
||||
|
||||
Integer numberOfPeriods = tenureInMonths / interestCompoundedEvery;
|
||||
|
||||
return fv(ratePerCompoundingPeriod, numberOfPeriods, deposit);
|
||||
}
|
||||
|
||||
private static Money fv(final BigDecimal ratePerCompoundingPeriod, final Integer numberOfPeriods, final Money presentValue) {
|
||||
|
||||
double pmtPerPeriod = Double.valueOf("0.0");
|
||||
double pv = presentValue.negated().getAmount().doubleValue();
|
||||
|
||||
double futureValue = fv(ratePerCompoundingPeriod.doubleValue(), numberOfPeriods.doubleValue(), pmtPerPeriod, pv, false);
|
||||
|
||||
return Money.of(presentValue.getCurrency(), BigDecimal.valueOf(futureValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Future value of an amount given the number of payments, rate, amount of
|
||||
* individual payment, present value and boolean value indicating whether
|
||||
* payments are due at the beginning of period (false => payments are due at
|
||||
* end of period)
|
||||
*/
|
||||
private static double fv(double ratePeriodCompoundingPeriod, double numberOfCompoundingPeriods, double pmtPerPeriod, double presentValue, boolean type) {
|
||||
double retval = 0;
|
||||
if (ratePeriodCompoundingPeriod == 0) {
|
||||
retval = -1 * (presentValue + (numberOfCompoundingPeriods * pmtPerPeriod));
|
||||
} else {
|
||||
double r1 = ratePeriodCompoundingPeriod + 1;
|
||||
retval = ((1 - Math.pow(r1, numberOfCompoundingPeriods)) * (type ? r1 : 1) * pmtPerPeriod) / ratePeriodCompoundingPeriod - presentValue
|
||||
* Math.pow(r1, numberOfCompoundingPeriods);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
+6
-4
@@ -10,6 +10,7 @@ import org.mifosng.platform.exceptions.ClientNotFoundException;
|
||||
import org.mifosng.platform.exceptions.DepositProductNotFoundException;
|
||||
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
|
||||
import org.mifosng.platform.saving.domain.DepositAccount;
|
||||
import org.mifosng.platform.saving.domain.FixedTermDepositInterestCalculator;
|
||||
import org.mifosng.platform.savingproduct.domain.DepositProduct;
|
||||
import org.mifosng.platform.savingproduct.domain.DepositProductRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -23,11 +24,14 @@ public class DepositAccountAssembler {
|
||||
|
||||
private final ClientRepository clientRepository;
|
||||
private final DepositProductRepository depositProductRepository;
|
||||
private final FixedTermDepositInterestCalculator fixedTermDepositInterestCalculator;
|
||||
|
||||
@Autowired
|
||||
public DepositAccountAssembler(final ClientRepository clientRepository, final DepositProductRepository depositProductRepository) {
|
||||
public DepositAccountAssembler(final ClientRepository clientRepository, final DepositProductRepository depositProductRepository,
|
||||
final FixedTermDepositInterestCalculator fixedTermDepositInterestCalculator) {
|
||||
this.clientRepository = clientRepository;
|
||||
this.depositProductRepository = depositProductRepository;
|
||||
this.fixedTermDepositInterestCalculator = fixedTermDepositInterestCalculator;
|
||||
}
|
||||
|
||||
public DepositAccount assembleFrom(final DepositAccountCommand command) {
|
||||
@@ -76,14 +80,12 @@ public class DepositAccountAssembler {
|
||||
}
|
||||
// end of details allowed to be overriden from product
|
||||
|
||||
// fixed-term interest calculator
|
||||
|
||||
DepositAccount account = DepositAccount.openNew(client, product, command.getExternalId(),
|
||||
deposit,
|
||||
maturityInterestRate,
|
||||
tenureInMonths, compoundingInterestEvery, compoundingInterestFrequency,
|
||||
command.getCommencementDate(),
|
||||
renewalAllowed, preClosureAllowed);
|
||||
renewalAllowed, preClosureAllowed, this.fixedTermDepositInterestCalculator);
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user