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