Merge pull request #880 from michaeldk-miagstan/michael-miagstan-github

Advanced deposit products and accounts (FD & RD) with interest rate extensions
This commit is contained in:
Vishwas Babu A J
2014-04-29 10:24:17 +05:30
27 changed files with 1129 additions and 140 deletions
@@ -210,4 +210,8 @@ public class InterestRateChartSlabFields {
}
return actualDepositPeriod;
}
public boolean isAmountBetween(final BigDecimal depositAmount){
return depositAmount.compareTo(amountRangeFrom) >= 0 && depositAmount.compareTo(amountRangeTo) <= 0;
}
}
@@ -69,6 +69,7 @@ public class DepositsApiConstants {
public static final String approvedOnDateParamName = "approvedOnDate";
public static final String activatedOnDateParamName = "activatedOnDate";
public static final String closedOnDateParamName = "closedOnDate";
public static final String expectedFirstDepositOnDateParamName = "expectedFirstDepositOnDate";
public static final String activeParamName = "active";
public static final String nameParamName = "name";
@@ -118,6 +119,8 @@ public class DepositsApiConstants {
public static final String inMultiplesOfDepositTermType = "inMultiplesOfDepositTermType";
public static final String depositAmountParamName = "depositAmount";
public static final String depositMinAmountParamName = "minDepositAmount";
public static final String depositMaxAmountParamName = "maxDepositAmount";
public static final String depositPeriodParamName = "depositPeriod";
public static final String depositPeriodFrequencyIdParamName = "depositPeriodFrequencyId";
@@ -211,11 +214,13 @@ public class DepositsApiConstants {
private static final Set<String> DEPOSIT_TERM_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(minDepositTermParamName,
maxDepositTermParamName, minDepositTermTypeIdParamName, maxDepositTermTypeIdParamName, inMultiplesOfDepositTermParamName,
inMultiplesOfDepositTermTypeIdParamName));
inMultiplesOfDepositTermTypeIdParamName, depositAmountParamName,
depositMinAmountParamName, depositMaxAmountParamName));
private static final Set<String> DEPOSIT_TERM_RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(minDepositTermParamName,
maxDepositTermParamName, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTermParamName,
inMultiplesOfDepositTermType));
inMultiplesOfDepositTermType, depositAmountParamName,
depositMinAmountParamName, depositMaxAmountParamName));
private static final Set<String> RECURRING_DETAILS_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(
recurringDepositAmountParamName, recurringDepositTypeIdParamName, recurringDepositFrequencyTypeIdParamName,
@@ -277,7 +282,7 @@ public class DepositsApiConstants {
interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName,
interestCalculationDaysInYearTypeParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName,
chargesParamName, chartsParamName, depositAmountParamName, depositPeriodParamName, depositPeriodFrequencyIdParamName,
savingsAccounts));
savingsAccounts, expectedFirstDepositOnDateParamName));
public static final Set<String> FIXED_DEPOSIT_ACCOUNT_REQUEST_DATA_PARAMETERS = fixedDepositAccountRequestData();
public static final Set<String> FIXED_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS = fixedDepositAccountResponseData();
@@ -20,11 +20,11 @@ public enum SavingsCompoundingInterestPeriodType {
DAILY(1, "savingsCompoundingInterestPeriodType.daily"), //
// WEEKLY(2, "savingsCompoundingInterestPeriodType.weekly"), //
// BIWEEKLY(3, "savingsCompoundingInterestPeriodType.biweekly"), //
MONTHLY(4, "savingsCompoundingInterestPeriodType.monthly");
MONTHLY(4, "savingsCompoundingInterestPeriodType.monthly"),
// QUATERLY(5, "savingsCompoundingInterestPeriodType.quarterly"), //
// BI_ANNUAL(6, "savingsCompoundingInterestPeriodType.biannual"), //
// ANNUAL(7, "savingsCompoundingInterestPeriodType.annual"), //
QUATERLY(5, "savingsCompoundingInterestPeriodType.quarterly"), //
BI_ANNUAL(6, "savingsCompoundingInterestPeriodType.biannual"), //
ANNUAL(7, "savingsCompoundingInterestPeriodType.annual"); //
// NO_COMPOUNDING_SIMPLE_INTEREST(8,
// "savingsCompoundingInterestPeriodType.nocompounding");
@@ -74,16 +74,16 @@ public enum SavingsCompoundingInterestPeriodType {
repaymentFrequencyType = SavingsCompoundingInterestPeriodType.MONTHLY;
break;
case 5:
// repaymentFrequencyType =
// SavingsCompoundingInterestPeriodType.QUATERLY;
repaymentFrequencyType =
SavingsCompoundingInterestPeriodType.QUATERLY;
break;
case 6:
// repaymentFrequencyType =
// SavingsCompoundingInterestPeriodType.BI_ANNUAL;
repaymentFrequencyType =
SavingsCompoundingInterestPeriodType.BI_ANNUAL;
break;
case 7:
// repaymentFrequencyType =
// SavingsCompoundingInterestPeriodType.ANNUAL;
repaymentFrequencyType =
SavingsCompoundingInterestPeriodType.ANNUAL;
break;
case 8:
// repaymentFrequencyType =
@@ -17,6 +17,7 @@ public enum SavingsPostingInterestPeriodType {
INVALID(0, "savingsPostingInterestPeriodType.invalid"), //
MONTHLY(4, "savingsPostingInterestPeriodType.monthly"), //
QUATERLY(5, "savingsPostingInterestPeriodType.quarterly"), //
BIANNUAL(6, "savingsPostingInterestPeriodType.biannual"),
ANNUAL(7, "savingsPostingInterestPeriodType.annual");
private final Integer value;
@@ -56,6 +57,9 @@ public enum SavingsPostingInterestPeriodType {
case 5:
repaymentFrequencyType = SavingsPostingInterestPeriodType.QUATERLY;
break;
case 6:
repaymentFrequencyType = SavingsPostingInterestPeriodType.BIANNUAL;
break;
case 7:
repaymentFrequencyType = SavingsPostingInterestPeriodType.ANNUAL;
break;
@@ -27,6 +27,9 @@ import static org.mifosplatform.portfolio.savings.DepositsApiConstants.preClosur
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyTypeIdParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositTypeIdParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositAmountParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositMinAmountParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositMaxAmountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName;
@@ -48,6 +51,8 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawal
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -109,6 +114,8 @@ public class DepositProductDataValidator {
validateDepositTermDeatilForCreate(element, baseDataValidator);
validateChartsData(element, baseDataValidator);
validateDepositAmountForCreate(element, baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -131,6 +138,8 @@ public class DepositProductDataValidator {
validateDepositTermDetailForUpdate(element, baseDataValidator);
validateChartsData(element, baseDataValidator);
validateDepositAmountForUpdate(element, baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -156,6 +165,8 @@ public class DepositProductDataValidator {
validateRecurringDetailForCreate(element, baseDataValidator);
validateChartsData(element, baseDataValidator);
validateDepositAmountForCreate(element, baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -180,6 +191,8 @@ public class DepositProductDataValidator {
validateRecurringDepositUpdate(element, baseDataValidator);
validateChartsData(element, baseDataValidator);
validateDepositAmountForUpdate(element, baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -733,4 +746,73 @@ public class DepositProductDataValidator {
.isOneOfTheseValues(SavingsPeriodFrequencyType.integerValues());
}
}
private void validateDepositAmountForCreate(JsonElement element, DataValidatorBuilder baseDataValidator) {
final BigDecimal depositAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(depositAmountParamName, element);
baseDataValidator.reset().parameter(depositAmountParamName).value(depositAmount).notNull().positiveAmount();
BigDecimal depositMinAmount = null;
if (this.fromApiJsonHelper.parameterExists(depositMinAmountParamName, element)) {
depositMinAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(depositMinAmountParamName, element);
baseDataValidator.reset().parameter(depositMinAmountParamName).value(depositMinAmount).notNull().positiveAmount();
}
BigDecimal depositMaxAmount = null;
if (this.fromApiJsonHelper.parameterExists(depositMaxAmountParamName, element)) {
depositMaxAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(depositMaxAmountParamName, element);
baseDataValidator.reset().parameter(depositMaxAmountParamName).value(depositMaxAmount).notNull().positiveAmount();
}
if (depositMaxAmount != null && depositMaxAmount.compareTo(BigDecimal.ZERO) != -1) {
if (depositMinAmount != null && depositMinAmount.compareTo(BigDecimal.ZERO) != -1) {
baseDataValidator.reset().parameter(depositMaxAmountParamName).value(depositMaxAmount).notLessThanMin(depositMinAmount);
if (depositMinAmount.compareTo(depositMaxAmount) <= 0) {
baseDataValidator.reset().parameter(depositAmountParamName).value(depositAmount)
.inMinAndMaxAmountRange(depositMinAmount, depositMaxAmount);
}
} else {
baseDataValidator.reset().parameter(depositAmountParamName).value(depositAmount).notGreaterThanMax(depositMaxAmount);
}
} else if (depositMinAmount != null && depositMinAmount.compareTo(BigDecimal.ZERO) != -1) {
baseDataValidator.reset().parameter(depositAmountParamName).value(depositAmount).notLessThanMin(depositMinAmount);
}
}
private void validateDepositAmountForUpdate(JsonElement element, DataValidatorBuilder baseDataValidator) {
BigDecimal depositAmount = null;
if (this.fromApiJsonHelper.parameterExists(depositAmountParamName, element)) {
depositAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(depositAmountParamName, element);
baseDataValidator.reset().parameter(depositMinAmountParamName).value(depositAmount).notNull().positiveAmount();
}
baseDataValidator.reset().parameter(depositAmountParamName).value(depositAmount).notNull().positiveAmount();
BigDecimal depositMinAmount = null;
if (this.fromApiJsonHelper.parameterExists(depositMinAmountParamName, element)) {
depositMinAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(depositMinAmountParamName, element);
baseDataValidator.reset().parameter(depositMinAmountParamName).value(depositMinAmount).notNull().positiveAmount();
}
BigDecimal depositMaxAmount = null;
if (this.fromApiJsonHelper.parameterExists(depositMaxAmountParamName, element)) {
depositMaxAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(depositMaxAmountParamName, element);
baseDataValidator.reset().parameter(depositMaxAmountParamName).value(depositMaxAmount).notNull().positiveAmount();
}
if(depositAmount != null) {
if (depositMaxAmount != null && depositMaxAmount.compareTo(BigDecimal.ZERO) != -1) {
if (depositMinAmount != null && depositMinAmount.compareTo(BigDecimal.ZERO) != -1) {
baseDataValidator.reset().parameter(depositMaxAmountParamName).value(depositMaxAmount).notLessThanMin(depositMinAmount);
if (depositMinAmount.compareTo(depositMaxAmount) <= 0) {
baseDataValidator.reset().parameter(depositAmountParamName).value(depositAmount)
.inMinAndMaxAmountRange(depositMinAmount, depositMaxAmount);
}
} else {
baseDataValidator.reset().parameter(depositAmountParamName).value(depositAmount).notGreaterThanMax(depositMaxAmount);
}
} else if (depositMinAmount != null && depositMinAmount.compareTo(BigDecimal.ZERO) != -1) {
baseDataValidator.reset().parameter(depositAmountParamName).value(depositAmount).notLessThanMin(depositMinAmount);
}
}
}
}
@@ -47,6 +47,7 @@ public class RecurringDepositAccountData extends DepositAccountData {
private EnumOptionData recurringDepositType;
private Integer recurringDepositFrequency;
private EnumOptionData recurringDepositFrequencyType;
private LocalDate expectedFirstDepositOnDate;
// used for account close
private EnumOptionData onAccountClosure;
@@ -73,7 +74,8 @@ public class RecurringDepositAccountData extends DepositAccountData {
final BigDecimal maturityAmount, final LocalDate maturityDate, final Integer depositPeriod,
final EnumOptionData depositPeriodFrequency, final BigDecimal recurringDepositAmount,
final EnumOptionData recurringDepositType, final Integer recurringDepositFrequency,
final EnumOptionData recurringDepositFrequencyType, final EnumOptionData onAccountClosure) {
final EnumOptionData recurringDepositFrequencyType, final EnumOptionData onAccountClosure,
final LocalDate expectedFirstDepositOnDate) {
final Collection<EnumOptionData> interestFreePeriodTypeOptions = null;
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
@@ -108,7 +110,7 @@ public class RecurringDepositAccountData extends DepositAccountData {
depositPeriod, depositPeriodFrequency, recurringDepositAmount, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions,
recurringDepositType, recurringDepositFrequency, recurringDepositFrequencyType, recurringDepositTypeOptions,
recurringDepositFrequencyTypeOptions, depositPeriodFrequencyOptions, depositType, onAccountClosure,
onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas);
onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, expectedFirstDepositOnDate);
}
public static RecurringDepositAccountData withInterestChart(final RecurringDepositAccountData account,
@@ -132,7 +134,8 @@ public class RecurringDepositAccountData extends DepositAccountData {
account.depositTermTypeOptions, account.inMultiplesOfDepositTermTypeOptions, account.recurringDepositType,
account.recurringDepositFrequency, account.recurringDepositFrequencyType, account.recurringDepositTypeOptions,
account.recurringDepositFrequencyTypeOptions, account.depositPeriodFrequencyOptions, account.depositType,
account.onAccountClosure, account.onAccountClosureOptions, account.paymentTypeOptions, account.savingsAccounts);
account.onAccountClosure, account.onAccountClosureOptions, account.paymentTypeOptions, account.savingsAccounts,
account.expectedFirstDepositOnDate);
}
public static RecurringDepositAccountData withTemplateOptions(final RecurringDepositAccountData account,
@@ -186,7 +189,7 @@ public class RecurringDepositAccountData extends DepositAccountData {
account.recurringDepositType, account.recurringDepositFrequency, account.recurringDepositFrequencyType,
template.recurringDepositTypeOptions, template.recurringDepositFrequencyTypeOptions,
template.depositPeriodFrequencyOptions, account.depositType, account.onAccountClosure, account.onAccountClosureOptions,
account.paymentTypeOptions, account.savingsAccounts);
account.paymentTypeOptions, account.savingsAccounts, account.expectedFirstDepositOnDate);
}
@@ -223,7 +226,7 @@ public class RecurringDepositAccountData extends DepositAccountData {
depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions, account.recurringDepositType,
account.recurringDepositFrequency, account.recurringDepositFrequencyType, recurringDepositTypeOptions,
recurringDepositFrequencyTypeOptions, depositPeriodFrequencyOptions, account.depositType, account.onAccountClosure,
account.onAccountClosureOptions, account.paymentTypeOptions, account.savingsAccounts);
account.onAccountClosureOptions, account.paymentTypeOptions, account.savingsAccounts, account.expectedFirstDepositOnDate);
}
public static RecurringDepositAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId,
@@ -301,6 +304,7 @@ public class RecurringDepositAccountData extends DepositAccountData {
final Collection<EnumOptionData> onAccountClosureOptions = null;
final Collection<CodeValueData> paymentTypeOptions = null;
final Collection<SavingsAccountData> savingsAccountDatas = null;
final LocalDate expectedFirstDepositOnDate = null;
return new RecurringDepositAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
@@ -316,7 +320,7 @@ public class RecurringDepositAccountData extends DepositAccountData {
depositPeriodFrequency, recurringDepositAmount, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions,
recurringDepositType, recurringDepositFrequency, recurringDepositFrequencyType, recurringDepositTypeOptions,
recurringDepositFrequencyTypeOptions, depositPeriodFrequencyOptions, depositType, onAccountClosure,
onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas);
onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, expectedFirstDepositOnDate);
}
public static RecurringDepositAccountData preClosureDetails(final Long accountId, BigDecimal maturityAmount,
@@ -394,6 +398,7 @@ public class RecurringDepositAccountData extends DepositAccountData {
final String groupName = null;
final Long clientId = null;
final String clientName = null;
final LocalDate expectedFirstDepositOnDate = null;
return new RecurringDepositAccountData(accountId, accountNo, externalId, groupId, groupName, clientId, clientName, productId,
productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
@@ -409,7 +414,7 @@ public class RecurringDepositAccountData extends DepositAccountData {
depositPeriodFrequency, recurringDepositAmount, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions,
recurringDepositType, recurringDepositFrequency, recurringDepositFrequencyType, recurringDepositTypeOptions,
recurringDepositFrequencyTypeOptions, depositPeriodFrequencyOptions, depositType, onAccountClosure,
onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas);
onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, expectedFirstDepositOnDate);
}
public static RecurringDepositAccountData withClosureTemplateDetails(final RecurringDepositAccountData account,
@@ -434,7 +439,8 @@ public class RecurringDepositAccountData extends DepositAccountData {
account.depositTermTypeOptions, account.inMultiplesOfDepositTermTypeOptions, account.recurringDepositType,
account.recurringDepositFrequency, account.recurringDepositFrequencyType, account.recurringDepositTypeOptions,
account.recurringDepositFrequencyTypeOptions, account.depositPeriodFrequencyOptions, account.depositType,
account.onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas);
account.onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas,
account.expectedFirstDepositOnDate);
}
private RecurringDepositAccountData(final Long id, final String accountNo, final String externalId, final Long groupId,
@@ -469,7 +475,8 @@ public class RecurringDepositAccountData extends DepositAccountData {
final Collection<EnumOptionData> recurringDepositFrequencyTypeOptions,
final Collection<EnumOptionData> depositPeriodFrequencyOptions, final EnumOptionData depositType,
final EnumOptionData onAccountClosure, final Collection<EnumOptionData> onAccountClosureOptions,
final Collection<CodeValueData> paymentTypeOptions, final Collection<SavingsAccountData> savingsAccountDatas) {
final Collection<CodeValueData> paymentTypeOptions, final Collection<SavingsAccountData> savingsAccountDatas,
final LocalDate expectedFirstDepositOnDate) {
super(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, fieldofficerId,
fieldofficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, interestPostingPeriodType,
@@ -501,6 +508,7 @@ public class RecurringDepositAccountData extends DepositAccountData {
this.recurringDepositFrequencyType = recurringDepositFrequencyType;
this.recurringDepositFrequency = recurringDepositFrequency;
this.recurringDepositType = recurringDepositType;
this.expectedFirstDepositOnDate = expectedFirstDepositOnDate;
// template data
this.interestFreePeriodTypeOptions = interestFreePeriodTypeOptions;
@@ -40,6 +40,9 @@ public class RecurringDepositProductData extends DepositProductData {
private EnumOptionData recurringDepositType;
private Integer recurringDepositFrequency;
private EnumOptionData recurringDepositFrequencyType;
private BigDecimal minDepositAmount;
private BigDecimal depositAmount;
private BigDecimal maxDepositAmount;
private Collection<EnumOptionData> interestFreePeriodTypeOptions;
private Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions;
@@ -96,7 +99,10 @@ public class RecurringDepositProductData extends DepositProductData {
final EnumOptionData maxDepositTermType = null;
final Integer inMultiplesOfDepositTerm = null;
final EnumOptionData inMultiplesOfDepositTermType = null;
final BigDecimal minDepositAmount = null;
final BigDecimal depositAmount = null;
final BigDecimal maxDepositAmount = null;
return new RecurringDepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate,
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
lockinPeriodFrequency, lockinPeriodFrequencyType, accountingRule, accountingMappings, paymentChannelToFundSourceMappings,
@@ -106,9 +112,10 @@ public class RecurringDepositProductData extends DepositProductData {
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, interestFreePeriodApplicable, interestFreeFromPeriod,
interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest,
preClosurePenalInterestOnType, interestFreePeriodTypes, preClosurePenalInterestOnTypeOptions, minDepositTerm,
maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType,
recurringDepositType, recurringDepositFrequency, recurringDepositFrequencyType, recurringDepositTypeOptions,
recurringDepositFrequencyTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions);
maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, recurringDepositType,
recurringDepositFrequency, recurringDepositFrequencyType, recurringDepositTypeOptions,
recurringDepositFrequencyTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions,
minDepositAmount, depositAmount, maxDepositAmount);
}
public static RecurringDepositProductData withCharges(final RecurringDepositProductData existingProduct,
@@ -134,7 +141,8 @@ public class RecurringDepositProductData extends DepositProductData {
existingProduct.recurringDepositType, existingProduct.recurringDepositFrequency,
existingProduct.recurringDepositFrequencyType, existingProduct.recurringDepositTypeOptions,
existingProduct.recurringDepositFrequencyTypeOptions, existingProduct.depositTermTypeOptions,
existingProduct.inMultiplesOfDepositTermTypeOptions);
existingProduct.inMultiplesOfDepositTermTypeOptions, existingProduct.minDepositAmount,
existingProduct.depositAmount, existingProduct.maxDepositAmount);
}
/**
@@ -175,7 +183,8 @@ public class RecurringDepositProductData extends DepositProductData {
existingProduct.minDepositTermType, existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm,
existingProduct.inMultiplesOfDepositTermType, existingProduct.recurringDepositType,
existingProduct.recurringDepositFrequency, existingProduct.recurringDepositFrequencyType, recurringDepositTypeOptions,
recurringDepositFrequencyTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions);
recurringDepositFrequencyTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions, existingProduct.minDepositAmount,
existingProduct.depositAmount, existingProduct.maxDepositAmount);
}
public static RecurringDepositProductData withAccountingDetails(final RecurringDepositProductData existingProduct,
@@ -216,7 +225,8 @@ public class RecurringDepositProductData extends DepositProductData {
existingProduct.recurringDepositType, existingProduct.recurringDepositFrequency,
existingProduct.recurringDepositFrequencyType, existingProduct.recurringDepositTypeOptions,
existingProduct.recurringDepositFrequencyTypeOptions, existingProduct.depositTermTypeOptions,
existingProduct.inMultiplesOfDepositTermTypeOptions);
existingProduct.inMultiplesOfDepositTermTypeOptions, existingProduct.minDepositAmount,
existingProduct.depositAmount, existingProduct.maxDepositAmount);
}
public static RecurringDepositProductData instance(final DepositProductData depositProductData,
@@ -226,7 +236,9 @@ public class RecurringDepositProductData extends DepositProductData {
final Integer maxDepositTerm, final EnumOptionData minDepositTermType, final EnumOptionData maxDepositTermType,
final Integer inMultiplesOfDepositTerm, final EnumOptionData inMultiplesOfDepositTermType,
final EnumOptionData recurringDepositType, final Integer recurringDepositFrequency,
final EnumOptionData recurringDepositFrequencyType) {
final EnumOptionData recurringDepositFrequencyType,
final BigDecimal minDepositAmount,final BigDecimal depositAmount,
final BigDecimal maxDepositAmount) {
final Map<String, Object> accountingMappings = null;
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings = null;
@@ -269,7 +281,8 @@ public class RecurringDepositProductData extends DepositProductData {
preClosurePenalInterestOnType, interestFreePeriodTypes, preClosurePenalInterestOnTypeOptions, minDepositTerm,
maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType,
recurringDepositType, recurringDepositFrequency, recurringDepositFrequencyType, recurringDepositTypeOptions,
recurringDepositFrequencyTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions);
recurringDepositFrequencyTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions,
minDepositAmount, depositAmount, maxDepositAmount);
}
public static RecurringDepositProductData lookup(final Long id, final String name) {
@@ -329,6 +342,9 @@ public class RecurringDepositProductData extends DepositProductData {
final EnumOptionData inMultiplesOfDepositTermType = null;
final Collection<EnumOptionData> depositTermTypeOptions = null;
final Collection<EnumOptionData> inMultiplesOfDepositTermTypeOptions = null;
final BigDecimal minDepositAmount = null;
final BigDecimal depositAmount = null;
final BigDecimal maxDepositAmount = null;
return new RecurringDepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate,
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
@@ -341,7 +357,8 @@ public class RecurringDepositProductData extends DepositProductData {
preClosurePenalInterestOnType, interestFreePeriodTypes, preClosurePenalInterestOnTypeOptions, minDepositTerm,
maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType,
recurringDepositType, recurringDepositFrequency, recurringDepositFrequencyType, recurringDepositTypeOptions,
recurringDepositFrequencyTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions);
recurringDepositFrequencyTypeOptions, depositTermTypeOptions, inMultiplesOfDepositTermTypeOptions,
minDepositAmount, depositAmount, maxDepositAmount);
}
public static RecurringDepositProductData withInterestChart(final RecurringDepositProductData product,
@@ -362,7 +379,8 @@ public class RecurringDepositProductData extends DepositProductData {
product.minDepositTerm, product.maxDepositTerm, product.minDepositTermType, product.maxDepositTermType,
product.inMultiplesOfDepositTerm, product.inMultiplesOfDepositTermType, product.recurringDepositType,
product.recurringDepositFrequency, product.recurringDepositFrequencyType, product.recurringDepositTypeOptions,
product.recurringDepositFrequencyTypeOptions, product.depositTermTypeOptions, product.inMultiplesOfDepositTermTypeOptions);
product.recurringDepositFrequencyTypeOptions, product.depositTermTypeOptions, product.inMultiplesOfDepositTermTypeOptions, product.minDepositAmount,
product.depositAmount, product.maxDepositAmount);
}
private RecurringDepositProductData(final Long id, final String name, final String shortName, final String description,
@@ -393,7 +411,8 @@ public class RecurringDepositProductData extends DepositProductData {
final EnumOptionData recurringDepositType, final Integer recurringDepositFrequency,
final EnumOptionData recurringDepositFrequencyType, final Collection<EnumOptionData> recurringDepositTypeOptions,
final Collection<EnumOptionData> recurringDepositFrequencyTypeOptions, final Collection<EnumOptionData> depositTermTypeOptions,
final Collection<EnumOptionData> inMultiplesOfDepositTermTypeOptions) {
final Collection<EnumOptionData> inMultiplesOfDepositTermTypeOptions,final BigDecimal minDepositAmount,
final BigDecimal depositAmount, final BigDecimal maxDepositAmount) {
super(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency,
@@ -419,6 +438,9 @@ public class RecurringDepositProductData extends DepositProductData {
this.recurringDepositFrequencyType = recurringDepositFrequencyType;
this.recurringDepositFrequency = recurringDepositFrequency;
this.recurringDepositType = recurringDepositType;
this.minDepositAmount = minDepositAmount;
this.depositAmount = depositAmount;
this.maxDepositAmount = maxDepositAmount;
// template data
this.interestFreePeriodTypeOptions = interestFreePeriodTypeOptions;
@@ -13,6 +13,7 @@ import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurring
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyTypeIdParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositTypeIdParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.expectedFirstDepositOnDateParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountNoParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.clientIdParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.externalIdParamName;
@@ -312,6 +313,7 @@ public class DepositAccountAssembler {
final Integer depositPeriodFrequencyId = command.integerValueOfParameterNamed(depositPeriodFrequencyIdParamName);
final SavingsPeriodFrequencyType depositPeriodFrequency = SavingsPeriodFrequencyType.fromInt(depositPeriodFrequencyId);
final SavingsAccount account = null;
final LocalDate expectedFirstDepositOnDate = command.localDateValueOfParameterNamed(expectedFirstDepositOnDateParamName);
// calculate maturity amount
final BigDecimal maturityAmount = null;// calculated and updated in
@@ -319,7 +321,7 @@ public class DepositAccountAssembler {
final LocalDate maturityDate = null;// calculated and updated in account
final DepositAccountOnClosureType accountOnClosureType = null;
return DepositAccountTermAndPreClosure.createNew(updatedProductPreClosure, updatedProductTerm, account, depositAmount,
maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, accountOnClosureType);
maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType);
}
public DepositAccountRecurringDetail assembleAccountRecurringDetail(final JsonCommand command,
@@ -179,8 +179,15 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
final LocalDate transactionDate = withdrawal.transactionLocalDate();
if (rdAccount.isReinvestOnClosure()) {
RecurringDepositAccount reinvestedDeposit = rdAccount.reInvest(transactionAmount);
final LocalDate transactionStartDate = rdAccount.depositStartDate();
rdAccount.updateMaturityDateAndAmount(mc, transactionStartDate);
depositAccountAssembler.assignSavingAccountHelpers(reinvestedDeposit);
final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
final LocalDate transactionStartDate = reinvestedDeposit.getActivationLocalDate();
final LocalDate nextTransactionStartDate = reinvestedDeposit.nextDepositDate(transactionStartDate);
reinvestedDeposit.updateMaturityDateAndAmount(mc, nextTransactionStartDate);
reinvestedDeposit.processAccountUponActivation(fmt);
reinvestedDeposit.updateMaturityDateAndAmount(mc, nextTransactionStartDate);
this.savingsAccountRepository.save(reinvestedDeposit);
autoGenerateAccountNumber(reinvestedDeposit);
} else if (rdAccount.isTransferToSavingsOnClosure()) {
@@ -119,10 +119,10 @@ public class DepositAccountInterestRateChart extends AbstractPersistable<Long> {
this.account = account;
}
public BigDecimal getApplicableInterestRate(@SuppressWarnings("unused") final BigDecimal depositAmount, final LocalDate periodStartDate, final LocalDate periodEndDate){
public BigDecimal getApplicableInterestRate(final BigDecimal depositAmount, final LocalDate periodStartDate, final LocalDate periodEndDate){
BigDecimal effectiveInterestRate = BigDecimal.ZERO;
for (DepositAccountInterestRateChartSlabs slab : setOfChartSlabs()) {
if(slab.slabFields().isBetweenPeriod(periodStartDate, periodEndDate)){
if(slab.slabFields().isBetweenPeriod(periodStartDate, periodEndDate) && slab.slabFields().isAmountBetween(depositAmount)){
effectiveInterestRate = slab.slabFields().annualInterestRate();
}
}
@@ -49,6 +49,10 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistable<Long> {
@Temporal(TemporalType.DATE)
@Column(name = "maturity_date", nullable = true)
private Date maturityDate;
@Temporal(TemporalType.DATE)
@Column(name = "expected_firstdepositon_date")
private Date expectedFirstDepositOnDate;
@Column(name = "deposit_period", nullable = true)
private Integer depositPeriod;
@@ -75,16 +79,16 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistable<Long> {
public static DepositAccountTermAndPreClosure createNew(DepositPreClosureDetail preClosureDetail, DepositTermDetail depositTermDetail,
SavingsAccount account, BigDecimal depositAmount, BigDecimal maturityAmount, final LocalDate maturityDate,
Integer depositPeriod, final SavingsPeriodFrequencyType depositPeriodFrequency,
Integer depositPeriod, final SavingsPeriodFrequencyType depositPeriodFrequency, final LocalDate expectedFirstDepositOnDate,
final DepositAccountOnClosureType accountOnClosureType) {
return new DepositAccountTermAndPreClosure(preClosureDetail, depositTermDetail, account, depositAmount, maturityAmount,
maturityDate, depositPeriod, depositPeriodFrequency, accountOnClosureType);
maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType);
}
private DepositAccountTermAndPreClosure(DepositPreClosureDetail preClosureDetail, DepositTermDetail depositTermDetail,
SavingsAccount account, BigDecimal depositAmount, BigDecimal maturityAmount, final LocalDate maturityDate,
Integer depositPeriod, final SavingsPeriodFrequencyType depositPeriodFrequency,
Integer depositPeriod, final SavingsPeriodFrequencyType depositPeriodFrequency, final LocalDate expectedFirstDepositOnDate,
final DepositAccountOnClosureType accountOnClosureType) {
this.depositAmount = depositAmount;
this.maturityAmount = maturityAmount;
@@ -94,6 +98,7 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistable<Long> {
this.preClosureDetail = preClosureDetail;
this.depositTermDetail = depositTermDetail;
this.account = account;
this.expectedFirstDepositOnDate = expectedFirstDepositOnDate == null ? null : expectedFirstDepositOnDate.toDate();
this.onAccountClosureType = (accountOnClosureType == null) ? null : accountOnClosureType.getValue();
}
@@ -174,6 +179,14 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistable<Long> {
}
return maturityLocalDate;
}
public LocalDate getExpectedFirstDepositOnDate() {
LocalDate expectedFirstDepositOnLocalDate = null;
if (this.expectedFirstDepositOnDate != null) {
expectedFirstDepositOnLocalDate = new LocalDate(this.expectedFirstDepositOnDate);
}
return expectedFirstDepositOnLocalDate;
}
public boolean isPreClosurePenalApplicable() {
if (this.preClosureDetail != null) { return this.preClosureDetail.preClosurePenalApplicable(); }
@@ -181,7 +194,11 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistable<Long> {
}
public Integer getActualDepositPeriod(final LocalDate interestPostingUpToDate, final SavingsPeriodFrequencyType periodFrequencyType) {
final LocalDate depositFromDate = this.account.accountSubmittedOrActivationDate();
LocalDate depositFromDate = getExpectedFirstDepositOnDate();
if(depositFromDate == null)
depositFromDate = this.account.accountSubmittedOrActivationDate();
Integer actualDepositPeriod = this.depositPeriod;
if (depositFromDate == null || getMaturityLocalDate() == null || interestPostingUpToDate.isEqual(getMaturityLocalDate())) { return actualDepositPeriod; }
@@ -225,14 +242,20 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistable<Long> {
public DepositAccountTermAndPreClosure deepCopy(Money depositAmount) {
final SavingsAccount account = null;
final BigDecimal maturityAmount = null;
final BigDecimal actualDepositAmount = null;
final LocalDate maturityDate = null;
final Integer depositPeriod = this.depositPeriod;
final SavingsPeriodFrequencyType depositPeriodFrequency = SavingsPeriodFrequencyType.fromInt(this.depositPeriodFrequency);
final DepositPreClosureDetail preClosureDetail = this.preClosureDetail.copy();
final DepositTermDetail depositTermDetail = this.depositTermDetail.copy();
final LocalDate expectedFirstDepositOnDate = null;
final DepositAccountOnClosureType accountOnClosureType = null;
return DepositAccountTermAndPreClosure.createNew(preClosureDetail, depositTermDetail, account, depositAmount.getAmount(), maturityAmount,
maturityDate, depositPeriod, depositPeriodFrequency, accountOnClosureType);
return DepositAccountTermAndPreClosure.createNew(preClosureDetail, depositTermDetail, account, actualDepositAmount, maturityAmount,
maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType);
}
public void updateExpectedFirstDepositDate(final LocalDate expectedFirstDepositOnDate) {
this.expectedFirstDepositOnDate = expectedFirstDepositOnDate.toDate();
}
}
@@ -0,0 +1,98 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.savings.domain;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
/**
* RecurringDepositProductAmountDetails encapsulates all recurring Deposit Amount of a
* {@link RecurringDepositProduct}.
*/
@Embeddable
public class DepositProductAmountDetails {
@Column(name = "min_deposit_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal minDepositAmount;
@Column(name = "max_deposit_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal maxDepositAmount;
@Column(name = "deposit_amount", scale = 6, precision = 19, nullable = false)
private BigDecimal depositAmount;
public static DepositProductAmountDetails createFrom(final BigDecimal minDepositAmount,
final BigDecimal depositAmount, final BigDecimal maxDepositAmount) {
return new DepositProductAmountDetails(minDepositAmount, depositAmount, maxDepositAmount);
}
protected DepositProductAmountDetails() {
//
}
public DepositProductAmountDetails(final BigDecimal minDepositAmount, final BigDecimal depositAmount, final BigDecimal maxDepositAmount) {
this.minDepositAmount = minDepositAmount;
this.depositAmount = depositAmount;
this.maxDepositAmount = maxDepositAmount;
}
public Map<String, Object> update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(20);
final String localeAsInput = command.locale();
final String minDepositAmountParamName = "minDepositAmount";
if (command.isChangeInBigDecimalParameterNamedWithNullCheck(minDepositAmountParamName, this.minDepositAmount)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(minDepositAmountParamName);
actualChanges.put(minDepositAmountParamName, newValue);
actualChanges.put("locale", localeAsInput);
this.minDepositAmount = newValue;
}
final String maxDepositAmountParamName = "maxDepositAmount";
if (command.isChangeInBigDecimalParameterNamedWithNullCheck(maxDepositAmountParamName, this.maxDepositAmount)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(maxDepositAmountParamName);
actualChanges.put(maxDepositAmountParamName, newValue);
actualChanges.put("locale", localeAsInput);
this.maxDepositAmount = newValue;
}
final String depositAmountParamName = "depositAmount";
if (command.isChangeInBigDecimalParameterNamedWithNullCheck(depositAmountParamName, this.depositAmount)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(depositAmountParamName);
actualChanges.put(depositAmountParamName, newValue);
actualChanges.put("locale", localeAsInput);
this.depositAmount = newValue;
}
return actualChanges;
}
public BigDecimal getMinDepositAmount() {
return this.minDepositAmount;
}
public BigDecimal getMaxDepositAmount() {
return this.maxDepositAmount;
}
public BigDecimal getDepositAmount() {
return this.depositAmount;
}
}
@@ -24,6 +24,9 @@ import static org.mifosplatform.portfolio.savings.DepositsApiConstants.preClosur
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositFrequencyTypeIdParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.recurringDepositTypeIdParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositAmountParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositMinAmountParamName;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.depositMaxAmountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName;
@@ -133,8 +136,9 @@ public class DepositProductAssembler {
final DepositPreClosureDetail preClosureDetail = this.assemblePreClosureDetail(command);
final DepositTermDetail depositTermDetail = this.assembleDepositTermDetail(command);
final DepositProductAmountDetails depositProductAmountDetails = this.assembleDepositAmountDetails(command);
final DepositProductTermAndPreClosure productTermAndPreClosure = DepositProductTermAndPreClosure.createNew(preClosureDetail,
depositTermDetail, null);
depositTermDetail, depositProductAmountDetails, null);
// Savings product charges
final Set<Charge> charges = assembleListOfSavingsProductCharges(command, currencyCode);
@@ -167,7 +171,7 @@ public class DepositProductAssembler {
.failWithCodeNoParameterAddedToErrorCode("max.term.lessthan.min.term");
}
fixedDepositProduct.validateCharts(baseDataValidator);
fixedDepositProduct.validateInterestPostingAndCompoundingPeriodTypes(baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -220,8 +224,9 @@ public class DepositProductAssembler {
final DepositPreClosureDetail preClosureDetail = this.assemblePreClosureDetail(command);
final DepositTermDetail depositTermDetail = this.assembleDepositTermDetail(command);
final DepositProductAmountDetails depositProductAmountDetails = this.assembleDepositAmountDetails(command);
final DepositProductTermAndPreClosure productTermAndPreClosure = DepositProductTermAndPreClosure.createNew(preClosureDetail,
depositTermDetail, null);
depositTermDetail, depositProductAmountDetails, null);
final DepositRecurringDetail recurringDetail = this.assembleRecurringDetail(command);
final DepositProductRecurringDetail productRecurringDetail = DepositProductRecurringDetail.createNew(recurringDetail, null);
@@ -260,6 +265,7 @@ public class DepositProductAssembler {
.failWithCodeNoParameterAddedToErrorCode("max.term.lessthan.min.term");
}
recurringDepositProduct.validateCharts(baseDataValidator);
recurringDepositProduct.validateInterestPostingAndCompoundingPeriodTypes(baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -535,4 +541,27 @@ public class DepositProductAssembler {
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
public DepositProductAmountDetails assembleDepositAmountDetails(final JsonCommand command) {
BigDecimal minDepositAmount = null;
if(command.parameterExists(depositMinAmountParamName)) {
minDepositAmount = command.bigDecimalValueOfParameterNamed(depositMinAmountParamName);
}
BigDecimal maxDepositAmount = null;
if(command.parameterExists(depositMaxAmountParamName)) {
maxDepositAmount = command.bigDecimalValueOfParameterNamed(depositMaxAmountParamName);
}
BigDecimal depositAmount = null;
if(command.parameterExists(depositAmountParamName)) {
depositAmount = command.bigDecimalValueOfParameterNamed(depositAmountParamName);
}
final DepositProductAmountDetails depositRecurringDetail = new DepositProductAmountDetails(minDepositAmount,
depositAmount, maxDepositAmount);
return depositRecurringDetail;
}
}
@@ -31,21 +31,26 @@ public class DepositProductTermAndPreClosure extends AbstractPersistable<Long> {
@OneToOne
@JoinColumn(name = "savings_product_id", nullable = false)
private FixedDepositProduct product;
@Embedded
private DepositProductAmountDetails depositProductAmountDetails;
protected DepositProductTermAndPreClosure() {
super();
}
public static DepositProductTermAndPreClosure createNew(DepositPreClosureDetail preClosureDetail, DepositTermDetail depositTermDetail,
DepositProductAmountDetails depositProductMinMaxAmountDetails,
SavingsProduct product) {
return new DepositProductTermAndPreClosure(preClosureDetail, depositTermDetail, product);
return new DepositProductTermAndPreClosure(preClosureDetail, depositTermDetail, depositProductMinMaxAmountDetails, product);
}
private DepositProductTermAndPreClosure(DepositPreClosureDetail preClosureDetail, DepositTermDetail depositTermDetail,
SavingsProduct product) {
DepositProductAmountDetails depositProductMinMaxAmountDetails, SavingsProduct product) {
this.preClosureDetail = preClosureDetail;
this.depositTermDetail = depositTermDetail;
this.depositProductAmountDetails = depositProductMinMaxAmountDetails;
this.product = (FixedDepositProduct)product;
}
@@ -58,6 +63,10 @@ public class DepositProductTermAndPreClosure extends AbstractPersistable<Long> {
if (this.depositTermDetail != null) {
actualChanges.putAll(this.depositTermDetail.update(command, baseDataValidator));
}
if(this.depositProductAmountDetails != null) {
actualChanges.putAll(this.depositProductAmountDetails.update(command, baseDataValidator));
}
return actualChanges;
}
@@ -69,6 +78,10 @@ public class DepositProductTermAndPreClosure extends AbstractPersistable<Long> {
return this.depositTermDetail;
}
public DepositProductAmountDetails depositProductAmountDetails() {
return this.depositProductAmountDetails;
}
public void updateProductReference(final SavingsProduct product){
this.product = (FixedDepositProduct)product;
}
@@ -11,6 +11,7 @@ import static org.mifosplatform.portfolio.savings.DepositsApiConstants.FIXED_DEP
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -260,5 +261,37 @@ public class FixedDepositProduct extends SavingsProduct {
public DepositProductTermAndPreClosure depositProductTermAndPreClosure() {
return this.productTermAndPreClosure;
}
@SuppressWarnings("unchecked")
public void validateInterestPostingAndCompoundingPeriodTypes(final DataValidatorBuilder baseDataValidator) {
Map<SavingsPostingInterestPeriodType, List<SavingsCompoundingInterestPeriodType>> postingtoCompoundMap =
new HashMap<SavingsPostingInterestPeriodType, List<SavingsCompoundingInterestPeriodType>>();
postingtoCompoundMap.put(SavingsPostingInterestPeriodType.MONTHLY,
Arrays.asList(new SavingsCompoundingInterestPeriodType[]{SavingsCompoundingInterestPeriodType.DAILY,SavingsCompoundingInterestPeriodType.MONTHLY}));
postingtoCompoundMap.put(SavingsPostingInterestPeriodType.QUATERLY,
Arrays.asList(new SavingsCompoundingInterestPeriodType[]{SavingsCompoundingInterestPeriodType.DAILY
,SavingsCompoundingInterestPeriodType.MONTHLY,SavingsCompoundingInterestPeriodType.QUATERLY}));
postingtoCompoundMap.put(SavingsPostingInterestPeriodType.BIANNUAL,
Arrays.asList(new SavingsCompoundingInterestPeriodType[]{SavingsCompoundingInterestPeriodType.DAILY,
SavingsCompoundingInterestPeriodType.MONTHLY,SavingsCompoundingInterestPeriodType.QUATERLY,SavingsCompoundingInterestPeriodType.BI_ANNUAL}));
postingtoCompoundMap.put(SavingsPostingInterestPeriodType.ANNUAL,
Arrays.asList(new SavingsCompoundingInterestPeriodType[]{SavingsCompoundingInterestPeriodType.DAILY,
SavingsCompoundingInterestPeriodType.MONTHLY,SavingsCompoundingInterestPeriodType.QUATERLY
,SavingsCompoundingInterestPeriodType.BI_ANNUAL,SavingsCompoundingInterestPeriodType.ANNUAL}));
SavingsPostingInterestPeriodType savingsPostingInterestPeriodType = SavingsPostingInterestPeriodType.fromInt(interestPostingPeriodType);
SavingsCompoundingInterestPeriodType savingsCompoundingInterestPeriodType = SavingsCompoundingInterestPeriodType.fromInt(interestCompoundingPeriodType);
if(postingtoCompoundMap.get(savingsPostingInterestPeriodType) == null ||
!postingtoCompoundMap.get(savingsPostingInterestPeriodType).contains(savingsCompoundingInterestPeriodType)) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("posting.period.type.is.less.than.compound.period.type",
savingsPostingInterestPeriodType.name(),savingsCompoundingInterestPeriodType.name());
}
}
}
@@ -99,6 +99,28 @@ public class RecurringDepositAccount extends SavingsAccount {
lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, savingsAccountCharges, accountTermAndPreClosure,
recurringDetail, chart, allowOverdraft, overdraftLimit);
}
public static RecurringDepositAccount createNewActivatedAccount(final Client client, final Group group,
final SavingsProduct product, final Staff fieldOfficer, final String accountNo, final String externalId,
final AccountType accountType, final LocalDate submittedOnDate, final AppUser submittedBy, final BigDecimal interestRate,
final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges,
final DepositAccountTermAndPreClosure accountTermAndPreClosure, final DepositAccountRecurringDetail recurringDetail,
final DepositAccountInterestRateChart chart) {
final boolean allowOverdraft = false;
final BigDecimal overdraftLimit = new BigDecimal(0);
final SavingsAccountStatusType status = SavingsAccountStatusType.ACTIVE;
return new RecurringDepositAccount(client, group, product, fieldOfficer, accountNo, externalId, status, accountType,
submittedOnDate, submittedBy, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, savingsAccountCharges, accountTermAndPreClosure,
recurringDetail, chart, allowOverdraft, overdraftLimit);
}
private RecurringDepositAccount(final Client client, final Group group, final SavingsProduct product, final Staff fieldOfficer,
final String accountNo, final String externalId, final SavingsAccountStatusType status, final AccountType accountType,
@@ -159,7 +181,7 @@ public class RecurringDepositAccount extends SavingsAccount {
}
}
final BigDecimal depositAmount = accountTermAndPreClosure.depositAmount();
final BigDecimal depositAmount = recurringDetail.recurringDepositAmount();
BigDecimal applicableInterestRate = this.chart.getApplicableInterestRate(depositAmount, depositStartDate(), depositCloseDate);
@@ -251,7 +273,7 @@ public class RecurringDepositAccount extends SavingsAccount {
.fromInt(this.interestCalculationDaysInYearType);
final List<LocalDateInterval> postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods(
accountSubmittedOrActivationDate(), maturityDate, postingPeriodType);
depositStartDate(), maturityDate, postingPeriodType);
final List<PostingPeriod> allPostingPeriods = new ArrayList<PostingPeriod>();
@@ -321,7 +343,7 @@ public class RecurringDepositAccount extends SavingsAccount {
return allTransactions;
}
private LocalDate nextDepositDate(LocalDate depositDate) {
public LocalDate nextDepositDate(LocalDate depositDate) {
final LocalDate depositStartDate = depositStartDate();
LocalDate nextDepositDate = depositStartDate;
while (!depositDate.isBefore(nextDepositDate)) {
@@ -357,7 +379,9 @@ public class RecurringDepositAccount extends SavingsAccount {
public LocalDate depositStartDate() {
// TODO: Support to add deposit start date which can be a date after
// account activation date.
final LocalDate depositStartDate = accountSubmittedOrActivationDate();
final LocalDate depositStartDate = accountTermAndPreClosure.getExpectedFirstDepositOnDate();
if(depositStartDate == null)
return accountSubmittedOrActivationDate();
return depositStartDate;
}
@@ -436,18 +460,19 @@ public class RecurringDepositAccount extends SavingsAccount {
}
@Override
protected void processAccountUponActivation(final DateTimeFormatter fmt) {
final Money recurringDepositAmount = Money.of(this.currency, this.recurringDetail.recurringDepositAmount());
if (recurringDepositAmount.isGreaterThanZero()) {
protected void processAccountUponActivation(final DateTimeFormatter fmt) {
final Money minRequiredOpeningBalance = Money.of(this.currency, this.minRequiredOpeningBalance);
if (minRequiredOpeningBalance.isGreaterThanZero()) {
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, getActivationLocalDate(),
recurringDepositAmount.getAmount(), null, new Date());
minRequiredOpeningBalance.getAmount(), null, new Date());
deposit(transactionDTO);
final LocalDate interestPostingUpToDate = interestPostingUpToDate(DateUtils.getLocalDateOfTenant());
// update existing transactions so derived balance fields are
// correct.
recalculateDailyBalances(Money.zero(this.currency), interestPostingUpToDate);
recalculateDailyBalances(Money.zero(this.currency), DateUtils.getLocalDateOfTenant());
}
}
public SavingsAccountTransaction close(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate,
@@ -778,6 +803,25 @@ public class RecurringDepositAccount extends SavingsAccount {
if (!chartInterval.contains(accountSubmittedOrActivationDate())) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("no.valid.interest.rate.slab.available.for.date.range");
}
final boolean firstDepositDateBeforeMaturityAndOnOrAfterActivationDate = firstDepositDateBeforeMaturityAndOnOrAfterActivationDate();
if(!firstDepositDateBeforeMaturityAndOnOrAfterActivationDate) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("expected.first.deposit.date.is.not.between.maturity.date.and.activation.or.submitted.date");
}
final BigDecimal recurringDepositAmount = recurringDetail.recurringDepositAmount();
BigDecimal applicableInterestRate = this.chart.getApplicableInterestRate(recurringDepositAmount, depositStartDate(), calculateMaturityDate());
if(applicableInterestRate.equals(BigDecimal.ZERO)) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("no.applicable.interest.rate.is.found.based.on.amount.and.deposit.period");
}
final boolean recurringFrequencyBeforeDepositPeriod = recurringFrequencyBeforeDepositPeriod();
if(!recurringFrequencyBeforeDepositPeriod) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("recurring.frequency.not.before.deposit.period");
}
}
public boolean isReinvestOnClosure() {
@@ -795,7 +839,7 @@ public class RecurringDepositAccount extends SavingsAccount {
final SavingsProduct product = this.product;
final InterestRateChart productChart = product.applicableChart(getClosedOnDate());
final DepositAccountInterestRateChart newChart = DepositAccountInterestRateChart.from(productChart);
final String accountNumber = this.accountNumber;
final String accountNumber = null;
final String externalId = this.externalId;
final AccountType accountType = AccountType.fromInt(this.accountType);
final SavingsPostingInterestPeriodType postingPeriodType = SavingsPostingInterestPeriodType.fromInt(this.interestPostingPeriodType);
@@ -804,17 +848,78 @@ public class RecurringDepositAccount extends SavingsAccount {
final SavingsInterestCalculationType interestCalculationType = SavingsInterestCalculationType.fromInt(this.interestCalculationType);
final SavingsInterestCalculationDaysInYearType daysInYearType = SavingsInterestCalculationDaysInYearType
.fromInt(this.interestCalculationDaysInYearType);
final BigDecimal minRequiredOpeningBalance = null;
final BigDecimal interestRate = null;
final BigDecimal minRequiredOpeningBalance = depositAmount.getAmount();
final BigDecimal interestRate = BigDecimal.ZERO;
final Set<SavingsAccountCharge> savingsAccountCharges = null;
final SavingsPeriodFrequencyType lockinPeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(this.lockinPeriodFrequencyType);
final Integer lockinPeriodFrequency = this.lockinPeriodFrequency;
final boolean withdrawalFeeApplicableForTransfer = false;
LocalDate now = LocalDate.now();
newAccountTermAndPreClosure.updateExpectedFirstDepositDate(now);
return RecurringDepositAccount.createNewApplicationForSubmittal(client, group, product, fieldOfficer, accountNumber, externalId,
RecurringDepositAccount rdAccount = RecurringDepositAccount.createNewActivatedAccount(client, group, product, fieldOfficer, accountNumber, externalId,
accountType, getClosedOnDate(), closedBy, interestRate, compoundingPeriodType, postingPeriodType, interestCalculationType,
daysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeApplicableForTransfer, savingsAccountCharges, newAccountTermAndPreClosure, recurringDetail, newChart);
rdAccount.setDatesFrom(now);
newAccountTermAndPreClosure.updateAccountReference(rdAccount);
recurringDetail.updateAccountReference(rdAccount);
return rdAccount;
}
private boolean firstDepositDateBeforeMaturityAndOnOrAfterActivationDate() {
LocalDate expectedFirstDepositLocalDate = accountTermAndPreClosure.getExpectedFirstDepositOnDate();
LocalDate maturityLocalDate = calculateMaturityDate();
LocalDate activateOnSubmittedLocalDate = accountSubmittedOrActivationDate();
return expectedFirstDepositLocalDate.isBefore(maturityLocalDate) &&
(activateOnSubmittedLocalDate.isEqual(expectedFirstDepositLocalDate) || activateOnSubmittedLocalDate.isBefore(expectedFirstDepositLocalDate));
}
public void setDatesFrom(final LocalDate now) {
this.rejectedOnDate = null;
this.rejectedBy = null;
this.withdrawnOnDate = null;
this.withdrawnBy = null;
this.closedOnDate = null;
this.closedBy = null;
this.activatedBy = null;
this.lockedInUntilDate = null;
this.activatedOnDate = now.toDate();
}
private boolean recurringFrequencyBeforeDepositPeriod() {
final int recurringFrequency = recurringDetail.recurringDepositFrequency();
final SavingsPeriodFrequencyType recurringfrequencyType = recurringDetail.recurringDepositFrequencyType();
LocalDate depositStartDate = accountTermAndPreClosure.getExpectedFirstDepositOnDate();
LocalDate endDateWithFirstRecurringFrequency = null;
switch (recurringfrequencyType) {
case DAYS:
endDateWithFirstRecurringFrequency = depositStartDate.plusDays(recurringFrequency);
break;
case MONTHS:
endDateWithFirstRecurringFrequency = depositStartDate.plusMonths(recurringFrequency);
break;
case WEEKS:
endDateWithFirstRecurringFrequency = depositStartDate.plusWeeks(recurringFrequency);
break;
case YEARS:
endDateWithFirstRecurringFrequency = depositStartDate.plusYears(recurringFrequency);
break;
default:
break;
}
return endDateWithFirstRecurringFrequency != null && endDateWithFirstRecurringFrequency.isBefore(calculateMaturityDate());
}
}
@@ -69,6 +69,15 @@ public final class SavingsHelper {
periodEndDate = new DateTime().withDate(year, 12, 31).toLocalDate();
}
break;
case BIANNUAL:
final int byear = periodStartDate.getYearOfEra();
final int bmonthofYear = periodStartDate.getMonthOfYear();
if (bmonthofYear <= 6) {
periodEndDate = new DateTime().withDate(byear, 6, 30).toLocalDate();
} else if (bmonthofYear <= 12) {
periodEndDate = new DateTime().withDate(byear, 12, 31).toLocalDate();
}
break;
case ANNUAL:
periodEndDate = periodStartDate.monthOfYear().withMaximumValue();
periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
@@ -0,0 +1,154 @@
package org.mifosplatform.portfolio.savings.domain.interest;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType;
public class AnnualCompoundingPeriod implements CompoundingPeriod {
@SuppressWarnings("unused")
private final LocalDateInterval periodInterval;
private final List<EndOfDayBalance> endOfDayBalances;
public static AnnualCompoundingPeriod create(final LocalDateInterval periodInterval, final List<EndOfDayBalance> allEndOfDayBalances, final LocalDate upToInterestCalculationDate) {
final List<EndOfDayBalance> endOfDayBalancesWithinPeriod = endOfDayBalancesWithinPeriodInterval(periodInterval, allEndOfDayBalances, upToInterestCalculationDate);
return new AnnualCompoundingPeriod(periodInterval, endOfDayBalancesWithinPeriod);
}
@Override
public BigDecimal calculateInterest(final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal interestEarned = BigDecimal.ZERO;
for (final EndOfDayBalance balance : this.endOfDayBalances) {
final BigDecimal interestOnBalanceUnrounded = balance.calculateInterestOnBalance(BigDecimal.ZERO, interestRateAsFraction,
daysInYear);
interestEarned = interestEarned.add(interestOnBalanceUnrounded);
}
return interestEarned;
}
@Override
public BigDecimal calculateInterest(final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType,
final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestToCompound,
final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal interestEarned = BigDecimal.ZERO;
switch (interestCalculationType) {
case DAILY_BALANCE:
interestEarned = calculateUsingDailyBalanceMethod(compoundingInterestPeriodType, interestToCompound,
interestRateAsFraction, daysInYear);
break;
case AVERAGE_DAILY_BALANCE:
interestEarned = calculateUsingAverageDailyBalanceMethod(interestToCompound, interestRateAsFraction, daysInYear);
break;
case INVALID:
break;
}
return interestEarned;
}
private BigDecimal calculateUsingAverageDailyBalanceMethod(final BigDecimal interestToCompound,
final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal cumulativeBalance = BigDecimal.ZERO;
Integer numberOfDays = Integer.valueOf(0);
for (final EndOfDayBalance balance : this.endOfDayBalances) {
final BigDecimal endOfDayCumulativeBalance = balance.cumulativeBalance(interestToCompound);
cumulativeBalance = cumulativeBalance.add(endOfDayCumulativeBalance);
final Integer balanceExistsForNumberOfDays = balance.getNumberOfDays();
numberOfDays = numberOfDays + balanceExistsForNumberOfDays;
}
BigDecimal interestEarned = BigDecimal.ZERO;
if (cumulativeBalance.compareTo(BigDecimal.ZERO) != 0 && numberOfDays > 0) {
final BigDecimal averageDailyBalance = cumulativeBalance.divide(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64)
.setScale(9, RoundingMode.HALF_EVEN);
final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64);
final BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64);
final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64);
interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, RoundingMode.HALF_EVEN);
}
return interestEarned;
}
private BigDecimal calculateUsingDailyBalanceMethod(final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType,
final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal interestEarned = BigDecimal.ZERO;
BigDecimal interestOnBalanceUnrounded = BigDecimal.ZERO;
for (final EndOfDayBalance balance : this.endOfDayBalances) {
switch (compoundingInterestPeriodType) {
case DAILY:
interestOnBalanceUnrounded = balance.calculateInterestOnBalanceAndInterest(interestToCompound, interestRateAsFraction,
daysInYear);
break;
case MONTHLY:
interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear);
break;
case QUATERLY:
interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear);
break;
// case WEEKLY:
// break;
// case BIWEEKLY:
// break;
case BI_ANNUAL:
interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear);
break;
case ANNUAL:
interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear);
break;
// case NO_COMPOUNDING_SIMPLE_INTEREST:
// break;
case INVALID:
break;
}
interestEarned = interestEarned.add(interestOnBalanceUnrounded);
}
return interestEarned;
}
private static List<EndOfDayBalance> endOfDayBalancesWithinPeriodInterval(final LocalDateInterval compoundingPeriodInterval,
final List<EndOfDayBalance> allEndOfDayBalances, final LocalDate upToInterestCalculationDate) {
final List<EndOfDayBalance> endOfDayBalancesForPeriodInterval = new ArrayList<EndOfDayBalance>();
for (final EndOfDayBalance endOfDayBalance : allEndOfDayBalances) {
if (compoundingPeriodInterval.contains(endOfDayBalance.date())) {
final EndOfDayBalance cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval, upToInterestCalculationDate);
endOfDayBalancesForPeriodInterval.add(cappedToPeriodEndDate);
} else if (endOfDayBalance.contains(compoundingPeriodInterval)) {
final EndOfDayBalance cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval, upToInterestCalculationDate);
endOfDayBalancesForPeriodInterval.add(cappedToPeriodEndDate);
}
}
return endOfDayBalancesForPeriodInterval;
}
private AnnualCompoundingPeriod(final LocalDateInterval periodInterval, final List<EndOfDayBalance> endOfDayBalances) {
this.periodInterval = periodInterval;
this.endOfDayBalances = endOfDayBalances;
}
}
@@ -0,0 +1,154 @@
package org.mifosplatform.portfolio.savings.domain.interest;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType;
public class BiAnnualCompoundingPeriod implements CompoundingPeriod {
@SuppressWarnings("unused")
private final LocalDateInterval periodInterval;
private final List<EndOfDayBalance> endOfDayBalances;
public static BiAnnualCompoundingPeriod create(final LocalDateInterval periodInterval, final List<EndOfDayBalance> allEndOfDayBalances, final LocalDate upToInterestCalculationDate) {
final List<EndOfDayBalance> endOfDayBalancesWithinPeriod = endOfDayBalancesWithinPeriodInterval(periodInterval, allEndOfDayBalances, upToInterestCalculationDate);
return new BiAnnualCompoundingPeriod(periodInterval, endOfDayBalancesWithinPeriod);
}
@Override
public BigDecimal calculateInterest(final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal interestEarned = BigDecimal.ZERO;
for (final EndOfDayBalance balance : this.endOfDayBalances) {
final BigDecimal interestOnBalanceUnrounded = balance.calculateInterestOnBalance(BigDecimal.ZERO, interestRateAsFraction,
daysInYear);
interestEarned = interestEarned.add(interestOnBalanceUnrounded);
}
return interestEarned;
}
@Override
public BigDecimal calculateInterest(final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType,
final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestToCompound,
final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal interestEarned = BigDecimal.ZERO;
switch (interestCalculationType) {
case DAILY_BALANCE:
interestEarned = calculateUsingDailyBalanceMethod(compoundingInterestPeriodType, interestToCompound,
interestRateAsFraction, daysInYear);
break;
case AVERAGE_DAILY_BALANCE:
interestEarned = calculateUsingAverageDailyBalanceMethod(interestToCompound, interestRateAsFraction, daysInYear);
break;
case INVALID:
break;
}
return interestEarned;
}
private BigDecimal calculateUsingAverageDailyBalanceMethod(final BigDecimal interestToCompound,
final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal cumulativeBalance = BigDecimal.ZERO;
Integer numberOfDays = Integer.valueOf(0);
for (final EndOfDayBalance balance : this.endOfDayBalances) {
final BigDecimal endOfDayCumulativeBalance = balance.cumulativeBalance(interestToCompound);
cumulativeBalance = cumulativeBalance.add(endOfDayCumulativeBalance);
final Integer balanceExistsForNumberOfDays = balance.getNumberOfDays();
numberOfDays = numberOfDays + balanceExistsForNumberOfDays;
}
BigDecimal interestEarned = BigDecimal.ZERO;
if (cumulativeBalance.compareTo(BigDecimal.ZERO) != 0 && numberOfDays > 0) {
final BigDecimal averageDailyBalance = cumulativeBalance.divide(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64)
.setScale(9, RoundingMode.HALF_EVEN);
final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64);
final BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64);
final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64);
interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, RoundingMode.HALF_EVEN);
}
return interestEarned;
}
private BigDecimal calculateUsingDailyBalanceMethod(final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType,
final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal interestEarned = BigDecimal.ZERO;
BigDecimal interestOnBalanceUnrounded = BigDecimal.ZERO;
for (final EndOfDayBalance balance : this.endOfDayBalances) {
switch (compoundingInterestPeriodType) {
case DAILY:
interestOnBalanceUnrounded = balance.calculateInterestOnBalanceAndInterest(interestToCompound, interestRateAsFraction,
daysInYear);
break;
case MONTHLY:
interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear);
break;
case QUATERLY:
interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear);
break;
// case WEEKLY:
// break;
// case BIWEEKLY:
// break;
case BI_ANNUAL:
interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear);
break;
case ANNUAL:
interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear);
break;
// case NO_COMPOUNDING_SIMPLE_INTEREST:
// break;
case INVALID:
break;
}
interestEarned = interestEarned.add(interestOnBalanceUnrounded);
}
return interestEarned;
}
private static List<EndOfDayBalance> endOfDayBalancesWithinPeriodInterval(final LocalDateInterval compoundingPeriodInterval,
final List<EndOfDayBalance> allEndOfDayBalances, final LocalDate upToInterestCalculationDate) {
final List<EndOfDayBalance> endOfDayBalancesForPeriodInterval = new ArrayList<EndOfDayBalance>();
for (final EndOfDayBalance endOfDayBalance : allEndOfDayBalances) {
if (compoundingPeriodInterval.contains(endOfDayBalance.date())) {
final EndOfDayBalance cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval, upToInterestCalculationDate);
endOfDayBalancesForPeriodInterval.add(cappedToPeriodEndDate);
} else if (endOfDayBalance.contains(compoundingPeriodInterval)) {
final EndOfDayBalance cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval, upToInterestCalculationDate);
endOfDayBalancesForPeriodInterval.add(cappedToPeriodEndDate);
}
}
return endOfDayBalancesForPeriodInterval;
}
private BiAnnualCompoundingPeriod(final LocalDateInterval periodInterval, final List<EndOfDayBalance> endOfDayBalances) {
this.periodInterval = periodInterval;
this.endOfDayBalances = endOfDayBalances;
}
}
@@ -6,6 +6,7 @@ import java.math.RoundingMode;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.monetary.domain.Money;
public class EndOfDayBalance {
@@ -89,14 +90,19 @@ public class EndOfDayBalance {
Money startingBalance = this.openingBalance;
LocalDate balanceStartDate = this.date;
LocalDate oldBalanceEndDate = this.date.plusDays(this.numberOfDays - 1);
int daysOfBalance = this.numberOfDays;
if (this.date.isBefore(compoundingPeriodInterval.startDate())) {
balanceStartDate = compoundingPeriodInterval.startDate();
startingBalance = this.endOfDayBalance;
final LocalDateInterval balancePeriodInterval = LocalDateInterval.create(balanceStartDate, oldBalanceEndDate);
daysOfBalance = balancePeriodInterval.daysInPeriodInclusiveOfEndDate();
}
int daysOfBalance = this.numberOfDays;
LocalDate balanceEndDate = balanceStartDate.plusDays(this.numberOfDays);
LocalDate balanceEndDate = balanceStartDate.plusDays(daysOfBalance - 1);
if (balanceEndDate.isAfter(compoundingPeriodInterval.endDate())) {
balanceEndDate = compoundingPeriodInterval.endDate();
final LocalDateInterval balancePeriodInterval = LocalDateInterval.create(balanceStartDate, balanceEndDate);
@@ -113,7 +119,7 @@ public class EndOfDayBalance {
public boolean contains(final LocalDateInterval compoundingPeriodInterval) {
final LocalDate balanceUpToDate = this.date.plusDays(this.numberOfDays);
final LocalDate balanceUpToDate = this.date.plusDays(this.numberOfDays - 1);
final LocalDateInterval balanceInterval = LocalDateInterval.create(this.date, balanceUpToDate);
return balanceInterval.containsPortionOf(compoundingPeriodInterval);
@@ -4,6 +4,7 @@ import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
@@ -185,14 +186,83 @@ public class PostingPeriod {
// break;
// case BIWEEKLY:
// break;
// case QUATERLY:
// break;
// case BI_ANNUAL:
// break;
// case ANNUAL:
// break;
// case NO_COMPOUNDING_SIMPLE_INTEREST:
// break;
case QUATERLY:
final LocalDate qPostingPeriodEndDate = postingPeriodInterval.endDate();
periodStartDate = postingPeriodInterval.startDate();
periodEndDate = periodStartDate;
while (!periodStartDate.isAfter(qPostingPeriodEndDate) && !periodEndDate.isAfter(qPostingPeriodEndDate)) {
periodEndDate = determineInterestPeriodEndDateFrom(periodStartDate, interestPeriodType, upToInterestCalculationDate);
if (periodEndDate.isAfter(qPostingPeriodEndDate)) {
periodEndDate = qPostingPeriodEndDate;
}
final LocalDateInterval compoundingPeriodInterval = LocalDateInterval.create(periodStartDate, periodEndDate);
if (postingPeriodInterval.contains(compoundingPeriodInterval)) {
compoundingPeriod = QuarterlyCompoundingPeriod.create(compoundingPeriodInterval, allEndOfDayBalances,
upToInterestCalculationDate);
compoundingPeriods.add(compoundingPeriod);
}
// move periodStartDate forward to day after this period
periodStartDate = periodEndDate.plusDays(1);
}
break;
case BI_ANNUAL:
final LocalDate bPostingPeriodEndDate = postingPeriodInterval.endDate();
periodStartDate = postingPeriodInterval.startDate();
periodEndDate = periodStartDate;
while (!periodStartDate.isAfter(bPostingPeriodEndDate) && !periodEndDate.isAfter(bPostingPeriodEndDate)) {
periodEndDate = determineInterestPeriodEndDateFrom(periodStartDate, interestPeriodType, upToInterestCalculationDate);
if (periodEndDate.isAfter(bPostingPeriodEndDate)) {
periodEndDate = bPostingPeriodEndDate;
}
final LocalDateInterval compoundingPeriodInterval = LocalDateInterval.create(periodStartDate, periodEndDate);
if (postingPeriodInterval.contains(compoundingPeriodInterval)) {
compoundingPeriod = BiAnnualCompoundingPeriod.create(compoundingPeriodInterval, allEndOfDayBalances,
upToInterestCalculationDate);
compoundingPeriods.add(compoundingPeriod);
}
// move periodStartDate forward to day after this period
periodStartDate = periodEndDate.plusDays(1);
}
break;
case ANNUAL:
final LocalDate aPostingPeriodEndDate = postingPeriodInterval.endDate();
periodStartDate = postingPeriodInterval.startDate();
periodEndDate = periodStartDate;
while (!periodStartDate.isAfter(aPostingPeriodEndDate) && !periodEndDate.isAfter(aPostingPeriodEndDate)) {
periodEndDate = determineInterestPeriodEndDateFrom(periodStartDate, interestPeriodType, upToInterestCalculationDate);
if (periodEndDate.isAfter(aPostingPeriodEndDate)) {
periodEndDate = aPostingPeriodEndDate;
}
final LocalDateInterval compoundingPeriodInterval = LocalDateInterval.create(periodStartDate, periodEndDate);
if (postingPeriodInterval.contains(compoundingPeriodInterval)) {
compoundingPeriod = AnnualCompoundingPeriod.create(compoundingPeriodInterval, allEndOfDayBalances,
upToInterestCalculationDate);
compoundingPeriods.add(compoundingPeriod);
}
// move periodStartDate forward to day after this period
periodStartDate = periodEndDate.plusDays(1);
}
break;
// case NO_COMPOUNDING_SIMPLE_INTEREST:
// break;
}
return compoundingPeriods;
@@ -221,39 +291,41 @@ public class PostingPeriod {
// produce period end date on last day of current month
periodEndDate = periodStartDate.dayOfMonth().withMaximumValue();
break;
// case QUATERLY:
// // jan 1st to mar 31st, 1st apr to jun 30, jul 1st to sept 30,
// // oct 1st to dec 31
// int year = periodStartDate.getYearOfEra();
// int monthofYear = periodStartDate.getMonthOfYear();
// if (monthofYear <= 3) {
// periodEndDate = new DateTime().withDate(year, 3, 31).toLocalDate();
// } else if (monthofYear <= 6) {
// periodEndDate = new DateTime().withDate(year, 6, 30).toLocalDate();
// } else if (monthofYear <= 9) {
// periodEndDate = new DateTime().withDate(year, 9, 30).toLocalDate();
// } else if (monthofYear <= 12) {
// periodEndDate = new DateTime().withDate(year, 12, 31).toLocalDate();
// }
// break;
// case BI_ANNUAL:
// // jan 1st to 30, jul 1st to dec 31
// year = periodStartDate.getYearOfEra();
// monthofYear = periodStartDate.getMonthOfYear();
// if (monthofYear <= 6) {
// periodEndDate = new DateTime().withDate(year, 6, 30).toLocalDate();
// } else if (monthofYear <= 12) {
// periodEndDate = new DateTime().withDate(year, 12, 31).toLocalDate();
// }
// break;
// case ANNUAL:
// periodEndDate = periodStartDate.monthOfYear().withMaximumValue();
// periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
// break;
// case NO_COMPOUNDING_SIMPLE_INTEREST:
// periodEndDate = periodStartDate.monthOfYear().withMaximumValue();
// periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
// break;
case QUATERLY:
// // jan 1st to mar 31st, 1st apr to jun 30, jul 1st to sept
// 30,
// // oct 1st to dec 31
int year = periodStartDate.getYearOfEra();
int monthofYear = periodStartDate.getMonthOfYear();
if (monthofYear <= 3) {
periodEndDate = new DateTime().withDate(year, 3, 31).toLocalDate();
} else if (monthofYear <= 6) {
periodEndDate = new DateTime().withDate(year, 6, 30).toLocalDate();
} else if (monthofYear <= 9) {
periodEndDate = new DateTime().withDate(year, 9, 30).toLocalDate();
} else if (monthofYear <= 12) {
periodEndDate = new DateTime().withDate(year, 12, 31).toLocalDate();
}
break;
case BI_ANNUAL:
// // jan 1st to 30, jul 1st to dec 31
year = periodStartDate.getYearOfEra();
monthofYear = periodStartDate.getMonthOfYear();
if (monthofYear <= 6) {
periodEndDate = new DateTime().withDate(year, 6, 30).toLocalDate();
} else if (monthofYear <= 12) {
periodEndDate = new DateTime().withDate(year, 12, 31).toLocalDate();
}
break;
case ANNUAL:
periodEndDate = periodStartDate.monthOfYear().withMaximumValue();
periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
break;
// case NO_COMPOUNDING_SIMPLE_INTEREST:
// periodEndDate = periodStartDate.monthOfYear().withMaximumValue();
// periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
// break;
}
return periodEndDate;
@@ -0,0 +1,154 @@
package org.mifosplatform.portfolio.savings.domain.interest;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType;
public class QuarterlyCompoundingPeriod implements CompoundingPeriod {
@SuppressWarnings("unused")
private final LocalDateInterval periodInterval;
private final List<EndOfDayBalance> endOfDayBalances;
public static QuarterlyCompoundingPeriod create(final LocalDateInterval periodInterval, final List<EndOfDayBalance> allEndOfDayBalances, final LocalDate upToInterestCalculationDate) {
final List<EndOfDayBalance> endOfDayBalancesWithinPeriod = endOfDayBalancesWithinPeriodInterval(periodInterval, allEndOfDayBalances, upToInterestCalculationDate);
return new QuarterlyCompoundingPeriod(periodInterval, endOfDayBalancesWithinPeriod);
}
@Override
public BigDecimal calculateInterest(final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal interestEarned = BigDecimal.ZERO;
for (final EndOfDayBalance balance : this.endOfDayBalances) {
final BigDecimal interestOnBalanceUnrounded = balance.calculateInterestOnBalance(BigDecimal.ZERO, interestRateAsFraction,
daysInYear);
interestEarned = interestEarned.add(interestOnBalanceUnrounded);
}
return interestEarned;
}
@Override
public BigDecimal calculateInterest(final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType,
final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestToCompound,
final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal interestEarned = BigDecimal.ZERO;
switch (interestCalculationType) {
case DAILY_BALANCE:
interestEarned = calculateUsingDailyBalanceMethod(compoundingInterestPeriodType, interestToCompound,
interestRateAsFraction, daysInYear);
break;
case AVERAGE_DAILY_BALANCE:
interestEarned = calculateUsingAverageDailyBalanceMethod(interestToCompound, interestRateAsFraction, daysInYear);
break;
case INVALID:
break;
}
return interestEarned;
}
private BigDecimal calculateUsingAverageDailyBalanceMethod(final BigDecimal interestToCompound,
final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal cumulativeBalance = BigDecimal.ZERO;
Integer numberOfDays = Integer.valueOf(0);
for (final EndOfDayBalance balance : this.endOfDayBalances) {
final BigDecimal endOfDayCumulativeBalance = balance.cumulativeBalance(interestToCompound);
cumulativeBalance = cumulativeBalance.add(endOfDayCumulativeBalance);
final Integer balanceExistsForNumberOfDays = balance.getNumberOfDays();
numberOfDays = numberOfDays + balanceExistsForNumberOfDays;
}
BigDecimal interestEarned = BigDecimal.ZERO;
if (cumulativeBalance.compareTo(BigDecimal.ZERO) != 0 && numberOfDays > 0) {
final BigDecimal averageDailyBalance = cumulativeBalance.divide(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64)
.setScale(9, RoundingMode.HALF_EVEN);
final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64);
final BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64);
final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64);
interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, RoundingMode.HALF_EVEN);
}
return interestEarned;
}
private BigDecimal calculateUsingDailyBalanceMethod(final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType,
final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal interestEarned = BigDecimal.ZERO;
BigDecimal interestOnBalanceUnrounded = BigDecimal.ZERO;
for (final EndOfDayBalance balance : this.endOfDayBalances) {
switch (compoundingInterestPeriodType) {
case DAILY:
interestOnBalanceUnrounded = balance.calculateInterestOnBalanceAndInterest(interestToCompound, interestRateAsFraction,
daysInYear);
break;
case MONTHLY:
interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear);
break;
case QUATERLY:
interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear);
break;
// case WEEKLY:
// break;
// case BIWEEKLY:
// break;
case BI_ANNUAL:
interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear);
break;
case ANNUAL:
interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear);
break;
// case NO_COMPOUNDING_SIMPLE_INTEREST:
// break;
case INVALID:
break;
}
interestEarned = interestEarned.add(interestOnBalanceUnrounded);
}
return interestEarned;
}
private static List<EndOfDayBalance> endOfDayBalancesWithinPeriodInterval(final LocalDateInterval compoundingPeriodInterval,
final List<EndOfDayBalance> allEndOfDayBalances, final LocalDate upToInterestCalculationDate) {
final List<EndOfDayBalance> endOfDayBalancesForPeriodInterval = new ArrayList<EndOfDayBalance>();
for (final EndOfDayBalance endOfDayBalance : allEndOfDayBalances) {
if (compoundingPeriodInterval.contains(endOfDayBalance.date())) {
final EndOfDayBalance cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval, upToInterestCalculationDate);
endOfDayBalancesForPeriodInterval.add(cappedToPeriodEndDate);
} else if (endOfDayBalance.contains(compoundingPeriodInterval)) {
final EndOfDayBalance cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval, upToInterestCalculationDate);
endOfDayBalancesForPeriodInterval.add(cappedToPeriodEndDate);
}
}
return endOfDayBalancesForPeriodInterval;
}
private QuarterlyCompoundingPeriod(final LocalDateInterval periodInterval, final List<EndOfDayBalance> endOfDayBalances) {
this.periodInterval = periodInterval;
this.endOfDayBalances = endOfDayBalances;
}
}
@@ -783,6 +783,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
sqlBuilder.append("datp.in_multiples_of_deposit_term_type_enum as inMultiplesOfDepositTermTypeId, ");
sqlBuilder.append("datp.deposit_amount as depositAmount, ");
sqlBuilder.append("datp.maturity_amount as maturityAmount, ");
sqlBuilder.append("datp.expected_firstdepositon_date as expectedFirstDepositOnDate, ");
sqlBuilder.append("datp.maturity_date as maturityDate, ");
sqlBuilder.append("datp.deposit_period as depositPeriod, ");
sqlBuilder.append("datp.deposit_period_frequency_enum as depositPeriodFrequencyTypeId, ");
@@ -847,12 +848,13 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
final Integer onAccountClosureId = JdbcSupport.getInteger(rs, "onAccountClosureId");
final EnumOptionData onAccountClosureType = (onAccountClosureId == null) ? null : SavingsEnumerations
.depositAccountOnClosureType(onAccountClosureId);
final LocalDate expectedFirstDepositOnDate = JdbcSupport.getLocalDate(rs, "expectedFirstDepositOnDate");
return RecurringDepositAccountData.instance(depositAccountData, interestFreePeriodApplicable, interestFreeFromPeriod,
interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest,
preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType,
inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod,
depositPeriodFrequencyType, recurringDepositAmount, recurringDepositType, recurringDepositFrequency,
recurringDepositFrequencyType, onAccountClosureType);
recurringDepositFrequencyType, onAccountClosureType, expectedFirstDepositOnDate);
}
}
@@ -1266,13 +1268,14 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
final Integer depositPeriod = null;
final EnumOptionData depositPeriodFrequencyType = null;
final BigDecimal recurringDepositAmount = null;
final LocalDate expectedFirstDepositOnDate = null;
final EnumOptionData onAccountClosureType = null;
return RecurringDepositAccountData.instance(depositAccountData, interestFreePeriodApplicable, interestFreeFromPeriod,
interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest,
preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType,
inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod,
depositPeriodFrequencyType, recurringDepositAmount, recurringDepositType, recurringDepositFrequency,
recurringDepositFrequencyType, onAccountClosureType);
recurringDepositFrequencyType, onAccountClosureType, expectedFirstDepositOnDate);
}
}
@@ -151,7 +151,7 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
((FixedDepositAccount) account).updateMaturityDateAndAmountBeforeAccountActivation(mc);
this.fixedDepositAccountRepository.saveAndFlush((FixedDepositAccount) account);
} else if (depositAccountType.isRecurringDeposit()) {
final LocalDate transactionStartDate = account.accountSubmittedOrActivationDate();
final LocalDate transactionStartDate = ((RecurringDepositAccount) account).depositStartDate();
((RecurringDepositAccount) account).updateMaturityDateAndAmount(mc, transactionStartDate);
this.recurringDepositAccountRepository.save((RecurringDepositAccount) account);
}
@@ -254,7 +254,7 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
if (depositAccountType.isFixedDeposit()) {
((FixedDepositAccount) account).updateMaturityDateAndAmountBeforeAccountActivation(mc);
} else if (depositAccountType.isRecurringDeposit()) {
final LocalDate transactionStartDate = account.accountSubmittedOrActivationDate();
final LocalDate transactionStartDate = ((RecurringDepositAccount) account).depositStartDate();
((RecurringDepositAccount) account).updateMaturityDateAndAmount(mc, transactionStartDate);
}
@@ -275,6 +275,9 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
sqlBuilder.append("dprd.recurring_deposit_type_enum as recurringDepositTypeId, ");
sqlBuilder.append("dprd.recurring_deposit_frequency as recurringDepositFrequency, ");
sqlBuilder.append("dprd.recurring_deposit_frequency_type_enum as recurringDepositFrequencyTypeId, ");
sqlBuilder.append("dptp.min_deposit_amount as minDepositAmount, ");
sqlBuilder.append("dptp.deposit_amount as depositAmount, ");
sqlBuilder.append("dptp.max_deposit_amount as maxDepositAmount, ");
sqlBuilder.append("dptp.min_deposit_term as minDepositTerm, ");
sqlBuilder.append("dptp.max_deposit_term as maxDepositTerm, ");
sqlBuilder.append("dptp.min_deposit_term_type_enum as minDepositTermTypeId, ");
@@ -315,6 +318,9 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
.recurringDepositType(recurringDepositTypeId);
final Integer recurringDepositFrequency = JdbcSupport.getInteger(rs, "recurringDepositFrequency");
final Integer recurringDepositFrequencyTypeId = JdbcSupport.getInteger(rs, "recurringDepositFrequencyTypeId");
final BigDecimal minDepositAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minDepositAmount");
final BigDecimal depositAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "depositAmount");
final BigDecimal maxDepositAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "maxDepositAmount");
final EnumOptionData recurringDepositFrequencyType = (recurringDepositFrequencyTypeId == null) ? null : SavingsEnumerations
.recurringDepositFrequencyType(recurringDepositFrequencyTypeId);
@@ -334,7 +340,8 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
return RecurringDepositProductData.instance(depositProductData, interestFreePeriodApplicable, interestFreeFromPeriod,
interestFreeToPeriod, interestFreePeriodFrequencyType, preClosurePenalApplicable, preClosurePenalInterest,
preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm,
inMultiplesOfDepositTermType, recurringDepositType, recurringDepositFrequency, recurringDepositFrequencyType);
inMultiplesOfDepositTermType, recurringDepositType, recurringDepositFrequency, recurringDepositFrequencyType,
minDepositAmount, depositAmount, maxDepositAmount);
}
}
@@ -51,12 +51,10 @@ public class SavingsDropdownReadPlatformServiceImpl implements SavingsDropdownRe
// //
// SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.BIWEEKLY),
// //
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.MONTHLY)
// SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.QUATERLY),
// //
// SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.BI_ANNUAL),
// //
// SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.ANNUAL),
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.MONTHLY),
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.QUATERLY),
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.BI_ANNUAL),
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.ANNUAL)
// //
// SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.NO_COMPOUNDING_SIMPLE_INTEREST)
// //
@@ -70,6 +68,7 @@ public class SavingsDropdownReadPlatformServiceImpl implements SavingsDropdownRe
final List<EnumOptionData> allowedOptions = Arrays.asList( //
SavingsEnumerations.interestPostingPeriodType(SavingsPostingInterestPeriodType.MONTHLY), //
SavingsEnumerations.interestPostingPeriodType(SavingsPostingInterestPeriodType.QUATERLY), //
SavingsEnumerations.interestPostingPeriodType(SavingsPostingInterestPeriodType.BIANNUAL), //
SavingsEnumerations.interestPostingPeriodType(SavingsPostingInterestPeriodType.ANNUAL) //
);
@@ -232,6 +232,10 @@ public class SavingsEnumerations {
optionData = new EnumOptionData(SavingsPostingInterestPeriodType.QUATERLY.getValue().longValue(), codePrefix
+ SavingsPostingInterestPeriodType.QUATERLY.getCode(), "Quarterly");
break;
case BIANNUAL:
optionData = new EnumOptionData(SavingsPostingInterestPeriodType.BIANNUAL.getValue().longValue(), codePrefix
+ SavingsPostingInterestPeriodType.BIANNUAL.getCode(), "BiAnnual");
break;
case ANNUAL:
optionData = new EnumOptionData(SavingsPostingInterestPeriodType.ANNUAL.getValue().longValue(), codePrefix
+ SavingsPostingInterestPeriodType.ANNUAL.getCode(), "Annually");
@@ -276,26 +280,18 @@ public class SavingsEnumerations {
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.MONTHLY.getValue().longValue(), codePrefix
+ SavingsCompoundingInterestPeriodType.MONTHLY.getCode(), "Monthly");
break;
// case QUATERLY:
// optionData = new
// EnumOptionData(SavingsCompoundingInterestPeriodType.QUATERLY.getValue().longValue(),
// codePrefix
// + SavingsCompoundingInterestPeriodType.QUATERLY.getCode(),
// "Quarterly");
// break;
// case BI_ANNUAL:
// optionData = new
// EnumOptionData(SavingsCompoundingInterestPeriodType.BI_ANNUAL.getValue().longValue(),
// codePrefix
// + SavingsCompoundingInterestPeriodType.BI_ANNUAL.getCode(),
// "Semi-Annual");
// break;
// case ANNUAL:
// optionData = new
// EnumOptionData(SavingsCompoundingInterestPeriodType.ANNUAL.getValue().longValue(),
// codePrefix
// + SavingsCompoundingInterestPeriodType.ANNUAL.getCode(), "Annually");
// break;
case QUATERLY:
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.QUATERLY.getValue().longValue(), codePrefix
+ SavingsCompoundingInterestPeriodType.QUATERLY.getCode(), "Quarterly");
break;
case BI_ANNUAL:
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.BI_ANNUAL.getValue().longValue(), codePrefix
+ SavingsCompoundingInterestPeriodType.BI_ANNUAL.getCode(), "Semi-Annual");
break;
case ANNUAL:
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.ANNUAL.getValue().longValue(), codePrefix
+ SavingsCompoundingInterestPeriodType.ANNUAL.getCode(), "Annually");
break;
// case NO_COMPOUNDING_SIMPLE_INTEREST:
// optionData = new
// EnumOptionData(SavingsCompoundingInterestPeriodType.NO_COMPOUNDING_SIMPLE_INTEREST.getValue().longValue(),