calculate future value of fixed term deposit on opening or creation of deposit account.2

This commit is contained in:
Keith Woodlock
2012-08-15 14:17:00 +01:00
parent 10c6f9c509
commit 392be3745c
3 changed files with 85 additions and 11 deletions
@@ -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() {
@@ -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;
}
}
@@ -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;
}