Add interest posting functionality for saving accounts

rename_patches_after_rebasing

remove existed db script file
This commit is contained in:
Madhukar
2013-01-22 13:48:29 +05:30
parent 2ab7097b85
commit e8cc870f21
27 changed files with 452 additions and 127 deletions
@@ -0,0 +1,3 @@
Alter TABLE `m_saving_schedule`
ADD COLUMN `interest_accured` decimal(21,4) DEFAULT '0.0000' AFTER `deposit_paid`
@@ -1,2 +0,0 @@
ALTER TABLE m_saving_account
ADD COLUMN `deposit_every` bigint(20) DEFAULT NULL AFTER `tenure_type`;
@@ -55,7 +55,7 @@ public class GoogleGsonPortfolioApiJsonSerializerService implements PortfolioApi
"preClosureAllowed", "preClosureInterestRate", "withdrawnonDate", "rejectedonDate", "closedonDate", "isLockinPeriodAllowed",
"lockinPeriod", "lockinPeriodType", "productOptions", "currencyOptions", "savingsProductTypeOptions", "tenureTypeOptions",
"savingFrequencyOptions", "savingsInterestTypeOptions", "lockinPeriodTypeOptions", "interestCalculationOptions","permissions","savingScheduleDatas",
"dueAmount", "savingScheduleData"));
"dueAmount", "savingScheduleData", "transactions","interestPostEvery","interestPostFrequency"));
// guarantors
private static final Set<String> GUARANTOR_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("externalGuarantor", "existingClientId",
@@ -817,7 +817,7 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo
Set<String> supportedParams = new HashSet<String>(Arrays.asList("clientId", "productId", "externalId", "currencyCode",
"digitsAfterDecimal", "savingsDepositAmountPerPeriod", "recurringInterestRate", "savingInterestRate", "tenure",
"commencementDate", "locale", "dateFormat", "isLockinPeriodAllowed", "lockinPeriod", "lockinPeriodType",
"savingProductType", "tenureType", "depositfrequency", "interestType", "interestCalculationMethod", "minimumBalanceForWithdrawal",
"tenureType", "depositfrequency", "interestType", "interestCalculationMethod", "minimumBalanceForWithdrawal",
"isPartialDepositAllowed", "depositEvery","interestPostEvery","interestPostFrequency"));
checkForUnsupportedParameters(requestMap, supportedParams);
Set<String> modifiedParameters = new HashSet<String>();
@@ -837,7 +837,6 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo
Integer lockinPeriodType = extractIntegerParameter("lockinPeriodType", requestMap, modifiedParameters);
LocalDate commencementDate = extractLocalDateParameter("commencementDate", requestMap, modifiedParameters);
Integer savingProductType = extractIntegerParameter("savingProductType", requestMap, modifiedParameters);
Integer tenureType = extractIntegerParameter("tenureType", requestMap, modifiedParameters);
Integer depositfrequency = extractIntegerParameter("depositfrequency", requestMap, modifiedParameters);
Integer interestType = extractIntegerParameter("interestType", requestMap, modifiedParameters);
@@ -851,7 +850,7 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo
return new SavingAccountCommand(modifiedParameters, resourceIdentifier, clientId, productId, externalId, currencyCode,
digitsAfterDecimalValue, savingsDepositAmount, recurringInterestRate, savingInterestRate, tenure, commencementDate,
savingProductType, tenureType, depositfrequency, interestType, minimumBalanceForWithdrawal, interestCalculationMethod,
tenureType, depositfrequency, interestType, minimumBalanceForWithdrawal, interestCalculationMethod,
isLockinPeriodAllowed, isPartialDepositAllowed, lockinPeriod, lockinPeriodType, depositEvery, interestPostEvery, interestPostFrequency);
}
@@ -42,6 +42,7 @@ import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountWithdrawa
import org.mifosplatform.portfolio.savingsaccount.command.SavingStateTransitionsCommand;
import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountData;
import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountForLookup;
import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountTransactionsData;
import org.mifosplatform.portfolio.savingsaccount.data.SavingPermissionData;
import org.mifosplatform.portfolio.savingsaccount.data.SavingScheduleData;
import org.mifosplatform.portfolio.savingsaccount.service.CalculateSavingSchedule;
@@ -103,7 +104,7 @@ public class SavingsAccountApiResource {
"interestCalculationMethod", "tenure", "tenureType", "projectedCommencementDate", "actualCommencementDate", "maturesOnDate",
"projectedInterestAccuredOnMaturity", "actualInterestAccured", "projectedMaturityAmount", "actualMaturityAmount",
"preClosureAllowed", "preClosureInterestRate", "withdrawnonDate", "rejectedonDate", "closedonDate", "isLockinPeriodAllowed",
"lockinPeriod", "lockinPeriodType","outstandingAmount","savingScheduleData"));
"lockinPeriod", "lockinPeriodType","outstandingAmount","savingScheduleData","transactions","interestPostEvery","interestPostFrequency"));
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@@ -173,6 +174,7 @@ public class SavingsAccountApiResource {
SavingPermissionData permissions = null;
SavingScheduleData savingScheduleData = null;
Collection<SavingAccountTransactionsData> transactions = null;
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
@@ -191,8 +193,8 @@ public class SavingsAccountApiResource {
Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
if (!associationParameters.isEmpty()) {
if (associationParameters.contains("all")) {
associationParameters.add("savingScheduleData");
responseParameters.addAll(Arrays.asList("permissions","savingScheduleData"));
associationParameters.addAll(Arrays.asList("savingScheduleData","transactions"));
responseParameters.addAll(Arrays.asList("permissions","savingScheduleData","transactions"));
} else {
responseParameters.addAll(associationParameters);
}
@@ -202,6 +204,10 @@ public class SavingsAccountApiResource {
savingScheduleData = this.savingAccountReadPlatformService.retrieveSavingsAccountSchedule(accountId,currencyData);
account = new SavingAccountData(account,savingScheduleData);
}
if (associationParameters.contains("transactions")) {
transactions = this.savingAccountReadPlatformService.retrieveSavingsAccountTransactions(accountId);
account = new SavingAccountData(account,transactions);
}
permissions = this.savingAccountReadPlatformService.retrieveSavingAccountPermissions(account);
account = new SavingAccountData(account, permissions);
}
@@ -258,10 +264,10 @@ public class SavingsAccountApiResource {
List<EnumOptionData> lockinPeriodTypeOptions = Arrays.asList(months);
EnumOptionData averagebal = SavingProductEnumerations.savingInterestCalculationMethod(SavingInterestCalculationMethod.AVERAGEBAL);
EnumOptionData minbal = SavingProductEnumerations.savingInterestCalculationMethod(SavingInterestCalculationMethod.MINBAL);
// EnumOptionData minbal = SavingProductEnumerations.savingInterestCalculationMethod(SavingInterestCalculationMethod.MINBAL);
EnumOptionData monthlyCollection = SavingProductEnumerations
.savingInterestCalculationMethod(SavingInterestCalculationMethod.MONTHLYCOLLECTION);
List<EnumOptionData> interestCalculationOptions = Arrays.asList(averagebal, minbal, monthlyCollection);
List<EnumOptionData> interestCalculationOptions = Arrays.asList(averagebal, monthlyCollection);
BigDecimal dueAmount = this.savingAccountReadPlatformService.deriveSavingDueAmount(account);
return new SavingAccountData(account, productOptions, currencyOptions, savingsProductTypeOptions,tenureTypeOptions,savingFrequencyOptions,
savingsInterestTypeOptions,lockinPeriodTypeOptions,interestCalculationOptions, dueAmount);
@@ -13,12 +13,14 @@ public class CalculateSavingScheduleCommand {
private final BigDecimal interestRate;
private final LocalDate paymentsStartingFromDate;
private final Integer tenure;
private final Integer tenureType;
private final Integer interestPostEvery;
private final Integer interestPostFrequency;
private final Integer interestCalculationMethod;
public CalculateSavingScheduleCommand(final Long productId, final BigDecimal deposit, final Integer depositEvery,
final Integer paymentFrequencyType, final BigDecimal interestRate, final LocalDate paymentsStartingFromDate,
final Integer tenure,final Integer interestPostEvery, final Integer interestPostFrequency) {
final Integer tenure, final Integer tenureType, final Integer interestPostEvery, final Integer interestPostFrequency, final Integer interestCalculationMethod) {
this.productId = productId;
this.deposit = deposit;
@@ -27,8 +29,10 @@ public class CalculateSavingScheduleCommand {
this.interestRate = interestRate;
this.paymentsStartingFromDate = paymentsStartingFromDate;
this.tenure = tenure;
this.tenureType = tenureType;
this.interestPostEvery = interestPostEvery;
this.interestPostFrequency = interestPostFrequency;
this.interestCalculationMethod = interestCalculationMethod;
}
@@ -68,4 +72,12 @@ public class CalculateSavingScheduleCommand {
return this.interestPostFrequency;
}
public Integer getTenureType() {
return this.tenureType;
}
public Integer getInterestCalculationMethod() {
return this.interestCalculationMethod;
}
}
@@ -24,7 +24,6 @@ public class SavingAccountCommand {
private final Integer lockinPeriodType;
private final LocalDate commencementDate;
private final Integer savingProductType;
private final Integer tenureType;
private final Integer frequency;
private final Integer interestType;
@@ -42,7 +41,7 @@ public class SavingAccountCommand {
public SavingAccountCommand(final Set<String> modifiedParameters, final Long id, final Long clientId, final Long productId,
final String externalId, final String currencyCode, final Integer digitsAfterDecimal, final BigDecimal savingsDepositAmount,
final BigDecimal recurringInterestRate, final BigDecimal savingInterestRate, final Integer tenure,
final LocalDate commencementDate, final Integer savingProductType, final Integer tenureType, final Integer frequency,
final LocalDate commencementDate, final Integer tenureType, final Integer frequency,
final Integer interestType, final BigDecimal minimumBalanceForWithdrawal, final Integer interestCalculationMethod,
final boolean isLockinPeriodAllowed, final boolean isPartialDepositAllowed, final Integer lockInPeriod,
final Integer lockinPeriodType, final Integer depositEvery,final Integer interestPostEvery,final Integer interestPostFrequency) {
@@ -61,7 +60,6 @@ public class SavingAccountCommand {
this.modifiedParameters = modifiedParameters;
this.commencementDate = commencementDate;
this.savingProductType = savingProductType;
this.tenureType = tenureType;
this.frequency = frequency;
this.interestType = interestType;
@@ -134,10 +132,6 @@ public class SavingAccountCommand {
return commencementDate;
}
public Integer getSavingProductType() {
return savingProductType;
}
public Integer getTenureType() {
return tenureType;
}
@@ -261,7 +255,7 @@ public class SavingAccountCommand {
public CalculateSavingScheduleCommand toCalculateSavingScheduleCommand() {
return new CalculateSavingScheduleCommand(productId, savingsDepositAmount, depositEvery, frequency, recurringInterestRate,
commencementDate, tenure, interestPostEvery, interestPostFrequency);
commencementDate, tenure, tenureType, interestPostEvery, interestPostFrequency,interestCalculationMethod);
}
}
@@ -32,7 +32,6 @@ public class SavingAccountCommandValidator {
baseDataValidator.reset().parameter("savingInterestRate").value(command.getSavingInterestRate()).notNull().zeroOrPositiveAmount();
baseDataValidator.reset().parameter("savingInterestRate")
.comapareMinAndMaxOfTwoBigDecmimalNos(command.getSavingInterestRate(), command.getRecurringInterestRate());
baseDataValidator.reset().parameter("savingProductType").value(command.getSavingProductType()).notNull();
baseDataValidator.reset().parameter("tenureType").value(command.getTenureType()).notNull();
baseDataValidator.reset().parameter("tenure").value(command.getTenure()).notNull();
baseDataValidator.reset().parameter("frequency").value(command.getFrequency()).notNull();
@@ -72,7 +71,6 @@ public class SavingAccountCommandValidator {
.zeroOrPositiveAmount();
baseDataValidator.reset().parameter("savingInterestRate")
.comapareMinAndMaxOfTwoBigDecmimalNos(command.getSavingInterestRate(), command.getRecurringInterestRate());
baseDataValidator.reset().parameter("savingProductType").value(command.getSavingProductType()).ignoreIfNull();
baseDataValidator.reset().parameter("tenureType").value(command.getTenureType()).ignoreIfNull();
baseDataValidator.reset().parameter("tenure").value(command.getTenure()).ignoreIfNull();
baseDataValidator.reset().parameter("frequency").value(command.getFrequency()).ignoreIfNull();
@@ -72,6 +72,10 @@ public class SavingAccountData {
private final BigDecimal outstandingAmount;
private final BigDecimal dueAmount;
private final SavingScheduleData savingScheduleData;
private final Collection<SavingAccountTransactionsData> transactions;
private final Integer interestPostEvery;
private final EnumOptionData interestPostFrequency;
public static SavingAccountData createFrom(final Long clientId, final String clientDisplayName) {
return new SavingAccountData(clientId, clientDisplayName);
@@ -85,7 +89,7 @@ public class SavingAccountData {
BigDecimal projectedInterestAccuredOnMaturity, BigDecimal actualInterestAccured, BigDecimal projectedMaturityAmount,
BigDecimal actualMaturityAmount, boolean preClosureAllowed, BigDecimal preClosureInterestRate, LocalDate withdrawnonDate,
LocalDate rejectedonDate, LocalDate closedonDate, boolean isLockinPeriodAllowed, Integer lockinPeriod,
EnumOptionData lockinPeriodType, Integer depositEvery, BigDecimal outstandingAmount) {
EnumOptionData lockinPeriodType, Integer depositEvery, BigDecimal outstandingAmount, Integer interestPostEvery, EnumOptionData interestPostFrequency) {
this.id = id;
this.status = status;
this.externalId = externalId;
@@ -133,6 +137,9 @@ public class SavingAccountData {
this.outstandingAmount = outstandingAmount;
this.dueAmount = BigDecimal.ZERO;
this.savingScheduleData = null;
this.transactions = null;
this.interestPostEvery = interestPostEvery;
this.interestPostFrequency = interestPostFrequency;
}
public SavingAccountData(Long clientId, String clientName) {
@@ -183,6 +190,9 @@ public class SavingAccountData {
this.outstandingAmount = BigDecimal.ZERO;
this.dueAmount =BigDecimal.ZERO;
this.savingScheduleData = null;
this.transactions = null;
this.interestPostEvery =null;
this.interestPostFrequency = null;
}
public SavingAccountData(SavingAccountData account, Collection<SavingProductLookup> productOptions, List<CurrencyData> currencyOptions,
@@ -238,6 +248,9 @@ public class SavingAccountData {
this.permissions = account.permissions;
this.outstandingAmount = account.outstandingAmount;
this.savingScheduleData = account.savingScheduleData;
this.transactions = account.transactions;
this.interestPostEvery = account.interestPostEvery;
this.interestPostFrequency = account.interestPostFrequency;
}
// FIXME - Madhukar - unused variables been passed into construction - why?
@@ -277,7 +290,7 @@ public class SavingAccountData {
this.withdrawnonDate = null;
this.rejectedonDate = null;
this.closedonDate = null;
this.isLockinPeriodAllowed = false;
this.isLockinPeriodAllowed = lockinPeriodAllowed;
this.lockinPeriod = lockinPeriod;
this.lockinPeriodType = lockinPeriodType;
this.depositEvery = depositEvery;
@@ -294,6 +307,9 @@ public class SavingAccountData {
this.outstandingAmount = BigDecimal.ZERO;
this.dueAmount = BigDecimal.ZERO;
this.savingScheduleData = null;
this.transactions = null;
this.interestPostEvery =3;
this.interestPostFrequency = savingsFrequencyType;
}
public SavingAccountData(SavingAccountData account, SavingPermissionData permissions) {
@@ -345,6 +361,9 @@ public class SavingAccountData {
this.outstandingAmount = account.outstandingAmount;
this.dueAmount = account.dueAmount;
this.savingScheduleData = account.savingScheduleData;
this.transactions = account.transactions;
this.interestPostEvery = account.interestPostEvery;
this.interestPostFrequency = account.interestPostFrequency;
}
public SavingAccountData(SavingAccountData account, SavingScheduleData savingScheduleData) {
@@ -396,6 +415,63 @@ public class SavingAccountData {
this.outstandingAmount = account.outstandingAmount;
this.dueAmount = account.dueAmount;
this.savingScheduleData = savingScheduleData;
this.transactions = account.transactions;
this.interestPostEvery = account.interestPostEvery;
this.interestPostFrequency = account.interestPostFrequency;
}
public SavingAccountData(SavingAccountData account, Collection<SavingAccountTransactionsData> transactions) {
this.id = account.id;
this.status = account.status;
this.externalId = account.externalId;
this.clientId = account.clientId;
this.clientName = account.clientName;
this.productId = account.productId;
this.productName = account.productName;
this.productType = account.productType;
this.currencyData = account.currencyData;
this.savingsDepositAmountPerPeriod = account.savingsDepositAmountPerPeriod;
this.savingsFrequencyType = account.savingsFrequencyType;
this.totalDepositAmount = account.totalDepositAmount;
this.recurringInterestRate = account.recurringInterestRate;
this.savingInterestRate = account.savingInterestRate;
this.interestType = account.interestType;
this.interestCalculationMethod = account.interestCalculationMethod;
this.tenure = account.tenure;
this.tenureType = account.tenureType;
this.projectedCommencementDate = account.projectedCommencementDate;
this.actualCommencementDate = account.actualCommencementDate;
this.maturesOnDate = account.maturesOnDate;
this.projectedInterestAccuredOnMaturity = account.projectedInterestAccuredOnMaturity;
this.actualInterestAccured = account.actualInterestAccured;
this.projectedMaturityAmount = account.projectedMaturityAmount;
this.actualMaturityAmount = account.actualMaturityAmount;
this.preClosureAllowed = account.preClosureAllowed;
this.preClosureInterestRate = account.preClosureInterestRate;
this.withdrawnonDate = account.withdrawnonDate;
this.rejectedonDate = account.rejectedonDate;
this.closedonDate = account.closedonDate;
this.isLockinPeriodAllowed = account.isLockinPeriodAllowed;
this.lockinPeriod = account.lockinPeriod;
this.lockinPeriodType = account.lockinPeriodType;
this.depositEvery = account.depositEvery;
this.productOptions = account.productOptions;
this.currencyOptions = account.currencyOptions;
this.savingsProductTypeOptions = account.savingsProductTypeOptions;
this.tenureTypeOptions = account.tenureTypeOptions;
this.savingFrequencyOptions = account.savingFrequencyOptions;
this.savingsInterestTypeOptions = account.savingsInterestTypeOptions;
this.lockinPeriodTypeOptions = account.lockinPeriodTypeOptions;
this.interestCalculationOptions = account.interestCalculationOptions;
this.savingScheduleDatas = account.savingScheduleDatas;
this.permissions = account.permissions;
this.outstandingAmount = account.outstandingAmount;
this.dueAmount = account.dueAmount;
this.savingScheduleData = account.savingScheduleData;
this.transactions = transactions;
this.interestPostEvery = account.interestPostEvery;
this.interestPostFrequency = account.interestPostFrequency;
}
public Long getId() {
@@ -569,5 +645,13 @@ public class SavingAccountData {
public BigDecimal getOutstandingAmount() {
return this.outstandingAmount;
}
public Integer getInterestPostEvery() {
return this.interestPostEvery;
}
public EnumOptionData getInterestPostFrequency() {
return this.interestPostFrequency;
}
}
@@ -0,0 +1,33 @@
package org.mifosplatform.portfolio.savingsaccount.data;
import java.math.BigDecimal;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
public class SavingAccountTransactionsData {
@SuppressWarnings("unused")
private final Long id;
@SuppressWarnings("unused")
private final LocalDate transactionDate;
@SuppressWarnings("unused")
private final EnumOptionData transactionType;
@SuppressWarnings("unused")
private final BigDecimal amount;
public SavingAccountTransactionsData() {
this.id = null;
this.transactionDate = null;
this.transactionType = null;
this.amount = BigDecimal.ZERO;
}
public SavingAccountTransactionsData(final Long id, final LocalDate transactionDate, EnumOptionData transactionType, BigDecimal amount){
this.id = id;
this.transactionDate = transactionDate;
this.transactionType = transactionType;
this.amount = amount;
}
}
@@ -9,12 +9,16 @@ public class SavingScheduleData {
private final CurrencyData currency;
private final BigDecimal cumulativeDepositDue;
private final BigDecimal cumulativeDepositPaid;
private final BigDecimal cummulativeInterestAccured;
private final Collection<SavingSchedulePeriodData> periods;
public SavingScheduleData(final CurrencyData currency, final BigDecimal cumulativeDepositDue,
final Collection<SavingSchedulePeriodData> periods) {
public SavingScheduleData(final CurrencyData currency, final BigDecimal cumulativeDepositDue, final BigDecimal cumulativeDepositPaid,
final BigDecimal cummulativeInterestAccured, final Collection<SavingSchedulePeriodData> periods) {
this.currency = currency;
this.cumulativeDepositDue = cumulativeDepositDue;
this.cumulativeDepositPaid = cumulativeDepositPaid;
this.cummulativeInterestAccured = cummulativeInterestAccured;
this.periods = periods;
}
@@ -27,7 +31,15 @@ public class SavingScheduleData {
return this.cumulativeDepositDue;
}
public Collection<SavingSchedulePeriodData> getPeriods() {
public BigDecimal getCumulativeDepositPaid() {
return this.cumulativeDepositPaid;
}
public BigDecimal getCummulativeInterestAccured() {
return this.cummulativeInterestAccured;
}
public Collection<SavingSchedulePeriodData> getPeriods() {
return this.periods;
}
@@ -10,17 +10,19 @@ public class SavingSchedulePeriodData {
private final LocalDate dueDate;
private final BigDecimal depositDue;
private final BigDecimal depositPaid;
private final BigDecimal interestAccured;
public SavingSchedulePeriodData(final Integer period, final LocalDate dueDate, final BigDecimal depositDue, final BigDecimal depositPaid) {
public SavingSchedulePeriodData(final Integer period, final LocalDate dueDate, final BigDecimal depositDue, final BigDecimal depositPaid, final BigDecimal interestAccured) {
this.period = period;
this.dueDate = dueDate;
this.depositDue = depositDue;
this.depositPaid = depositPaid;
this.interestAccured = interestAccured;
}
public static SavingSchedulePeriodData addScheduleInformation(int periodNumber, LocalDate scheduleDate,
BigDecimal depositAmountPerPeriod) {
return new SavingSchedulePeriodData(periodNumber, scheduleDate, depositAmountPerPeriod, BigDecimal.ZERO);
BigDecimal depositAmountPerPeriod, BigDecimal interestAccured) {
return new SavingSchedulePeriodData(periodNumber, scheduleDate, depositAmountPerPeriod, BigDecimal.ZERO, interestAccured);
}
public Integer getPeriod() {
@@ -39,5 +41,7 @@ public class SavingSchedulePeriodData {
return this.depositPaid;
}
public BigDecimal getInterestAccured() {
return this.interestAccured;
}
}
@@ -3,11 +3,11 @@ package org.mifosplatform.portfolio.savingsaccount.domain;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
import org.mifosplatform.portfolio.savingsaccountproduct.domain.SavingFrequencyType;
public interface DepositScheduleDateGenerator {
List<LocalDate> generate(LocalDate startDate, Integer paymentPeriods, Integer depositFrequency, PeriodFrequencyType depositFrequencyType);
List<LocalDate> generate(LocalDate startDate, Integer paymentPeriods, Integer depositFrequency, SavingFrequencyType savingFrequencyType);
}
@@ -34,27 +34,29 @@ public class ReccuringDepositInterestCalculator {
if(savingInterestCalculationMethod.isAverageBalance()){
BigDecimal totalAmount = BigDecimal.ZERO;
BigDecimal termAmount = BigDecimal.ZERO;
BigDecimal totalInterest = BigDecimal.ZERO;
BigDecimal closingBalance = BigDecimal.ZERO;
BigDecimal amountPerPeriod = savingsDepositPerPeriod.getAmount();
Integer depositEveryOriginalValue = depositEvery;
BigDecimal amountPermonth = amountPerPeriod.divide(BigDecimal.valueOf(depositEvery.doubleValue()));
BigDecimal amountPermonth = BigDecimal.valueOf(amountPerPeriod.doubleValue()/depositEvery.doubleValue());
while (noOfTimestobeCalculated > 0) {
while (depositEvery > 0) {
termAmount = termAmount.add(amountPermonth);
totalAmount = totalAmount.add(termAmount);
closingBalance = closingBalance.add(amountPermonth);
termAmount = termAmount.add(closingBalance);
depositEvery--;
}
BigDecimal averageAmount = BigDecimal.valueOf(totalAmount.doubleValue()/depositEveryOriginalValue.doubleValue());
BigDecimal averageAmount = BigDecimal.valueOf(termAmount.doubleValue()/depositEveryOriginalValue.doubleValue());
BigDecimal interestRatePerTerm = BigDecimal.valueOf(interestRateAsFraction.doubleValue()/monthsInYear.doubleValue()*depositEveryOriginalValue.doubleValue());
BigDecimal interestperTerm = averageAmount.multiply(interestRatePerTerm);
totalInterest = totalInterest.add(interestperTerm);
depositEvery = depositEveryOriginalValue;
totalAmount = BigDecimal.ZERO;
termAmount = BigDecimal.ZERO;
noOfTimestobeCalculated--;
}
finalAmount = termAmount.add(totalInterest);
finalAmount = closingBalance.add(totalInterest);
} else if(savingInterestCalculationMethod.isMonthlyCollection()){
/*
@@ -73,14 +75,15 @@ public class ReccuringDepositInterestCalculator {
BigDecimal monthsExpressedInYears;
BigDecimal timeforOneCalculationPeriod;
BigDecimal amountPerPeriod;
Integer tempTenure = tenure;
while (noOfTimestobeCalculated > 0) {
monthsExpressedInYears = BigDecimal.valueOf(tenure.doubleValue() / monthsInYear.doubleValue());
monthsExpressedInYears = BigDecimal.valueOf(tempTenure.doubleValue() / monthsInYear.doubleValue());
timeforOneCalculationPeriod = BigDecimal.valueOf(noofTimesInterestCompoundPerYear * monthsExpressedInYears.doubleValue());
amountPerPeriod = savingsDepositPerPeriod.getAmount().multiply(
BigDecimal.valueOf(Math.pow(onePlusInterestRatePerPeriod.doubleValue(), timeforOneCalculationPeriod.doubleValue())));
finalAmount = finalAmount.add(amountPerPeriod);
noOfTimestobeCalculated--;
tempTenure = tempTenure-depositEvery;
}
}
return Money.of(savingsDepositPerPeriod.getCurrency(), finalAmount);
@@ -204,12 +204,13 @@ public class SavingAccount extends AbstractAuditableCustom<AppUser, Long> {
Integer depositEvery, Integer interestPostEvery, Integer interestPostFrequency) {
Money futureValueOnMaturity = null;
if (savingProductType.isReccuring()) {
if (savingProductType.isReccuring() && tenureTypeEnum.isFixedPeriod()) {
futureValueOnMaturity = reccuringDepositInterestCalculator.calculateInterestOnMaturityFor(savingsDeposit, tenure,
reccuringInterestRate, commencementDate, tenureTypeEnum, savingFrequencyType, savingInterestCalculationMethod,depositEvery);
} else if (savingProductType.isRegular()) {
futureValueOnMaturity = reccuringDepositInterestCalculator.calculateInterestOnMaturityFor(savingsDeposit, tenure,
savingInterestRate, commencementDate, tenureTypeEnum, savingFrequencyType, savingInterestCalculationMethod,depositEvery);
} else if (savingProductType.isReccuring() && tenureTypeEnum.isPerpetual()) {
futureValueOnMaturity = savingsDeposit;
} else {
futureValueOnMaturity = savingsDeposit;
}
return new SavingAccount(client, externalId, product, savingsDeposit, reccuringInterestRate, savingInterestRate, tenure,
@@ -255,8 +256,8 @@ public class SavingAccount extends AbstractAuditableCustom<AppUser, Long> {
this.lockinPeriod = lockinPeriod;
this.lockinPeriodType = lockinPeriodType.getValue();
this.projectedTotalOnMaturity = futureValueOnMaturity.getAmount();
this.projectedInterestAccruedOnMaturity = futureValueOnMaturity.getAmount().subtract(
BigDecimal.valueOf(savingsDeposit.getAmount().doubleValue() * tenure.doubleValue() / payEvery)); //presently only for months
BigDecimal projectedInterestForReccuring = futureValueOnMaturity.getAmount().subtract(BigDecimal.valueOf(savingsDeposit.getAmount().doubleValue() * tenure.doubleValue() / payEvery)); //presently only for months
this.projectedInterestAccruedOnMaturity = savingProductType.isReccuring() && tenureTypeEnum.isFixedPeriod() ? projectedInterestForReccuring : BigDecimal.ZERO;
this.payEvery = payEvery;
this.outstandingAmount = BigDecimal.ZERO;
this.interestPostEvery = interestPostEvery;
@@ -267,15 +268,17 @@ public class SavingAccount extends AbstractAuditableCustom<AppUser, Long> {
BigDecimal savingInterestRate, Integer tenure, LocalDate commencementDate, TenureTypeEnum tenureTypeEnum,
SavingProductType savingProductType, SavingFrequencyType savingFrequencyType,
SavingInterestCalculationMethod savingInterestCalculationMethod, boolean isLockinPeriodAllowed, Integer lockinPeriod,
PeriodFrequencyType lockinPeriodType, ReccuringDepositInterestCalculator reccuringDepositInterestCalculator,Integer payEvery) {
PeriodFrequencyType lockinPeriodType, ReccuringDepositInterestCalculator reccuringDepositInterestCalculator,Integer payEvery,
SavingsInterestType interestType, Integer interestPostEvery, Integer interestPostFrequency) {
Money futureValueOnMaturity = null;
if (savingProductType.isReccuring()) {
Money futureValueOnMaturity = null;
if (savingProductType.isReccuring() && tenureTypeEnum.isFixedPeriod()) {
futureValueOnMaturity = reccuringDepositInterestCalculator.calculateInterestOnMaturityFor(savingsDeposit, tenure,
reccuringInterestRate, commencementDate, tenureTypeEnum, savingFrequencyType, savingInterestCalculationMethod,payEvery);
} else if (savingProductType.isRegular()) {
futureValueOnMaturity = reccuringDepositInterestCalculator.calculateInterestOnMaturityFor(savingsDeposit, tenure,
savingInterestRate, commencementDate, tenureTypeEnum, savingFrequencyType, savingInterestCalculationMethod,payEvery);
} else if (savingProductType.isReccuring() && tenureTypeEnum.isPerpetual()) {
futureValueOnMaturity = savingsDeposit;
} else {
futureValueOnMaturity = savingsDeposit;
}
this.product = product;
@@ -288,17 +291,20 @@ public class SavingAccount extends AbstractAuditableCustom<AppUser, Long> {
this.tenureType = tenureTypeEnum.getValue();
this.savingProductType = savingProductType.getValue();
this.frequency = savingFrequencyType.getValue();
this.interestType = interestType.getValue();
this.interestCalculationMethod = savingInterestCalculationMethod.getValue();
this.isLockinPeriodAllowed = isLockinPeriodAllowed;
this.lockinPeriod = lockinPeriod;
this.lockinPeriodType = lockinPeriodType.getValue();
this.payEvery = payEvery;
this.outstandingAmount = BigDecimal.ZERO;
this.interestPostEvery = interestPostEvery;
this.interestPostFrequency=interestPostFrequency;
this.savingScheduleInstallments.clear();
// FIXME - MADHUKAR - futureValueOnMaturity is possibly null
this.projectedTotalOnMaturity = futureValueOnMaturity.getAmount();
this.projectedInterestAccruedOnMaturity = futureValueOnMaturity.getAmount().subtract(
BigDecimal.valueOf(savingsDeposit.getAmount().doubleValue() * tenure.doubleValue()));
BigDecimal projectedInterestForReccuring = futureValueOnMaturity.getAmount().subtract(BigDecimal.valueOf(savingsDeposit.getAmount().doubleValue() * tenure.doubleValue() / payEvery)); //presently only for months
this.projectedInterestAccruedOnMaturity = savingProductType.isReccuring() && tenureTypeEnum.isFixedPeriod() ? projectedInterestForReccuring : BigDecimal.ZERO;
}
public boolean isDeleted() {
@@ -456,6 +462,7 @@ public class SavingAccount extends AbstractAuditableCustom<AppUser, Long> {
this.maturesOnDate = null;
this.withdrawnOnDate = withdrawnOn.toDateTimeAtCurrentTime().toDate();
this.closedOnDate = withdrawnOn.toDateTimeAtCurrentTime().toDate();
this.savingScheduleInstallments.clear();
if (withdrawnOn.isBefore(projectedCommencementDate())) {
@@ -480,7 +487,6 @@ public class SavingAccount extends AbstractAuditableCustom<AppUser, Long> {
this.closedOnDate = new Date();
}
@SuppressWarnings("null")
public void approveSavingAccount(LocalDate approvalDate, BigDecimal savingsDepositAmountPerPeriod, BigDecimal recurringInterestRate,
BigDecimal savingInterestRate, Integer interestType, Integer tenure, Integer tenureType, Integer frequency, Integer payEvery,
DepositLifecycleStateMachine depositLifecycleStateMachine, ReccuringDepositInterestCalculator reccuringDepositInterestCalculator,
@@ -496,12 +502,13 @@ public class SavingAccount extends AbstractAuditableCustom<AppUser, Long> {
SavingInterestCalculationMethod interestCalculationMethod = SavingInterestCalculationMethod.fromInt(this.interestCalculationMethod);
Money futureValueOnMaturity = null;
if (SavingProductType.fromInt(product.getSavingProductRelatedDetail().getSavingProductType()).isReccuring()) {
if (SavingProductType.fromInt(this.savingProductType).isReccuring() && tenureTypeEnum.isFixedPeriod()) {
futureValueOnMaturity = reccuringDepositInterestCalculator.calculateInterestOnMaturityFor(savingsAmountPerPeriod, tenure,
recurringInterestRate, approvalDate, tenureTypeEnum, savingFrequencyType, interestCalculationMethod,payEvery);
} else if (SavingProductType.fromInt(product.getSavingProductRelatedDetail().getSavingProductType()).isRegular()) {
futureValueOnMaturity = reccuringDepositInterestCalculator.calculateInterestOnMaturityFor(savingsAmountPerPeriod, tenure,
savingInterestRate, approvalDate, tenureTypeEnum, savingFrequencyType, interestCalculationMethod,payEvery);
} else if (SavingProductType.fromInt(this.savingProductType).isReccuring() && tenureTypeEnum.isPerpetual()) {
futureValueOnMaturity = savingsAmountPerPeriod;
} else {
futureValueOnMaturity = savingsAmountPerPeriod;
}
this.actualCommencementDate = approvalDate.toDate();
@@ -515,7 +522,8 @@ public class SavingAccount extends AbstractAuditableCustom<AppUser, Long> {
this.payEvery = payEvery;
this.savingScheduleInstallments.clear();
this.total=futureValueOnMaturity.getAmount();
this.interestAccrued = futureValueOnMaturity.getAmount().subtract(BigDecimal.valueOf(savingsDepositAmountPerPeriod.doubleValue() * tenure/payEvery.doubleValue()));
BigDecimal interestForReccuring = futureValueOnMaturity.getAmount().subtract(BigDecimal.valueOf(savingsDepositAmountPerPeriod.doubleValue() * tenure.doubleValue()/payEvery.doubleValue())); //presently only for months
this.interestAccrued = SavingProductType.fromInt(this.savingProductType).isReccuring() && tenureTypeEnum.isFixedPeriod() ? interestForReccuring : BigDecimal.ZERO;
this.interestPostEvery = interestPostEvery;
this.interestPostFrequency = interestPostFrequency;
@@ -537,25 +545,32 @@ public class SavingAccount extends AbstractAuditableCustom<AppUser, Long> {
this.outstandingAmount = this.outstandingAmount.add(command.getSavingsDepostiAmountPerPeriod());
this.totalSavingsAmount = this.totalSavingsAmount.add(command.getSavingsDepostiAmountPerPeriod());
for(SavingScheduleInstallments savingScheduleInstallment : this.savingScheduleInstallments){
if(!savingScheduleInstallment.isCompleted()){
depositAmount = savingScheduleInstallment.getDeposit();
remainAmountToBePaid = depositAmount.subtract(savingScheduleInstallment.getDepositPaid());
if (remainAmountToBePaid.doubleValue() > 0) {
if (commandRemainDepositAmount.doubleValue() > 0) {
if (commandRemainDepositAmount.doubleValue() >= remainAmountToBePaid.doubleValue()) {
savingScheduleInstallment.setDepositPaid(remainAmountToBePaid.add(savingScheduleInstallment.getDepositPaid()));
savingScheduleInstallment.setPaymentDate(command.getDepositDate().toDate());
savingScheduleInstallment.setCompleted(true);
commandRemainDepositAmount = commandRemainDepositAmount.subtract(remainAmountToBePaid);
} else if(commandRemainDepositAmount.doubleValue() < remainAmountToBePaid.doubleValue()){
savingScheduleInstallment.setDepositPaid(commandRemainDepositAmount.add(savingScheduleInstallment.getDepositPaid()));
savingScheduleInstallment.setPaymentDate(command.getDepositDate().toDate());
commandRemainDepositAmount = BigDecimal.ZERO;
if(SavingProductType.fromInt(this.savingProductType).isReccuring() && TenureTypeEnum.fromInt(tenureType).isFixedPeriod()) {
for(SavingScheduleInstallments savingScheduleInstallment : this.savingScheduleInstallments){
if(!savingScheduleInstallment.isCompleted()){
depositAmount = savingScheduleInstallment.getDeposit();
remainAmountToBePaid = depositAmount.subtract(savingScheduleInstallment.getDepositPaid());
if (remainAmountToBePaid.doubleValue() > 0) {
if (commandRemainDepositAmount.doubleValue() > 0) {
if (commandRemainDepositAmount.doubleValue() >= remainAmountToBePaid.doubleValue()) {
savingScheduleInstallment.setDepositPaid(remainAmountToBePaid.add(savingScheduleInstallment.getDepositPaid()));
savingScheduleInstallment.setPaymentDate(command.getDepositDate().toDate());
savingScheduleInstallment.setCompleted(true);
commandRemainDepositAmount = commandRemainDepositAmount.subtract(remainAmountToBePaid);
} else if(commandRemainDepositAmount.doubleValue() < remainAmountToBePaid.doubleValue()){
savingScheduleInstallment.setDepositPaid(commandRemainDepositAmount.add(savingScheduleInstallment.getDepositPaid()));
savingScheduleInstallment.setPaymentDate(command.getDepositDate().toDate());
commandRemainDepositAmount = BigDecimal.ZERO;
}
}
}
}
}
} else {
for(SavingScheduleInstallments savingScheduleInstallment : this.savingScheduleInstallments){
savingScheduleInstallment.setDepositPaid(remainAmountToBePaid.add(savingScheduleInstallment.getDepositPaid()));
savingScheduleInstallment.setPaymentDate(command.getDepositDate().toDate());
}
}
}
@@ -582,7 +597,7 @@ public class SavingAccount extends AbstractAuditableCustom<AppUser, Long> {
this.externalId = this.getId() + "_" + this.externalId;
}
public void postInterest(ReccuringDepositInterestCalculator reccuringDepositInterestCalculator) {
public void postInterest() {
LocalDate lastInterestPostedDate = getLastInterestPostedDate();
@SuppressWarnings("unused")
@@ -643,8 +658,9 @@ public class SavingAccount extends AbstractAuditableCustom<AppUser, Long> {
termTotalAmount = termTotalAmount.add(totalOutstandingAmount);
transactionStartDate = transactionEndDate;
isOutstandingAmountCalculated = true;
}
// FIXME-MADHUKAR calculate interest on calculation method
// TODO- calculate interest on calculation method
// if (savingInterestCalculationMethod.isAverageBalance()) {
averageBalanceForTerm =BigDecimal.valueOf(termTotalAmount.doubleValue()/this.interestPostEvery);
@@ -45,6 +45,10 @@ public class SavingScheduleInstallments extends AbstractAuditableCustom<AppUser,
@Column(name = "deposit_paid", scale = 6, precision = 19, nullable = true)
private BigDecimal depositPaid;
@SuppressWarnings("unused")
@Column(name = "interest_accured", scale = 6, precision = 19, nullable = true)
private BigDecimal interstAccured;
@SuppressWarnings("unused")
@Column(name = "completed_derived", nullable = false)
private boolean completed;
@@ -55,7 +59,7 @@ public class SavingScheduleInstallments extends AbstractAuditableCustom<AppUser,
this.paymentDate = null;
}
public SavingScheduleInstallments(SavingAccount account, Date dueDate, Integer installmentNumber, BigDecimal deposit) {
public SavingScheduleInstallments(SavingAccount account, Date dueDate, Integer installmentNumber, BigDecimal deposit, BigDecimal interstAccured) {
this.savingAccount = account;
this.dueDate = dueDate;
this.installmentNumber = installmentNumber;
@@ -63,6 +67,7 @@ public class SavingScheduleInstallments extends AbstractAuditableCustom<AppUser,
this.paymentDate = null;
this.depositPaid = BigDecimal.ZERO;
this.completed = false;
this.interstAccured = interstAccured;
}
public void updateAccount(SavingAccount account) {
@@ -1,17 +1,24 @@
package org.mifosplatform.portfolio.savingsaccount.service;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepository;
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
import org.mifosplatform.portfolio.savingsaccount.command.CalculateSavingScheduleCommand;
import org.mifosplatform.portfolio.savingsaccount.data.SavingScheduleData;
import org.mifosplatform.portfolio.savingsaccount.data.SavingSchedulePeriodData;
import org.mifosplatform.portfolio.savingsaccountproduct.domain.SavingFrequencyType;
import org.mifosplatform.portfolio.savingsaccountproduct.domain.SavingInterestCalculationMethod;
import org.mifosplatform.portfolio.savingsaccountproduct.domain.SavingProduct;
import org.mifosplatform.portfolio.savingsaccountproduct.domain.SavingProductRepository;
import org.mifosplatform.portfolio.savingsaccountproduct.domain.SavingProductType;
import org.mifosplatform.portfolio.savingsaccountproduct.exception.SavingProductNotFoundException;
import org.mifosplatform.portfolio.savingsdepositproduct.domain.TenureTypeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -36,23 +43,36 @@ public class CalculateSavingScheduleImpl implements CalculateSavingSchedule {
public SavingScheduleData calculateSavingSchedule(CalculateSavingScheduleCommand command) {
context.authenticatedUser();
SavingScheduleData savingScheduleData = null;
// FIXME Validations should write here
final SavingProduct savingProduct = this.savingProductRepository.findOne(command.getProductId());
if (savingProduct == null) throw new SavingProductNotFoundException(command.getProductId());
final Integer depositFrequency = command.getDepositEvery();
final PeriodFrequencyType depositFrequencyType = PeriodFrequencyType.fromInt(command.getPaymentFrequencyType());
final SavingFrequencyType savingFrequencyType = SavingFrequencyType.fromInt(command.getPaymentFrequencyType());
final LocalDate scheduleStartDate = command.getPaymentsStartingFromDate();
@SuppressWarnings("unused")
final BigDecimal interestRate = command.getInterestRate();
final BigDecimal depositAmountPerPeriod = command.getDeposit();
final Integer tenure = command.getTenure();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneByCode(savingProduct.getCurrency()
.getCode());
SavingScheduleData savingScheduleData = savingScheduleGenerator.generate(scheduleStartDate, depositAmountPerPeriod,
depositFrequency, depositFrequencyType, savingProduct, tenure, applicationCurrency);
SavingProductType savingProductType = SavingProductType.fromInt(savingProduct.getSavingProductRelatedDetail().getSavingProductType());
TenureTypeEnum tenureType = TenureTypeEnum.fromInt(command.getTenureType());
SavingInterestCalculationMethod interestCalculationMethod = SavingInterestCalculationMethod.fromInt(command.getInterestCalculationMethod());
if (savingProductType.isReccuring() && tenureType.isFixedPeriod()) {
savingScheduleData = savingScheduleGenerator.generate(scheduleStartDate, depositAmountPerPeriod,
depositFrequency, savingFrequencyType, savingProduct, tenure, applicationCurrency,interestRate,interestCalculationMethod);
} else {
final Collection<SavingSchedulePeriodData> periods = new ArrayList<SavingSchedulePeriodData>();
CurrencyData currencyData = new CurrencyData(applicationCurrency.getCode(), applicationCurrency.getName(),
savingProduct.getCurrency().getDigitsAfterDecimal(), applicationCurrency.getDisplaySymbol(), applicationCurrency.getNameCode());
SavingSchedulePeriodData installment = SavingSchedulePeriodData.addScheduleInformation(1, new LocalDate(command.getPaymentsStartingFromDate()).plusMonths(depositFrequency), command.getDeposit(), BigDecimal.ZERO);
periods.add(installment);
savingScheduleData = new SavingScheduleData(currencyData, command.getDeposit(), BigDecimal.ZERO, BigDecimal.ZERO, periods);
}
return savingScheduleData;
}
@@ -4,14 +4,14 @@ import java.util.ArrayList;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
import org.mifosplatform.portfolio.savingsaccount.domain.DepositScheduleDateGenerator;
import org.mifosplatform.portfolio.savingsaccountproduct.domain.SavingFrequencyType;
public class DefaultDepositScheduleDateGenerator implements DepositScheduleDateGenerator {
@Override
public List<LocalDate> generate(LocalDate scheuleStartDate, Integer paymentPeriods, Integer depositFrequency,
PeriodFrequencyType depositFrequencyType) {
SavingFrequencyType savingFrequencyType) {
List<LocalDate> duePaymentPeriodDates = new ArrayList<LocalDate>(depositFrequency);
LocalDate startDate = scheuleStartDate;
@@ -22,21 +22,24 @@ public class DefaultDepositScheduleDateGenerator implements DepositScheduleDateG
if (period == 1) {
duePaymentPeriodDate = startDate;
} else {
switch (depositFrequencyType) {
case DAYS:
switch (savingFrequencyType) {
case DAILY:
duePaymentPeriodDate = startDate.plusDays(depositFrequency);
break;
case WEEKS:
duePaymentPeriodDate = startDate.plusWeeks(depositFrequency);
break;
case MONTHS:
case MONTHLY:
duePaymentPeriodDate = startDate.plusMonths(depositFrequency);
break;
case YEARS:
duePaymentPeriodDate = startDate.plusYears(depositFrequency);
case QUATERLY:
duePaymentPeriodDate = startDate.plusMonths(3*depositFrequency);
break;
case INVALID:
case HALFYEARLY:
duePaymentPeriodDate = startDate.plusMonths(6*depositFrequency);
break;
case YEARLY:
duePaymentPeriodDate = startDate.plusYears(depositFrequency);
break;
default:
break;
}
}
duePaymentPeriodDates.add(duePaymentPeriodDate);
@@ -80,9 +80,6 @@ public class SavingAccountAssembler {
}
SavingProductType savingProductType = SavingProductType.fromInt(product.getSavingProductRelatedDetail().getSavingProductType());
if (command.getSavingProductType() != null) {
savingProductType = SavingProductType.fromInt(command.getSavingProductType());
}
SavingFrequencyType savingFrequencyType = SavingFrequencyType.fromInt(product.getSavingProductRelatedDetail().getFrequency());
if (command.getFrequency() != null) {
@@ -135,7 +132,7 @@ public class SavingAccountAssembler {
for (SavingSchedulePeriodData savingSchedulePeriodData : savingScheduleData.getPeriods()) {
final SavingScheduleInstallments installment = new SavingScheduleInstallments(account, savingSchedulePeriodData.getDueDate()
.toDate(), savingSchedulePeriodData.getPeriod(), savingSchedulePeriodData.getDepositDue());
.toDate(), savingSchedulePeriodData.getPeriod(), savingSchedulePeriodData.getDepositDue(), savingSchedulePeriodData.getInterestAccured());
account.addSavingScheduleInstallment(installment);
}
@@ -177,9 +174,6 @@ public class SavingAccountAssembler {
}
SavingProductType savingProductType = SavingProductType.fromInt(account.getSavingProductType());
if (command.isSavingProductTypeChanged()) {
savingProductType = SavingProductType.fromInt(command.getSavingProductType());
}
SavingFrequencyType savingFrequencyType = SavingFrequencyType.fromInt(account.getFrequency());
if (command.isSavingFrequencyTypeChanged()) {
@@ -220,9 +214,28 @@ public class SavingAccountAssembler {
if(command.isDepositEveryChanged()){
payEvery = command.getDepositEvery();
}
SavingsInterestType interestType = SavingsInterestType.fromInt(product.getSavingProductRelatedDetail().getInterestType());
if (command.getInterestType() != null) {
interestType = SavingsInterestType.fromInt(command.getInterestType());
}
Integer interestPostEvery = command.getInterestPostEvery();
Integer interestPostFrequency =command.getInterestPostFrequency();
account.modifyAccount(product, externalId, savingsDeposit, reccuringInterestRate, savingInterestRate, tenure, commencementDate,
tenureTypeEnum, savingProductType, savingFrequencyType, savingInterestCalculationMethod, isLockinPeriodAllowed,
lockinPeriod, lockinPeriodType, this.reccuringDepositInterestCalculator,payEvery);
lockinPeriod, lockinPeriodType, this.reccuringDepositInterestCalculator,payEvery, interestType, interestPostEvery, interestPostFrequency);
SavingScheduleData savingScheduleData = this.calculateSavingSchedule.calculateSavingSchedule(command
.toCalculateSavingScheduleCommand());
for (SavingSchedulePeriodData savingSchedulePeriodData : savingScheduleData.getPeriods()) {
final SavingScheduleInstallments installment = new SavingScheduleInstallments(account, savingSchedulePeriodData.getDueDate()
.toDate(), savingSchedulePeriodData.getPeriod(), savingSchedulePeriodData.getDepositDue(), savingSchedulePeriodData.getInterestAccured());
account.addSavingScheduleInstallment(installment);
}
}
@@ -292,13 +305,13 @@ public class SavingAccountAssembler {
CalculateSavingScheduleCommand calculateSavingScheduleCommand = new CalculateSavingScheduleCommand(account.getProduct().getId(), savingsDepositAmountPerPeriod, payEvery,
frequency, recurringInterestRate, approvalDate, tenure,interestPostEvery,interestPostFrequency);
frequency, recurringInterestRate, approvalDate, tenure,tenureType,interestPostEvery,interestPostFrequency,account.getInterestCalculationMethod());
SavingScheduleData savingScheduleData = this.calculateSavingSchedule.calculateSavingSchedule(calculateSavingScheduleCommand);
for (SavingSchedulePeriodData savingSchedulePeriodData : savingScheduleData.getPeriods()) {
final SavingScheduleInstallments installment = new SavingScheduleInstallments(account, savingSchedulePeriodData.getDueDate()
.toDate(), savingSchedulePeriodData.getPeriod(), savingSchedulePeriodData.getDepositDue());
.toDate(), savingSchedulePeriodData.getPeriod(), savingSchedulePeriodData.getDepositDue(),savingSchedulePeriodData.getInterestAccured());
account.addSavingScheduleInstallment(installment);
}
}
@@ -315,7 +328,7 @@ public class SavingAccountAssembler {
}
public void postInterest(SavingAccount account) {
account.postInterest(this.reccuringDepositInterestCalculator);
account.postInterest();
}
}
@@ -6,6 +6,7 @@ import java.util.Collection;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountData;
import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountForLookup;
import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountTransactionsData;
import org.mifosplatform.portfolio.savingsaccount.data.SavingPermissionData;
import org.mifosplatform.portfolio.savingsaccount.data.SavingScheduleData;
@@ -24,4 +25,6 @@ public interface SavingAccountReadPlatformService {
SavingScheduleData retrieveSavingsAccountSchedule(Long accountId, CurrencyData currencyData);
Collection<SavingAccountForLookup> retrieveSavingAccountsForLookUp();
Collection<SavingAccountTransactionsData> retrieveSavingsAccountTransactions(Long accountId);
}
@@ -17,6 +17,7 @@ import org.mifosplatform.portfolio.client.data.ClientData;
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountData;
import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountForLookup;
import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountTransactionsData;
import org.mifosplatform.portfolio.savingsaccount.data.SavingPermissionData;
import org.mifosplatform.portfolio.savingsaccount.data.SavingScheduleData;
import org.mifosplatform.portfolio.savingsaccount.data.SavingSchedulePeriodData;
@@ -25,6 +26,7 @@ import org.mifosplatform.portfolio.savingsaccountproduct.data.SavingProductLooku
import org.mifosplatform.portfolio.savingsaccountproduct.service.SavingProductEnumerations;
import org.mifosplatform.portfolio.savingsaccountproduct.service.SavingProductReadPlatformService;
import org.mifosplatform.portfolio.savingsdepositaccount.service.DepositAccountEnumerations;
import org.mifosplatform.portfolio.savingsdepositaccount.service.DepositAccountTransactionEnumerations;
import org.mifosplatform.portfolio.savingsdepositproduct.domain.TenureTypeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -85,6 +87,7 @@ public class SavingAccountReadPlatformServiceImpl implements SavingAccountReadPl
+ " sa.pre_closure_interest_rate AS preClosureInterestRate, sa.is_lock_in_period_allowed AS isLockinPeriodAllowed,"
+ " sa.lock_in_period AS lockinPeriod, sa.lock_in_period_type AS lockinPeriodType, sa.withdrawnon_date AS withdrawnonDate,"
+ " sa.rejectedon_date AS rejectedonDate, sa.closedon_date AS closedonDate, ps.name AS productName, sa.deposit_every as depositEvery, "
+ " sa.interest_posting_every AS interestPostEvery, sa.interest_posting_frequency AS interestPostFrequency, "
+ " c.firstname AS firstname, c.lastname AS lastname, curr.name AS currencyName, "
+ " curr.internationalized_name_code AS currencyNameCode, curr.display_symbol AS currencyDisplaySymbol "
+ " FROM m_saving_account sa " + " JOIN m_client c ON c.id = sa.client_id"
@@ -150,13 +153,17 @@ public class SavingAccountReadPlatformServiceImpl implements SavingAccountReadPl
Integer lockinPeriodTypeValue = JdbcSupport.getInteger(rs, "lockinPeriodType");
EnumOptionData lockinPeriodType = SavingProductEnumerations.savingsLockinPeriod(lockinPeriodTypeValue);
Integer depositEvery = JdbcSupport.getInteger(rs, "depositEvery");
Integer interestPostEvery = JdbcSupport.getInteger(rs, "interestPostEvery");
EnumOptionData interestPostFrequency = SavingProductEnumerations.interestFrequencyType(JdbcSupport.getInteger(rs, "interestPostFrequency"));
return new SavingAccountData(id, status, externalId, clientId, clientName, productId, productName, productType, currencyData,
savingsDepostiAmountPerPeriod, savingsFrequencyType, totalDepositAmount, reccuringInterestRate, savingInterestRate,
interestType, interestCalculationMethod, tenure, tenureType, projectedCommencementDate, actualCommencementDate,
maturesOnDate, projectedInterestAccuredOnMaturity, actualInterestAccured, projectedMaturityAmount,
actualMaturityAmount, preClosureAllowed, preClosureInterestRate, withdrawnonDate, rejectedonDate, closedonDate,
isLockinPeriodAllowed, lockinPeriod, lockinPeriodType,depositEvery,outstandingAmount);
isLockinPeriodAllowed, lockinPeriod, lockinPeriodType,depositEvery,outstandingAmount, interestPostEvery, interestPostFrequency);
}
}
@@ -229,10 +236,10 @@ public class SavingAccountReadPlatformServiceImpl implements SavingAccountReadPl
Integer noOfMonths = Months.monthsBetween(date, new LocalDate()).getMonths();
Integer noOfPeriodsExistedTillDate = noOfMonths/depositEvery;
BigDecimal totalAmountToBePaidTillDate = BigDecimal.valueOf(noOfPeriodsExistedTillDate.doubleValue()*amountPerPeriod.doubleValue());
dueAmount = dueAmount.add(BigDecimal.valueOf(totalAmountToBePaidTillDate.doubleValue() - account.getTotalDepositAmount().doubleValue()));
dueAmount = dueAmount.add(BigDecimal.valueOf(totalAmountToBePaidTillDate.doubleValue() - account.getOutstandingAmount().doubleValue()));
}
dueAmount = dueAmount.doubleValue() < 0 ? BigDecimal.ZERO : dueAmount;
return dueAmount;
}
@@ -243,10 +250,14 @@ public class SavingAccountReadPlatformServiceImpl implements SavingAccountReadPl
String sql = "Select " + mapper.savingScheduleSchema() +" where ss.saving_account_id =? order by ss.installment";
List<SavingSchedulePeriodData> periods = this.jdbcTemplate.query(sql, mapper, new Object[]{accountId});
BigDecimal cumulativeDepositDue = BigDecimal.ZERO;
BigDecimal cumulativeDepositPaid = BigDecimal.ZERO;
BigDecimal cummulativeInterestAccured = BigDecimal.ZERO;
for(SavingSchedulePeriodData data : periods){
cumulativeDepositDue = cumulativeDepositDue.add(data.getDepositDue()).subtract(data.getDepositPaid());
cumulativeDepositDue = cumulativeDepositDue.add(data.getDepositDue())/*.subtract(data.getDepositPaid())*/;
cumulativeDepositPaid = cumulativeDepositPaid.add(data.getDepositPaid());
cummulativeInterestAccured = cummulativeInterestAccured.add(data.getInterestAccured());
}
SavingScheduleData scheduleData = new SavingScheduleData(currency, cumulativeDepositDue, periods);
SavingScheduleData scheduleData = new SavingScheduleData(currency, cumulativeDepositDue, cumulativeDepositPaid, cummulativeInterestAccured, periods);
return scheduleData;
}
@@ -255,7 +266,7 @@ public class SavingAccountReadPlatformServiceImpl implements SavingAccountReadPl
public String savingScheduleSchema(){
return "ss.id as id, ss.saving_account_id as savingAccountId, ss.duedate as dueDate, ss.installment as installment, "
+ " ss.deposit as deposit, ss.payment_date as paymentDate, ss.deposit_paid as depositPaid, "
+ " ss.completed_derived as completedDerived FROM m_saving_schedule ss";
+ " ss.completed_derived as completedDerived, ss.interest_accured AS interestAccured FROM m_saving_schedule ss";
}
@Override
@@ -265,8 +276,9 @@ public class SavingAccountReadPlatformServiceImpl implements SavingAccountReadPl
Integer installment = JdbcSupport.getInteger(rs, "installment");
BigDecimal depositDue = rs.getBigDecimal("deposit");
BigDecimal depositPaid = rs.getBigDecimal("depositPaid");
BigDecimal interestAccured = rs.getBigDecimal("interestAccured");
return new SavingSchedulePeriodData(installment, dueDate, depositDue, depositPaid);
return new SavingSchedulePeriodData(installment, dueDate, depositDue, depositPaid, interestAccured);
}
}
@@ -292,4 +304,30 @@ public class SavingAccountReadPlatformServiceImpl implements SavingAccountReadPl
}
@Override
public Collection<SavingAccountTransactionsData> retrieveSavingsAccountTransactions(Long accountId) {
this.context.authenticatedUser();
SavingAccountTransactionMapper savingAccountTransactionMapper = new SavingAccountTransactionMapper();
String sql = "select "+ savingAccountTransactionMapper.schema() + " where stxn.saving_account_id = ? ";
return jdbcTemplate.query(sql, savingAccountTransactionMapper, new Object[] {accountId});
}
private static final class SavingAccountTransactionMapper implements RowMapper<SavingAccountTransactionsData> {
public String schema() {
return "stxn.id as id, stxn.transaction_type_enum as type, stxn.transaction_date as transactionDate, stxn.amount as amount FROM m_saving_account_transaction stxn";
}
@Override
public SavingAccountTransactionsData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
Long id = rs.getLong("id");
LocalDate transactionDate = JdbcSupport.getLocalDate(rs, "transactionDate");
Integer transactionTypeValue = JdbcSupport.getInteger(rs, "type");
EnumOptionData transactionType = DepositAccountTransactionEnumerations.depositType(transactionTypeValue);
BigDecimal amount = rs.getBigDecimal("amount");
return new SavingAccountTransactionsData(id, transactionDate, transactionType, amount);
}
}
}
@@ -1,6 +1,8 @@
package org.mifosplatform.portfolio.savingsaccount.service;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -10,19 +12,21 @@ import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
import org.mifosplatform.portfolio.savingsaccount.data.SavingScheduleData;
import org.mifosplatform.portfolio.savingsaccount.data.SavingSchedulePeriodData;
import org.mifosplatform.portfolio.savingsaccount.domain.DepositScheduleDateGenerator;
import org.mifosplatform.portfolio.savingsaccountproduct.domain.SavingFrequencyType;
import org.mifosplatform.portfolio.savingsaccountproduct.domain.SavingInterestCalculationMethod;
import org.mifosplatform.portfolio.savingsaccountproduct.domain.SavingProduct;
public class SavingScheduleGenerator {
private final DepositScheduleDateGenerator scheduledDateGenerator = new DefaultDepositScheduleDateGenerator();
private final Integer monthsInYear = 12;
public SavingScheduleData generate(final LocalDate scheduleStartDate, final BigDecimal depositAmountPerPeriod,
final Integer depositFrequency, final PeriodFrequencyType depositFrequencyType, final SavingProduct savingProduct,
final Integer tenure, final ApplicationCurrency applicationCurrency) {
final Integer depositFrequency, final SavingFrequencyType savingFrequencyType, final SavingProduct savingProduct,
final Integer tenure, final ApplicationCurrency applicationCurrency, final BigDecimal interestRate, final SavingInterestCalculationMethod interestCalculationMethod) {
LocalDate startDate = scheduleStartDate;
int periodNumber = 1;
@@ -30,23 +34,92 @@ public class SavingScheduleGenerator {
Integer paymentPeriods = tenure / depositFrequency;
final Collection<SavingSchedulePeriodData> periods = new ArrayList<SavingSchedulePeriodData>();
final List<LocalDate> scheduledDates = this.scheduledDateGenerator.generate(startDate, paymentPeriods, depositFrequency,
depositFrequencyType);
savingFrequencyType);
final MonetaryCurrency currency = savingProduct.getCurrency();
final Money depositAmount = Money.of(currency, depositAmountPerPeriod);
Money totalDeposit = Money.zero(currency);
MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
BigDecimal interestRateAsFraction = interestRate.divide(BigDecimal.valueOf(100), mc);
Integer noofTimesInterestCompoundPerYear = determineIntValue(savingFrequencyType);
BigDecimal interestRatePerPeriod = BigDecimal.valueOf(interestRateAsFraction.doubleValue()/ noofTimesInterestCompoundPerYear.doubleValue());
BigDecimal onePlusInterestRatePerPeriod = BigDecimal.ONE.add(interestRatePerPeriod);
BigDecimal cummulativeDepositPaid = BigDecimal.ZERO;
BigDecimal cummulativeInterestAccured = BigDecimal.ZERO;
Integer tempTenure = tenure;
BigDecimal termAmount = BigDecimal.ZERO;
BigDecimal totalInterest = BigDecimal.ZERO;
BigDecimal closingBalance = BigDecimal.ZERO;
for (LocalDate scheduleDate : scheduledDates) {
totalDeposit = totalDeposit.plus(depositAmount);
SavingSchedulePeriodData installment = SavingSchedulePeriodData.addScheduleInformation(periodNumber, scheduleDate,
depositAmount.getAmount());
periods.add(installment);
periodNumber++;
if (interestCalculationMethod.isMonthlyCollection()) {
BigDecimal monthsExpressedInYears = BigDecimal.valueOf(tempTenure.doubleValue() / monthsInYear.doubleValue());
BigDecimal timeforOneCalculationPeriod = BigDecimal.valueOf(noofTimesInterestCompoundPerYear * monthsExpressedInYears.doubleValue());
BigDecimal amountPerPeriod = depositAmountPerPeriod.multiply(BigDecimal.valueOf(Math.pow(onePlusInterestRatePerPeriod.doubleValue(), timeforOneCalculationPeriod.doubleValue())));
BigDecimal interestAccured = amountPerPeriod.subtract(depositAmountPerPeriod);
totalDeposit = totalDeposit.plus(depositAmount);
cummulativeInterestAccured = cummulativeInterestAccured.add(interestAccured);
SavingSchedulePeriodData installment = SavingSchedulePeriodData.addScheduleInformation(periodNumber, scheduleDate,
depositAmount.getAmount(), interestAccured);
periods.add(installment);
periodNumber++;
tempTenure = tempTenure - depositFrequency;
} else if (interestCalculationMethod.isAverageBalance()){
BigDecimal amountPerPeriod = depositAmount.getAmount();
Integer depositEveryOriginalValue = depositFrequency;
BigDecimal amountPermonth = BigDecimal.valueOf(amountPerPeriod.doubleValue()/depositFrequency.doubleValue());
while (depositEveryOriginalValue > 0) {
closingBalance = closingBalance.add(amountPermonth);
termAmount = termAmount.add(closingBalance);
depositEveryOriginalValue--;
}
BigDecimal averageAmount = BigDecimal.valueOf(termAmount.doubleValue()/depositFrequency.doubleValue());
BigDecimal interestRatePerTerm = BigDecimal.valueOf(interestRateAsFraction.doubleValue()/monthsInYear.doubleValue()*depositFrequency.doubleValue());
BigDecimal interestAccured = averageAmount.multiply(interestRatePerTerm);
totalInterest = totalInterest.add(interestAccured);
depositEveryOriginalValue = depositFrequency;
termAmount = BigDecimal.ZERO;
totalDeposit = totalDeposit.plus(depositAmount);
cummulativeInterestAccured = totalInterest;
SavingSchedulePeriodData installment = SavingSchedulePeriodData.addScheduleInformation(periodNumber, scheduleDate,
depositAmount.getAmount(), interestAccured);
periods.add(installment);
periodNumber++;
}
}
CurrencyData currencyData = new CurrencyData(applicationCurrency.getCode(), applicationCurrency.getName(),
currency.getDigitsAfterDecimal(), applicationCurrency.getDisplaySymbol(), applicationCurrency.getNameCode());
return new SavingScheduleData(currencyData, totalDeposit.getAmount(), periods);
return new SavingScheduleData(currencyData, totalDeposit.getAmount(),cummulativeDepositPaid, cummulativeInterestAccured, periods);
}
private Integer determineIntValue(SavingFrequencyType frequencyType){
Integer noofTimesInterestCompoundPerYear = Integer.valueOf(0);
switch (frequencyType) {
case MONTHLY:
noofTimesInterestCompoundPerYear = monthsInYear;
break;
case QUATERLY:
noofTimesInterestCompoundPerYear = monthsInYear / 3;
break;
case HALFYEARLY:
noofTimesInterestCompoundPerYear = monthsInYear / 6;
break;
case YEARLY:
noofTimesInterestCompoundPerYear = monthsInYear / 12;
break;
default:
throw new RuntimeException("The specified frequency not supported");
}
return noofTimesInterestCompoundPerYear;
}
}
@@ -38,4 +38,12 @@ public enum TenureTypeEnum {
}
return tenureTypeEnum;
}
public boolean isFixedPeriod() {
return this.value.equals(TenureTypeEnum.FIXED_PERIOD.value);
}
public boolean isPerpetual() {
return this.value.equals(TenureTypeEnum.PERPETUAL.value);
}
}